Skip to content

Commit a4b91d2

Browse files
authored
Merge pull request #15666 from slavapestov/se-0193-default-args
SE-0193: Public default argument generators cannot reference declarations that are @usableFromInline
2 parents 161afd6 + 96901f1 commit a4b91d2

File tree

9 files changed

+123
-72
lines changed

9 files changed

+123
-72
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3765,10 +3765,10 @@ ERROR(resilience_decl_unavailable,
37653765

37663766
#undef FRAGILE_FUNC_KIND
37673767

3768-
NOTE(resilience_decl_declared_here,
3768+
NOTE(resilience_decl_declared_here_public,
37693769
none, "%0 %1 is not public", (DescriptiveDeclKind, DeclName))
37703770

3771-
NOTE(resilience_decl_declared_here_versioned,
3771+
NOTE(resilience_decl_declared_here,
37723772
none, "%0 %1 is not '@usableFromInline' or public", (DescriptiveDeclKind, DeclName))
37733773

37743774
ERROR(class_designated_init_inlinable_resilient,none,

lib/AST/Decl.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2140,8 +2140,9 @@ static AccessLevel getTestableAccess(const ValueDecl *decl) {
21402140
}
21412141

21422142
AccessLevel ValueDecl::getEffectiveAccess() const {
2143-
auto effectiveAccess = getFormalAccess(/*useDC=*/nullptr,
2144-
/*respectVersionedAttr=*/true);
2143+
auto effectiveAccess =
2144+
getFormalAccess(/*useDC=*/nullptr,
2145+
/*treatUsableFromInlineAsPublic=*/true);
21452146

21462147
// Handle @testable.
21472148
switch (effectiveAccess) {

lib/AST/DeclContext.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -364,8 +364,10 @@ ResilienceExpansion DeclContext::getResilienceExpansion() const {
364364
// if the type is formally fixed layout.
365365
if (isa<PatternBindingInitializer>(dc)) {
366366
if (auto *NTD = dyn_cast<NominalTypeDecl>(dc->getParent())) {
367-
if (!NTD->getFormalAccessScope(/*useDC=*/nullptr,
368-
/*respectVersionedAttr=*/true).isPublic())
367+
auto nominalAccess =
368+
NTD->getFormalAccessScope(/*useDC=*/nullptr,
369+
/*treatUsableFromInlineAsPublic=*/true);
370+
if (!nominalAccess.isPublic())
369371
return ResilienceExpansion::Maximal;
370372

371373
if (NTD->isFormallyResilient())
@@ -386,10 +388,13 @@ ResilienceExpansion DeclContext::getResilienceExpansion() const {
386388
if (!AFD->hasAccess())
387389
break;
388390

391+
auto funcAccess =
392+
AFD->getFormalAccessScope(/*useDC=*/nullptr,
393+
/*treatUsableFromInlineAsPublic=*/true);
394+
389395
// If the function is not externally visible, we will not be serializing
390396
// its body.
391-
if (!AFD->getFormalAccessScope(/*useDC=*/nullptr,
392-
/*respectVersionedAttr=*/true).isPublic())
397+
if (!funcAccess.isPublic())
393398
break;
394399

395400
// Bodies of public transparent and always-inline functions are

lib/SIL/SILDeclRef.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -456,8 +456,9 @@ IsSerialized_t SILDeclRef::isSerialized() const {
456456
// marked as @_fixed_layout.
457457
if (isStoredPropertyInitializer()) {
458458
auto *nominal = cast<NominalTypeDecl>(d->getDeclContext());
459-
auto scope = nominal->getFormalAccessScope(/*useDC=*/nullptr,
460-
/*respectVersionedAttr=*/true);
459+
auto scope =
460+
nominal->getFormalAccessScope(/*useDC=*/nullptr,
461+
/*treatUsableFromInlineAsPublic=*/true);
461462
if (!scope.isPublic())
462463
return IsNotSerialized;
463464
if (nominal->isFormallyResilient())

lib/Sema/ResilienceDiagnostics.cpp

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,24 @@
2323
using namespace swift;
2424
using FragileFunctionKind = TypeChecker::FragileFunctionKind;
2525

26-
FragileFunctionKind TypeChecker::getFragileFunctionKind(const DeclContext *DC) {
26+
std::pair<FragileFunctionKind, bool>
27+
TypeChecker::getFragileFunctionKind(const DeclContext *DC) {
2728
for (; DC->isLocalContext(); DC = DC->getParent()) {
28-
if (isa<DefaultArgumentInitializer>(DC))
29-
return FragileFunctionKind::DefaultArgument;
29+
if (isa<DefaultArgumentInitializer>(DC)) {
30+
// Default argument generators of public functions cannot reference
31+
// @usableFromInline declarations; all other fragile function kinds
32+
// can.
33+
auto *VD = cast<ValueDecl>(DC->getInnermostDeclarationDeclContext());
34+
auto access =
35+
VD->getFormalAccessScope(/*useDC=*/nullptr,
36+
/*treatUsableFromInlineAsPublic=*/false);
37+
return std::make_pair(FragileFunctionKind::DefaultArgument,
38+
!access.isPublic());
39+
}
3040

3141
if (isa<PatternBindingInitializer>(DC))
32-
return FragileFunctionKind::PropertyInitializer;
42+
return std::make_pair(FragileFunctionKind::PropertyInitializer,
43+
/*treatUsableFromInlineAsPublic=*/true);
3344

3445
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(DC)) {
3546
// If the function is a nested function, we will serialize its body if
@@ -40,20 +51,24 @@ FragileFunctionKind TypeChecker::getFragileFunctionKind(const DeclContext *DC) {
4051
// Bodies of public transparent and always-inline functions are
4152
// serialized, so use conservative access patterns.
4253
if (AFD->isTransparent())
43-
return FragileFunctionKind::Transparent;
54+
return std::make_pair(FragileFunctionKind::Transparent,
55+
/*treatUsableFromInlineAsPublic=*/true);
4456

4557
if (AFD->getAttrs().hasAttribute<InlinableAttr>())
46-
return FragileFunctionKind::Inlinable;
58+
return std::make_pair(FragileFunctionKind::Inlinable,
59+
/*treatUsableFromInlineAsPublic=*/true);
4760

4861
if (auto attr = AFD->getAttrs().getAttribute<InlineAttr>())
4962
if (attr->getKind() == InlineKind::Always)
50-
return FragileFunctionKind::InlineAlways;
63+
return std::make_pair(FragileFunctionKind::InlineAlways,
64+
/*treatUsableFromInlineAsPublic=*/true);
5165

5266
// If a property or subscript is @inlinable, the accessors are
5367
// @inlinable also.
5468
if (auto accessor = dyn_cast<AccessorDecl>(AFD))
5569
if (accessor->getStorage()->getAttrs().getAttribute<InlinableAttr>())
56-
return FragileFunctionKind::Inlinable;
70+
return std::make_pair(FragileFunctionKind::Inlinable,
71+
/*treatUsableFromInlineAsPublic=*/true);
5772
}
5873
}
5974

@@ -64,21 +79,18 @@ void TypeChecker::diagnoseInlinableLocalType(const NominalTypeDecl *NTD) {
6479
auto *DC = NTD->getDeclContext();
6580
auto expansion = DC->getResilienceExpansion();
6681
if (expansion == ResilienceExpansion::Minimal) {
82+
auto kind = getFragileFunctionKind(DC);
6783
diagnose(NTD, diag::local_type_in_inlinable_function,
6884
NTD->getFullName(),
69-
static_cast<unsigned>(getFragileFunctionKind(DC)));
85+
static_cast<unsigned>(kind.first));
7086
}
7187
}
7288

7389
bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc,
7490
const ValueDecl *D,
75-
const DeclContext *DC) {
76-
auto expansion = DC->getResilienceExpansion();
77-
78-
// Internal declarations referenced from non-inlinable contexts are OK.
79-
if (expansion == ResilienceExpansion::Maximal)
80-
return false;
81-
91+
const DeclContext *DC,
92+
FragileFunctionKind Kind,
93+
bool TreatUsableFromInlineAsPublic) {
8294
// Local declarations are OK.
8395
if (D->getDeclContext()->isLocalContext())
8496
return false;
@@ -89,7 +101,7 @@ bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc,
89101

90102
// Public declarations are OK.
91103
if (D->getFormalAccessScope(/*useDC=*/nullptr,
92-
/*respectVersionedAttr=*/true).isPublic())
104+
TreatUsableFromInlineAsPublic).isPublic())
93105
return false;
94106

95107
// Enum cases are handled as part of their containing enum.
@@ -114,23 +126,13 @@ bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc,
114126
diagnose(loc, diag::resilience_decl_unavailable,
115127
D->getDescriptiveKind(), D->getFullName(),
116128
D->getFormalAccessScope().accessLevelForDiagnostics(),
117-
static_cast<unsigned>(getFragileFunctionKind(DC)));
118-
119-
bool isDefaultArgument = false;
120-
while (DC->isLocalContext()) {
121-
if (isa<DefaultArgumentInitializer>(DC)) {
122-
isDefaultArgument = true;
123-
break;
124-
}
125-
126-
DC = DC->getParent();
127-
}
129+
static_cast<unsigned>(Kind));
128130

129-
if (isDefaultArgument) {
131+
if (TreatUsableFromInlineAsPublic) {
130132
diagnose(D, diag::resilience_decl_declared_here,
131133
D->getDescriptiveKind(), D->getFullName());
132134
} else {
133-
diagnose(D, diag::resilience_decl_declared_here_versioned,
135+
diagnose(D, diag::resilience_decl_declared_here_public,
134136
D->getDescriptiveKind(), D->getFullName());
135137
}
136138

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2233,10 +2233,18 @@ class AvailabilityWalker : public ASTWalker {
22332233
DeclContext *DC;
22342234
MemberAccessContext AccessContext = MemberAccessContext::Getter;
22352235
SmallVector<const Expr *, 16> ExprStack;
2236+
ResilienceExpansion Expansion;
2237+
Optional<TypeChecker::FragileFunctionKind> FragileKind;
2238+
bool TreatUsableFromInlineAsPublic = false;
22362239

22372240
public:
22382241
AvailabilityWalker(
2239-
TypeChecker &TC, DeclContext *DC) : TC(TC), DC(DC) {}
2242+
TypeChecker &TC, DeclContext *DC) : TC(TC), DC(DC) {
2243+
Expansion = DC->getResilienceExpansion();
2244+
if (Expansion == ResilienceExpansion::Minimal)
2245+
std::tie(FragileKind, TreatUsableFromInlineAsPublic)
2246+
= TC.getFragileFunctionKind(DC);
2247+
}
22402248

22412249
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
22422250
ExprStack.push_back(E);
@@ -2457,9 +2465,11 @@ bool AvailabilityWalker::diagAvailability(const ValueDecl *D, SourceRange R,
24572465
return true;
24582466
}
24592467

2460-
if (R.isValid())
2461-
if (TC.diagnoseInlinableDeclRef(R.Start, D, DC))
2462-
return true;
2468+
if (FragileKind)
2469+
if (R.isValid())
2470+
if (TC.diagnoseInlinableDeclRef(R.Start, D, DC, *FragileKind,
2471+
TreatUsableFromInlineAsPublic))
2472+
return true;
24632473

24642474
if (TC.diagnoseExplicitUnavailability(D, R, DC, call))
24652475
return true;

lib/Sema/TypeCheckStmt.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,29 +1397,27 @@ static void checkDefaultArguments(TypeChecker &tc,
13971397
/// Check the default arguments that occur within this pattern.
13981398
void TypeChecker::checkDefaultArguments(ArrayRef<ParameterList *> paramLists,
13991399
ValueDecl *VD) {
1400+
auto access =
1401+
VD->getFormalAccessScope(/*useDC=*/nullptr,
1402+
/*treatUsableFromInlineAsPublic=*/true);
1403+
14001404
// In Swift 4 mode, default argument bodies are inlined into the
14011405
// caller.
14021406
if (auto *func = dyn_cast<AbstractFunctionDecl>(VD)) {
14031407
auto expansion = func->getResilienceExpansion();
1404-
if (!Context.isSwiftVersion3() &&
1405-
func->getFormalAccessScope(/*useDC=*/nullptr,
1406-
/*respectVersionedAttr=*/true).isPublic())
1408+
if (!Context.isSwiftVersion3() && access.isPublic())
14071409
expansion = ResilienceExpansion::Minimal;
14081410

14091411
func->setDefaultArgumentResilienceExpansion(expansion);
14101412
} else {
14111413
auto *EED = cast<EnumElementDecl>(VD);
1412-
auto expansion = EED->getParentEnum()->getResilienceExpansion();
1413-
// Enum payloads parameter lists may have default arguments as of Swift 5.
1414-
if (Context.isSwiftVersionAtLeast(5) &&
1415-
EED->getFormalAccessScope(/*useDC=*/nullptr,
1416-
/*respectVersionedAttr=*/true).isPublic())
1414+
auto expansion = ResilienceExpansion::Maximal;
1415+
if (access.isPublic())
14171416
expansion = ResilienceExpansion::Minimal;
14181417

14191418
EED->setDefaultArgumentResilienceExpansion(expansion);
14201419
}
14211420

1422-
14231421
unsigned nextArgIndex = 0;
14241422
for (auto *paramList : paramLists)
14251423
::checkDefaultArguments(*this, paramList, nextArgIndex);
@@ -1671,9 +1669,10 @@ bool TypeChecker::typeCheckConstructorBodyUntil(ConstructorDecl *ctor,
16711669
// declarations.
16721670
if (!isDelegating && ClassD->isResilient() &&
16731671
ctor->getResilienceExpansion() == ResilienceExpansion::Minimal) {
1672+
auto kind = getFragileFunctionKind(ctor);
16741673
diagnose(ctor, diag::class_designated_init_inlinable_resilient,
16751674
ClassD->getDeclaredInterfaceType(),
1676-
static_cast<unsigned>(getFragileFunctionKind(ctor)));
1675+
static_cast<unsigned>(kind.first));
16771676
}
16781677
}
16791678

lib/Sema/TypeChecker.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2298,9 +2298,6 @@ class TypeChecker final : public LazyResolver {
22982298

22992299
void diagnoseInlinableLocalType(const NominalTypeDecl *NTD);
23002300

2301-
bool diagnoseInlinableDeclRef(SourceLoc loc, const ValueDecl *D,
2302-
const DeclContext *DC);
2303-
23042301
/// Used in diagnostic %selects.
23052302
enum class FragileFunctionKind : unsigned {
23062303
Transparent,
@@ -2310,11 +2307,20 @@ class TypeChecker final : public LazyResolver {
23102307
PropertyInitializer
23112308
};
23122309

2310+
bool diagnoseInlinableDeclRef(SourceLoc loc, const ValueDecl *D,
2311+
const DeclContext *DC,
2312+
FragileFunctionKind Kind,
2313+
bool TreatUsableFromInlineAsPublic);
2314+
23132315
/// Given that \p DC is within a fragile context for some reason, describe
23142316
/// why.
23152317
///
2318+
/// The second element of the pair is true if references to @usableFromInline
2319+
/// declarations are permitted.
2320+
///
23162321
/// \see FragileFunctionKind
2317-
FragileFunctionKind getFragileFunctionKind(const DeclContext *DC);
2322+
std::pair<FragileFunctionKind, bool>
2323+
getFragileFunctionKind(const DeclContext *DC);
23182324

23192325
/// \name Availability checking
23202326
///

test/decl/func/default-values-swift4.swift

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,27 @@
22
// RUN: %target-typecheck-verify-swift -swift-version 4 -enable-testing
33

44
private func privateFunction() {}
5-
// expected-note@-1 4{{global function 'privateFunction()' is not public}}
5+
// expected-note@-1 2{{global function 'privateFunction()' is not public}}
66
fileprivate func fileprivateFunction() {}
7-
// expected-note@-1 4{{global function 'fileprivateFunction()' is not public}}
7+
// expected-note@-1 2{{global function 'fileprivateFunction()' is not public}}
88
func internalFunction() {}
9-
// expected-note@-1 4{{global function 'internalFunction()' is not public}}
9+
// expected-note@-1 2{{global function 'internalFunction()' is not public}}
1010
@usableFromInline func versionedFunction() {}
11+
// expected-note@-1 5{{global function 'versionedFunction()' is not public}}
1112
public func publicFunction() {}
1213

1314
func internalIntFunction() -> Int {}
14-
// expected-note@-1 2{{global function 'internalIntFunction()' is not public}}
15+
// expected-note@-1 {{global function 'internalIntFunction()' is not public}}
16+
17+
private func privateFunction2() {}
18+
// expected-note@-1 2{{global function 'privateFunction2()' is not '@usableFromInline' or public}}
19+
fileprivate func fileprivateFunction2() {}
20+
// expected-note@-1 2{{global function 'fileprivateFunction2()' is not '@usableFromInline' or public}}
21+
func internalFunction2() {}
22+
// expected-note@-1 2{{global function 'internalFunction2()' is not '@usableFromInline' or public}}
23+
24+
func internalIntFunction2() -> Int {}
25+
// expected-note@-1 {{global function 'internalIntFunction2()' is not '@usableFromInline' or public}}
1526

1627
func internalFunctionWithDefaultValue(
1728
x: Int = {
@@ -44,17 +55,17 @@ func internalFunctionWithDefaultValue(
4455
// OK
4556
versionedFunction()
4657
// OK
47-
internalFunction()
48-
// expected-error@-1 2{{global function 'internalFunction()' is internal and cannot be referenced from a default argument value}}
49-
fileprivateFunction()
50-
// expected-error@-1 2{{global function 'fileprivateFunction()' is fileprivate and cannot be referenced from a default argument value}}
51-
privateFunction()
52-
// expected-error@-1 2{{global function 'privateFunction()' is private and cannot be referenced from a default argument value}}
58+
internalFunction2()
59+
// expected-error@-1 2{{global function 'internalFunction2()' is internal and cannot be referenced from a default argument value}}
60+
fileprivateFunction2()
61+
// expected-error@-1 2{{global function 'fileprivateFunction2()' is fileprivate and cannot be referenced from a default argument value}}
62+
privateFunction2()
63+
// expected-error@-1 2{{global function 'privateFunction2()' is private and cannot be referenced from a default argument value}}
5364

5465
return 0
5566
}(),
56-
y: Int = internalIntFunction()) {}
57-
// expected-error@-1 {{global function 'internalIntFunction()' is internal and cannot be referenced from a default argument value}}
67+
y: Int = internalIntFunction2()) {}
68+
// expected-error@-1 {{global function 'internalIntFunction2()' is internal and cannot be referenced from a default argument value}}
5869

5970
public func publicFunctionWithDefaultValue(
6071
x: Int = {
@@ -64,13 +75,16 @@ public func publicFunctionWithDefaultValue(
6475
// FIXME: Some errors below are diagnosed twice
6576

6677
publicFunction()
67-
// OK
78+
6879
versionedFunction()
69-
// OK
80+
// expected-error@-1 2{{global function 'versionedFunction()' is internal and cannot be referenced from a default argument value}}
81+
7082
internalFunction()
7183
// expected-error@-1 2{{global function 'internalFunction()' is internal and cannot be referenced from a default argument value}}
84+
7285
fileprivateFunction()
7386
// expected-error@-1 2{{global function 'fileprivateFunction()' is fileprivate and cannot be referenced from a default argument value}}
87+
7488
privateFunction()
7589
// expected-error@-1 2{{global function 'privateFunction()' is private and cannot be referenced from a default argument value}}
7690

@@ -83,3 +97,16 @@ public func publicFunctionWithDefaultValue(
8397
public class MyClass {
8498
public func method<T>(_: T.Type = T.self) -> T { }
8599
}
100+
101+
public func evilCode(
102+
x: Int = {
103+
let _ = publicFunction()
104+
let _ = versionedFunction()
105+
// expected-error@-1 2{{global function 'versionedFunction()' is internal and cannot be referenced from a default argument value}}
106+
107+
func localFunction() {
108+
publicFunction()
109+
versionedFunction()
110+
// expected-error@-1 {{global function 'versionedFunction()' is internal and cannot be referenced from a default argument value}}
111+
}
112+
}()) {}

0 commit comments

Comments
 (0)