Skip to content

Commit 66c3abd

Browse files
authored
Limit access of implicit typealiases for associated type witnesses (#20891)
When conformance checking infers an associated type, it makes a new typealias to represent that type. Unfortunately, the access of that typealias was always the same as the conforming type's, which (1) might have leaked implementation details if the protocol wasn't as public as the conforming type, and (2) might have been straight-up incorrect if the /inferred type/ wasn't as public as the conforming type. Fix this in pre-Swift-5 modes by limiting the access to fit the underlying type (technically source-breaking, but most code that relied on this would have failed to link anyway), and in Swift 5 mode by using the minimum possible access (the intersection of the protocol and conforming type). rdar://problem/46143405 (cherry picked from commit 859e2b0)
1 parent f5c4cb0 commit 66c3abd

File tree

2 files changed

+82
-5
lines changed

2 files changed

+82
-5
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "swift/Basic/StringExtras.h"
2424
#include "swift/Basic/Statistic.h"
2525
#include "swift/AST/AccessScope.h"
26+
#include "swift/AST/AccessScopeChecker.h"
2627
#include "swift/AST/GenericSignatureBuilder.h"
2728
#include "swift/AST/ASTContext.h"
2829
#include "swift/AST/ASTMangler.h"
@@ -2474,11 +2475,46 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType,
24742475

24752476
// Inject the typealias into the nominal decl that conforms to the protocol.
24762477
if (auto nominal = DC->getSelfNominalTypeDecl()) {
2477-
// FIXME: Ideally this would use the protocol's access too---that is,
2478-
// a typealias added for an internal protocol shouldn't need to be
2479-
// public---but that can be problematic if the same type conforms to two
2480-
// protocols with different access levels.
2481-
aliasDecl->copyFormalAccessFrom(nominal, /*sourceIsParentContext*/true);
2478+
AccessScope requiredAccessScope = getRequiredAccessScope();
2479+
2480+
if (!TC.Context.isSwiftVersionAtLeast(5) &&
2481+
DC->getParentModule()->getResilienceStrategy() !=
2482+
ResilienceStrategy::Resilient) {
2483+
// HACK: In pre-Swift-5, these typealiases were synthesized with the
2484+
// same access level as the conforming type, which might be more
2485+
// visible than the associated type witness. Preserve that behavior
2486+
// when the underlying type has sufficient access, but only in
2487+
// non-resilient modules.
2488+
Optional<AccessScope> underlyingTypeScope =
2489+
TypeAccessScopeChecker::getAccessScope(type, DC,
2490+
/*usableFromInline*/false);
2491+
assert(underlyingTypeScope.hasValue() &&
2492+
"the type is already invalid and we shouldn't have gotten here");
2493+
2494+
AccessScope nominalAccessScope = nominal->getFormalAccessScope(DC);
2495+
Optional<AccessScope> widestPossibleScope =
2496+
underlyingTypeScope->intersectWith(nominalAccessScope);
2497+
assert(widestPossibleScope.hasValue() &&
2498+
"we found the nominal and the type witness, didn't we?");
2499+
requiredAccessScope = widestPossibleScope.getValue();
2500+
}
2501+
2502+
// An associated type witness can never be less than fileprivate, since
2503+
// it must always be at least as visible as the enclosing type.
2504+
AccessLevel requiredAccess =
2505+
std::max(requiredAccessScope.accessLevelForDiagnostics(),
2506+
AccessLevel::FilePrivate);
2507+
2508+
aliasDecl->setAccess(requiredAccess);
2509+
if (isUsableFromInlineRequired()) {
2510+
auto *attr = new (TC.Context) UsableFromInlineAttr(/*implicit=*/true);
2511+
aliasDecl->getAttrs().add(attr);
2512+
}
2513+
2514+
bool unused;
2515+
assert(TC.Context.hadError() ||
2516+
!checkWitnessAccess(assocType, aliasDecl, &unused));
2517+
(void)unused;
24822518

24832519
if (nominal == DC) {
24842520
nominal->addMember(aliasDecl);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-ide-test -skip-deinit=false -print-ast-typechecked -print-access -source-filename=%s -swift-version 4| %FileCheck -check-prefix=CHECK -check-prefix=CHECK-4 %s
4+
// RUN: %target-swift-frontend -emit-module-path %t/accessibility_print.swiftmodule -module-name accessibility_print %s -swift-version 4
5+
// RUN: %target-swift-ide-test -skip-deinit=false -print-module -print-access -module-to-print=accessibility_print -I %t -source-filename=%s -swift-version 4 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-4 %s
6+
7+
// RUN: %target-swift-ide-test -skip-deinit=false -print-ast-typechecked -print-access -source-filename=%s -swift-version 5 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-5 %s
8+
// RUN: %target-swift-frontend -emit-module-path %t/accessibility_print.swiftmodule -module-name accessibility_print %s -swift-version 5
9+
// RUN: %target-swift-ide-test -skip-deinit=false -print-module -print-access -module-to-print=accessibility_print -I %t -source-filename=%s -swift-version 5 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-5 %s
10+
11+
// Enabling resilience means opting into the new behavior.
12+
// RUN: %target-swift-frontend -emit-module-path %t/accessibility_print.swiftmodule -module-name accessibility_print %s -swift-version 4 -enable-resilience
13+
// RUN: %target-swift-ide-test -skip-deinit=false -print-module -print-access -module-to-print=accessibility_print -I %t -source-filename=%s -swift-version 4 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-5 %s
14+
15+
internal struct InternalStruct {}
16+
17+
public protocol PublicAssocTypeProto {
18+
associatedtype PublicValue
19+
var publicValue: PublicValue { get }
20+
}
21+
fileprivate protocol FilePrivateAssocTypeProto {
22+
associatedtype FilePrivateValue
23+
var filePrivateValue: FilePrivateValue { get }
24+
}
25+
26+
// CHECK-LABEL: private{{(\*/)?}} class PrivateImpl : PublicAssocTypeProto, FilePrivateAssocTypeProto {
27+
private class PrivateImpl: PublicAssocTypeProto, FilePrivateAssocTypeProto {
28+
fileprivate var publicValue: InternalStruct?
29+
fileprivate var filePrivateValue: Int?
30+
// CHECK-DAG: {{^}} fileprivate typealias PublicValue
31+
// CHECK-DAG: {{^}} fileprivate typealias FilePrivateValue
32+
} // CHECK: {{^[}]}}
33+
34+
// CHECK-LABEL: public{{(\*/)?}} class PublicImpl : PublicAssocTypeProto, FilePrivateAssocTypeProto {
35+
public class PublicImpl: PublicAssocTypeProto, FilePrivateAssocTypeProto {
36+
public var publicValue: Int?
37+
fileprivate var filePrivateValue: InternalStruct?
38+
// CHECK-DAG: {{^}} public typealias PublicValue
39+
// CHECK-4-DAG: {{^}} internal typealias FilePrivateValue
40+
// CHECK-5-DAG: {{^}} fileprivate typealias FilePrivateValue
41+
} // CHECK: {{^[}]}}

0 commit comments

Comments
 (0)