Skip to content

Commit de9148e

Browse files
authored
Merge pull request #85868 from xymus/exportability-nle-split
Sema: Distinguish implicitly visible memory layouts from the typical `@frozen` ones
2 parents e60ca69 + e3571eb commit de9148e

13 files changed

+263
-160
lines changed

include/swift/AST/Decl.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ namespace swift {
8181
class DiagnosticEngine;
8282
class DynamicSelfType;
8383
class Type;
84+
enum class ExportedLevel;
8485
class Expr;
8586
struct ExternalSourceLocs;
8687
class CaptureListExpr;
@@ -6755,10 +6756,7 @@ class VarDecl : public AbstractStorageDecl {
67556756
///
67566757
/// From the standpoint of access control and exportability checking, this
67576758
/// var will behave as if it was public, even if it is internal or private.
6758-
///
6759-
/// If \p applyImplicit, consider implicitly exposed layouts as well.
6760-
/// This applies to non-resilient modules.
6761-
bool isLayoutExposedToClients(bool applyImplicit = false) const;
6759+
ExportedLevel isLayoutExposedToClients() const;
67626760

67636761
/// Is this a special debugger variable?
67646762
bool isDebuggerVar() const { return Bits.VarDecl.IsDebuggerVar; }

include/swift/AST/DeclExportabilityVisitor.h

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@
2424

2525
namespace swift {
2626

27+
/// How a decl is exported.
28+
enum class ExportedLevel {
29+
/// Not exported.
30+
None,
31+
32+
/// Exported implicitly for types in non-library-evolution mode not marked
33+
/// `@_implementationOnly`.
34+
ImplicitlyExported,
35+
36+
/// Explicitly marked as exported with public or `@frozen`.
37+
Exported
38+
};
39+
2740
/// This visitor determines whether a declaration is "exportable", meaning whether
2841
/// it can be referenced by other modules. For example, a function with a public
2942
/// access level or with the `@usableFromInline` attribute is exportable.
@@ -102,7 +115,7 @@ class DeclExportabilityVisitor
102115
}
103116

104117
bool visitVarDecl(const VarDecl *var) {
105-
if (var->isLayoutExposedToClients())
118+
if (var->isLayoutExposedToClients() == ExportedLevel::Exported)
106119
return true;
107120

108121
// Consider all lazy var storage as exportable.
@@ -183,13 +196,13 @@ class DeclExportabilityVisitor
183196
/// Check if a declaration is exported as part of a module's external interface.
184197
/// This includes public and @usableFromInline decls.
185198
/// FIXME: This is legacy that should be subsumed by `DeclExportabilityVisitor`
186-
bool isExported(const Decl *D);
199+
ExportedLevel isExported(const Decl *D);
187200

188201
/// A specialization of `isExported` for `ValueDecl`.
189-
bool isExported(const ValueDecl *VD);
202+
ExportedLevel isExported(const ValueDecl *VD);
190203

191204
/// A specialization of `isExported` for `ExtensionDecl`.
192-
bool isExported(const ExtensionDecl *ED);
205+
ExportedLevel isExported(const ExtensionDecl *ED);
193206

194207
/// Returns true if the extension declares any protocol conformances that
195208
/// require the extension to be exported.

include/swift/AST/DiagnosticsSema.def

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3840,14 +3840,18 @@ NOTE(enum_raw_value_incrementing_from_zero,none,
38403840
NOTE(construct_raw_representable_from_unwrapped_value,none,
38413841
"construct %0 from unwrapped %1 value", (Type, Type))
38423842

3843+
#define EXPORTABILITY_REASON_SELECT "select{" \
3844+
"here|as property wrapper here|" \
3845+
"as result builder here|" \
3846+
"in an extension with public or '@usableFromInline' members|" \
3847+
"in an extension with conditional conformances|" \
3848+
"in a public or '@usableFromInline' conformance|" \
3849+
"in an '@available' attribute here|" \
3850+
"in a property declaration marked public or in a '@frozen' or '@usableFromInline' context|" \
3851+
"in a property declaration member of a type not marked '@_implementationOnly'}"
3852+
38433853
ERROR(decl_from_hidden_module,none,
3844-
"cannot use %kind0 %select{here|as property wrapper here|"
3845-
"as result builder here|"
3846-
"in an extension with public or '@usableFromInline' members|"
3847-
"in an extension with conditional conformances|"
3848-
"in a public or '@usableFromInline' conformance|"
3849-
"in an '@available' attribute here|"
3850-
"in a property declaration marked public or in a '@frozen' or '@usableFromInline' context}1; "
3854+
"cannot use %kind0 %" EXPORTABILITY_REASON_SELECT "1; "
38513855
"%select{%2 has been imported as implementation-only|"
38523856
"it is an SPI imported from %2|"
38533857
"it is SPI|"
@@ -3859,14 +3863,7 @@ ERROR(decl_from_hidden_module,none,
38593863
"%0 is marked '@_implementationOnly'}3",
38603864
(const Decl *, unsigned, Identifier, unsigned))
38613865
ERROR(typealias_desugars_to_type_from_hidden_module,none,
3862-
"%0 aliases '%1.%2' and cannot be used %select{here|"
3863-
"as property wrapper here|"
3864-
"as result builder here|"
3865-
"in an extension with public or '@usableFromInline' members|"
3866-
"in an extension with conditional conformance|"
3867-
"in a public or '@usableFromInline' conformance|"
3868-
"<<ERROR>>|"
3869-
"in a property declaration marked public or in a '@frozen' or '@usableFromInline' context}3 "
3866+
"%0 aliases '%1.%2' and cannot be used %" EXPORTABILITY_REASON_SELECT "3 "
38703867
"because %select{%4 has been imported as implementation-only|"
38713868
"it is an SPI imported from %4|"
38723869
"<<ERROR>>|"
@@ -3878,12 +3875,7 @@ ERROR(typealias_desugars_to_type_from_hidden_module,none,
38783875
"%0 is marked '@_implementationOnly'}5",
38793876
(const TypeAliasDecl *, StringRef, StringRef, unsigned, Identifier, unsigned))
38803877
ERROR(conformance_from_implementation_only_module,none,
3881-
"cannot use conformance of %0 to %1 %select{here|as property wrapper here|"
3882-
"as result builder here|"
3883-
"in an extension with public or '@usableFromInline' members|"
3884-
"in an extension with conditional conformances|"
3885-
"<<ERROR>>|<<ERROR>>|"
3886-
"in a property declaration marked public or in a '@frozen' or '@usableFromInline' context}2; "
3878+
"cannot use conformance of %0 to %1 %" EXPORTABILITY_REASON_SELECT "2; "
38873879
"%select{%3 has been imported as implementation-only|"
38883880
"the conformance is declared as SPI in %3|"
38893881
"the conformance is declared as SPI|"

lib/AST/Availability.cpp

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -972,7 +972,7 @@ bool ASTContext::supportsVersionedAvailability() const {
972972
return minimumAvailableOSVersionForTriple(LangOpts.Target).has_value();
973973
}
974974

975-
bool swift::isExported(const Decl *D) {
975+
ExportedLevel swift::isExported(const Decl *D) {
976976
if (auto *VD = dyn_cast<ValueDecl>(D)) {
977977
return isExported(VD);
978978
}
@@ -982,32 +982,35 @@ bool swift::isExported(const Decl *D) {
982982
return isExported(VD);
983983
}
984984

985-
return false;
985+
return ExportedLevel::None;
986986
}
987987
if (auto *ED = dyn_cast<ExtensionDecl>(D)) {
988988
return isExported(ED);
989989
}
990990

991-
return true;
991+
return ExportedLevel::Exported;
992992
}
993993

994-
bool swift::isExported(const ValueDecl *VD) {
994+
ExportedLevel swift::isExported(const ValueDecl *VD) {
995995
if (VD->getAttrs().hasAttribute<ImplementationOnlyAttr>())
996-
return false;
996+
return ExportedLevel::None;
997997
if (VD->isObjCMemberImplementation())
998-
return false;
998+
return ExportedLevel::None;
999999

10001000
// Is this part of the module's API or ABI?
10011001
AccessScope accessScope =
10021002
VD->getFormalAccessScope(nullptr,
10031003
/*treatUsableFromInlineAsPublic*/ true);
10041004
if (accessScope.isPublic())
1005-
return true;
1005+
return ExportedLevel::Exported;
10061006

10071007
// Is this a stored property in a @frozen struct or class?
10081008
if (auto *property = dyn_cast<VarDecl>(VD))
1009-
if (property->isLayoutExposedToClients(/*applyImplicit=*/true))
1010-
return true;
1009+
return property->isLayoutExposedToClients();
1010+
1011+
// Case of an enum not marked @_implementationOnly in a non-resilient module?
1012+
if (auto *EED = dyn_cast<EnumElementDecl>(VD))
1013+
return isExported(EED->getParentEnum());
10111014

10121015
// Is this a type exposed by default in a non-resilient module?
10131016
if (isa<NominalTypeDecl>(VD) &&
@@ -1016,13 +1019,9 @@ bool swift::isExported(const ValueDecl *VD) {
10161019
VD->getDeclContext()->getParentModule()->getResilienceStrategy() !=
10171020
ResilienceStrategy::Resilient &&
10181021
!VD->getAttrs().hasAttribute<ImplementationOnlyAttr>())
1019-
return true;
1020-
1021-
// Case of an enum not marked @_implementationOnly in a non-resilient module?
1022-
if (auto *EED = dyn_cast<EnumElementDecl>(VD))
1023-
return isExported(EED->getParentEnum());
1022+
return ExportedLevel::ImplicitlyExported;
10241023

1025-
return false;
1024+
return ExportedLevel::None;
10261025
}
10271026

10281027
bool swift::hasConformancesToPublicProtocols(const ExtensionDecl *ED) {
@@ -1046,23 +1045,22 @@ bool swift::hasConformancesToPublicProtocols(const ExtensionDecl *ED) {
10461045
return false;
10471046
}
10481047

1049-
bool swift::isExported(const ExtensionDecl *ED) {
1048+
ExportedLevel swift::isExported(const ExtensionDecl *ED) {
10501049
// An extension can only be exported if it extends an exported type.
10511050
if (auto *NTD = ED->getExtendedNominal()) {
1052-
if (!isExported(NTD))
1053-
return false;
1054-
}
1055-
1056-
// If there are any exported members then the extension is exported.
1057-
for (const Decl *D : ED->getMembers()) {
1058-
if (isExported(D))
1059-
return true;
1051+
if (isExported(NTD) == ExportedLevel::None)
1052+
return ExportedLevel::None;
10601053
}
10611054

10621055
// If the extension declares a conformance to a public protocol then the
10631056
// extension is exported.
10641057
if (hasConformancesToPublicProtocols(ED))
1065-
return true;
1058+
return ExportedLevel::Exported;
10661059

1067-
return false;
1060+
// If there are any exported members then the extension is exported.
1061+
ExportedLevel exported = ExportedLevel::None;
1062+
for (const Decl *D : ED->getMembers())
1063+
exported = std::max(exported, isExported(D));
1064+
1065+
return exported;
10681066
}

lib/AST/AvailabilityScopeBuilder.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,7 @@ class AvailabilityScopeBuilder : private ASTWalker {
479479
if (decl->isSPI())
480480
return true;
481481

482-
return !isExported(decl);
482+
return isExported(decl) == ExportedLevel::None;
483483
}
484484

485485
/// Returns the source range which should be refined by declaration. This

lib/AST/Decl.cpp

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2773,44 +2773,44 @@ bool VarDecl::isInitExposedToClients() const {
27732773
if (getAttrs().hasAttribute<LazyAttr>())
27742774
return false;
27752775

2776-
return hasInitialValue() && isLayoutExposedToClients();
2776+
return hasInitialValue() &&
2777+
isLayoutExposedToClients() == ExportedLevel::Exported;
27772778
}
27782779

2779-
bool VarDecl::isLayoutExposedToClients(bool applyImplicit) const {
2780+
ExportedLevel VarDecl::isLayoutExposedToClients() const {
27802781
auto parent = dyn_cast<NominalTypeDecl>(getDeclContext());
2781-
if (!parent) return false;
2782-
if (isStatic()) return false;
2782+
if (!parent) return ExportedLevel::None;
2783+
if (isStatic()) return ExportedLevel::None;
27832784

2784-
auto M = getDeclContext()->getParentModule();
2785+
// Does it contribute to the layout?
2786+
if (!hasStorage() &&
2787+
!getAttrs().hasAttribute<LazyAttr>() &&
2788+
!hasAttachedPropertyWrapper()) {
2789+
return ExportedLevel::None;
2790+
}
2791+
2792+
// Is it a member of a frozen type?
27852793
auto nominalAccess =
27862794
parent->getFormalAccessScope(/*useDC=*/nullptr,
27872795
/*treatUsableFromInlineAsPublic=*/true);
2796+
if (nominalAccess.isPublic() &&
2797+
(parent->getAttrs().hasAttribute<FrozenAttr>() ||
2798+
parent->getAttrs().hasAttribute<FixedLayoutAttr>()))
2799+
return ExportedLevel::Exported;
27882800

2789-
// Resilient modules and classes hide layouts by default.
2790-
bool layoutIsHiddenByDefault = !applyImplicit ||
2791-
!getASTContext().LangOpts.hasFeature(Feature::CheckImplementationOnly) ||
2792-
M->getResilienceStrategy() == ResilienceStrategy::Resilient;
2793-
if (layoutIsHiddenByDefault) {
2794-
if (!nominalAccess.isPublic())
2795-
return false;
2801+
// Is it a member of an `@_implementationOnly` type?
2802+
if (parent->getAttrs().hasAttribute<ImplementationOnlyAttr>())
2803+
return ExportedLevel::None;
27962804

2797-
if (!parent->getAttrs().hasAttribute<FrozenAttr>() &&
2798-
!parent->getAttrs().hasAttribute<FixedLayoutAttr>())
2799-
return false;
2805+
auto M = getDeclContext()->getParentModule();
2806+
if (getASTContext().LangOpts.hasFeature(Feature::CheckImplementationOnly) &&
2807+
M->getResilienceStrategy() != ResilienceStrategy::Resilient) {
2808+
// Non-resilient module expose layouts by default.
2809+
return ExportedLevel::ImplicitlyExported;
28002810
} else {
2801-
// Non-resilient module: layouts are exposed by default unless marked
2802-
// otherwise.
2803-
if (parent->getAttrs().hasAttribute<ImplementationOnlyAttr>())
2804-
return false;
2811+
// Resilient modules hide layouts by default.
2812+
return ExportedLevel::None;
28052813
}
2806-
2807-
if (!hasStorage() &&
2808-
!getAttrs().hasAttribute<LazyAttr>() &&
2809-
!hasAttachedPropertyWrapper()) {
2810-
return false;
2811-
}
2812-
2813-
return true;
28142814
}
28152815

28162816
/// Check whether the given type representation will be

lib/Sema/ResilienceDiagnostics.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ static bool shouldDiagnoseDeclAccess(const ValueDecl *D,
261261
case ExportabilityReason::ResultBuilder:
262262
case ExportabilityReason::PropertyWrapper:
263263
case ExportabilityReason::PublicVarDecl:
264+
case ExportabilityReason::ImplicitlyPublicVarDecl:
264265
return false;
265266
}
266267
}
@@ -294,6 +295,9 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D,
294295
}
295296
});
296297

298+
if (where.canReferenceOrigin(originKind))
299+
return false;
300+
297301
auto fragileKind = where.getFragileFunctionKind();
298302
switch (originKind) {
299303
case DisallowedOriginKind::None:
@@ -326,14 +330,10 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D,
326330
if (reason && reason == ExportabilityReason::AvailableAttribute &&
327331
ctx.LangOpts.LibraryLevel == LibraryLevel::API)
328332
return false;
329-
LLVM_FALLTHROUGH;
333+
break;
330334

331335
case DisallowedOriginKind::SPIImported:
332336
case DisallowedOriginKind::SPILocal:
333-
if (fragileKind.kind == FragileFunctionKind::EmbeddedAlwaysEmitIntoClient)
334-
return false;
335-
break;
336-
337337
case DisallowedOriginKind::ImplementationOnly:
338338
case DisallowedOriginKind::FragileCxxAPI:
339339
case DisallowedOriginKind::ImplementationOnlyMemoryLayout:

lib/Sema/TypeCheckAccess.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,7 +1498,7 @@ class UsableFromInlineChecker : public AccessControlCheckerBase,
14981498
/// the struct instead of the VarDecl, and customize the diagnostics.
14991499
static const ValueDecl *
15001500
getFixedLayoutStructContext(const VarDecl *VD) {
1501-
if (VD->isLayoutExposedToClients())
1501+
if (VD->isLayoutExposedToClients() == ExportedLevel::Exported)
15021502
return dyn_cast<NominalTypeDecl>(VD->getDeclContext());
15031503

15041504
return nullptr;
@@ -2383,8 +2383,12 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
23832383
if (seenVars.count(theVar))
23842384
return;
23852385

2386+
ExportabilityReason reason =
2387+
Where.getExportedLevel() == ExportedLevel::ImplicitlyExported ?
2388+
ExportabilityReason::ImplicitlyPublicVarDecl :
2389+
ExportabilityReason::PublicVarDecl;
23862390
checkType(theVar->getValueInterfaceType(), /*typeRepr*/nullptr, theVar,
2387-
ExportabilityReason::PublicVarDecl);
2391+
reason);
23882392

23892393
for (auto attr : theVar->getAttachedPropertyWrappers()) {
23902394
checkType(attr->getType(), attr->getTypeRepr(), theVar,
@@ -2405,9 +2409,13 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
24052409
anyVar = V;
24062410
});
24072411

2412+
ExportabilityReason reason =
2413+
Where.getExportedLevel() == ExportedLevel::ImplicitlyExported ?
2414+
ExportabilityReason::ImplicitlyPublicVarDecl :
2415+
ExportabilityReason::PublicVarDecl;
24082416
checkType(TP->hasType() ? TP->getType() : Type(),
24092417
TP->getTypeRepr(), anyVar ? (Decl *)anyVar : (Decl *)PBD,
2410-
ExportabilityReason::PublicVarDecl);
2418+
reason);
24112419

24122420
// Check the property wrapper types.
24132421
if (anyVar) {
@@ -2603,7 +2611,7 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
26032611
auto *valueMember = dyn_cast<ValueDecl>(member);
26042612
if (!valueMember)
26052613
return false;
2606-
return isExported(valueMember);
2614+
return isExported(valueMember) == ExportedLevel::Exported;
26072615
});
26082616

26092617
Where = wasWhere.withExported(hasExportedMembers);

0 commit comments

Comments
 (0)