Skip to content

Commit 64181cb

Browse files
committed
AST: Accept @_weakLinked on import decls to force weak linkage of symbols from a module.
The effect of declaring an import `@_weakLinked` is to treat every declaration from the module as if it were declared with `@_weakLinked`. This is useful in environments where entire modules may not be present at runtime. Although it is already possible to instruct the linker to weakly link an entire dylib, a Swift attribute provides a way to declare intent in source code and also opens the door to diagnostics and other compiler behaviors that depend on knowing that all the module's symbols will be weakly linked. rdar://96098097
1 parent a8dbc5f commit 64181cb

File tree

15 files changed

+309
-11
lines changed

15 files changed

+309
-11
lines changed

include/swift/AST/Attr.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,8 @@ DECL_ATTR(_clangImporterSynthesizedType, ClangImporterSynthesizedType,
456456
74)
457457
SIMPLE_DECL_ATTR(_weakLinked, WeakLinked,
458458
OnNominalType | OnAssociatedType | OnFunc | OnAccessor | OnVar |
459-
OnSubscript | OnConstructor | OnEnumElement | OnExtension | UserInaccessible |
459+
OnSubscript | OnConstructor | OnEnumElement | OnExtension | OnImport |
460+
UserInaccessible |
460461
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
461462
75)
462463
SIMPLE_DECL_ATTR(frozen, Frozen,

include/swift/AST/FileUnit.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ class FileUnit : public DeclContext, public ASTAllocated<FileUnit> {
123123
const ModuleDecl *importedModule,
124124
SmallSetVector<Identifier, 4> &spiGroups) const {};
125125

126+
virtual bool importsModuleAsWeakLinked(const ModuleDecl *module) const {
127+
return false;
128+
}
129+
126130
virtual Optional<Fingerprint>
127131
loadFingerprint(const IterableDeclContext *IDC) const { return None; }
128132

include/swift/AST/Import.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ enum class ImportFlags {
8484
/// concurrency.
8585
Preconcurrency = 0x20,
8686

87+
/// The module's symbols are linked weakly.
88+
WeakLinked = 0x40,
89+
8790
/// Used for DenseMap.
8891
Reserved = 0x80
8992
};

include/swift/AST/Module.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,9 @@ class ModuleDecl
686686
// Is \p spiGroup accessible as an explicitly imported SPI from this module?
687687
bool isImportedAsSPI(Identifier spiGroup, const ModuleDecl *fromModule) const;
688688

689+
/// Is \p importedModule imported as \c @_weakLinked from this module?
690+
bool importsModuleAsWeakLinked(const ModuleDecl *importedModule) const;
691+
689692
/// \sa getImportedModules
690693
enum class ImportFilterKind {
691694
/// Include imports declared with `@_exported`.

include/swift/AST/SourceFile.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,9 @@ class SourceFile final : public FileUnit {
356356
const ModuleDecl *importedModule,
357357
llvm::SmallSetVector<Identifier, 4> &spiGroups) const override;
358358

359+
/// Is \p module imported as \c @_weakLinked by this file?
360+
bool importsModuleAsWeakLinked(const ModuleDecl *module) const override;
361+
359362
// Is \p targetDecl accessible as an explicitly imported SPI from this file?
360363
bool isImportedAsSPI(const ValueDecl *targetDecl) const;
361364

include/swift/SIL/SILFunction.h

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ enum IsDistributed_t {
6464
IsNotDistributed,
6565
IsDistributed,
6666
};
67+
enum IsWeakImported_t {
68+
IsNotWeakImported,
69+
IsWeakImportedByModule,
70+
IsAlwaysWeakImported,
71+
};
6772

6873
enum class PerformanceConstraints : uint8_t {
6974
None = 0,
@@ -317,9 +322,8 @@ class SILFunction
317322
/// would indicate.
318323
unsigned HasCReferences : 1;
319324

320-
/// Whether cross-module references to this function should always use
321-
/// weak linking.
322-
unsigned IsWeakImported : 1;
325+
/// Whether cross-module references to this function should use weak linking.
326+
unsigned IsWeakImported : 2;
323327

324328
/// Whether the implementation can be dynamically replaced.
325329
unsigned IsDynamicReplaceable : 1;
@@ -801,12 +805,18 @@ class SILFunction
801805

802806
/// Returns whether this function's symbol must always be weakly referenced
803807
/// across module boundaries.
804-
bool isAlwaysWeakImported() const { return IsWeakImported; }
808+
bool isAlwaysWeakImported() const {
809+
return IsWeakImported == IsWeakImported_t::IsAlwaysWeakImported;
810+
}
805811

806-
void setAlwaysWeakImported(bool value) {
807-
IsWeakImported = value;
812+
/// Returns whether this function's symbol was referenced by a module that
813+
/// imports the defining module \c @_weakLinked.
814+
bool isWeakImportedByModule() const {
815+
return IsWeakImported == IsWeakImported_t::IsWeakImportedByModule;
808816
}
809817

818+
void setIsWeakImported(IsWeakImported_t value) { IsWeakImported = value; }
819+
810820
bool isWeakImported() const;
811821

812822
/// Returns whether this function implementation can be dynamically replaced.

lib/AST/Decl.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,9 @@ bool Decl::isWeakImported(ModuleDecl *fromModule) const {
10021002
if (isAlwaysWeakImported())
10031003
return true;
10041004

1005+
if (fromModule->importsModuleAsWeakLinked(getModuleContext()))
1006+
return true;
1007+
10051008
auto availability = getAvailabilityForLinkage();
10061009
if (availability.isAlwaysAvailable())
10071010
return false;

lib/AST/Module.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2573,6 +2573,15 @@ bool SourceFile::isImportedAsSPI(const ValueDecl *targetDecl) const {
25732573
return false;
25742574
}
25752575

2576+
bool SourceFile::importsModuleAsWeakLinked(const ModuleDecl *module) const {
2577+
for (auto &import : *Imports) {
2578+
if (import.options.contains(ImportFlags::WeakLinked) &&
2579+
module == import.module.importedModule)
2580+
return true;
2581+
}
2582+
return false;
2583+
}
2584+
25762585
bool ModuleDecl::isImportedAsSPI(const SpecializeAttr *attr,
25772586
const ValueDecl *targetDecl) const {
25782587
auto targetModule = targetDecl->getModuleContext();
@@ -2598,6 +2607,15 @@ bool ModuleDecl::isImportedAsSPI(Identifier spiGroup,
25982607
return importedSPIGroups.count(spiGroup);
25992608
}
26002609

2610+
bool ModuleDecl::importsModuleAsWeakLinked(
2611+
const ModuleDecl *importedModule) const {
2612+
for (auto file : getFiles()) {
2613+
if (file->importsModuleAsWeakLinked(importedModule))
2614+
return true;
2615+
}
2616+
return false;
2617+
}
2618+
26012619
bool Decl::isSPI() const {
26022620
return !getSPIGroups().empty();
26032621
}

lib/SIL/IR/SILFunction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ bool SILFunction::isWeakImported() const {
471471
if (!isAvailableExternally())
472472
return false;
473473

474-
if (isAlwaysWeakImported())
474+
if (isAlwaysWeakImported() || isWeakImportedByModule())
475475
return true;
476476

477477
if (Availability.isAlwaysAvailable())

lib/SIL/IR/SILFunctionBuilder.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,14 @@ SILFunction *SILFunctionBuilder::getOrCreateFunction(
312312
F->setClangNodeOwner(decl);
313313

314314
F->setAvailabilityForLinkage(decl->getAvailabilityForLinkage());
315-
F->setAlwaysWeakImported(decl->isAlwaysWeakImported());
315+
316+
if (decl->isAlwaysWeakImported()) {
317+
F->setIsWeakImported(IsWeakImported_t::IsAlwaysWeakImported);
318+
} else if (decl->isWeakImported(mod.getSwiftModule())) {
319+
F->setIsWeakImported(IsWeakImported_t::IsWeakImportedByModule);
320+
} else {
321+
F->setIsWeakImported(IsWeakImported_t::IsNotWeakImported);
322+
}
316323

317324
if (auto *accessor = dyn_cast<AccessorDecl>(decl)) {
318325
auto *storage = accessor->getStorage();

lib/SIL/Parser/ParseSIL.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6483,7 +6483,9 @@ bool SILParserState::parseDeclSIL(Parser &P) {
64836483
if (!objCReplacementFor.empty())
64846484
FunctionState.F->setObjCReplacement(objCReplacementFor);
64856485
FunctionState.F->setSpecialPurpose(specialPurpose);
6486-
FunctionState.F->setAlwaysWeakImported(isWeakImported);
6486+
FunctionState.F->setIsWeakImported(
6487+
isWeakImported ? IsWeakImported_t::IsAlwaysWeakImported
6488+
: IsWeakImported_t::IsNotWeakImported);
64876489
FunctionState.F->setAvailabilityForLinkage(availability);
64886490
FunctionState.F->setWithoutActuallyEscapingThunk(
64896491
isWithoutActuallyEscapingThunk);

lib/Sema/ImportResolution.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,9 @@ UnboundImport::UnboundImport(ImportDecl *ID)
554554
import.options |= ImportFlags::Preconcurrency;
555555
import.preconcurrencyRange = attr->getRangeWithAt();
556556
}
557+
558+
if (auto attr = ID->getAttrs().getAttribute<WeakLinkedAttr>())
559+
import.options |= ImportFlags::WeakLinked;
557560
}
558561

559562
bool UnboundImport::checkNotTautological(const SourceFile &SF) {

lib/Serialization/DeserializeSIL.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,9 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn,
661661
fn->setEffectsKind(EffectsKind(effect));
662662
fn->setOptimizationMode(OptimizationMode(optimizationMode));
663663
fn->setPerfConstraints((PerformanceConstraints)perfConstr);
664-
fn->setAlwaysWeakImported(isWeakImported);
664+
fn->setIsWeakImported(isWeakImported
665+
? IsWeakImported_t::IsAlwaysWeakImported
666+
: IsWeakImported_t::IsNotWeakImported);
665667
fn->setClassSubclassScope(SubclassScope(subclassScope));
666668
fn->setHasCReferences(bool(hasCReferences));
667669

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
public func fn() {}
2+
3+
public var globalStored = 0
4+
5+
public var globalComputed: Int {
6+
get { return 1 }
7+
set {}
8+
}
9+
10+
public struct S {
11+
public func fn() {}
12+
13+
public var storedProp = 0
14+
15+
public var computedProp: Int {
16+
get { return 1 }
17+
set {}
18+
}
19+
20+
public init() {}
21+
22+
public subscript(_: Int) -> Int {
23+
get { return 1 }
24+
set {}
25+
}
26+
}
27+
28+
public enum E {
29+
case basic
30+
case assoc(Int)
31+
}
32+
33+
open class C {
34+
open func fn() {}
35+
36+
open var storedProp = 0
37+
38+
open var computedProp: Int {
39+
get { return 1 }
40+
set {}
41+
}
42+
43+
public init() {}
44+
45+
open subscript(_: Int) -> Int {
46+
get { return 1 }
47+
set {}
48+
}
49+
}
50+
51+
public protocol P {
52+
func fn()
53+
54+
var prop: Int { get set }
55+
56+
init()
57+
58+
subscript(_: Int) -> Int { get set }
59+
}
60+
61+
public struct GenericS<T> {}
62+
63+
public enum GenericE<T> {}
64+
65+
open class GenericC<T> {
66+
public init() {}
67+
}
68+
69+
public protocol OtherProtocol {}
70+
public struct ConcreteType: OtherProtocol {}
71+
72+
public protocol ProtocolWithAssoc {
73+
associatedtype T: OtherProtocol = ConcreteType
74+
func f()
75+
}
76+
77+
extension ProtocolWithAssoc {
78+
public func f() {}
79+
}
80+
81+
public protocol BaseP {}
82+
extension S: BaseP {}

0 commit comments

Comments
 (0)