Skip to content

Commit 829664b

Browse files
committed
Sema: Fix source compatibility break from relaxed witness matching rules
This is fix for a source compat regression from: commit 790625a Author: Doug Gregor <[email protected]> Date: Mon Mar 19 15:29:32 2018 -0700 Allow a witness's noescape parameter to match a requirement's escaping parameter The regression is not severe but its easy enough to fix. With the above change, it was possible for an optional requirement that did not have a witness in Swift 4.1 to pick up a witness in Swift 4.2, because the escaping/noescape mismatch prevented it from being considered in Swift 4.1. If the new witness was not sufficiently visible, this caused a source compatibility regression. Work around this by discarding the witness if its not sufficiently visible. In -swift-version 5, the hack expires, and we revert to the stricter, more consistent behavior. Fixes <rdar://problem/39614880>.
1 parent f56a941 commit 829664b

File tree

3 files changed

+43
-0
lines changed

3 files changed

+43
-0
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2763,6 +2763,21 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
27632763

27642764
case CheckKind::Access:
27652765
case CheckKind::AccessOfSetter: {
2766+
// Swift 4.2 relaxed some rules for protocol witness matching.
2767+
//
2768+
// This meant that it was possible for an optional protocol requirement
2769+
// to have a witness where previously in Swift 4.1 it did not.
2770+
//
2771+
// Since witnesses must be as visible as the protocol, this caused a
2772+
// source compatibility break if the witness was not sufficiently
2773+
// visible.
2774+
//
2775+
// Work around this by discarding the witness if its not sufficiently
2776+
// visible.
2777+
if (!TC.Context.isSwiftVersionAtLeast(5))
2778+
if (requirement->getAttrs().hasAttribute<OptionalAttr>())
2779+
return ResolveWitnessResult::Missing;
2780+
27662781
// Avoid relying on the lifetime of 'this'.
27672782
const DeclContext *DC = this->DC;
27682783
diagnoseOrDefer(requirement, false,
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %target-typecheck-verify-swift -enable-objc-interop -swift-version 4
2+
3+
@objc protocol Opt {
4+
@objc optional func f(callback: @escaping () -> ())
5+
}
6+
7+
class Conforms : Opt {
8+
private func f(callback: () -> ()) {} // expected-note {{'f' declared here}}
9+
}
10+
11+
func g(x: Conforms) {
12+
_ = x.f(callback: {}) // expected-error {{'f' is inaccessible due to 'private' protection level}}
13+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-typecheck-verify-swift -enable-objc-interop -swift-version 5
2+
3+
@objc protocol Opt {
4+
@objc optional func f(callback: @escaping () -> ())
5+
}
6+
7+
class Conforms : Opt {
8+
private func f(callback: () -> ()) {}
9+
// expected-error@-1 {{method 'f(callback:)' must be declared internal because it matches a requirement in internal protocol 'Opt'}}
10+
// expected-note@-2 {{mark the instance method as 'internal' to satisfy the requirement}}
11+
}
12+
13+
func g(x: Conforms) {
14+
_ = x.f(callback: {})
15+
}

0 commit comments

Comments
 (0)