Skip to content

Commit ddc0494

Browse files
authored
Merge pull request swiftlang#32993 from brentdax/override-sally-ride
Allow shadowing of unavailable members
2 parents 182683a + 57469a6 commit ddc0494

File tree

6 files changed

+120
-42
lines changed

6 files changed

+120
-42
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2415,6 +2415,9 @@ NOTE(multiple_override_prev,none,
24152415
ERROR(override_unavailable, none,
24162416
"cannot override %0 which has been marked unavailable%select{|: %1}1",
24172417
(DeclBaseName, StringRef))
2418+
NOTE(suggest_removing_override, none,
2419+
"remove 'override' modifier to declare a new %0",
2420+
(DeclBaseName))
24182421

24192422
ERROR(override_less_available,none,
24202423
"overriding %0 must be as available as declaration it overrides",

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,19 @@ bool swift::isOverrideBasedOnType(const ValueDecl *decl, Type declTy,
214214
return canDeclTy == canParentDeclTy;
215215
}
216216

217+
static bool isUnavailableInAllVersions(ValueDecl *decl) {
218+
ASTContext &ctx = decl->getASTContext();
219+
auto *attr = decl->getAttrs().getUnavailable(ctx);
220+
221+
if (!attr)
222+
return false;
223+
if (attr->isUnconditionallyUnavailable())
224+
return true;
225+
226+
return attr->getVersionAvailability(ctx)
227+
== AvailableVersionComparison::Unavailable;
228+
}
229+
217230
/// Perform basic checking to determine whether a declaration can override a
218231
/// declaration in a superclass.
219232
static bool areOverrideCompatibleSimple(ValueDecl *decl,
@@ -246,6 +259,15 @@ static bool areOverrideCompatibleSimple(ValueDecl *decl,
246259
if (decl->getKind() != parentDecl->getKind())
247260
return false;
248261

262+
// If the parent decl is unavailable, the subclass decl can shadow it, but it
263+
// can't override it. To avoid complex version logic, we don't apply this to
264+
// `obsoleted` members, only `unavailable` ones.
265+
// FIXME: Refactor to allow that when the minimum version is always satisfied.
266+
if (isUnavailableInAllVersions(parentDecl))
267+
// If the subclass decl is trying to override, we'll diagnose it later.
268+
if (!decl->getAttrs().hasAttribute<OverrideAttr>())
269+
return false;
270+
249271
// Ignore invalid parent declarations.
250272
// FIXME: Do we really need this?
251273
if (parentDecl->isInvalid())
@@ -1864,6 +1886,15 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) {
18641886
// FIXME: Possibly should extend to more availability checking.
18651887
if (auto *attr = base->getAttrs().getUnavailable(ctx)) {
18661888
diagnoseUnavailableOverride(override, base, attr);
1889+
1890+
if (isUnavailableInAllVersions(base)) {
1891+
auto modifier = override->getAttrs().getAttribute<OverrideAttr>();
1892+
if (modifier && modifier->isValid()) {
1893+
diags.diagnose(override, diag::suggest_removing_override,
1894+
override->getBaseName())
1895+
.fixItRemove(modifier->getRange());
1896+
}
1897+
}
18671898
}
18681899

18691900
if (!ctx.LangOpts.DisableAvailabilityChecking) {

localization/diagnostics/en.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6081,6 +6081,10 @@
60816081
msg: >-
60826082
cannot override %0 which has been marked unavailable%select{|: %1}1
60836083
6084+
- id: suggest_removing_override
6085+
msg: >-
6086+
remove 'override' modifier to declare a new %0
6087+
60846088
- id: override_less_available
60856089
msg: >-
60866090
overriding %0 must be as available as declaration it overrides

test/SILGen/vtables.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class C : B {
66
override func bar() {}
77
// bas inherited from A
88
override func qux() {}
9+
func flux() {}
910

1011
// zim inherited from B
1112
override func zang() {}
@@ -20,9 +21,11 @@ class C : B {
2021
// CHECK: #A.bar: {{.*}} : @$s7vtables1CC3bar{{[_0-9a-zA-Z]*}}F
2122
// CHECK: #A.bas: {{.*}} : @$s7vtables1AC3bas{{[_0-9a-zA-Z]*}}F
2223
// CHECK: #A.qux: {{.*}} : @$s7vtables1CC3qux{{[_0-9a-zA-Z]*}}F
24+
// CHECK: #A.flux: {{.*}} : @$s7vtables1BC4flux{{[_0-9a-zA-Z]*}}F
2325
// CHECK: #B.init!allocator: {{.*}} : @$s7vtables1CC{{[_0-9a-zA-Z]*}}fC
2426
// CHECK: #B.zim: {{.*}} : @$s7vtables1BC3zim{{[_0-9a-zA-Z]*}}F
2527
// CHECK: #B.zang: {{.*}} : @$s7vtables1CC4zang{{[_0-9a-zA-Z]*}}F
28+
// CHECK: #C.flux: {{.*}} : @$s7vtables1CC4flux{{[_0-9a-zA-Z]*}}F
2629
// CHECK: #C.flopsy: {{.*}} : @$s7vtables1CC6flopsy{{[_0-9a-zA-Z]*}}F
2730
// CHECK: #C.mopsy: {{.*}} : @$s7vtables1CC5mopsy{{[_0-9a-zA-Z]*}}F
2831
// CHECK: }
@@ -32,13 +35,15 @@ class A {
3235
func bar() {}
3336
func bas() {}
3437
func qux() {}
38+
func flux() {}
3539
}
3640

3741
// CHECK: sil_vtable A {
3842
// CHECK: #A.foo: {{.*}} : @$s7vtables1AC3foo{{[_0-9a-zA-Z]*}}F
3943
// CHECK: #A.bar: {{.*}} : @$s7vtables1AC3bar{{[_0-9a-zA-Z]*}}F
4044
// CHECK: #A.bas: {{.*}} : @$s7vtables1AC3bas{{[_0-9a-zA-Z]*}}F
4145
// CHECK: #A.qux: {{.*}} : @$s7vtables1AC3qux{{[_0-9a-zA-Z]*}}F
46+
// CHECK: #A.flux: {{.*}} : @$s7vtables1AC4flux{{[_0-9a-zA-Z]*}}F
4247
// CHECK: #A.init!allocator: {{.*}} : @$s7vtables1AC{{[_0-9a-zA-Z]*}}fC
4348
// CHECK: }
4449

@@ -49,6 +54,7 @@ class B : A {
4954
// bar inherited from A
5055
// bas inherited from A
5156
override func qux() {}
57+
@available(*, unavailable) override func flux() {}
5258

5359
func zim() {}
5460
func zang() {}
@@ -59,6 +65,7 @@ class B : A {
5965
// CHECK: #A.bar: {{.*}} : @$s7vtables1AC3bar{{[_0-9a-zA-Z]*}}F
6066
// CHECK: #A.bas: {{.*}} : @$s7vtables1AC3bas{{[_0-9a-zA-Z]*}}F
6167
// CHECK: #A.qux: {{.*}} : @$s7vtables1BC3qux{{[_0-9a-zA-Z]*}}F
68+
// CHECK: #A.flux: {{.*}} : @$s7vtables1BC4flux{{[_0-9a-zA-Z]*}}F
6269
// CHECK: #B.init!allocator: {{.*}} : @$s7vtables1BC{{[_0-9a-zA-Z]*}}fC
6370
// CHECK: #B.zim: {{.*}} : @$s7vtables1BC3zim{{[_0-9a-zA-Z]*}}F
6471
// CHECK: #B.zang: {{.*}} : @$s7vtables1BC4zang{{[_0-9a-zA-Z]*}}F

test/Sema/availability.swift

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,35 +27,62 @@ func foo(x : NSUInteger) { // expected-error {{'NSUInteger' is unavailable: use
2727
let z3 : MyModule.Outer.NSUInteger = 42 // expected-error {{'NSUInteger' is unavailable: use 'UInt' instead}}
2828
}
2929

30-
// Test preventing overrides of unavailable methods.
30+
// Test preventing overrides (but allowing shadowing) of unavailable methods.
3131
class ClassWithUnavailable {
3232
@available(*, unavailable)
3333
func doNotOverride() {} // expected-note {{'doNotOverride()' has been explicitly marked unavailable here}}
3434

3535
// FIXME: extraneous diagnostic here
3636
@available(*, unavailable)
37-
init(int _: Int) {} // expected-note 2 {{'init(int:)' has been explicitly marked unavailable here}}
37+
init(int _: Int) {} // expected-note 3 {{'init(int:)' has been explicitly marked unavailable here}}
3838

39+
@available(*, unavailable)
3940
convenience init(otherInt: Int) {
4041
self.init(int: otherInt) // expected-error {{'init(int:)' is unavailable}}
4142
}
4243

4344
@available(*, unavailable)
44-
subscript (i: Int) -> Int { // expected-note{{'subscript(_:)' has been explicitly marked unavailable here}}
45+
required init(necessaryInt: Int) {}
46+
47+
@available(*, unavailable)
48+
subscript (i: Int) -> Int { // expected-note 2 {{'subscript(_:)' has been explicitly marked unavailable here}}
4549
return i
4650
}
4751
}
4852

4953
class ClassWithOverride : ClassWithUnavailable {
50-
override func doNotOverride() {} // expected-error {{cannot override 'doNotOverride' which has been marked unavailable}}
54+
override func doNotOverride() {}
55+
// expected-error@-1 {{cannot override 'doNotOverride' which has been marked unavailable}}
56+
// expected-note@-2 {{remove 'override' modifier to declare a new 'doNotOverride'}} {{3-12=}}
57+
58+
override init(int: Int) {}
59+
// expected-error@-1 {{cannot override 'init' which has been marked unavailable}}
60+
// expected-note@-2 {{remove 'override' modifier to declare a new 'init'}} {{3-12=}}
61+
62+
// can't override convenience inits
63+
// can't override required inits
64+
65+
override subscript (i: Int) -> Int { return i }
66+
// expected-error@-1 {{cannot override 'subscript' which has been marked unavailable}}
67+
// expected-note@-2 {{remove 'override' modifier to declare a new 'subscript'}} {{3-12=}}
68+
}
69+
70+
class ClassWithShadowing : ClassWithUnavailable {
71+
func doNotOverride() {} // no-error
72+
init(int: Int) {} // no-error
73+
convenience init(otherInt: Int) { self.init(int: otherInt) } // no-error
74+
required init(necessaryInt: Int) {} // no-error
75+
subscript (i: Int) -> Int { return i } // no-error
5176
}
5277

5378
func testInit() {
5479
ClassWithUnavailable(int: 0) // expected-error {{'init(int:)' is unavailable}} // expected-warning{{unused}}
80+
ClassWithShadowing(int: 0) // expected-warning {{unused}}
5581
}
5682

57-
func testSubscript(cwu: ClassWithUnavailable) {
83+
func testSubscript(cwu: ClassWithUnavailable, cws: ClassWithShadowing) {
5884
_ = cwu[5] // expected-error{{'subscript(_:)' is unavailable}}
85+
_ = cws[5] // no-error
5986
}
6087

6188
/* FIXME 'nil == a' fails to type-check with a bogus error message

0 commit comments

Comments
 (0)