Skip to content

Commit 4a6badd

Browse files
authored
Merge pull request #85846 from sepy97/public-decl-internal-protocol
Diagnose public protocol requirements witnessed by internal protocol extensions
2 parents cc5b498 + 6280a70 commit 4a6badd

File tree

6 files changed

+54
-15
lines changed

6 files changed

+54
-15
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3085,6 +3085,12 @@ ERROR(witness_not_as_sendable,none,
30853085
NOTE(less_sendable_reqt_here,none,
30863086
"expected sendability to match requirement here",
30873087
())
3088+
WARNING(witness_not_accessible_strict_check,none,
3089+
"%select{initializer %1|method %1|%select{|setter for }2property %1"
3090+
"|subscript%select{| setter}2}0 must be as accessible as its enclosing "
3091+
"type because it matches a requirement in protocol %5",
3092+
(RequirementKind, const ValueDecl *, bool, AccessLevel, AccessLevel,
3093+
const ProtocolDecl *))
30883094
ERROR(witness_not_accessible_type,none,
30893095
"%select{initializer %1|method %1|%select{|setter for }2property %1"
30903096
"|subscript%select{| setter}2}0 must be as accessible as its enclosing "

include/swift/AST/RequirementMatch.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,9 @@ enum class CheckKind : unsigned {
218218
/// The witness is less accessible than the requirement.
219219
Access,
220220

221+
/// Strict check for access holes making swiftinterface not usable
222+
AccessStrict,
223+
221224
/// The witness needs to be @usableFromInline.
222225
UsableFromInline,
223226

@@ -265,8 +268,12 @@ class RequirementCheck {
265268
ASSERT(kind != CheckKind::Availability);
266269
}
267270

268-
RequirementCheck(AccessScope requiredAccessScope, bool forSetter)
269-
: Kind(CheckKind::Access), Access{requiredAccessScope, forSetter} {}
271+
RequirementCheck(CheckKind accessKind, AccessScope requiredAccessScope,
272+
bool forSetter)
273+
: Kind(accessKind), Access{requiredAccessScope, forSetter} {
274+
ASSERT(accessKind == CheckKind::Access ||
275+
accessKind == CheckKind::AccessStrict);
276+
}
270277

271278
RequirementCheck(AvailabilityConstraint constraint,
272279
AvailabilityContext requiredContext)
@@ -291,7 +298,7 @@ class RequirementCheck {
291298
/// The required access scope for checks that failed due to the witness being
292299
/// less accessible than the requirement.
293300
AccessScope getRequiredAccessScope() const {
294-
ASSERT(Kind == CheckKind::Access);
301+
ASSERT(Kind == CheckKind::Access || Kind == CheckKind::AccessStrict);
295302
return Access.requiredScope;
296303
}
297304

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1865,8 +1865,15 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
18651865
std::make_pair(AccessScope::getPublic(), false));
18661866

18671867
bool isSetter = false;
1868+
if (match.Witness->isAccessibleFrom(requiredAccessLevel.getDeclContext(),
1869+
/*forConformance=*/true) &&
1870+
!match.Witness->isAccessibleFrom(requiredAccessLevel.getDeclContext(),
1871+
/*forConformance=*/false)) {
1872+
return RequirementCheck(CheckKind::AccessStrict, requiredAccessLevel,
1873+
isSetter);
1874+
}
18681875
if (checkWitnessAccess(DC, requirement, match.Witness, &isSetter))
1869-
return RequirementCheck(requiredAccessLevel, isSetter);
1876+
return RequirementCheck(CheckKind::Access, requiredAccessLevel, isSetter);
18701877

18711878
if (mustBeUsableFromInline) {
18721879
bool witnessIsUsableFromInline = match.Witness->getFormalAccessScope(
@@ -4414,7 +4421,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
44144421
switch (check.getKind()) {
44154422
case CheckKind::Success:
44164423
break;
4417-
4424+
case CheckKind::AccessStrict:
44184425
case CheckKind::Access: {
44194426
// Swift 4.2 relaxed some rules for protocol witness matching.
44204427
//
@@ -4446,14 +4453,15 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
44464453
auto diagKind = protoForcesAccess
44474454
? diag::witness_not_accessible_proto
44484455
: diag::witness_not_accessible_type;
4456+
if (check.getKind() == CheckKind::AccessStrict)
4457+
diagKind = diag::witness_not_accessible_strict_check;
44494458
bool isSetter = check.isForSetterAccess();
44504459

44514460
auto &diags = DC->getASTContext().Diags;
44524461
diags.diagnose(getLocForDiagnosingWitness(conformance, witness),
44534462
diagKind, getProtocolRequirementKind(requirement),
44544463
witness, isSetter, requiredAccess,
4455-
protoAccessScope.accessLevelForDiagnostics(),
4456-
proto);
4464+
protoAccessScope.accessLevelForDiagnostics(), proto);
44574465

44584466
auto *decl = dyn_cast<AbstractFunctionDecl>(witness);
44594467
if (decl && decl->isSynthesized())

test/Generics/rdar123013710.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ public protocol P {
99
}
1010

1111
protocol Q: P where B == Never {} // expected-error@:10 {{circular reference}}
12-
12+
// expected-warning@+1 {{property 'b' must be as accessible as its enclosing type because it matches a requirement in protocol 'P'}}
1313
extension Never: Q, P { // expected-note@:1 {{through reference here}}
1414
public typealias A = Never
1515
public static func f() -> Any? { nil }
1616
}
1717

1818
extension Q {
19+
// expected-note@+1 {{mark the property as 'public' to satisfy the requirement}}
1920
public var b: Never { fatalError() } // expected-note {{through reference here}}
2021
}
2122

test/SILGen/internal_protocol_refines_public_protocol_with_public_default_implementation.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ public protocol A {
88
protocol B: A { }
99

1010
extension B {
11-
public subscript() -> Int { return 0 }
11+
public subscript() -> Int { return 0 } // expected-note {{mark the subscript as 'public' to satisfy the requirement}}
1212
}
1313

14-
public struct S: B {
14+
public struct S: B { // expected-warning {{subscript must be as accessible as its enclosing type because it matches a requirement in protocol 'A'}}
1515
}

test/decl/protocol/conforms/access_corner_case.swift

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ private protocol S : R {
2626
}
2727

2828
extension S {
29-
public func publicRequirement() {}
30-
internal func internalRequirement() {}
29+
public func publicRequirement() {} // expected-note {{mark the instance method as 'public' to satisfy the requirement}}
30+
internal func internalRequirement() {} // expected-note {{mark the instance method as 'internal' to satisfy the requirement}}
3131
fileprivate func fileprivateRequirement() {}
3232
fileprivate func privateRequirement() {}
3333

@@ -38,6 +38,8 @@ extension S {
3838
public struct T : S {}
3939
// expected-error@-1 {{type 'T' does not conform to protocol 'S'}}
4040
// expected-note@-2 {{add stubs for conformance}}
41+
// expected-warning@-3 {{method 'internalRequirement()' must be as accessible as its enclosing type because it matches a requirement in protocol 'Q'}}
42+
// expected-warning@-4 {{method 'publicRequirement()' must be as accessible as its enclosing type because it matches a requirement in protocol 'P'}}
4143

4244
protocol Qpkg : Pkg {
4345
func internalRequirement()
@@ -53,9 +55,9 @@ private protocol Spkg : Rpkg {
5355
}
5456

5557
extension Spkg {
56-
public func publicRequirement() {}
57-
package func packageRequirement() {}
58-
internal func internalRequirement() {}
58+
public func publicRequirement() {} // expected-note {{mark the instance method as 'public' to satisfy the requirement}}
59+
package func packageRequirement() {} // expected-note {{mark the instance method as 'package' to satisfy the requirement}}
60+
internal func internalRequirement() {} // expected-note {{mark the instance method as 'internal' to satisfy the requirement}}
5961
fileprivate func fileprivateRequirement() {}
6062
fileprivate func privateRequirement() {}
6163

@@ -66,6 +68,9 @@ extension Spkg {
6668
public struct Tpkg : Spkg {}
6769
// expected-error@-1 {{type 'Tpkg' does not conform to protocol 'Spkg'}}
6870
// expected-note@-2 {{add stubs for conformance}}
71+
// expected-warning@-3 {{method 'internalRequirement()' must be as accessible as its enclosing type because it matches a requirement in protocol 'Qpkg'}}
72+
// expected-warning@-4 {{method 'packageRequirement()' must be as accessible as its enclosing type because it matches a requirement in protocol 'Pkg'}}
73+
// expected-warning@-5 {{method 'publicRequirement()' must be as accessible as its enclosing type because it matches a requirement in protocol 'P'}}
6974

7075
// This is also OK
7176
@usableFromInline
@@ -111,3 +116,15 @@ extension Q2pkg {
111116
}
112117

113118
public struct T2pkg : Q2pkg {} // expected-error {{method 'publicRequirement()' must be declared public because it matches a requirement in public protocol 'P2'}}
119+
120+
public struct Foo {
121+
public init(value: Int) {}
122+
}
123+
public protocol PublicProtocol {
124+
init?(integer: Int)
125+
}
126+
protocol InternalProtocol: PublicProtocol {}
127+
extension InternalProtocol {
128+
public init(integer: Int) {} // expected-note {{mark the initializer as 'public' to satisfy the requirement}}
129+
}
130+
extension Foo: PublicProtocol, InternalProtocol {} // expected-warning {{initializer 'init(integer:)' must be as accessible as its enclosing type because it matches a requirement in protocol 'PublicProtocol'}}

0 commit comments

Comments
 (0)