Skip to content

Commit 9489f10

Browse files
committed
Use Swift-3-style access checking to downgrade errors to warnings.
...instead of just ignoring the errors in certain cases, in service of source compatibility. Swift 3.0 wasn't nearly as strict as checking access control for types because it didn't look at the TypeRepr at all (except to highlight a particular part of the type in diagnostics). It also looked through typealiases in certain cases. Approximate this behavior by running the access checking logic for Types (rather than TypeReprs), and downgrade access violation errors to warnings when the checks disagree. Part of rdar://problem/29855782.
1 parent 9c0f690 commit 9489f10

File tree

4 files changed

+103
-105
lines changed

4 files changed

+103
-105
lines changed

lib/Sema/TypeCheckDecl.cpp

Lines changed: 79 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,7 @@ class AccessScopeChecker {
11471147
Cache(caches[File]),
11481148
Context(File->getASTContext()) {}
11491149

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

@@ -1158,23 +1158,6 @@ class AccessScopeChecker {
11581158
return true;
11591159
}
11601160

1161-
// Simulation for Swift 3 bugs where typealiases got canonicalized away and
1162-
// thus a reference to a private typealias would not be diagnosed.
1163-
if (auto *TAD = dyn_cast<TypeAliasDecl>(VD)) {
1164-
if (Context.isSwiftVersion3()) {
1165-
// - If the typealias was a dependent type, Swift 3 resolved it down to
1166-
// its underlying type.
1167-
if (VD->getInterfaceType()->hasTypeParameter())
1168-
return true;
1169-
// - If the typealias was a function type in parameter position, Swift 3
1170-
// would rebuild the type to mark it non-escaping, losing the sugar.
1171-
if (isInParameter &&
1172-
TAD->getDeclaredInterfaceType()->getAs<AnyFunctionType>()) {
1173-
return true;
1174-
}
1175-
}
1176-
}
1177-
11781161
auto cached = Cache.find(VD);
11791162
if (cached != Cache.end()) {
11801163
Scope = Scope->intersectWith(cached->second);
@@ -1192,72 +1175,39 @@ class AccessScopeChecker {
11921175
};
11931176

11941177
class TypeReprAccessScopeChecker : private ASTWalker, AccessScopeChecker {
1195-
SmallVector<const TypeRepr *, 4> ParamParents;
1196-
11971178
TypeReprAccessScopeChecker(const DeclContext *useDC,
1198-
decltype(TypeChecker::TypeAccessScopeCache) &caches,
1199-
bool isParameter)
1179+
decltype(TypeChecker::TypeAccessScopeCache) &caches)
12001180
: AccessScopeChecker(useDC, caches) {
1201-
if (isParameter)
1202-
ParamParents.push_back(nullptr);
1203-
}
1204-
1205-
bool isParamParent(const TypeRepr *TR) const {
1206-
return !ParamParents.empty() && ParamParents.back() == TR;
12071181
}
12081182

12091183
bool walkToTypeReprPre(TypeRepr *TR) override {
1210-
if (auto CITR = dyn_cast<ComponentIdentTypeRepr>(TR)) {
1211-
// In Swift 3, components other than the last one were not properly
1212-
// checked for availability.
1213-
// FIXME: We should try to downgrade these errors to warnings, not just
1214-
// skip diagnosing them.
1215-
if (Context.LangOpts.isSwiftVersion3()) {
1216-
const TypeRepr *parent = Parent.getAsTypeRepr();
1217-
if (auto *compound = dyn_cast_or_null<CompoundIdentTypeRepr>(parent))
1218-
if (compound->Components.back() != CITR)
1219-
return true;
1220-
}
1221-
1222-
return visitDecl(CITR->getBoundDecl(),
1223-
isParamParent(Parent.getAsTypeRepr()));
1224-
}
1225-
1226-
auto parentTR = Parent.getAsTypeRepr();
1227-
if (auto parentFn = dyn_cast_or_null<FunctionTypeRepr>(parentTR)) {
1228-
// The argument tuple for a function is a parent of parameter TRs.
1229-
if (parentFn->getArgsTypeRepr() == TR)
1230-
ParamParents.push_back(TR);
1231-
} else if (isa<CompoundIdentTypeRepr>(TR) && isParamParent(parentTR)) {
1232-
// Compound TRs that would have been parameter TRs contain parameter
1233-
// TRs.
1234-
ParamParents.push_back(TR);
1235-
}
1236-
1184+
if (auto CITR = dyn_cast<ComponentIdentTypeRepr>(TR))
1185+
return visitDecl(CITR->getBoundDecl());
12371186
return true;
12381187
}
12391188

12401189
bool walkToTypeReprPost(TypeRepr *TR) override {
1241-
if (isParamParent(TR))
1242-
ParamParents.pop_back();
12431190
return Scope.hasValue();
12441191
}
12451192

12461193
public:
12471194
static Optional<AccessScope>
12481195
getAccessScope(TypeRepr *TR, const DeclContext *useDC,
1249-
decltype(TypeChecker::TypeAccessScopeCache) &caches,
1250-
bool isParameter) {
1251-
TypeReprAccessScopeChecker checker(useDC, caches, isParameter);
1196+
decltype(TypeChecker::TypeAccessScopeCache) &caches) {
1197+
TypeReprAccessScopeChecker checker(useDC, caches);
12521198
TR->walk(checker);
12531199
return checker.Scope;
12541200
}
12551201
};
12561202

12571203
class TypeAccessScopeChecker : private TypeWalker, AccessScopeChecker {
1204+
bool CanonicalizeParentTypes;
1205+
12581206
TypeAccessScopeChecker(const DeclContext *useDC,
1259-
decltype(TypeChecker::TypeAccessScopeCache) &caches)
1260-
: AccessScopeChecker(useDC, caches) {}
1207+
decltype(TypeChecker::TypeAccessScopeCache) &caches,
1208+
bool canonicalizeParentTypes)
1209+
: AccessScopeChecker(useDC, caches),
1210+
CanonicalizeParentTypes(canonicalizeParentTypes) {}
12611211

12621212
Action walkToTypePre(Type T) override {
12631213
ValueDecl *VD;
@@ -1268,14 +1218,34 @@ class TypeAccessScopeChecker : private TypeWalker, AccessScopeChecker {
12681218
else
12691219
VD = nullptr;
12701220

1271-
return visitDecl(VD) ? Action::Continue : Action::Stop;
1221+
if (!visitDecl(VD))
1222+
return Action::Stop;
1223+
1224+
if (!CanonicalizeParentTypes)
1225+
return Action::Continue;
1226+
1227+
Type nominalParentTy;
1228+
if (auto nominalTy = dyn_cast<NominalType>(T.getPointer())) {
1229+
nominalParentTy = nominalTy->getParent();
1230+
} else if (auto genericTy = dyn_cast<BoundGenericType>(T.getPointer())) {
1231+
nominalParentTy = genericTy->getParent();
1232+
for (auto genericArg : genericTy->getGenericArgs())
1233+
genericArg.walk(*this);
1234+
} else {
1235+
return Action::Continue;
1236+
}
1237+
1238+
if (nominalParentTy)
1239+
nominalParentTy->getCanonicalType().walk(*this);
1240+
return Action::SkipChildren;
12721241
}
12731242

12741243
public:
12751244
static Optional<AccessScope>
12761245
getAccessScope(Type T, const DeclContext *useDC,
1277-
decltype(TypeChecker::TypeAccessScopeCache) &caches) {
1278-
TypeAccessScopeChecker checker(useDC, caches);
1246+
decltype(TypeChecker::TypeAccessScopeCache) &caches,
1247+
bool canonicalizeParentTypes = false) {
1248+
TypeAccessScopeChecker checker(useDC, caches, canonicalizeParentTypes);
12791249
T.walk(checker);
12801250
return checker.Scope;
12811251
}
@@ -1311,8 +1281,7 @@ void TypeChecker::computeDefaultAccessibility(ExtensionDecl *ED) {
13111281
auto accessScope =
13121282
TypeReprAccessScopeChecker::getAccessScope(TL.getTypeRepr(),
13131283
ED->getDeclContext(),
1314-
TypeAccessScopeCache,
1315-
/*isParameter*/false);
1284+
TypeAccessScopeCache);
13161285
// This is an error case and will be diagnosed elsewhere.
13171286
if (!accessScope.hasValue())
13181287
return Accessibility::Public;
@@ -1533,7 +1502,7 @@ using CheckTypeAccessCallback =
15331502
/// is never null.
15341503
static void checkTypeAccessibilityImpl(
15351504
TypeChecker &TC, TypeLoc TL, AccessScope contextAccessScope,
1536-
const DeclContext *useDC, bool isParameter,
1505+
const DeclContext *useDC,
15371506
llvm::function_ref<CheckTypeAccessCallback> diagnose) {
15381507
if (!TC.getLangOpts().EnableAccessControl)
15391508
return;
@@ -1551,8 +1520,7 @@ static void checkTypeAccessibilityImpl(
15511520
auto typeAccessScope =
15521521
(TL.getTypeRepr()
15531522
? TypeReprAccessScopeChecker::getAccessScope(TL.getTypeRepr(), useDC,
1554-
TC.TypeAccessScopeCache,
1555-
isParameter)
1523+
TC.TypeAccessScopeCache)
15561524
: TypeAccessScopeChecker::getAccessScope(TL.getType(), useDC,
15571525
TC.TypeAccessScopeCache));
15581526

@@ -1562,17 +1530,48 @@ static void checkTypeAccessibilityImpl(
15621530
if (!typeAccessScope.hasValue())
15631531
return;
15641532

1565-
if (typeAccessScope->isPublic() ||
1566-
typeAccessScope->hasEqualDeclContextWith(contextAccessScope) ||
1567-
contextAccessScope.isChildOf(*typeAccessScope))
1533+
auto shouldComplainAboutAccessScope =
1534+
[contextAccessScope](AccessScope scope) -> bool {
1535+
if (scope.isPublic())
1536+
return false;
1537+
if (scope.hasEqualDeclContextWith(contextAccessScope))
1538+
return false;
1539+
if (contextAccessScope.isChildOf(scope))
1540+
return false;
1541+
return true;
1542+
};
1543+
1544+
if (!shouldComplainAboutAccessScope(typeAccessScope.getValue()))
15681545
return;
15691546

1547+
// Swift 3.0 wasn't nearly as strict as checking types because it didn't
1548+
// look at the TypeRepr at all except to highlight a particular part of the
1549+
// type in diagnostics, and looked through typealiases in other cases.
1550+
// Approximate this behavior by running our non-TypeRepr-based check again
1551+
// and downgrading to a warning when the checks disagree.
1552+
auto downgradeToWarning = DowngradeToWarning::No;
1553+
if (TC.getLangOpts().isSwiftVersion3()) {
1554+
auto typeOnlyAccessScope =
1555+
TypeAccessScopeChecker::getAccessScope(TL.getType(), useDC,
1556+
TC.TypeAccessScopeCache,
1557+
/*canonicalizeParents*/true);
1558+
if (typeOnlyAccessScope.hasValue()) {
1559+
// If Swift 4 would have complained about a private type, but Swift 4
1560+
// would only diagnose an internal type, complain about the Swift 3
1561+
// offense first to avoid confusing users.
1562+
if (shouldComplainAboutAccessScope(typeOnlyAccessScope.getValue()))
1563+
typeAccessScope = typeOnlyAccessScope;
1564+
else
1565+
downgradeToWarning = DowngradeToWarning::Yes;
1566+
}
1567+
}
1568+
15701569
const TypeRepr *complainRepr =
15711570
TypeAccessScopeDiagnoser::findTypeWithScope(
15721571
TL.getTypeRepr(),
15731572
*typeAccessScope,
15741573
useDC);
1575-
diagnose(*typeAccessScope, complainRepr, DowngradeToWarning::No);
1574+
diagnose(*typeAccessScope, complainRepr, downgradeToWarning);
15761575
}
15771576

15781577
/// Checks if the access scope of the type described by \p TL is valid for the
@@ -1587,17 +1586,15 @@ static void checkTypeAccessibility(
15871586
TypeChecker &TC, TypeLoc TL, const ValueDecl *context,
15881587
llvm::function_ref<CheckTypeAccessCallback> diagnose) {
15891588
const DeclContext *DC = context->getDeclContext();
1590-
bool isParam = false;
15911589
if (isa<ParamDecl>(context)) {
1592-
isParam = true;
15931590
context = dyn_cast<AbstractFunctionDecl>(DC);
15941591
if (!context)
15951592
context = cast<SubscriptDecl>(DC);
15961593
DC = context->getDeclContext();
15971594
}
15981595

15991596
AccessScope contextAccessScope = context->getFormalAccessScope();
1600-
checkTypeAccessibilityImpl(TC, TL, contextAccessScope, DC, isParam,
1597+
checkTypeAccessibilityImpl(TC, TL, contextAccessScope, DC,
16011598
[=](AccessScope requiredAccessScope,
16021599
const TypeRepr *offendingTR,
16031600
DowngradeToWarning downgradeToWarning) {
@@ -1654,7 +1651,6 @@ static void checkGenericParamAccessibility(TypeChecker &TC,
16541651
assert(param->getInherited().size() == 1);
16551652
checkTypeAccessibilityImpl(TC, param->getInherited().front(), accessScope,
16561653
owner->getDeclContext(),
1657-
/*isParameter*/false,
16581654
[&](AccessScope typeAccessScope,
16591655
const TypeRepr *thisComplainRepr,
16601656
DowngradeToWarning thisDowngrade) {
@@ -1690,23 +1686,23 @@ static void checkGenericParamAccessibility(TypeChecker &TC,
16901686
case RequirementReprKind::TypeConstraint:
16911687
checkTypeAccessibilityImpl(TC, requirement.getSubjectLoc(),
16921688
accessScope, owner->getDeclContext(),
1693-
/*isParameter*/false, callback);
1689+
callback);
16941690
checkTypeAccessibilityImpl(TC, requirement.getConstraintLoc(),
16951691
accessScope, owner->getDeclContext(),
1696-
/*isParameter*/false, callback);
1692+
callback);
16971693
break;
16981694
case RequirementReprKind::LayoutConstraint:
16991695
checkTypeAccessibilityImpl(TC, requirement.getSubjectLoc(),
17001696
accessScope, owner->getDeclContext(),
1701-
/*isParameter*/false, callback);
1697+
callback);
17021698
break;
17031699
case RequirementReprKind::SameType:
17041700
checkTypeAccessibilityImpl(TC, requirement.getFirstTypeLoc(),
17051701
accessScope, owner->getDeclContext(),
1706-
/*isParameter*/false, callback);
1702+
callback);
17071703
checkTypeAccessibilityImpl(TC, requirement.getSecondTypeLoc(),
17081704
accessScope, owner->getDeclContext(),
1709-
/*isParameter*/false, callback);
1705+
callback);
17101706
break;
17111707
}
17121708
}

test/Compatibility/accessibility_compound.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,35 @@ public struct PublicStruct {
77
internal struct Internal {}
88
}
99

10-
private typealias PrivateAlias = PublicStruct // expected-note {{type declared here}}
10+
private typealias PrivateAlias = PublicStruct // expected-note * {{type declared here}}
1111

12-
public let a: PrivateAlias.Inner?
12+
public let a0 = nil as PrivateAlias.Inner? // expected-warning {{constant should not be declared public because its type 'PrivateAlias.Inner?' uses a private type}}
13+
public let a: PrivateAlias.Inner? // expected-warning {{constant should not be declared public because its type uses a private type}}
1314
public let b: PrivateAlias.Internal? // expected-error {{constant cannot be declared public because its type uses an internal type}}
1415
public let c: Pair<PrivateAlias.Inner, PublicStruct.Internal>? // expected-error {{constant cannot be declared public because its type uses an internal type}}
1516
public let c2: Pair<PublicStruct.Internal, PrivateAlias.Inner>? // expected-error {{constant cannot be declared public because its type uses an internal type}}
1617
public let d: PrivateAlias? // expected-error {{constant cannot be declared public because its type uses a private type}}
1718

1819

1920
// rdar://problem/21408035
20-
private class PrivateBox<T> { // expected-note {{type declared here}}
21+
private class PrivateBox<T> { // expected-note * {{type declared here}}
2122
typealias ValueType = T
2223
typealias AlwaysFloat = Float
2324
}
2425

25-
let boxUnboxInt: PrivateBox<Int>.ValueType = 0 // FIXME: This used to error in Swift 3.0.
26+
let boxUnboxInt: PrivateBox<Int>.ValueType = 0 // expected-warning {{constant should be declared private because its type uses a private type}}
2627
let boxFloat: PrivateBox<Int>.AlwaysFloat = 0 // expected-error {{constant must be declared private or fileprivate because its type uses a private type}}
2728

2829
private protocol PrivateProto {
2930
associatedtype Inner
3031
}
3132
extension PublicStruct: PrivateProto {}
3233

33-
private class SpecificBox<T: PrivateProto> { // expected-note {{type declared here}}
34+
private class SpecificBox<T: PrivateProto> { // expected-note * {{type declared here}}
3435
typealias InnerType = T.Inner
3536
typealias AlwaysFloat = Float
3637
}
3738

38-
let specificBoxUnboxInt: SpecificBox<PublicStruct>.InnerType = .init() // FIXME: This used to error in Swift 3.0.
39+
let specificBoxUnboxInt: SpecificBox<PublicStruct>.InnerType = .init() // expected-warning {{constant should be declared private because its type uses a private type}}
3940
let specificBoxFloat: SpecificBox<PublicStruct>.AlwaysFloat = 0 // expected-error {{constant must be declared private or fileprivate because its type uses a private type}}
4041

test/Compatibility/accessibility_typealias.swift

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ struct S<T> : P {
1111
}
1212

1313
public struct G<T> {
14-
typealias A = S<T>
14+
typealias A = S<T> // expected-note {{declared here}}
1515

16-
public func foo<U : P>(u: U) where U.Element == A.Element {}
16+
public func foo<U : P>(u: U) where U.Element == A.Element {} // expected-warning {{instance method should not be declared public because its generic requirement uses an internal type}}
1717
}
1818

1919
public final class ReplayableGenerator<S: Sequence> : IteratorProtocol {
20-
typealias Sequence = S
21-
public typealias Element = Sequence.Iterator.Element
20+
typealias Sequence = S // expected-note {{declared here}}
21+
public typealias Element = Sequence.Iterator.Element // expected-warning {{type alias should not be declared public because its underlying type uses an internal type}}
2222

2323
public func next() -> Element? {
2424
return nil
@@ -29,9 +29,9 @@ struct Generic<T> {
2929
fileprivate typealias Dependent = T
3030
}
3131

32-
var x: Generic<Int>.Dependent = 3
32+
var x: Generic<Int>.Dependent = 3 // expected-warning {{variable should be declared fileprivate because its type uses a fileprivate type}}
3333

34-
func internalFuncWithFileprivateAlias() -> Generic<Int>.Dependent {
34+
func internalFuncWithFileprivateAlias() -> Generic<Int>.Dependent { // expected-warning {{function should be declared fileprivate because its result uses a fileprivate type}}
3535
return 3
3636
}
3737

@@ -44,14 +44,14 @@ var y = privateFuncWithFileprivateAlias()
4444

4545
private typealias FnType = (_ x: Int) -> Void // expected-note * {{type declared here}}
4646

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) {}
47+
public var fn1: (FnType) -> Void = { _ in } // expected-warning {{should not be declared public}}
48+
public var fn2: (_ x: FnType) -> Void = { _ in } // expected-warning {{should not be declared public}}
49+
public var fn3: (main.FnType) -> Void = { _ in } // expected-warning {{should not be declared public}}
50+
public var fn4: (_ x: main.FnType) -> Void = { _ in } // expected-warning {{should not be declared public}}
51+
public var nested1: (_ x: (FnType) -> Void) -> Void = { _ in } // expected-warning {{should not be declared public}}
52+
public var nested2: (_ x: (main.FnType) -> Void) -> Void = { _ in } // expected-warning {{should not be declared public}}
53+
public func test1(x: FnType) {} // expected-warning {{should not be declared public}}
54+
public func test2(x: main.FnType) {} // expected-warning {{should not be declared public}}
5555

5656

5757
public func reject1(x: FnType?) {} // expected-error {{cannot be declared public}}
@@ -68,8 +68,8 @@ public var escaping3: (@escaping main.FnType) -> Void = { _ in } // expected-err
6868
public var escaping4: (_ x: @escaping main.FnType) -> Void = { _ in } // expected-error {{cannot be declared public}}
6969

7070
public struct SubscriptTest {
71-
public subscript(test1 x: FnType) -> () { return }
72-
public subscript(test2 x: main.FnType) -> () { return }
71+
public subscript(test1 x: FnType) -> () { return } // expected-warning {{should not be declared public}}
72+
public subscript(test2 x: main.FnType) -> () { return } // expected-warning {{should not be declared public}}
7373

7474
public subscript(reject1 x: FnType?) -> () { return } // expected-error {{cannot be declared public}}
7575
public subscript(reject2 x: main.FnType?) -> () { return } // expected-error {{cannot be declared public}}

0 commit comments

Comments
 (0)