Skip to content

Commit c221099

Browse files
authored
Simulate buggy Swift 3 function-type-alias access checking. (swiftlang#6418)
Like 9fba89b, typealiases in parameter position were sometimes canonicalized away in Swift 3.0, leading to the compiler not diagnosing improper uses of typealiases. These only occurred in "safe" circumstances where the typealias wouldn't be leaked to clients of a library (that is, a client would never see a name they wouldn't be allowed to type), but it was inconsistent with other typealiases (which were preserved) and did not follow the intended model. This particular issue comes from typealiases for function types needing to be marked as non-escaping in the compiler when used in parameter position, which requires rebuilding the function type. This lost the original "spelling" of the parameter as using a typealias in its type, which meant the compiler did not see it. The change in a32e11a to use TypeReprs to check access meant the model was now correctly enforced, but broke source compatibility for this "accidental feature". The fix is to track when a typealias for a function type is being used in parameter position, and to not check its access in that case. rdar://problem/29739577
1 parent 663b658 commit c221099

File tree

3 files changed

+173
-27
lines changed

3 files changed

+173
-27
lines changed

lib/Sema/TypeCheckDecl.cpp

+73-25
Original file line numberDiff line numberDiff line change
@@ -1145,7 +1145,7 @@ class AccessScopeChecker {
11451145
Cache(caches[File]),
11461146
Scope(AccessScope::getPublic()) {}
11471147

1148-
bool visitDecl(ValueDecl *VD) {
1148+
bool visitDecl(ValueDecl *VD, bool isInParameter = false) {
11491149
if (!VD || isa<GenericTypeParamDecl>(VD))
11501150
return true;
11511151

@@ -1156,11 +1156,22 @@ class AccessScopeChecker {
11561156
return true;
11571157
}
11581158

1159-
// Simulation for Swift 3 bug.
1160-
if (isa<TypeAliasDecl>(VD) &&
1161-
VD->getInterfaceType()->hasTypeParameter() &&
1162-
Context.isSwiftVersion3())
1163-
return true;
1159+
// Simulation for Swift 3 bugs where typealiases got canonicalized away and
1160+
// thus a reference to a private typealias would not be diagnosed.
1161+
if (auto *TAD = dyn_cast<TypeAliasDecl>(VD)) {
1162+
if (Context.isSwiftVersion3()) {
1163+
// - If the typealias was a dependent type, Swift 3 resolved it down to
1164+
// its underlying type.
1165+
if (VD->getInterfaceType()->hasTypeParameter())
1166+
return true;
1167+
// - If the typealias was a function type in parameter position, Swift 3
1168+
// would rebuild the type to mark it non-escaping, losing the sugar.
1169+
if (isInParameter &&
1170+
TAD->getDeclaredInterfaceType()->getAs<AnyFunctionType>()) {
1171+
return true;
1172+
}
1173+
}
1174+
}
11641175

11651176
auto cached = Cache.find(VD);
11661177
if (cached != Cache.end()) {
@@ -1179,27 +1190,52 @@ class AccessScopeChecker {
11791190
};
11801191

11811192
class TypeReprAccessScopeChecker : private ASTWalker, AccessScopeChecker {
1193+
SmallVector<const TypeRepr *, 4> ParamParents;
1194+
11821195
TypeReprAccessScopeChecker(const DeclContext *useDC,
1183-
decltype(TypeChecker::TypeAccessScopeCache) &caches)
1184-
: AccessScopeChecker(useDC, caches) {}
1196+
decltype(TypeChecker::TypeAccessScopeCache) &caches,
1197+
bool isParameter)
1198+
: AccessScopeChecker(useDC, caches) {
1199+
if (isParameter)
1200+
ParamParents.push_back(nullptr);
1201+
}
1202+
1203+
bool isParamParent(const TypeRepr *TR) const {
1204+
return !ParamParents.empty() && ParamParents.back() == TR;
1205+
}
11851206

11861207
bool walkToTypeReprPre(TypeRepr *TR) override {
1187-
auto CITR = dyn_cast<ComponentIdentTypeRepr>(TR);
1188-
if (!CITR)
1189-
return true;
1208+
if (auto CITR = dyn_cast<ComponentIdentTypeRepr>(TR)) {
1209+
return visitDecl(CITR->getBoundDecl(),
1210+
isParamParent(Parent.getAsTypeRepr()));
1211+
}
1212+
1213+
auto parentTR = Parent.getAsTypeRepr();
1214+
if (auto parentFn = dyn_cast_or_null<FunctionTypeRepr>(parentTR)) {
1215+
// The argument tuple for a function is a parent of parameter TRs.
1216+
if (parentFn->getArgsTypeRepr() == TR)
1217+
ParamParents.push_back(TR);
1218+
} else if (isa<CompoundIdentTypeRepr>(TR) && isParamParent(parentTR)) {
1219+
// Compound TRs that would have been parameter TRs contain parameter
1220+
// TRs.
1221+
ParamParents.push_back(TR);
1222+
}
11901223

1191-
return visitDecl(CITR->getBoundDecl());
1224+
return true;
11921225
}
11931226

11941227
bool walkToTypeReprPost(TypeRepr *TR) override {
1228+
if (isParamParent(TR))
1229+
ParamParents.pop_back();
11951230
return Scope.hasValue();
11961231
}
11971232

11981233
public:
11991234
static Optional<AccessScope>
12001235
getAccessScope(TypeRepr *TR, const DeclContext *useDC,
1201-
decltype(TypeChecker::TypeAccessScopeCache) &caches) {
1202-
TypeReprAccessScopeChecker checker(useDC, caches);
1236+
decltype(TypeChecker::TypeAccessScopeCache) &caches,
1237+
bool isParameter) {
1238+
TypeReprAccessScopeChecker checker(useDC, caches, isParameter);
12031239
TR->walk(checker);
12041240
return checker.Scope;
12051241
}
@@ -1262,7 +1298,8 @@ void TypeChecker::computeDefaultAccessibility(ExtensionDecl *ED) {
12621298
auto accessScope =
12631299
TypeReprAccessScopeChecker::getAccessScope(TL.getTypeRepr(),
12641300
ED->getDeclContext(),
1265-
TypeAccessScopeCache);
1301+
TypeAccessScopeCache,
1302+
/*isParameter*/false);
12661303
// This is an error case and will be diagnosed elsewhere.
12671304
if (!accessScope.hasValue())
12681305
return Accessibility::Public;
@@ -1475,7 +1512,7 @@ enum class DowngradeToWarning: bool {
14751512
/// is never null.
14761513
static void checkTypeAccessibilityImpl(
14771514
TypeChecker &TC, TypeLoc TL, AccessScope contextAccessScope,
1478-
const DeclContext *useDC,
1515+
const DeclContext *useDC, bool isParameter,
14791516
llvm::function_ref<void(AccessScope, const TypeRepr *)> diagnose) {
14801517
if (!TC.getLangOpts().EnableAccessControl)
14811518
return;
@@ -1493,7 +1530,8 @@ static void checkTypeAccessibilityImpl(
14931530
auto typeAccessScope =
14941531
(TL.getTypeRepr()
14951532
? TypeReprAccessScopeChecker::getAccessScope(TL.getTypeRepr(), useDC,
1496-
TC.TypeAccessScopeCache)
1533+
TC.TypeAccessScopeCache,
1534+
isParameter)
14971535
: TypeAccessScopeChecker::getAccessScope(TL.getType(), useDC,
14981536
TC.TypeAccessScopeCache));
14991537

@@ -1528,9 +1566,18 @@ static void checkTypeAccessibility(
15281566
TypeChecker &TC, TypeLoc TL, const ValueDecl *context,
15291567
llvm::function_ref<void(AccessScope, const TypeRepr *,
15301568
DowngradeToWarning)> diagnose) {
1569+
const DeclContext *DC = context->getDeclContext();
1570+
bool isParam = false;
1571+
if (auto *param = dyn_cast<ParamDecl>(context)) {
1572+
isParam = true;
1573+
context = dyn_cast<AbstractFunctionDecl>(DC);
1574+
if (!context)
1575+
context = cast<SubscriptDecl>(DC);
1576+
DC = context->getDeclContext();
1577+
}
1578+
15311579
AccessScope contextAccessScope = context->getFormalAccessScope();
1532-
checkTypeAccessibilityImpl(TC, TL, contextAccessScope,
1533-
context->getDeclContext(),
1580+
checkTypeAccessibilityImpl(TC, TL, contextAccessScope, DC, isParam,
15341581
[=](AccessScope requiredAccessScope,
15351582
const TypeRepr *offendingTR) {
15361583
auto downgradeToWarning = DowngradeToWarning::No;
@@ -1591,6 +1638,7 @@ static void checkGenericParamAccessibility(TypeChecker &TC,
15911638
assert(param->getInherited().size() == 1);
15921639
checkTypeAccessibilityImpl(TC, param->getInherited().front(), accessScope,
15931640
owner->getDeclContext(),
1641+
/*isParameter*/false,
15941642
[&](AccessScope typeAccessScope,
15951643
const TypeRepr *thisComplainRepr) {
15961644
if (typeAccessScope.isChildOf(minAccessScope) ||
@@ -1618,18 +1666,18 @@ static void checkGenericParamAccessibility(TypeChecker &TC,
16181666
case RequirementReprKind::TypeConstraint:
16191667
checkTypeAccessibilityImpl(TC, requirement.getSubjectLoc(),
16201668
accessScope, owner->getDeclContext(),
1621-
callback);
1669+
/*isParameter*/false, callback);
16221670
checkTypeAccessibilityImpl(TC, requirement.getConstraintLoc(),
16231671
accessScope, owner->getDeclContext(),
1624-
callback);
1672+
/*isParameter*/false, callback);
16251673
break;
16261674
case RequirementReprKind::SameType:
16271675
checkTypeAccessibilityImpl(TC, requirement.getFirstTypeLoc(),
16281676
accessScope, owner->getDeclContext(),
1629-
callback);
1677+
/*isParameter*/false, callback);
16301678
checkTypeAccessibilityImpl(TC, requirement.getSecondTypeLoc(),
16311679
accessScope, owner->getDeclContext(),
1632-
callback);
1680+
/*isParameter*/false, callback);
16331681
break;
16341682
}
16351683
}
@@ -1975,7 +2023,7 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
19752023
bool problemIsElement = false;
19762024

19772025
for (auto &P : *SD->getIndices()) {
1978-
checkTypeAccessibility(TC, P->getTypeLoc(), SD,
2026+
checkTypeAccessibility(TC, P->getTypeLoc(), P,
19792027
[&](AccessScope typeAccessScope,
19802028
const TypeRepr *thisComplainRepr,
19812029
DowngradeToWarning downgradeDiag) {
@@ -2047,7 +2095,7 @@ static void checkAccessibility(TypeChecker &TC, const Decl *D) {
20472095

20482096
for (auto *PL : fn->getParameterLists().slice(isTypeContext)) {
20492097
for (auto &P : *PL) {
2050-
checkTypeAccessibility(TC, P->getTypeLoc(), fn,
2098+
checkTypeAccessibility(TC, P->getTypeLoc(), P,
20512099
[&](AccessScope typeAccessScope,
20522100
const TypeRepr *thisComplainRepr,
20532101
DowngradeToWarning downgradeDiag) {

test/Compatibility/accessibility_typealias.swift

+50-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -swift-version 3
1+
// RUN: %target-typecheck-verify-swift -swift-version 3 -module-name main
22

33
public protocol P {
44
associatedtype Element
@@ -40,3 +40,52 @@ private func privateFuncWithFileprivateAlias() -> Generic<Int>.Dependent {
4040
}
4141

4242
var y = privateFuncWithFileprivateAlias()
43+
44+
45+
private typealias FnType = (_ x: Int) -> Void // expected-note * {{type declared here}}
46+
47+
public var fn1: (FnType) -> Void = { _ in }
48+
public var fn2: (_ x: FnType) -> Void = { _ in }
49+
public var fn3: (main.FnType) -> Void = { _ in }
50+
public var fn4: (_ x: main.FnType) -> Void = { _ in }
51+
public var nested1: (_ x: (FnType) -> Void) -> Void = { _ in }
52+
public var nested2: (_ x: (main.FnType) -> Void) -> Void = { _ in }
53+
public func test1(x: FnType) {}
54+
public func test2(x: main.FnType) {}
55+
56+
57+
public func reject1(x: FnType?) {} // expected-error {{cannot be declared public}}
58+
public func reject2(x: main.FnType?) {} // expected-error {{cannot be declared public}}
59+
public func reject3() -> FnType { fatalError() } // expected-error {{cannot be declared public}}
60+
public func reject4() -> main.FnType { fatalError() } // expected-error {{cannot be declared public}}
61+
public var rejectVar1: FnType = {_ in } // expected-error {{cannot be declared public}}
62+
public var rejectVar2: main.FnType = {_ in } // expected-error {{cannot be declared public}}
63+
public var rejectVar3: FnType? // expected-error {{cannot be declared public}}
64+
public var rejectVar4: main.FnType? // expected-error {{cannot be declared public}}
65+
public var escaping1: (@escaping FnType) -> Void = { _ in } // expected-error {{cannot be declared public}}
66+
public var escaping2: (_ x: @escaping FnType) -> Void = { _ in } // expected-error {{cannot be declared public}}
67+
public var escaping3: (@escaping main.FnType) -> Void = { _ in } // expected-error {{cannot be declared public}}
68+
public var escaping4: (_ x: @escaping main.FnType) -> Void = { _ in } // expected-error {{cannot be declared public}}
69+
70+
public struct SubscriptTest {
71+
public subscript(test1 x: FnType) -> () { return }
72+
public subscript(test2 x: main.FnType) -> () { return }
73+
74+
public subscript(reject1 x: FnType?) -> () { return } // expected-error {{cannot be declared public}}
75+
public subscript(reject2 x: main.FnType?) -> () { return } // expected-error {{cannot be declared public}}
76+
public subscript(reject3 x: Int) -> FnType { fatalError() } // expected-error {{cannot be declared public}}
77+
public subscript(reject4 x: Int) -> main.FnType { fatalError() } // expected-error {{cannot be declared public}}
78+
}
79+
80+
private struct ActuallyPrivate {} // expected-note * {{declared here}}
81+
private typealias ActuallyPrivateAlias = ActuallyPrivate
82+
83+
public var failFn: (ActuallyPrivate) -> Void = { _ in } // expected-error {{cannot be declared public}}
84+
public var failFn2: (_ x: ActuallyPrivate) -> Void = { _ in } // expected-error {{cannot be declared public}}
85+
public var failFn3: (main.ActuallyPrivate) -> Void = { _ in } // expected-error {{cannot be declared public}}
86+
public var failFn4: (_ x: main.ActuallyPrivate) -> Void = { _ in } // expected-error {{cannot be declared public}}
87+
public var failNested1: (_ x: (ActuallyPrivate) -> Void) -> Void = { _ in } // expected-error {{cannot be declared public}}
88+
public var failNested2: (_ x: (main.ActuallyPrivate) -> Void) -> Void = { _ in } // expected-error {{cannot be declared public}}
89+
public func failTest(x: ActuallyPrivate) {} // expected-error {{cannot be declared public}}
90+
public func failTest2(x: main.ActuallyPrivate) {} // expected-error {{cannot be declared public}}
91+

test/Sema/accessibility_typealias.swift

+50-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -swift-version 4
1+
// RUN: %target-typecheck-verify-swift -swift-version 4 -module-name main
22

33
public protocol P {
44
associatedtype Element
@@ -44,3 +44,52 @@ private func privateFuncWithFileprivateAlias() -> Generic<Int>.Dependent {
4444

4545
// FIXME: No error here
4646
var y = privateFuncWithFileprivateAlias()
47+
48+
49+
private typealias FnType = (_ x: Int) -> Void // expected-note * {{type declared here}}
50+
51+
public var fn1: (FnType) -> Void = { _ in } // expected-error {{cannot be declared public}}
52+
public var fn2: (_ x: FnType) -> Void = { _ in } // expected-error {{cannot be declared public}}
53+
public var fn3: (main.FnType) -> Void = { _ in } // expected-error {{cannot be declared public}}
54+
public var fn4: (_ x: main.FnType) -> Void = { _ in } // expected-error {{cannot be declared public}}
55+
public var nested1: (_ x: (FnType) -> Void) -> Void = { _ in } // expected-error {{cannot be declared public}}
56+
public var nested2: (_ x: (main.FnType) -> Void) -> Void = { _ in } // expected-error {{cannot be declared public}}
57+
public func test1(x: FnType) {} // expected-error {{cannot be declared public}}
58+
public func test2(x: main.FnType) {} // expected-error {{cannot be declared public}}
59+
60+
61+
public func reject1(x: FnType?) {} // expected-error {{cannot be declared public}}
62+
public func reject2(x: main.FnType?) {} // expected-error {{cannot be declared public}}
63+
public func reject3() -> FnType { fatalError() } // expected-error {{cannot be declared public}}
64+
public func reject4() -> main.FnType { fatalError() } // expected-error {{cannot be declared public}}
65+
public var rejectVar1: FnType = {_ in } // expected-error {{cannot be declared public}}
66+
public var rejectVar2: main.FnType = {_ in } // expected-error {{cannot be declared public}}
67+
public var rejectVar3: FnType? // expected-error {{cannot be declared public}}
68+
public var rejectVar4: main.FnType? // expected-error {{cannot be declared public}}
69+
public var escaping1: (@escaping FnType) -> Void = { _ in } // expected-error {{cannot be declared public}}
70+
public var escaping2: (_ x: @escaping FnType) -> Void = { _ in } // expected-error {{cannot be declared public}}
71+
public var escaping3: (@escaping main.FnType) -> Void = { _ in } // expected-error {{cannot be declared public}}
72+
public var escaping4: (_ x: @escaping main.FnType) -> Void = { _ in } // expected-error {{cannot be declared public}}
73+
74+
public struct SubscriptTest {
75+
public subscript(test1 x: FnType) -> () { return } // expected-error {{cannot be declared public}}
76+
public subscript(test2 x: main.FnType) -> () { return } // expected-error {{cannot be declared public}}
77+
78+
public subscript(reject1 x: FnType?) -> () { return } // expected-error {{cannot be declared public}}
79+
public subscript(reject2 x: main.FnType?) -> () { return } // expected-error {{cannot be declared public}}
80+
public subscript(reject3 x: Int) -> FnType { fatalError() } // expected-error {{cannot be declared public}}
81+
public subscript(reject4 x: Int) -> main.FnType { fatalError() } // expected-error {{cannot be declared public}}
82+
}
83+
84+
private struct ActuallyPrivate {} // expected-note * {{declared here}}
85+
private typealias ActuallyPrivateAlias = ActuallyPrivate
86+
87+
public var failFn: (ActuallyPrivate) -> Void = { _ in } // expected-error {{cannot be declared public}}
88+
public var failFn2: (_ x: ActuallyPrivate) -> Void = { _ in } // expected-error {{cannot be declared public}}
89+
public var failFn3: (main.ActuallyPrivate) -> Void = { _ in } // expected-error {{cannot be declared public}}
90+
public var failFn4: (_ x: main.ActuallyPrivate) -> Void = { _ in } // expected-error {{cannot be declared public}}
91+
public var failNested1: (_ x: (ActuallyPrivate) -> Void) -> Void = { _ in } // expected-error {{cannot be declared public}}
92+
public var failNested2: (_ x: (main.ActuallyPrivate) -> Void) -> Void = { _ in } // expected-error {{cannot be declared public}}
93+
public func failTest(x: ActuallyPrivate) {} // expected-error {{cannot be declared public}}
94+
public func failTest2(x: main.ActuallyPrivate) {} // expected-error {{cannot be declared public}}
95+

0 commit comments

Comments
 (0)