Skip to content

Commit 860f2db

Browse files
authored
Merge pull request #84630 from xymus/non-neic-funcs-are-fragile
Sema: In embedded, consider functions as fragile unless `@_neverEmitIntoClient`
2 parents d030c8f + 9380507 commit 860f2db

11 files changed

+517
-26
lines changed

include/swift/AST/DeclContext.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ struct ConformanceDiagnostic {
202202
ProtocolDecl *ExistingExplicitProtocol;
203203
};
204204

205-
/// Used in diagnostic %selects.
205+
/// Used in diagnostic %selects via FRAGILE_FUNC_KIND.
206206
struct FragileFunctionKind {
207207
enum Kind : unsigned {
208208
Transparent,
@@ -211,6 +211,7 @@ struct FragileFunctionKind {
211211
DefaultArgument,
212212
PropertyInitializer,
213213
BackDeploy,
214+
EmbeddedAlwaysEmitIntoClient,
214215
None
215216
};
216217

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7312,7 +7312,8 @@ ERROR(usable_from_inline_attr_in_protocol,none,
73127312
"an '@_alwaysEmitIntoClient' function|" \
73137313
"a default argument value|" \
73147314
"a property initializer in a '@frozen' type|" \
7315-
"a '@backDeployed' function'}"
7315+
"a '@backDeployed' function|" \
7316+
"an embedded function not marked '@_neverEmitIntoClient'}"
73167317

73177318
ERROR(local_type_in_inlinable_function,
73187319
none, "type %0 cannot be nested inside " FRAGILE_FUNC_KIND "1",

lib/AST/DeclContext.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,11 @@ ResilienceExpansion DeclContext::getResilienceExpansion() const {
488488
case FragileFunctionKind::PropertyInitializer:
489489
case FragileFunctionKind::BackDeploy:
490490
return ResilienceExpansion::Minimal;
491+
492+
/// Embedded functions are treated as fragile for diagnostics only.
493+
/// For code gen they are treated as normal and optimized later.
494+
case FragileFunctionKind::EmbeddedAlwaysEmitIntoClient:
495+
491496
case FragileFunctionKind::None:
492497
return ResilienceExpansion::Maximal;
493498
}
@@ -551,14 +556,32 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator,
551556
if (AFD->getDeclContext()->isLocalContext())
552557
continue;
553558

559+
// Delay checking the implicit conditions after explicit declarations.
560+
auto checkEmbeddedAlwaysEmitIntoClient = [&](const ValueDecl *VD) {
561+
if (!VD->getASTContext().LangOpts.hasFeature(Feature::Embedded))
562+
return FragileFunctionKind::None;
563+
564+
bool funcIsNEIC = VD->isNeverEmittedIntoClient();
565+
bool storageIsNEIC = false;
566+
if (auto accessor = dyn_cast<AccessorDecl>(VD))
567+
storageIsNEIC = accessor->getStorage()->isNeverEmittedIntoClient();
568+
569+
// Accessors are implicitly EmbeddedAlwaysEmitIntoClient if neither the
570+
// accessor or starage is marked @_neverEmitIntoClient.
571+
if (!funcIsNEIC && !storageIsNEIC)
572+
return FragileFunctionKind::EmbeddedAlwaysEmitIntoClient;
573+
return FragileFunctionKind::None;
574+
};
575+
554576
auto funcAccess =
555577
AFD->getFormalAccessScope(/*useDC=*/nullptr,
556578
/*treatUsableFromInlineAsPublic=*/true);
557579

558580
// If the function is not externally visible, we will not be serializing
559581
// its body.
560582
if (!funcAccess.isPublic()) {
561-
return {FragileFunctionKind::None};
583+
// For non-public decls, only check embedded mode correctness.
584+
return {checkEmbeddedAlwaysEmitIntoClient(AFD)};
562585
}
563586

564587
// If the function is public, @_transparent implies @inlinable.
@@ -592,6 +615,10 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator,
592615
return {FragileFunctionKind::BackDeploy};
593616
}
594617
}
618+
619+
auto implicitKind = checkEmbeddedAlwaysEmitIntoClient(AFD);
620+
if (implicitKind != FragileFunctionKind::None)
621+
return {implicitKind};
595622
}
596623
}
597624

lib/AST/TypeCheckRequests.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,9 @@ void swift::simple_display(llvm::raw_ostream &out,
689689
case FragileFunctionKind::BackDeploy:
690690
out << "backDeploy";
691691
return;
692+
case FragileFunctionKind::EmbeddedAlwaysEmitIntoClient:
693+
out << "embeddedAlwaysEmitIntoClient";
694+
return;
692695
case FragileFunctionKind::None:
693696
out << "none";
694697
return;

lib/Sema/MiscDiagnostics.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5223,7 +5223,8 @@ static bool diagnoseAvailabilityCondition(PoundAvailableInfo *info,
52235223
// restriction, macros would need to either be expanded when printed in
52245224
// swiftinterfaces or be parsable as macros by module clients.
52255225
auto fragileKind = DC->getFragileFunctionKind();
5226-
if (fragileKind.kind != FragileFunctionKind::None) {
5226+
if (fragileKind.kind != FragileFunctionKind::None &&
5227+
fragileKind.kind != FragileFunctionKind::EmbeddedAlwaysEmitIntoClient) {
52275228
for (auto availSpec : info->getQueries()) {
52285229
if (availSpec->getMacroLoc().isValid()) {
52295230
diags.diagnose(availSpec->getMacroLoc(),

lib/Sema/ResilienceDiagnostics.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,14 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,
9797
return false;
9898
}
9999

100+
// Embedded functions can reference non-public decls as they are visible
101+
// to clients. Still report references to decls imported non-publicly
102+
// to enforce access-level on imports.
103+
ImportAccessLevel problematicImport = D->getImportAccessFrom(DC);
104+
if (fragileKind.kind == FragileFunctionKind::EmbeddedAlwaysEmitIntoClient &&
105+
!problematicImport)
106+
return false;
107+
100108
DowngradeToWarning downgradeToWarning = DowngradeToWarning::No;
101109

102110
// Swift 4.2 did not perform any checks for type aliases.
@@ -127,7 +135,6 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,
127135

128136
Context.Diags.diagnose(D, diag::resilience_decl_declared_here, D);
129137

130-
ImportAccessLevel problematicImport = D->getImportAccessFrom(DC);
131138
if (problematicImport.has_value() &&
132139
problematicImport->accessLevel < D->getFormalAccess()) {
133140
Context.Diags.diagnose(problematicImport->importLoc,

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2946,7 +2946,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
29462946

29472947
// We don't allow nested types inside inlinable contexts.
29482948
auto kind = DC->getFragileFunctionKind();
2949-
if (kind.kind != FragileFunctionKind::None) {
2949+
if (kind.kind != FragileFunctionKind::None &&
2950+
kind.kind != FragileFunctionKind::EmbeddedAlwaysEmitIntoClient) {
29502951
NTD->diagnose(diag::local_type_in_inlinable_function, NTD->getName(),
29512952
kind.getSelector());
29522953
}
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/// Test @_implementationOnly internal import exportability diagnostics in embedded mode.
2+
3+
// RUN: %empty-directory(%t)
4+
// RUN: %target-swift-frontend -emit-module -o %t/indirects.swiftmodule \
5+
// RUN: %S/Inputs/implementation-only-imports/indirects.swift \
6+
// RUN: -swift-version 5 -target arm64-apple-none-macho \
7+
// RUN: -enable-experimental-feature Embedded
8+
// RUN: %target-swift-frontend -emit-module -o %t/directs.swiftmodule -I %t \
9+
// RUN: %S/Inputs/implementation-only-imports/directs.swift \
10+
// RUN: -swift-version 5 -target arm64-apple-none-macho \
11+
// RUN: -enable-experimental-feature Embedded
12+
13+
// RUN: %target-swift-frontend -typecheck -verify -verify-ignore-unrelated %s -I %t \
14+
// RUN: -swift-version 5 -target arm64-apple-none-macho \
15+
// RUN: -enable-experimental-feature Embedded
16+
17+
// REQUIRES: swift_feature_Embedded
18+
// REQUIRES: embedded_stdlib_cross_compiling
19+
20+
@_implementationOnly internal import directs
21+
// expected-warning @-1 {{using '@_implementationOnly' without enabling library evolution for 'main' may lead to instability during execution}}
22+
// expected-note @-2 19 {{struct 'StructFromDirect' imported as 'internal' from 'directs' here}}
23+
// expected-note @-3 12 {{initializer 'init()' imported as 'internal' from 'directs' here}}
24+
import indirects
25+
26+
internal func localInternalFunc() {} // expected-note {{global function 'localInternalFunc()' is not '@usableFromInline' or public}}
27+
28+
@inlinable
29+
public func explicitlyInlinable(arg: StructFromDirect = StructFromDirect()) {
30+
// expected-error @-1 {{initializer 'init()' is internal and cannot be referenced from a default argument value}}
31+
// expected-error @-2 {{struct 'StructFromDirect' is internal and cannot be referenced from a default argument value}}
32+
// expected-error @-3 {{struct 'StructFromDirect' is internal and cannot be referenced from an '@inlinable' function}}
33+
// expected-error @-4 {{function cannot be declared public because its parameter uses an internal type}}
34+
// expected-note @-5 {{struct 'StructFromDirect' is imported by this file as 'internal' from 'directs'}}
35+
_ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an '@inlinable' function}}
36+
// expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an '@inlinable' function}}
37+
38+
if (true) {
39+
_ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an '@inlinable' function}}
40+
// expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an '@inlinable' function}}
41+
}
42+
43+
func nested() {
44+
_ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an '@inlinable' function}}
45+
// expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an '@inlinable' function}}
46+
}
47+
nested()
48+
49+
localInternalFunc() // expected-error {{global function 'localInternalFunc()' is internal and cannot be referenced from an '@inlinable' function}}
50+
51+
explicitlyInlinable()
52+
implicitlyInlinablePublic()
53+
implicitlyInlinablePrivate() // expected-error {{global function 'implicitlyInlinablePrivate(arg:)' is private and cannot be referenced from an '@inlinable' function}}
54+
explicitNonInliable()
55+
}
56+
57+
public func implicitlyInlinablePublic(arg: StructFromDirect = StructFromDirect()) {
58+
// expected-error @-1 {{initializer 'init()' is internal and cannot be referenced from a default argument value}}
59+
// expected-error @-2 {{struct 'StructFromDirect' is internal and cannot be referenced from a default argument value}}
60+
// expected-error @-3 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
61+
// expected-error @-4 {{function cannot be declared public because its parameter uses an internal type}}
62+
// expected-note @-5 {{struct 'StructFromDirect' is imported by this file as 'internal' from 'directs'}}
63+
_ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
64+
// expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
65+
66+
if (true) {
67+
_ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
68+
// expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
69+
}
70+
71+
func nested() {
72+
_ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
73+
// expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
74+
}
75+
nested()
76+
77+
localInternalFunc()
78+
79+
explicitlyInlinable()
80+
implicitlyInlinablePublic()
81+
implicitlyInlinablePrivate()
82+
explicitNonInliable()
83+
}
84+
85+
private func implicitlyInlinablePrivate(arg: StructFromDirect = StructFromDirect()) {
86+
// expected-error @-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
87+
// expected-note @-2 {{global function 'implicitlyInlinablePrivate(arg:)' is not '@usableFromInline' or public}}
88+
_ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
89+
// expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
90+
91+
if (true) {
92+
_ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
93+
// expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
94+
}
95+
96+
func nested() {
97+
_ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
98+
// expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
99+
}
100+
nested()
101+
102+
localInternalFunc()
103+
104+
explicitlyInlinable()
105+
implicitlyInlinablePublic()
106+
implicitlyInlinablePrivate()
107+
explicitNonInliable()
108+
}
109+
110+
@_neverEmitIntoClient
111+
public func explicitNonInliable(arg: StructFromDirect = StructFromDirect()) {
112+
// expected-error @-1 {{initializer 'init()' is internal and cannot be referenced from a default argument value}}
113+
// expected-error @-2 {{struct 'StructFromDirect' is internal and cannot be referenced from a default argument value}}
114+
// expected-error @-3 {{cannot use struct 'StructFromDirect' here; 'directs' has been imported as implementation-only}}
115+
// expected-error @-4 {{function cannot be declared public because its parameter uses an internal type}}
116+
// expected-note @-5 {{struct 'StructFromDirect' is imported by this file as 'internal' from 'directs'}}
117+
_ = StructFromDirect()
118+
119+
if (true) {
120+
_ = StructFromDirect()
121+
}
122+
123+
@_neverEmitIntoClient
124+
func nested() {
125+
_ = StructFromDirect()
126+
}
127+
nested()
128+
129+
localInternalFunc()
130+
131+
explicitlyInlinable()
132+
implicitlyInlinablePublic()
133+
implicitlyInlinablePrivate()
134+
explicitNonInliable()
135+
}
136+
137+
@_neverEmitIntoClient
138+
internal func explicitNonInliableInternal(arg: StructFromDirect = StructFromDirect()) {
139+
_ = StructFromDirect()
140+
141+
if (true) {
142+
_ = StructFromDirect()
143+
}
144+
145+
@_neverEmitIntoClient
146+
func nested() {
147+
_ = StructFromDirect()
148+
}
149+
nested()
150+
151+
localInternalFunc()
152+
153+
explicitlyInlinable()
154+
implicitlyInlinablePublic()
155+
implicitlyInlinablePrivate()
156+
explicitNonInliable()
157+
}
158+
159+
public func legalAccessToIndirect(arg: StructFromIndirect = StructFromIndirect()) {
160+
_ = StructFromIndirect()
161+
162+
if (true) {
163+
_ = StructFromIndirect()
164+
}
165+
166+
func nested() {
167+
_ = StructFromIndirect()
168+
}
169+
nested()
170+
}
171+
172+
public struct ExposedLayoutPublic {
173+
public var publicField: StructFromDirect // expected-error {{property cannot be declared public because its type uses an internal type}}
174+
// expected-error @-1 {{cannot use struct 'StructFromDirect' here; 'directs' has been imported as implementation-only}}
175+
// expected-note @-2 {{struct 'StructFromDirect' is imported by this file as 'internal' from 'directs'}}
176+
177+
private var privateField: StructFromDirect
178+
}
179+
180+
private struct ExposedLayoutPrivate {
181+
private var privateField: StructFromDirect
182+
}

0 commit comments

Comments
 (0)