Skip to content

Commit 4883212

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 1383fcd commit 4883212

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
@@ -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))
@@ -7349,7 +7352,8 @@ ERROR(inlinable_decl_ref_from_hidden_module,
73497352
"%2 was not imported by this file|"
73507353
"C++ APIs from imported module %2 do not support library evolution|"
73517354
"it was imported via the internal bridging header|"
7352-
"%2 was not imported publicly}3",
7355+
"%2 was not imported publicly|"
7356+
"it is a struct marked '@_implementationOnly'}3",
73537357
(const ValueDecl *, unsigned, Identifier, unsigned))
73547358

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

73677372
NOTE(missing_import_inserted,

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

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)