Skip to content

Commit 579a228

Browse files
authored
Merge pull request #85179 from xymus/exportability-nle-structs
Sema: Opt-in check for structs references to hidden dependencies in non-library-evolution mode
2 parents dad2637 + bf44341 commit 579a228

File tree

12 files changed

+295
-15
lines changed

12 files changed

+295
-15
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6727,7 +6727,10 @@ class VarDecl : public AbstractStorageDecl {
67276727
///
67286728
/// From the standpoint of access control and exportability checking, this
67296729
/// var will behave as if it was public, even if it is internal or private.
6730-
bool isLayoutExposedToClients() const;
6730+
///
6731+
/// If \p applyImplicit, consider implicitly exposed layouts as well.
6732+
/// This applies to non-resilient modules.
6733+
bool isLayoutExposedToClients(bool applyImplicit = false) const;
67316734

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

include/swift/AST/DeclAttr.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ SIMPLE_DECL_ATTR(_alwaysEmitIntoClient, AlwaysEmitIntoClient,
474474
83)
475475

476476
SIMPLE_DECL_ATTR(_implementationOnly, ImplementationOnly,
477-
OnImport | OnFunc | OnConstructor | OnVar | OnSubscript,
477+
OnImport | OnFunc | OnConstructor | OnVar | OnSubscript | OnStruct,
478478
UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr,
479479
84)
480480

include/swift/AST/DiagnosticsSema.def

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3847,7 +3847,8 @@ ERROR(decl_from_hidden_module,none,
38473847
"%2 was not imported by this file|"
38483848
"C++ types from imported module %2 do not support library evolution|"
38493849
"it was imported via the internal bridging header|"
3850-
"%2 was not imported publicly}3",
3850+
"%2 was not imported publicly|"
3851+
"it is a struct marked '@_implementationOnly'}3",
38513852
(const Decl *, unsigned, Identifier, unsigned))
38523853
ERROR(typealias_desugars_to_type_from_hidden_module,none,
38533854
"%0 aliases '%1.%2' and cannot be used %select{here|"
@@ -3865,7 +3866,8 @@ ERROR(typealias_desugars_to_type_from_hidden_module,none,
38653866
"%4 was not imported by this file|"
38663867
"C++ types from imported module %4 do not support library evolution|"
38673868
"it was imported via the internal bridging header|"
3868-
"%4 was not imported publicly}5",
3869+
"%4 was not imported publicly|"
3870+
"it is a struct marked '@_implementationOnly'}5",
38693871
(const TypeAliasDecl *, StringRef, StringRef, unsigned, Identifier, unsigned))
38703872
ERROR(conformance_from_implementation_only_module,none,
38713873
"cannot use conformance of %0 to %1 %select{here|as property wrapper here|"
@@ -3881,7 +3883,8 @@ ERROR(conformance_from_implementation_only_module,none,
38813883
"%3 was not imported by this file|"
38823884
"C++ types from imported module %3 do not support library evolution|"
38833885
"it was imported via the internal bridging header|"
3884-
"%3 was not imported publicly}4",
3886+
"%3 was not imported publicly|"
3887+
"it is a struct marked '@_implementationOnly'}4",
38853888
(Type, Identifier, unsigned, Identifier, unsigned))
38863889
NOTE(assoc_conformance_from_implementation_only_module,none,
38873890
"in associated type %0 (inferred as %1)", (Type, Type))
@@ -3946,6 +3949,9 @@ ERROR(implementation_only_override_import_without_attr,none,
39463949
"override of %kindonly0 imported as implementation-only must be declared "
39473950
"'@_implementationOnly'",
39483951
(const ValueDecl *))
3952+
ERROR(implementation_only_on_structs_feature,none,
3953+
"'@_implementationOnly' on structs requires "
3954+
"'-enable-experimental-feature CheckImplementationOnly'", ())
39493955

39503956
ERROR(import_attr_conflict,none,
39513957
"%0 inconsistently imported with %1",
@@ -4128,7 +4134,7 @@ WARNING(attr_has_no_effect_on_decl_with_access_level,none,
41284134
(DeclAttribute, AccessLevel))
41294135
ERROR(attr_not_on_decl_with_invalid_access_level,none,
41304136
"'%0' may not be used on "
4131-
"%select{private|fileprivate|internal|package|%error|%error}1 declarations",
4137+
"%select{private|fileprivate|internal|package|public|%error}1 declarations",
41324138
(DeclAttribute, AccessLevel))
41334139

41344140
ERROR(attr_has_no_effect_decl_not_available_before,none,
@@ -7350,7 +7356,8 @@ ERROR(inlinable_decl_ref_from_hidden_module,
73507356
"%2 was not imported by this file|"
73517357
"C++ APIs from imported module %2 do not support library evolution|"
73527358
"it was imported via the internal bridging header|"
7353-
"%2 was not imported publicly}3",
7359+
"%2 was not imported publicly|"
7360+
"it is a struct marked '@_implementationOnly'}3",
73547361
(const ValueDecl *, unsigned, Identifier, unsigned))
73557362

73567363
ERROR(inlinable_typealias_desugars_to_type_from_hidden_module,
@@ -7362,7 +7369,8 @@ ERROR(inlinable_typealias_desugars_to_type_from_hidden_module,
73627369
"%4 was not imported by this file|"
73637370
"C++ types from imported module %4 do not support library evolution|"
73647371
"it was imported via the internal bridging header|"
7365-
"%4 was not imported publicly}5",
7372+
"%4 was not imported publicly|"
7373+
"it is a struct marked '@_implementationOnly'}5",
73667374
(const TypeAliasDecl *, StringRef, StringRef, unsigned, Identifier, unsigned))
73677375

73687376
NOTE(missing_import_inserted,

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,9 @@ EXPERIMENTAL_FEATURE(EmbeddedExistentials, false)
558558
/// Allow use of the 'anyAppleOS' availability domain.
559559
EXPERIMENTAL_FEATURE(AnyAppleOSAvailability, true)
560560

561+
/// Check @_implementationOnly imports in non-library-evolution mode.
562+
EXPERIMENTAL_FEATURE(CheckImplementationOnly, true)
563+
561564
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
562565
#undef EXPERIMENTAL_FEATURE
563566
#undef UPCOMING_FEATURE

lib/AST/Availability.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1006,7 +1006,7 @@ bool swift::isExported(const ValueDecl *VD) {
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())
1009+
if (property->isLayoutExposedToClients(/*applyImplicit=*/true))
10101010
return true;
10111011

10121012
return false;

lib/AST/Decl.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2753,20 +2753,33 @@ bool VarDecl::isInitExposedToClients() const {
27532753
return hasInitialValue() && isLayoutExposedToClients();
27542754
}
27552755

2756-
bool VarDecl::isLayoutExposedToClients() const {
2756+
bool VarDecl::isLayoutExposedToClients(bool applyImplicit) const {
27572757
auto parent = dyn_cast<NominalTypeDecl>(getDeclContext());
27582758
if (!parent) return false;
27592759
if (isStatic()) return false;
27602760

2761-
2761+
auto M = getDeclContext()->getParentModule();
27622762
auto nominalAccess =
27632763
parent->getFormalAccessScope(/*useDC=*/nullptr,
27642764
/*treatUsableFromInlineAsPublic=*/true);
2765-
if (!nominalAccess.isPublic()) return false;
27662765

2767-
if (!parent->getAttrs().hasAttribute<FrozenAttr>() &&
2768-
!parent->getAttrs().hasAttribute<FixedLayoutAttr>())
2766+
// Resilient modules and classes hide layouts by default.
2767+
bool layoutIsHiddenByDefault = !applyImplicit ||
2768+
!getASTContext().LangOpts.hasFeature(Feature::CheckImplementationOnly) ||
2769+
M->getResilienceStrategy() == ResilienceStrategy::Resilient;
2770+
if (layoutIsHiddenByDefault) {
2771+
if (!nominalAccess.isPublic())
2772+
return false;
2773+
2774+
if (!parent->getAttrs().hasAttribute<FrozenAttr>() &&
2775+
!parent->getAttrs().hasAttribute<FixedLayoutAttr>())
27692776
return false;
2777+
} else {
2778+
// Non-resilient module: layouts are exposed by default unless marked
2779+
// otherwise.
2780+
if (parent->getAttrs().hasAttribute<ImplementationOnlyAttr>())
2781+
return false;
2782+
}
27702783

27712784
if (!hasStorage() &&
27722785
!getAttrs().hasAttribute<LazyAttr>() &&

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ UNINTERESTING_FEATURE(ExtractConstantsFromMembers)
144144
UNINTERESTING_FEATURE(GroupActorErrors)
145145
UNINTERESTING_FEATURE(SameElementRequirements)
146146
UNINTERESTING_FEATURE(SendingArgsAndResults)
147+
UNINTERESTING_FEATURE(CheckImplementationOnly)
147148

148149
static bool findUnderscoredLifetimeAttr(Decl *decl) {
149150
auto hasUnderscoredLifetimeAttr = [](Decl *decl) {

lib/Sema/ResilienceDiagnostics.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D,
338338

339339
case DisallowedOriginKind::ImplementationOnly:
340340
case DisallowedOriginKind::FragileCxxAPI:
341+
case DisallowedOriginKind::ImplementationOnlyMemoryLayout:
341342
break;
342343
}
343344

lib/Sema/TypeCheckAccess.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2163,6 +2163,14 @@ swift::getDisallowedOriginKind(const Decl *decl,
21632163
Feature::AssumeResilientCxxTypes))
21642164
return DisallowedOriginKind::FragileCxxAPI;
21652165

2166+
if (isa<StructDecl>(decl) || isa<EnumDecl>(decl)) {
2167+
if (decl->getASTContext().LangOpts.hasFeature(
2168+
Feature::CheckImplementationOnly) &&
2169+
decl->getAttrs().hasAttribute<ImplementationOnlyAttr>()) {
2170+
return DisallowedOriginKind::ImplementationOnlyMemoryLayout;
2171+
}
2172+
}
2173+
21662174
// Report non-public import last as it can be ignored by the caller.
21672175
// See \c diagnoseValueDeclRefExportability.
21682176
auto importSource = decl->getImportAccessFrom(where.getDeclContext());

lib/Sema/TypeCheckAccess.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class SourceFile;
3232
/// itself. Related checks may also be performed.
3333
void checkAccessControl(Decl *D);
3434

35-
/// Problematic origin of an exported type.
35+
/// Problematic origin of a decl that may restrict its exportability.
3636
///
3737
/// This enum must be kept in sync with a number of diagnostics:
3838
/// diag::inlinable_decl_ref_from_hidden_module
@@ -52,6 +52,7 @@ enum class DisallowedOriginKind : uint8_t {
5252
InternalBridgingHeaderImport,
5353

5454
NonPublicImport,
55+
ImplementationOnlyMemoryLayout,
5556
None
5657
};
5758

0 commit comments

Comments
 (0)