Skip to content

Commit bc5f13c

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 9151db5 commit bc5f13c

15 files changed

+316
-11
lines changed

Diff for: include/swift/AST/Attr.def

+2-1
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,

Diff for: include/swift/AST/FileUnit.h

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

126+
/// Checks whether this file imports \c module as \c @_weakLinked.
127+
virtual bool importsModuleAsWeakLinked(const ModuleDecl *module) const {
128+
// For source files, this should be overridden to inspect the import
129+
// declarations in the file. Other kinds of file units, like serialized
130+
// modules, can just use this default implementation since the @_weakLinked
131+
// attribute is not transitive. If module C is imported @_weakLinked by
132+
// module B, that does not imply that module A imports module C @_weakLinked
133+
// if it imports module B.
134+
return false;
135+
}
136+
126137
virtual Optional<Fingerprint>
127138
loadFingerprint(const IterableDeclContext *IDC) const { return None; }
128139

Diff for: include/swift/AST/Import.h

+3
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
};

Diff for: include/swift/AST/Module.h

+3
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`.

Diff for: include/swift/AST/SourceFile.h

+3
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

Diff for: include/swift/SIL/SILFunction.h

+16-6
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;
@@ -797,12 +801,18 @@ class SILFunction
797801

798802
/// Returns whether this function's symbol must always be weakly referenced
799803
/// across module boundaries.
800-
bool isAlwaysWeakImported() const { return IsWeakImported; }
804+
bool isAlwaysWeakImported() const {
805+
return IsWeakImported == IsWeakImported_t::IsAlwaysWeakImported;
806+
}
801807

802-
void setAlwaysWeakImported(bool value) {
803-
IsWeakImported = value;
808+
/// Returns whether this function's symbol was referenced by a module that
809+
/// imports the defining module \c @_weakLinked.
810+
bool isWeakImportedByModule() const {
811+
return IsWeakImported == IsWeakImported_t::IsWeakImportedByModule;
804812
}
805813

814+
void setIsWeakImported(IsWeakImported_t value) { IsWeakImported = value; }
815+
806816
bool isWeakImported() const;
807817

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

Diff for: lib/AST/Decl.cpp

+3
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;

Diff for: lib/AST/Module.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -2574,6 +2574,15 @@ bool SourceFile::isImportedAsSPI(const ValueDecl *targetDecl) const {
25742574
return false;
25752575
}
25762576

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

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

Diff for: lib/SIL/IR/SILFunction.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ bool SILFunction::isWeakImported() const {
462462
if (!isAvailableExternally())
463463
return false;
464464

465-
if (isAlwaysWeakImported())
465+
if (isAlwaysWeakImported() || isWeakImportedByModule())
466466
return true;
467467

468468
if (Availability.isAlwaysAvailable())

Diff for: lib/SIL/IR/SILFunctionBuilder.cpp

+8-1
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();

Diff for: lib/SIL/Parser/ParseSIL.cpp

+3-1
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);

Diff for: lib/Sema/ImportResolution.cpp

+3
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) {

Diff for: lib/Serialization/DeserializeSIL.cpp

+3-1
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

Diff for: test/IRGen/Inputs/weaklinked_import_helper.swift

+82
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)