Skip to content

Commit 22289b0

Browse files
authored
Merge pull request #32231 from owenv/opaque-note
[EduNotes] Explain some opaque type diagnostics
2 parents 75a24ca + c69baa8 commit 22289b0

File tree

3 files changed

+83
-4
lines changed

3 files changed

+83
-4
lines changed

include/swift/AST/EducationalNotes.def

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,17 @@ EDUCATIONAL_NOTES(property_wrapper_failable_init,
5959
EDUCATIONAL_NOTES(property_wrapper_type_requirement_not_accessible,
6060
"property-wrapper-requirements.md")
6161

62+
EDUCATIONAL_NOTES(opaque_type_var_no_init, "opaque-type-inference.md")
63+
EDUCATIONAL_NOTES(opaque_type_no_underlying_type_candidates,
64+
"opaque-type-inference.md")
65+
EDUCATIONAL_NOTES(opaque_type_mismatched_underlying_type_candidates,
66+
"opaque-type-inference.md")
67+
EDUCATIONAL_NOTES(opaque_type_self_referential_underlying_type,
68+
"opaque-type-inference.md")
69+
EDUCATIONAL_NOTES(opaque_type_var_no_underlying_type,
70+
"opaque-type-inference.md")
71+
72+
6273
EDUCATIONAL_NOTES(missing_append_interpolation,
6374
"string-interpolation-conformance.md")
6475
EDUCATIONAL_NOTES(append_interpolation_static,

test/type/opaque.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class C {}
1313
class D: C, P, Q { func paul() {}; func priscilla() {}; func quinn() {} }
1414

1515
let property: some P = 1
16-
let deflessLet: some P // expected-error{{has no initializer}}
16+
let deflessLet: some P // expected-error{{has no initializer}} {{educational-notes=opaque-type-inference}}
1717
var deflessVar: some P // expected-error{{has no initializer}}
1818

1919
struct GenericProperty<T: P> {
@@ -173,13 +173,13 @@ func recursion(x: Int) -> some P {
173173
return recursion(x: x - 1)
174174
}
175175

176-
func noReturnStmts() -> some P {} // expected-error {{function declares an opaque return type, but has no return statements in its body from which to infer an underlying type}}
176+
func noReturnStmts() -> some P {} // expected-error {{function declares an opaque return type, but has no return statements in its body from which to infer an underlying type}} {{educational-notes=opaque-type-inference}}
177177

178178
func returnUninhabited() -> some P { // expected-note {{opaque return type declared here}}
179179
fatalError() // expected-error{{return type of global function 'returnUninhabited()' requires that 'Never' conform to 'P'}}
180180
}
181181

182-
func mismatchedReturnTypes(_ x: Bool, _ y: Int, _ z: String) -> some P { // expected-error{{do not have matching underlying types}}
182+
func mismatchedReturnTypes(_ x: Bool, _ y: Int, _ z: String) -> some P { // expected-error{{do not have matching underlying types}} {{educational-notes=opaque-type-inference}}
183183
if x {
184184
return y // expected-note{{underlying type 'Int'}}
185185
} else {
@@ -209,7 +209,7 @@ func jan() -> some P {
209209
return [marcia(), marcia(), marcia()]
210210
}
211211
func marcia() -> some P {
212-
return [marcia(), marcia(), marcia()] // expected-error{{defines the opaque type in terms of itself}}
212+
return [marcia(), marcia(), marcia()] // expected-error{{defines the opaque type in terms of itself}} {{educational-notes=opaque-type-inference}}
213213
}
214214

215215
protocol R {
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Underlying Type Inference for Opaque Result Types
2+
3+
Opaque result types are a useful tool for abstracting the return type of a function or subscript, or type of a property. Although the concrete underlying type of an opaque type is hidden from clients, it is still inferred by the compiler, which enforces certain usage requirements:
4+
5+
- Property declarations with opaque types must have an initializer expression or getter, and functions or subscripts returning opaque types must have at least one `return` statement:
6+
7+
```swift
8+
let x: some Equatable // error: property declares an opaque return type, but has no initializer expression from which to infer an underlying type
9+
let y: some Equatable = 42 // OK
10+
let z: some Equatable { // Also OK
11+
return "hello, " + "world!"
12+
}
13+
14+
func foo() -> some Equatable { // error: function declares an opaque return type, but has no return statements in its body from which to infer an underlying type
15+
fatalError("Unimplemented")
16+
}
17+
18+
func bar() -> some Equatable { // OK
19+
fatalError("Unimplemented")
20+
return 42
21+
}
22+
```
23+
24+
- The underlying type of an opaque type must be unique. In other words, if a function or subscript returns an opaque type, it must return values of the same underlying type from every `return` statement in its body.
25+
26+
```swift
27+
func foo(bar: Bool) -> some Equatable { // error: function declares an opaque return type, but the return statements in its body do not have matching underlying types
28+
if bar {
29+
return "hello, world!" // note: return statement has underlying type 'String'
30+
} else {
31+
return 1 // note: return statement has underlying type 'Int'
32+
}
33+
}
34+
35+
func bar(baz: Bool) -> some Equatable { // OK, both branches of the if statement return a value of the same underlying type, Int.
36+
if baz {
37+
return 100
38+
} else {
39+
return 200
40+
}
41+
}
42+
```
43+
44+
- Functions returning opaque types may be recursive. However, such functions must have at least one `return` statement that returns a concrete underlying type as opposed to the function's own opaque result type. Additionally, recursive calls may not be used to create an infinitely recursive opaque type.
45+
46+
```swift
47+
func foo(_ x: Int) -> some Equatable { // error: function declares an opaque return type, but has no return statements in its body from which to infer an underlying type
48+
// Not allowed because there aren't any non-recursive returns to infer the underlying type from.
49+
return foo(x+1)
50+
}
51+
52+
struct EquatableWrapper<T: Equatable>: Equatable { var value: T }
53+
func foo() -> some Equatable { // error: function opaque return type was inferred as 'EquatableWrapper<some Equatable>', which defines the opaque type in terms of itself
54+
// Not allowed because the use of EquatableWrapper creates an infinitely recursive underlying type: EquatableWrapper<EquatableWrapper<EquatableWrapper<...>>>...>
55+
return EquatableWrapper(value: foo())
56+
}
57+
58+
func bar(_ x: Int) -> some Equatable { // OK, the underlying type can be inferred from the second return statement.
59+
if x > 0 {
60+
return bar(x-1)
61+
} else {
62+
return x
63+
}
64+
}
65+
```
66+
67+
To learn more about opaque result types, see the [Opaque Types](https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html) section of _The Swift Programming Language_.
68+

0 commit comments

Comments
 (0)