diff --git a/include/swift/AST/ProtocolConformance.h b/include/swift/AST/ProtocolConformance.h index 3284519033c..07ba9d119bf 100644 --- a/include/swift/AST/ProtocolConformance.h +++ b/include/swift/AST/ProtocolConformance.h @@ -817,6 +817,12 @@ public: std::optional getExplicitCodeGenerationModel() const; + /// Compute the code generation model for the conformance, combining the + /// explicitly-specified information from attributes with defaults + /// based on Embedded Swift or feature flags. + CodeGenerationModel + getEffectiveCodeGenerationModel() const; + /// Whether this conformance represents the conformance of one protocol's /// conforming types to another protocol. /// diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 851bb665bb6..9d461e0c570 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2369,6 +2369,37 @@ Decl::getExplicitCodeGenerationModel() const { return std::nullopt; } +/// Determine the code generation model that is required by the given +/// declaration. +/// +/// This accounts for limitations of the code generation model. For example, +/// a generic declaration can only be treated as @export(implementation) in +/// Embedded Swift, because there are no unspecialized generics. +static std::optional +getRequiredCodeGenerationModel(const Decl *decl) { + bool isEmbedded = decl->getASTContext().LangOpts.hasFeature(Feature::Embedded); + + // A generic declaration must be @export(implementation) in Embedded Swift. + auto dc = decl->getInnermostDeclContext(); + if (auto sig = dc->getGenericSignatureOfContext()) { + if (!sig->areAllParamsConcrete() && isEmbedded) + return CodeGenerationModel::Implementation; + } + + // Foreign types are always @export(implementation). + if (auto nominal = dyn_cast(decl)) { + if (isa(nominal->getModuleScopeContext())) + return CodeGenerationModel::Implementation; + } + + // Types must be @export(interface) in non-Embedded Swift, because the type + // metadata symbols need to be unique. + if (isa(decl) && !isEmbedded) + return CodeGenerationModel::Interface; + + return std::nullopt; +} + CodeGenerationModel Decl::getEffectiveCodeGenerationModel() const { // If there is an explicit attribute that specifies the model for this @@ -2376,7 +2407,11 @@ Decl::getEffectiveCodeGenerationModel() const { if (auto explicitModel = getExplicitCodeGenerationModel()) return *explicitModel; - // Otherwise, apply the model-level defaults. + // If there is a required code generation model, return that. + if (auto required = getRequiredCodeGenerationModel(this)) + return *required; + + // Otherwise, apply the module-level default. return getModuleContext()->codeGenerationModel(); } diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index 75b66a168eb..ed96f6d5f6a 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -32,6 +32,7 @@ #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/Types.h" #include "swift/Basic/Assertions.h" +#include "swift/Basic/CodeGenerationModel.h" #include "swift/Basic/Statistic.h" #include "swift/ClangImporter/ClangImporter.h" #include "llvm/ADT/Statistic.h" @@ -432,6 +433,43 @@ NormalProtocolConformance::getExplicitCodeGenerationModel() const { return std::nullopt; } +static std::optional +getRequiredCodeGenerationModel(const NormalProtocolConformance *conformance) { + auto dc = conformance->getDeclContext(); + bool isEmbedded = dc->getASTContext().LangOpts.hasFeature(Feature::Embedded); + + // A conformance in a generic context must be @export(implementation) in + // Embedded Swift. + if (auto sig = dc->getGenericSignatureOfContext()) { + if (!sig->areAllParamsConcrete() && isEmbedded) + return CodeGenerationModel::Implementation; + } + + // Synthesized conformances are always @export(implementation). + if (conformance->isSynthesized()) + return CodeGenerationModel::Implementation; + + // Other onformances must be @export(interface) in non-Embedded Swift, + // because the witness table symbols must be unique. + if (!isEmbedded) { + return CodeGenerationModel::Interface; + } + + return std::nullopt; +} + +CodeGenerationModel +NormalProtocolConformance::getEffectiveCodeGenerationModel() const { + if (auto explicitModel = getExplicitCodeGenerationModel()) + return *explicitModel; + + if (auto required = getRequiredCodeGenerationModel(this)) + return *required; + + // Otherwise, apply the module-level default. + return getDeclContext()->getParentModule()->codeGenerationModel(); +} + bool NormalProtocolConformance::isConformanceOfProtocol() const { return getDeclContext()->getSelfProtocolDecl() != nullptr; } diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 4a29ff60dac..a60b2a79fb3 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -1067,8 +1067,8 @@ void IRGenModule::emitClassDecl(ClassDecl *D) { emitClassMetadata(*this, D, fragileLayout, resilientLayout); emitFieldDescriptor(D); } else { - bool isExportInterface = !D->isGenericContext() && - D->getExplicitCodeGenerationModel() == CodeGenerationModel::Interface; + bool isExportInterface = + D->getEffectiveCodeGenerationModel() == CodeGenerationModel::Interface; if (isExportInterface) { emitEmbeddedClassMetadata(*this, D); } else { diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index ebeda9079ed..2a4ae5cc8f4 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -1591,16 +1591,12 @@ bool IRGenerator::hasLazyMetadata(TypeDecl *type) { if (langOpts.hasFeature(Feature::Embedded) && (isa(type) || isa(type))) { auto *nominal = cast(type); - bool isGeneric = nominal->isGenericContext(); // @export(interface) types have a unique definition in their defining // module; importing modules reference them as external symbols rather than // lazily emitting their own copy. - bool isExportInterface = false; - if (!isGeneric) { - if (auto model = nominal->getExplicitCodeGenerationModel()) - isExportInterface = *model == CodeGenerationModel::Interface; - } - bool isLazy = !isGeneric && !isExportInterface; + bool isExportInterface = + nominal->getEffectiveCodeGenerationModel() == CodeGenerationModel::Interface; + bool isLazy = !nominal->isGenericContext() && !isExportInterface; HasLazyMetadata[type] = isLazy; return isLazy; } diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index ca9f77aa803..0b9c695ef39 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -5727,9 +5727,9 @@ void irgen::emitLazyClassMetadata(IRGenModule &IGM, CanType classTy) { // lazily emitting their own copy. if (IGM.isEmbeddedWithExistentials()) { if (auto *classDecl = classTy->getClassOrBoundGenericClass()) { - if (auto model = classDecl->getExplicitCodeGenerationModel()) - if (*model == CodeGenerationModel::Interface) - return; + if (classDecl->getEffectiveCodeGenerationModel() + == CodeGenerationModel::Interface) + return; } } diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index bbb2edec9fc..89d0db606c7 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -1443,10 +1443,16 @@ bool IRGenerator::canEmitWitnessTableLazily(SILWitnessTable *wt) { // @export(interface) conformances, which have a unique strong definition // in the owning module. if (SIL.getASTContext().LangOpts.hasFeature(Feature::Embedded)) { - if (auto *normal = dyn_cast(wt->getConformance())) - if (auto model = normal->getExplicitCodeGenerationModel()) - if (*model == CodeGenerationModel::Interface) + if (auto *normal = dyn_cast(wt->getConformance())) { + switch (normal->getEffectiveCodeGenerationModel()) { + case CodeGenerationModel::Interface: return false; + case CodeGenerationModel::Implementation: + case CodeGenerationModel::Inlinable: + return true; + } + } + return true; } diff --git a/lib/IRGen/Linking.cpp b/lib/IRGen/Linking.cpp index dcc120cb877..381eae24445 100644 --- a/lib/IRGen/Linking.cpp +++ b/lib/IRGen/Linking.cpp @@ -718,14 +718,16 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const { if (isForcedShared()) return SILLinkage::Shared; - // In embedded existenitals mode we generate lazy public metadata on demand - // which makes it non unique. - if (isLazyEmissionOfPublicSymbolInMultipleModulesPossible(getType())) - return SILLinkage::Shared; - auto *nominal = getType().getAnyNominal(); switch (getMetadataAddress()) { case TypeMetadataAddress::FullMetadata: { + // In embedded existentials mode we generate lazy public metadata on + // demand which makes the full metadata non-unique. (The address-point + // alias still uses the formal declaration linkage so that it survives + // GlobalDCE under -internalize-at-link.) + if (isLazyEmissionOfPublicSymbolInMultipleModulesPossible(getType())) + return SILLinkage::Shared; + // For imported types, the full metadata object is a candidate // for uniquing. if (getDeclLinkage(nominal) == FormalLinkage::PublicNonUnique) @@ -740,11 +742,10 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const { // module, so the FullMetadata must be externally linkable. bool isEmbedded = nominal->getASTContext().LangOpts.hasFeature(Feature::Embedded); - if (isEmbedded && !nominal->isGenericContext()) { - if (auto model = nominal->getExplicitCodeGenerationModel()) { - if (*model == CodeGenerationModel::Interface) { - return getSILLinkage(FormalLinkage::PublicUnique, forDefinition); - } + if (isEmbedded) { + if (nominal->getEffectiveCodeGenerationModel() + == CodeGenerationModel::Interface) { + return getSILLinkage(FormalLinkage::PublicUnique, forDefinition); } } @@ -1816,6 +1817,13 @@ bool LinkEntity::hasNonUniqueDefinition() const { if (getKind() == Kind::TypeMetadata || getKind() == Kind::ValueWitnessTable) { + // The address-point alias of type metadata is uniquely defined per + // binary even when the full metadata it references is shared, so it + // gets the formal declaration linkage rather than linkonce_odr. + if (getKind() == Kind::TypeMetadata && + getMetadataAddress() == TypeMetadataAddress::AddressPoint) + return false; + // For a nominal type, check its declaration. CanType type = getType(); if (auto nominal = type->getAnyNominal()) { @@ -1835,10 +1843,16 @@ bool LinkEntity::hasNonUniqueDefinition() const { Feature::Embedded)) { if (auto *normal = dyn_cast( getProtocolConformance()->getRootConformance())) { - if (auto model = normal->getExplicitCodeGenerationModel()) - if (*model == CodeGenerationModel::Interface) + switch (normal->getEffectiveCodeGenerationModel()) { + case CodeGenerationModel::Interface: return false; + + case CodeGenerationModel::Implementation: + case CodeGenerationModel::Inlinable: + return true; + } } + return true; } } diff --git a/lib/SIL/IR/SIL.cpp b/lib/SIL/IR/SIL.cpp index c7a11ee31b0..094fcee32bd 100644 --- a/lib/SIL/IR/SIL.cpp +++ b/lib/SIL/IR/SIL.cpp @@ -104,9 +104,14 @@ swift::getLinkageForProtocolConformance(const ProtocolConformance *C, // externally. if (auto *normal = dyn_cast( C->getRootConformance())) { - if (auto model = normal->getExplicitCodeGenerationModel()) - if (*model == CodeGenerationModel::Interface) + switch (normal->getEffectiveCodeGenerationModel()) { + case CodeGenerationModel::Interface: return (definition ? SILLinkage::Public : SILLinkage::PublicExternal); + + case CodeGenerationModel::Implementation: + case CodeGenerationModel::Inlinable: + return SILLinkage::Shared; + } } // Other embedded conformances are emitted lazily with shared linkage, diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index fbb566d683f..5d200d8bdc0 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -1250,14 +1250,20 @@ bool SILDeclRef::declHasNonUniqueDefinition(const ValueDecl *decl) { auto module = decl->getModuleContext(); auto &ctx = module->getASTContext(); - /// With deferred code generation, declarations are emitted as late as - /// possible, so they must have non-unique definitions. - if (module->codeGenerationModel() == CodeGenerationModel::Implementation) + switch (decl->getEffectiveCodeGenerationModel()) { + case CodeGenerationModel::Implementation: + /// When deferring all code generation, declarations are emitted as late + /// as possible, so they must have non-unique definitions. return true; - // If the declaration is not from the main module, treat its definition as - // non-unique. - return module != ctx.MainModule && ctx.MainModule; + case CodeGenerationModel::Inlinable: + // If the declaration is not from the main module, treat its definition as + // non-unique. + return module != ctx.MainModule && ctx.MainModule; + + case CodeGenerationModel::Interface: + return false; + } } bool SILDeclRef::isForeignToNativeThunk() const { diff --git a/lib/SIL/IR/SILFunctionBuilder.cpp b/lib/SIL/IR/SILFunctionBuilder.cpp index bf5fa7f3c1a..3a83ac2e693 100644 --- a/lib/SIL/IR/SILFunctionBuilder.cpp +++ b/lib/SIL/IR/SILFunctionBuilder.cpp @@ -391,7 +391,10 @@ SILFunction *SILFunctionBuilder::getOrCreateFunction( F->setAvailabilityForLinkage(*availability); F->setIsAlwaysWeakImported(decl->isAlwaysWeakImported()); - if (auto cgModel = decl->getExplicitCodeGenerationModel()) { + auto cgModel = decl->getExplicitCodeGenerationModel(); + if (!cgModel && mod.getOptions().EmbeddedSwift) + cgModel = decl->getEffectiveCodeGenerationModel(); + if (cgModel) { switch (*cgModel) { case CodeGenerationModel::Interface: case CodeGenerationModel::Implementation: diff --git a/test/embedded/linkage/leaf_application.swift b/test/embedded/linkage/leaf_application.swift index 54a720af7fd..7271d404dea 100644 --- a/test/embedded/linkage/leaf_application.swift +++ b/test/embedded/linkage/leaf_application.swift @@ -92,8 +92,8 @@ public func createsExistential() -> any Reflectable { // LIBRARY-IR: define linkonce_odr hidden void @_swift_dead_method_stub -// LIBRARY-SIL: sil @$e7Library5helloSaySiGyF -// LIBRARY-SIL: sil @$e7Library8getArraySaySiGyF : $@convention(thin) () -> @owned Array { +// LIBRARY-SIL: sil [export_implementation] @$e7Library5helloSaySiGyF +// LIBRARY-SIL: sil [export_implementation] @$e7Library8getArraySaySiGyF : $@convention(thin) () -> @owned Array { //--- Application.swift import Library @@ -105,10 +105,10 @@ public func testMe() { // APPLICATION-IR: define {{(protected |dllexport )?}}swiftcc void @"$e11Application6testMeyyF"() -// APPLICATION-SIL: sil public_external @$e7Library5helloSaySiGyF : $@convention(thin) () -> @owned Array { +// APPLICATION-SIL: sil public_external [export_implementation] @$e7Library5helloSaySiGyF : $@convention(thin) () -> @owned Array { // APPLICATION-IR: define linkonce_odr hidden swiftcc ptr @"$e7Library5helloSaySiGyF"() -// APPLICATION-SIL: sil public_external @$e7Library8getArraySaySiGyF : $@convention(thin) () -> @owned Array { +// APPLICATION-SIL: sil public_external [export_implementation] @$e7Library8getArraySaySiGyF : $@convention(thin) () -> @owned Array { // APPLICATION-IR: define linkonce_odr hidden swiftcc ptr @"$e7Library8getArraySaySiGyF"() // APPLICATION-IR: define {{(protected |dllexport )?}}i32 @Application_main diff --git a/test/embedded/serialization.swift b/test/embedded/serialization.swift index ac0160295d0..d6ef1fd5500 100644 --- a/test/embedded/serialization.swift +++ b/test/embedded/serialization.swift @@ -16,7 +16,7 @@ func internalFunc() { } -// LIBRARY-SIL: sil [asmname "swift_dosomething"] @$e7Library17swift_dosomethingyyFTo : $@convention(c) () -> () { +// LIBRARY-SIL: sil [export_implementation] [asmname "swift_dosomething"] @$e7Library17swift_dosomethingyyFTo : $@convention(c) () -> () { @c public func swift_dosomething() { internalFunc()