Skip to content

Commit 9020533

Browse files
authored
Merge pull request swiftlang#79423 from tshortli/case-iterable-invalid-available
AST: Type-check `@available` attributes before synthesizing `CaseIterable`
2 parents 6ff951f + b6ee079 commit 9020533

File tree

7 files changed

+137
-32
lines changed

7 files changed

+137
-32
lines changed

lib/AST/Decl.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6663,11 +6663,9 @@ bool EnumDecl::hasOnlyCasesWithoutAssociatedValues() const {
66636663
bool hasAssociatedValues = false;
66646664

66656665
for (auto elt : getAllElements()) {
6666-
for (auto Attr : elt->getAttrs()) {
6667-
if (auto AvAttr = dyn_cast<AvailableAttr>(Attr)) {
6668-
if (!AvAttr->isInvalid())
6669-
hasAnyUnavailableValues = true;
6670-
}
6666+
for (auto Attr : elt->getSemanticAvailableAttrs()) {
6667+
// FIXME: [availability] Deprecation doesn't make an element unavailable
6668+
hasAnyUnavailableValues = true;
66716669
}
66726670

66736671
if (!elt->isAvailableDuringLowering())
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %target-swift-frontend -emit-silgen %s -parse-as-library -enable-library-evolution -module-name Test -experimental-lazy-typecheck | %FileCheck %s
2+
3+
public enum E: CaseIterable {
4+
case a
5+
@available(deprecated, renamed: "a") // Intentionally invalid
6+
case b
7+
}
8+
9+
// CHECK: sil_witness_table E: CaseIterable module Test

test/Sema/Inputs/enum_conformance_synthesis_other.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Note that for the test to be effective, each of these enums must only have
22
// its Equatable or Hashable conformance referenced /once/ in the primary file.
33
enum FromOtherFile : String {
4-
// expected-note@-1 {{type declared here}}
54
case A = "a"
65
}
76
enum AlsoFromOtherFile : Int {
@@ -29,7 +28,3 @@ extension ImpliedMain: ImplierMain {}
2928
enum ImpliedOther: ImplierOther {
3029
case a(Int)
3130
}
32-
33-
enum CaseIterableAcrossFiles {
34-
case A
35-
}

test/Sema/enum_conformance_synthesis.swift

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,18 @@
22

33
var hasher = Hasher()
44

5-
enum Foo: CaseIterable {
5+
enum Foo {
66
case A, B
77
}
88

99
func foo() {
1010
if Foo.A == .B { }
1111
var _: Int = Foo.A.hashValue
1212
Foo.A.hash(into: &hasher)
13-
_ = Foo.allCases
14-
1513
Foo.A == Foo.B // expected-warning {{result of operator '==' is unused}}
1614
}
1715

18-
enum Generic<T>: CaseIterable {
16+
enum Generic<T> {
1917
case A, B
2018

2119
static func method() -> Int {
@@ -29,7 +27,6 @@ func generic() {
2927
if Generic<Foo>.A == .B { }
3028
var _: Int = Generic<Foo>.A.hashValue
3129
Generic<Foo>.A.hash(into: &hasher)
32-
_ = Generic<Foo>.allCases
3330
}
3431

3532
func localEnum() -> Bool {
@@ -98,7 +95,7 @@ func useEnumBeforeDeclaration() {
9895
if .A == overloadFromOtherFile() {}
9996
}
10097

101-
// Complex enums are not automatically Equatable, Hashable, or CaseIterable.
98+
// Complex enums are not automatically Equatable, or Hashable.
10299
enum Complex {
103100
case A(Int)
104101
case B
@@ -204,13 +201,6 @@ enum Instrument {
204201

205202
extension Instrument : Equatable {}
206203

207-
extension Instrument : CaseIterable {}
208-
209-
enum UnusedGeneric<T> {
210-
case a, b, c
211-
}
212-
extension UnusedGeneric : CaseIterable {}
213-
214204
// Explicit conformance should work too
215205
public enum Medicine {
216206
case Antibiotic
@@ -229,21 +219,13 @@ enum Complex2 {
229219
case B
230220
}
231221
extension Complex2 : Hashable {}
232-
extension Complex2 : CaseIterable {} // expected-error {{type 'Complex2' does not conform to protocol 'CaseIterable'}} expected-note {{add stubs for conformance}}
233-
extension FromOtherFile: CaseIterable {} // expected-error {{extension outside of file declaring enum 'FromOtherFile' prevents automatic synthesis of 'allCases' for protocol 'CaseIterable'}} expected-note {{add stubs for conformance}}
234-
extension CaseIterableAcrossFiles: CaseIterable {
235-
public static var allCases: [CaseIterableAcrossFiles] {
236-
return [ .A ]
237-
}
238-
}
239222

240223
// No explicit conformance and it cannot be derived.
241224
enum NotExplicitlyHashableAndCannotDerive {
242225
case A(NotHashable) //expected-note {{associated value type 'NotHashable' does not conform to protocol 'Hashable', preventing synthesized conformance of 'NotExplicitlyHashableAndCannotDerive' to 'Hashable'}}
243226
// expected-note@-1 {{associated value type 'NotHashable' does not conform to protocol 'Equatable', preventing synthesized conformance of 'NotExplicitlyHashableAndCannotDerive' to 'Equatable'}}
244227
}
245228
extension NotExplicitlyHashableAndCannotDerive : Hashable {} // expected-error 2 {{does not conform}} expected-note {{add stubs for conformance}}
246-
extension NotExplicitlyHashableAndCannotDerive : CaseIterable {} // expected-error {{does not conform}} expected-note {{add stubs for conformance}}
247229

248230
// Verify that conformance (albeit manually implemented) can still be added to
249231
// a type in a different file.
@@ -255,7 +237,6 @@ extension OtherFileNonconforming: Hashable {
255237
}
256238
// ...but synthesis in a type defined in another file doesn't work yet.
257239
extension YetOtherFileNonconforming: Equatable {} // expected-error {{extension outside of file declaring enum 'YetOtherFileNonconforming' prevents automatic synthesis of '==' for protocol 'Equatable'}} expected-note {{add stubs for conformance}}
258-
extension YetOtherFileNonconforming: CaseIterable {} // expected-error {{does not conform}} expected-note {{add stubs for conformance}}
259240

260241
// Verify that an indirect enum doesn't emit any errors as long as its "leaves"
261242
// are conformant.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
enum FromOtherFile { // expected-unsupported-note {{type declared here}}
2+
case a
3+
case b
4+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %target-swift-frontend -typecheck -verify -primary-file %s %S/../Inputs/case_iterable_other.swift
2+
3+
enum Simple: CaseIterable {
4+
case a, b
5+
6+
static func staticMethod() -> Int {
7+
return Self.allCases.count
8+
}
9+
}
10+
11+
enum Generic<T>: CaseIterable {
12+
case a, b
13+
14+
static func staticMethod() -> Int {
15+
return Self.allCases.count
16+
}
17+
}
18+
19+
enum InExtension {
20+
case a, b
21+
}
22+
23+
extension InExtension: CaseIterable {}
24+
25+
enum UnavailableCase: CaseIterable {
26+
case a
27+
@available(*, unavailable)
28+
case b
29+
30+
public static var allCases: [UnavailableCase] {
31+
return [.a]
32+
}
33+
}
34+
35+
extension FromOtherFile: CaseIterable {
36+
public static var allCases: [FromOtherFile] {
37+
return [.a, .b]
38+
}
39+
}
40+
41+
enum InvalidAvailableAttribute: CaseIterable {
42+
case a
43+
@available(deprecated, renamed: "a") // expected-warning {{unknown platform 'deprecated' for attribute 'available'}}
44+
case b
45+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// RUN: %target-swift-frontend -typecheck -verify -primary-file %s %S/../Inputs/case_iterable_other.swift -verify-additional-prefix unsupported- -verify-ignore-unknown
2+
3+
extension FromOtherFile: CaseIterable {} // expected-error {{extension outside of file declaring enum 'FromOtherFile' prevents automatic synthesis of 'allCases' for protocol 'CaseIterable'}} expected-note {{add stubs for conformance}}
4+
5+
enum NotCaseIterableAssociatedValues: CaseIterable { // expected-error {{type 'NotCaseIterableAssociatedValues' does not conform to protocol 'CaseIterable'}}
6+
// expected-note@-1 {{add stubs for conformance}}
7+
case a(Int)
8+
case b
9+
}
10+
11+
// FIXME: [availability] Deprecation should not block this conformance synthesis
12+
enum NotCaseIterableUniversallyDeprecatedCase: CaseIterable { // expected-error {{type 'NotCaseIterableUniversallyDeprecatedCase' does not conform to protocol 'CaseIterable'}}
13+
// expected-note@-1 {{add stubs for conformance}}
14+
case a
15+
@available(*, deprecated)
16+
case b
17+
}
18+
19+
enum NotCaseIterableUniversallyUnavailableCase: CaseIterable { // expected-error {{type 'NotCaseIterableUniversallyUnavailableCase' does not conform to protocol 'CaseIterable'}}
20+
// expected-note@-1 {{add stubs for conformance}}
21+
case a
22+
@available(*, unavailable)
23+
case b
24+
}
25+
26+
enum NotCaseIterableSwiftIntroducedLaterCase: CaseIterable { // expected-error {{type 'NotCaseIterableSwiftIntroducedLaterCase' does not conform to protocol 'CaseIterable'}}
27+
// expected-note@-1 {{add stubs for conformance}}
28+
case a
29+
@available(swift, introduced: 99)
30+
case b
31+
}
32+
33+
enum NotCaseIterableSwiftIntroducedEarlierCase: CaseIterable { // expected-error {{type 'NotCaseIterableSwiftIntroducedEarlierCase' does not conform to protocol 'CaseIterable'}}
34+
// expected-note@-1 {{add stubs for conformance}}
35+
case a
36+
@available(swift, introduced: 4)
37+
case b
38+
}
39+
40+
enum NotCaseIterableSwiftObsoletedLaterCase: CaseIterable { // expected-error {{type 'NotCaseIterableSwiftObsoletedLaterCase' does not conform to protocol 'CaseIterable'}}
41+
// expected-note@-1 {{add stubs for conformance}}
42+
case a
43+
@available(swift, obsoleted: 99)
44+
case b
45+
}
46+
47+
enum NotCaseIterableSwiftObsoletedEarlierCase: CaseIterable { // expected-error {{type 'NotCaseIterableSwiftObsoletedEarlierCase' does not conform to protocol 'CaseIterable'}}
48+
// expected-note@-1 {{add stubs for conformance}}
49+
case a
50+
@available(swift, obsoleted: 4)
51+
case b
52+
}
53+
54+
enum NotCaseIterableMacOSUnavailableCase: CaseIterable { // expected-error {{type 'NotCaseIterableMacOSUnavailableCase' does not conform to protocol 'CaseIterable'}}
55+
// expected-note@-1 {{add stubs for conformance}}
56+
case a
57+
@available(macOS, unavailable)
58+
case b
59+
}
60+
61+
enum NotCaseIterableMacOSPotentiallyUnavailableCase: CaseIterable { // expected-error {{type 'NotCaseIterableMacOSPotentiallyUnavailableCase' does not conform to protocol 'CaseIterable'}}
62+
// expected-note@-1 {{add stubs for conformance}}
63+
case a
64+
@available(macOS, introduced: 99)
65+
case b
66+
}
67+
68+
enum NotCaseIterableMacOSObsoletedCase: CaseIterable { // expected-error {{type 'NotCaseIterableMacOSObsoletedCase' does not conform to protocol 'CaseIterable'}}
69+
// expected-note@-1 {{add stubs for conformance}}
70+
case a
71+
@available(macOS, obsoleted: 10.9)
72+
case b
73+
}

0 commit comments

Comments
 (0)