Skip to content

Commit e9d809d

Browse files
committed
Sema: Consider non-LE structs to be of a restricted type by default
In non-library-evolution mode, gated behind the CheckImplementationOnly feature flag, report references to structs marked with `@_implementationOnly` from a fragile context. Preventing references from inlinable functions and structs not marked `@_implementationOnly`.
1 parent cfa968a commit e9d809d

File tree

5 files changed

+227
-6
lines changed

5 files changed

+227
-6
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3840,7 +3840,8 @@ ERROR(decl_from_hidden_module,none,
38403840
"%2 was not imported by this file|"
38413841
"C++ types from imported module %2 do not support library evolution|"
38423842
"it was imported via the internal bridging header|"
3843-
"%2 was not imported publicly}3",
3843+
"%2 was not imported publicly|"
3844+
"it is a struct marked '@_implementationOnly'}3",
38443845
(const Decl *, unsigned, Identifier, unsigned))
38453846
ERROR(typealias_desugars_to_type_from_hidden_module,none,
38463847
"%0 aliases '%1.%2' and cannot be used %select{here|"
@@ -3858,7 +3859,8 @@ ERROR(typealias_desugars_to_type_from_hidden_module,none,
38583859
"%4 was not imported by this file|"
38593860
"C++ types from imported module %4 do not support library evolution|"
38603861
"it was imported via the internal bridging header|"
3861-
"%4 was not imported publicly}5",
3862+
"%4 was not imported publicly|"
3863+
"it is a struct marked '@_implementationOnly'}5",
38623864
(const TypeAliasDecl *, StringRef, StringRef, unsigned, Identifier, unsigned))
38633865
ERROR(conformance_from_implementation_only_module,none,
38643866
"cannot use conformance of %0 to %1 %select{here|as property wrapper here|"
@@ -3874,7 +3876,8 @@ ERROR(conformance_from_implementation_only_module,none,
38743876
"%3 was not imported by this file|"
38753877
"C++ types from imported module %3 do not support library evolution|"
38763878
"it was imported via the internal bridging header|"
3877-
"%3 was not imported publicly}4",
3879+
"%3 was not imported publicly|"
3880+
"it is a struct marked '@_implementationOnly'}4",
38783881
(Type, Identifier, unsigned, Identifier, unsigned))
38793882
NOTE(assoc_conformance_from_implementation_only_module,none,
38803883
"in associated type %0 (inferred as %1)", (Type, Type))
@@ -7350,7 +7353,8 @@ ERROR(inlinable_decl_ref_from_hidden_module,
73507353
"%2 was not imported by this file|"
73517354
"C++ APIs from imported module %2 do not support library evolution|"
73527355
"it was imported via the internal bridging header|"
7353-
"%2 was not imported publicly}3",
7356+
"%2 was not imported publicly|"
7357+
"it is a struct marked '@_implementationOnly'}3",
73547358
(const ValueDecl *, unsigned, Identifier, unsigned))
73557359

73567360
ERROR(inlinable_typealias_desugars_to_type_from_hidden_module,
@@ -7362,7 +7366,8 @@ ERROR(inlinable_typealias_desugars_to_type_from_hidden_module,
73627366
"%4 was not imported by this file|"
73637367
"C++ types from imported module %4 do not support library evolution|"
73647368
"it was imported via the internal bridging header|"
7365-
"%4 was not imported publicly}5",
7369+
"%4 was not imported publicly|"
7370+
"it is a struct marked '@_implementationOnly'}5",
73667371
(const TypeAliasDecl *, StringRef, StringRef, unsigned, Identifier, unsigned))
73677372

73687373
NOTE(missing_import_inserted,

lib/Sema/ResilienceDiagnostics.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D,
335335
case DisallowedOriginKind::SPIImported:
336336
case DisallowedOriginKind::SPILocal:
337337
case DisallowedOriginKind::FragileCxxAPI:
338+
case DisallowedOriginKind::ImplementationOnlyMemoryLayout:
338339
break;
339340
}
340341

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

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
/// Test @_implementationOnly import exportability diagnostics in non-library-evolution mode
2+
3+
/// Standard / non-embedded
4+
5+
// RUN: %empty-directory(%t)
6+
// RUN: %target-swift-frontend -emit-module -o %t/indirects.swiftmodule \
7+
// RUN: %S/Inputs/implementation-only-imports/indirects.swift \
8+
// RUN: -swift-version 5
9+
// RUN: %target-swift-frontend -emit-module -o %t/directs.swiftmodule -I %t\
10+
// RUN: %S/Inputs/implementation-only-imports/directs.swift \
11+
// RUN: -swift-version 5
12+
13+
/// Old diags
14+
// RUN: %target-swift-frontend -emit-module -verify -verify-ignore-unrelated %s -I %t \
15+
// RUN: -swift-version 5 \
16+
// RUN: -verify-additional-prefix not-opt-in-
17+
18+
/// New diags
19+
// RUN: %target-swift-frontend -emit-module -verify -verify-ignore-unrelated %s -I %t \
20+
// RUN: -swift-version 5 \
21+
// RUN: -verify-additional-prefix opt-in- -DUseImplementationOnly \
22+
// RUN: -enable-experimental-feature CheckImplementationOnly
23+
24+
/// Embedded
25+
/// Will also show errors in non-@_neverEmitIntoClient functions.
26+
27+
// RUN: %empty-directory(%t)
28+
// RUN: %target-swift-frontend -emit-module -o %t/indirects.swiftmodule \
29+
// RUN: %S/Inputs/implementation-only-imports/indirects.swift \
30+
// RUN: -swift-version 5 -target arm64-apple-none-macho \
31+
// RUN: -enable-experimental-feature Embedded
32+
// RUN: %target-swift-frontend -emit-module -o %t/directs.swiftmodule -I %t\
33+
// RUN: %S/Inputs/implementation-only-imports/directs.swift \
34+
// RUN: -swift-version 5 -target arm64-apple-none-macho \
35+
// RUN: -enable-experimental-feature Embedded
36+
37+
/// Old diags
38+
// RUN: %target-swift-frontend -emit-module -verify -verify-ignore-unrelated %s -I %t \
39+
// RUN: -swift-version 5 -target arm64-apple-none-macho \
40+
// RUN: -enable-experimental-feature Embedded \
41+
// RUN: -verify-additional-prefix not-opt-in-
42+
43+
/// New diags
44+
// RUN: %target-swift-frontend -emit-module -verify -verify-ignore-unrelated %s -I %t \
45+
// RUN: -swift-version 5 -target arm64-apple-none-macho \
46+
// RUN: -enable-experimental-feature Embedded \
47+
// RUN: -verify-additional-prefix opt-in- -DUseImplementationOnly \
48+
// RUN: -verify-additional-prefix embedded-opt-in- \
49+
// RUN: -enable-experimental-feature CheckImplementationOnly
50+
51+
// REQUIRES: swift_feature_Embedded
52+
// REQUIRES: swift_feature_CheckImplementationOnly
53+
// REQUIRES: embedded_stdlib_cross_compiling
54+
55+
@_implementationOnly import directs
56+
// expected-warning @-1 {{using '@_implementationOnly' without enabling library evolution for 'main' may lead to instability during execution}}
57+
import indirects
58+
59+
/// Referenced types
60+
61+
public struct ExposedLayoutPublic {
62+
public init() { fatalError() }
63+
}
64+
65+
private struct ExposedLayoutPrivate {
66+
// expected-note @-1 2 {{struct 'ExposedLayoutPrivate' is not '@usableFromInline' or public}}
67+
// expected-opt-in-note @-2 {{type declared here}}
68+
init() { fatalError() } // expected-note {{initializer 'init()' is not '@usableFromInline' or public}}
69+
}
70+
71+
#if UseImplementationOnly
72+
@_implementationOnly
73+
private struct HiddenLayout {
74+
// expected-opt-in-note @-1 2 {{struct 'HiddenLayout' is not '@usableFromInline' or public}}
75+
// expected-opt-in-note @-2 1 {{initializer 'init()' is not '@usableFromInline' or public}}
76+
// expected-opt-in-note @-3 2 {{struct declared here}}
77+
// expected-opt-in-note @-4 {{type declared here}}
78+
}
79+
#else
80+
private struct HiddenLayout {
81+
// expected-not-opt-in-note @-1 2 {{struct 'HiddenLayout' is not '@usableFromInline' or public}}
82+
// expected-not-opt-in-note @-2 1 {{initializer 'init()' is not '@usableFromInline' or public}}
83+
}
84+
#endif
85+
86+
public enum ExposedEnumPublic {
87+
case A
88+
case B
89+
}
90+
91+
private enum ExposedEnumPrivate {
92+
// expected-note @-1 2 {{enum 'ExposedEnumPrivate' is not '@usableFromInline' or public}}
93+
case A
94+
// expected-note @-1 1 {{enum case 'A' is not '@usableFromInline' or public}}
95+
case B
96+
}
97+
98+
/// Function use sites
99+
100+
@inlinable
101+
public func explicitlyInlinable() {
102+
let _: ExposedLayoutPublic = ExposedLayoutPublic()
103+
let _: ExposedLayoutPrivate = ExposedLayoutPrivate()
104+
// expected-error @-1 2 {{struct 'ExposedLayoutPrivate' is private and cannot be referenced from an '@inlinable' function}}
105+
// expected-error @-2 {{initializer 'init()' is private and cannot be referenced from an '@inlinable' function}}
106+
107+
let _: HiddenLayout = HiddenLayout()
108+
// expected-error @-1 2 {{struct 'HiddenLayout' is private and cannot be referenced from an '@inlinable' function}}
109+
// expected-error @-2 {{initializer 'init()' is private and cannot be referenced from an '@inlinable' function}}
110+
111+
let _: ExposedEnumPublic = ExposedEnumPublic.A
112+
let _: ExposedEnumPrivate = ExposedEnumPrivate.A
113+
// expected-error @-1 2 {{enum 'ExposedEnumPrivate' is private and cannot be referenced from an '@inlinable' function}}
114+
// expected-error @-2 {{enum case 'A' is private and cannot be referenced from an '@inlinable' function}}
115+
}
116+
117+
public func implicitlyInlinablePublic() {
118+
let _: ExposedLayoutPublic = ExposedLayoutPublic()
119+
let _: ExposedLayoutPrivate = ExposedLayoutPrivate()
120+
let _: HiddenLayout = HiddenLayout()
121+
// expected-embedded-opt-in-error @-1 2 {{struct 'HiddenLayout' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because it is a struct marked '@_implementationOnly'}}
122+
123+
let _: ExposedEnumPublic = ExposedEnumPublic.A
124+
let _: ExposedEnumPrivate = ExposedEnumPrivate.A
125+
}
126+
127+
private func implicitlyInlinablePrivate() {
128+
let _: ExposedLayoutPublic = ExposedLayoutPublic()
129+
let _: ExposedLayoutPrivate = ExposedLayoutPrivate()
130+
let _: HiddenLayout = HiddenLayout()
131+
// expected-embedded-opt-in-error @-1 2 {{struct 'HiddenLayout' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because it is a struct marked '@_implementationOnly'}}
132+
133+
let _: ExposedEnumPublic = ExposedEnumPublic.A
134+
let _: ExposedEnumPrivate = ExposedEnumPrivate.A
135+
}
136+
137+
@_neverEmitIntoClient
138+
public func explicitNonInliable() {
139+
let _: ExposedLayoutPublic = ExposedLayoutPublic()
140+
let _: ExposedLayoutPrivate = ExposedLayoutPrivate()
141+
let _: HiddenLayout = HiddenLayout()
142+
let _: ExposedEnumPublic = ExposedEnumPublic.A
143+
let _: ExposedEnumPrivate = ExposedEnumPrivate.A
144+
}
145+
146+
@_neverEmitIntoClient
147+
internal func explicitNonInliableInternal() {
148+
let _: ExposedLayoutPublic = ExposedLayoutPublic()
149+
let _: ExposedLayoutPrivate = ExposedLayoutPrivate()
150+
let _: HiddenLayout = HiddenLayout()
151+
let _: ExposedEnumPublic = ExposedEnumPublic.A
152+
let _: ExposedEnumPrivate = ExposedEnumPrivate.A
153+
}
154+
155+
/// Struct use sites
156+
157+
public struct ExposedLayoutPublicUser {
158+
159+
public var publicField: StructFromDirect
160+
// expected-error @-1 {{cannot use struct 'StructFromDirect' in a property declaration marked public or in a '@frozen' or '@usableFromInline' context; 'directs' has been imported as implementation-only}}
161+
162+
private var privateField: StructFromDirect
163+
// expected-opt-in-error @-1 {{cannot use struct 'StructFromDirect' in a property declaration marked public or in a '@frozen' or '@usableFromInline' context; 'directs' has been imported as implementation-only}}
164+
165+
private var a: ExposedLayoutPublic
166+
private var b: ExposedLayoutPrivate
167+
// expected-opt-in-error @-1 {{type referenced from a stored property in a '@frozen' struct must be '@usableFromInline' or public}}
168+
169+
private var c: HiddenLayout
170+
// expected-opt-in-error @-1 {{cannot use struct 'HiddenLayout' in a property declaration marked public or in a '@frozen' or '@usableFromInline' context; it is a struct marked '@_implementationOnly'}}
171+
// expected-opt-in-error @-2 {{type referenced from a stored property in a '@frozen' struct must be '@usableFromInline' or public}}
172+
173+
private func privateFunc(h: HiddenLayout) {}
174+
// expected-embedded-opt-in-error @-1 {{struct 'HiddenLayout' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because it is a struct marked '@_implementationOnly'}}
175+
}
176+
177+
private struct ExposedLayoutPrivateUser {
178+
179+
private var privateField: StructFromDirect
180+
// expected-opt-in-error @-1 {{cannot use struct 'StructFromDirect' in a property declaration marked public or in a '@frozen' or '@usableFromInline' context; 'directs' has been imported as implementation-only}}
181+
182+
private var a: ExposedLayoutPublic
183+
private var b: ExposedLayoutPrivate
184+
private var c: HiddenLayout
185+
// expected-opt-in-error @-1 {{cannot use struct 'HiddenLayout' in a property declaration marked public or in a '@frozen' or '@usableFromInline' context; it is a struct marked '@_implementationOnly'}}
186+
187+
private func privateFunc(h: HiddenLayout) {}
188+
// expected-embedded-opt-in-error @-1 {{struct 'HiddenLayout' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because it is a struct marked '@_implementationOnly'}}
189+
}
190+
191+
#if UseImplementationOnly
192+
@_implementationOnly
193+
private struct HiddenLayoutUser {
194+
public var publicField: StructFromDirect
195+
private var privateField: StructFromDirect
196+
private var a: ExposedLayoutPublic
197+
private var b: ExposedLayoutPrivate
198+
private var c: HiddenLayout
199+
200+
@_neverEmitIntoClient
201+
private func privateFunc(h: HiddenLayout) {}
202+
}
203+
204+
@_implementationOnly // expected-opt-in-error {{'@_implementationOnly' may not be used on public declarations}}
205+
public struct PublicHiddenStruct {}
206+
#endif

0 commit comments

Comments
 (0)