Skip to content

Commit d2138d0

Browse files
author
willtunnels
authored
Rename --enable-experimental-opaque-return-types and gate structural … (#38780)
* Rename --enable-experimental-opaque-return-types and gate structural opaque types with a flag * Separate out structural opaque type result builder tests
1 parent 79d45c0 commit d2138d0

13 files changed

+153
-102
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1861,6 +1861,9 @@ WARNING(spi_attribute_on_import_of_public_module,none,
18611861
(DeclName, StringRef))
18621862

18631863
// Opaque return types
1864+
ERROR(structural_opaque_types_are_experimental,none,
1865+
"'opaque' types cannot be nested inside other types; "
1866+
"structural 'opaque' types are an experimental feature", ())
18641867
ERROR(opaque_type_invalid_constraint,none,
18651868
"an 'opaque' type must specify only 'Any', 'AnyObject', protocols, "
18661869
"and/or a base class", ())

include/swift/Basic/LangOptions.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -298,10 +298,13 @@ namespace swift {
298298
/// Enable experimental concurrency model.
299299
bool EnableExperimentalConcurrency = false;
300300

301-
/// Enable experimental support for additional opaque return type features,
302-
/// i.e. named opaque return types (with 'where' clause support), and opaque
303-
/// types in nested position within the function return type.
304-
bool EnableExperimentalOpaqueReturnTypes = false;
301+
/// Enable experimental support for named opaque result types, e.g.
302+
/// `func f() -> <T> T`.
303+
bool EnableExperimentalNamedOpaqueTypes = false;
304+
305+
/// Enable experimental support for structural opaque result types, e.g.
306+
/// `func f() -> (some P)?`.
307+
bool EnableExperimentalStructuralOpaqueTypes = false;
305308

306309
/// Enable experimental flow-sensitive concurrent captures.
307310
bool EnableExperimentalFlowSensitiveConcurrentCaptures = false;

include/swift/Option/FrontendOptions.td

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -431,9 +431,13 @@ def enable_experimental_static_assert :
431431
Flag<["-"], "enable-experimental-static-assert">,
432432
HelpText<"Enable experimental #assert">;
433433

434-
def enable_experimental_opaque_return_types :
435-
Flag<["-"], "enable-experimental-opaque-return-types">,
436-
HelpText<"Enable experimental extensions to opaque return type support">;
434+
def enable_experimental_named_opaque_types :
435+
Flag<["-"], "enable-experimental-named-opaque-types">,
436+
HelpText<"Enable experimental support for named opaque result types">;
437+
438+
def enable_experimental_structural_opaque_types :
439+
Flag<["-"], "enable-experimental-structural-opaque-types">,
440+
HelpText<"Enable experimental support for structural opaque result types">;
437441

438442
def enable_deserialization_recovery :
439443
Flag<["-"], "enable-deserialization-recovery">,

lib/Frontend/CompilerInvocation.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,8 +422,11 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
422422
Opts.EnableExperimentalConcurrency |=
423423
Args.hasArg(OPT_enable_experimental_concurrency);
424424

425-
Opts.EnableExperimentalOpaqueReturnTypes |=
426-
Args.hasArg(OPT_enable_experimental_opaque_return_types);
425+
Opts.EnableExperimentalNamedOpaqueTypes |=
426+
Args.hasArg(OPT_enable_experimental_named_opaque_types);
427+
428+
Opts.EnableExperimentalStructuralOpaqueTypes |=
429+
Args.hasArg(OPT_enable_experimental_structural_opaque_types);
427430

428431
Opts.EnableExperimentalDistributed |=
429432
Args.hasArg(OPT_enable_experimental_distributed);

lib/Parse/ParseType.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ ParserResult<TypeRepr> Parser::parseType(
550550

551551
ParserResult<TypeRepr> Parser::parseTypeWithOpaqueParams(Diag<> MessageID) {
552552
GenericParamList *genericParams = nullptr;
553-
if (Context.LangOpts.EnableExperimentalOpaqueReturnTypes) {
553+
if (Context.LangOpts.EnableExperimentalNamedOpaqueTypes) {
554554
auto result = maybeParseGenericParams();
555555
genericParams = result.getPtrOrNull();
556556
if (result.hasCodeCompletion())

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,14 @@ OpaqueResultTypeRequest::evaluate(Evaluator &evaluator,
116116
auto *dc = originatingDecl->getInnermostDeclContext();
117117
auto &ctx = dc->getASTContext();
118118

119+
// Support for structural opaque result types is hidden behind a compiler flag
120+
// until the proposal gets approved.
121+
if (!ctx.LangOpts.EnableExperimentalStructuralOpaqueTypes &&
122+
!isa<OpaqueReturnTypeRepr>(repr)) {
123+
ctx.Diags.diagnose(repr->getLoc(), diag::structural_opaque_types_are_experimental);
124+
return nullptr;
125+
}
126+
119127
// Protocol requirements can't have opaque return types.
120128
//
121129
// TODO: Maybe one day we could treat this as sugar for an associated type.
@@ -136,8 +144,8 @@ OpaqueResultTypeRequest::evaluate(Evaluator &evaluator,
136144
out << "associatedtype " << placeholder << ": ";
137145
// FIXME [OPAQUE SUPPORT]: to produce the right associate type for the
138146
// replacement in general, we would need to recurse into the type repr and
139-
// replace every `OpaqueReturnType` with its 'base'. Things get trickier
140-
// when we allow named opaque return types.
147+
// replace every `OpaqueReturnType` with its 'constraint'. Things get
148+
// trickier when we allow named opaque return types.
141149
if (isa<OpaqueReturnTypeRepr>(repr)) {
142150
cast<OpaqueReturnTypeRepr>(repr)->getConstraint()->print(out);
143151
} else {

test/Constraints/result_builder_infer.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,6 @@ struct TupleMe: Tupled {
6666
}
6767
}
6868

69-
struct TupleMeStructural: Tupled {
70-
var tuple: (some Any, Int) {
71-
"hello"
72-
0
73-
}
74-
}
75-
7669
// Witness is separated from the context declaring conformance, so don't infer
7770
// the result builder.
7871
struct DoNotTupleMe {
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-structural-opaque-types -disable-availability-checking
2+
3+
@resultBuilder
4+
struct TupleBuilder {
5+
static func buildBlock<T1, T2>(_ t1: T1, _ t2: T2) -> (T1, T2) {
6+
return (t1, t2)
7+
}
8+
}
9+
10+
protocol Tupled {
11+
associatedtype TupleType
12+
13+
@TupleBuilder var tuple: TupleType { get }
14+
}
15+
16+
struct TupleMeStructural: Tupled {
17+
var tuple: (some Any, Int) {
18+
"hello"
19+
0
20+
}
21+
}

test/IDE/print_ast_named_opaque_return.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ func f1() -> <T> Int {
55
func f2() -> <T: SignedInteger, U: SignedInteger> Int {
66
}
77

8-
// RUN: %target-swift-ide-test -print-ast-typechecked -enable-experimental-opaque-return-types -source-filename %s | %FileCheck %s -check-prefix=CHECK1
8+
// RUN: %target-swift-ide-test -print-ast-typechecked -enable-experimental-named-opaque-types -source-filename %s | %FileCheck %s -check-prefix=CHECK1
99
// CHECK1: {{^}}func f0() {{{$}}
1010
// CHECK1: {{^}}}{{$}}
1111
// CHECK1: {{^}}func f1() -> <T> Int {{{$}}

test/type/opaque.swift

Lines changed: 4 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -75,81 +75,11 @@ struct Test {
7575

7676
let zingle = {() -> some P in 1 } // expected-error{{'some' types are only implemented}}
7777

78-
// Structural positions
7978

80-
struct S1<T> {
81-
var x: T
82-
}
83-
struct S2<T, U> {
84-
var x: T
85-
var y: U
86-
}
87-
struct R1<T: P> {
88-
var x: T
89-
}
90-
struct R2<T: P, U: Q> {
91-
var x: T
92-
var y: U
93-
}
94-
95-
// TODO: cases that we should support, but don't yet:
96-
//
97-
// WARNING: using '!' is not allowed here; treating this as '?' instead
98-
// func asUnwrappedOptionalBase() -> (some P)! { return 1 }
99-
//
100-
// ERROR: generic parameter 'τ_0_0' could not be inferred
101-
// func asHOFRetRet() -> () -> some P { return { 1 } }
102-
//
103-
// ERROR: function declares an opaque return type, but has no return statements in its body from which to infer an underlying type
104-
// func asHOFRetArg() -> (some P) -> () { return { (x: String) -> () in } }
105-
//
106-
// ERROR: 'some' types are only implemented for the declared type of properties and subscripts and the return type of functions
107-
// let x = { () -> some P in return ConcreteP() }
108-
109-
func twoOpaqueTypes() -> (some P, some P) { return (1, 2) } // expected-error{{only one 'opaque' type is supported}}
110-
func asTupleElemBad() -> (P, some Q) { return (1, C()) } // expected-note{{opaque return type declared here}} expected-error{{requires that 'C' conform to 'Q'}}
111-
112-
func asTupleElem() -> (P, some Q) { return (1, 2) }
113-
func asArrayElem() -> [some P] { return [1] }
114-
func asOptionalBase() -> (some P)? { return 1 }
115-
116-
let asTupleElemLet: (P, some Q) = (1, 2)
117-
let asArrayElemLet: [some P] = [1]
118-
let asOptionalBaseLet: (some P)? = 1
119-
120-
func asUnconstrainedGeneric1() -> S1<some P> { return S1(x: 1) }
121-
func asUnconstrainedGeneric2() -> S2<P, some Q> { return S2(x: 1, y: 2) }
122-
func asConstrainedGeneric1() -> R1<some P> { return R1(x: 1) }
123-
func asConstrainedGeneric2() -> R2<Int, some Q> { return R2(x: 1, y: 2) }
124-
func asNestedGenericDirect() -> S1<S1<some P>> { return S1(x: S1(x: 1)) }
125-
func asNestedGenericIndirect() -> S1<S1<(Int, some P)>> { return S1(x: S1(x: (1, 2))) }
126-
127-
let asUnconstrainedGeneric2Let: S2<P, some Q> = S2(x: 1, y: 2)
128-
let asNestedGenericIndirectLet: S1<S1<(Int, some P)>> = S1(x: S1(x: (1, 2)))
129-
130-
// Tests an interesting SILGen case. For the underlying opaque type, we have to
131-
// use the generic calling convention for closures.
132-
func funcToAnyOpaqueCoercion() -> S1<some Any> {
133-
let f: () -> () = {}
134-
return S1(x: f)
135-
}
136-
137-
// TODO: We should give better error messages here. The opaque types have
138-
// underlying types 'Int' and 'String', but the return statments have underlying
139-
// types '(Int, Int)' and '(String, Int)'.
140-
func structuralMismatchedReturnTypes(_ x: Bool, _ y: Int, _ z: String) -> (some P, Int) { // expected-error{{do not have matching underlying types}}
141-
if x {
142-
return (y, 1) // expected-note{{return statement has underlying type 'Int'}}
143-
} else {
144-
return (z, 1) // expected-note{{return statement has underlying type 'String'}}
145-
}
146-
}
147-
148-
func structuralMemberLookupBad() {
149-
var tup: (some P, Int) = (1, 1)
150-
tup.0.paul();
151-
tup.0.d(); // expected-error{{value of type 'some P' has no member 'd'}}
152-
}
79+
// Support for structural opaque result types is hidden behind a compiler flag
80+
// until the proposal gets approved.
81+
func twoOpaqueTypes() -> (some P, some P) { return (1, 2) } // expected-error{{'opaque' types cannot be nested inside other types}}
82+
func asArrayElem() -> (some P)! { return [1] } // expected-error{{'opaque' types cannot be nested inside other types}}
15383

15484
// Invalid positions
15585

test/type/opaque_experimental.swift renamed to test/type/opaque_return_named.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -enable-experimental-opaque-return-types -disable-availability-checking
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-named-opaque-types -disable-availability-checking
22

33
// Tests for experimental extensions to opaque return type support.
44

@@ -16,4 +16,4 @@ var x1: <T> (Int, Int) = (1, 1)
1616
var x2: <T> (<U> Int, Int) = (1, 1) // expected-error{{expected type}} expected-error{{cannot convert value of type '(Int, Int)' to specified type 'Int'}}
1717
for _: <T> Int in [1, 2, 3] { }
1818

19-
struct S0 { subscript(i: Int) -> <T> Int { 1 } }
19+
struct S0 { subscript(i: Int) -> <T> Int { 1 } }
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-structural-opaque-types -disable-availability-checking
2+
3+
// Tests for experimental extensions to opaque return type support.
4+
5+
protocol P { func paul() }
6+
protocol Q {}
7+
8+
extension Int: P, Q { func paul() {} }
9+
extension String: P, Q { func paul() {} }
10+
11+
class C {}
12+
class D: P, Q { func paul() {}; func d() {} }
13+
14+
15+
// TODO: cases that we should support, but don't yet:
16+
//
17+
// WARNING: using '!' is not allowed here; treating this as '?' instead
18+
// func asUnwrappedOptionalBase() -> (some P)! { return 1 }
19+
//
20+
// ERROR: generic parameter 'τ_0_0' could not be inferred
21+
// func asHOFRetRet() -> () -> some P { return { 1 } }
22+
//
23+
// ERROR: function declares an opaque return type, but has no return statements in its body from which to infer an underlying type
24+
// func asHOFRetArg() -> (some P) -> () { return { (x: Int) -> () in } }
25+
//
26+
// ERROR: 'some' types are only implemented for the declared type of properties and subscripts and the return type of functions
27+
// let x = { () -> some P in return 1 }
28+
29+
func twoOpaqueTypes() -> (some P, some P) { return (1, 2) } // expected-error{{only one 'opaque' type is supported}}
30+
func asTupleElemBad() -> (P, some Q) { return (1, C()) } // expected-note{{opaque return type declared here}} expected-error{{requires that 'C' conform to 'Q'}}
31+
32+
func asTupleElem() -> (P, some Q) { return (1, 2) }
33+
func asArrayElem() -> [some P] { return [1] }
34+
func asOptionalBase() -> (some P)? { return 1 }
35+
36+
let asTupleElemLet: (P, some Q) = (1, 2)
37+
let asArrayElemLet: [some P] = [1]
38+
let asOptionalBaseLet: (some P)? = 1
39+
40+
struct S1<T> {
41+
var x: T
42+
}
43+
struct S2<T, U> {
44+
var x: T
45+
var y: U
46+
}
47+
struct R1<T: P> {
48+
var x: T
49+
}
50+
struct R2<T: P, U: Q> {
51+
var x: T
52+
var y: U
53+
}
54+
55+
func asUnconstrainedGeneric1() -> S1<some P> { return S1(x: 1) }
56+
func asUnconstrainedGeneric2() -> S2<P, some Q> { return S2(x: 1, y: 2) }
57+
func asConstrainedGeneric1() -> R1<some P> { return R1(x: 1) }
58+
func asConstrainedGeneric2() -> R2<Int, some Q> { return R2(x: 1, y: 2) }
59+
func asNestedGenericDirect() -> S1<S1<some P>> { return S1(x: S1(x: 1)) }
60+
func asNestedGenericIndirect() -> S1<S1<(Int, some P)>> { return S1(x: S1(x: (1, 2))) }
61+
62+
let asUnconstrainedGeneric2Let: S2<P, some Q> = S2(x: 1, y: 2)
63+
let asNestedGenericIndirectLet: S1<S1<(Int, some P)>> = S1(x: S1(x: (1, 2)))
64+
65+
// Tests an interesting SILGen case. For the underlying opaque type, we have to
66+
// use the generic calling convention for closures.
67+
func funcToAnyOpaqueCoercion() -> S1<some Any> {
68+
let f: () -> () = {}
69+
return S1(x: f)
70+
}
71+
72+
// TODO: We should give better error messages here. The opaque types have
73+
// underlying types 'Int' and 'String', but the return statments have underlying
74+
// types '(Int, Int)' and '(String, Int)'.
75+
func structuralMismatchedReturnTypes(_ x: Bool, _ y: Int, _ z: String) -> (some P, Int) { // expected-error{{do not have matching underlying types}}
76+
if x {
77+
return (y, 1) // expected-note{{return statement has underlying type 'Int'}}
78+
} else {
79+
return (z, 1) // expected-note{{return statement has underlying type 'String'}}
80+
}
81+
}
82+
83+
func structuralMemberLookupBad() {
84+
var tup: (some P, Int) = (D(), 1)
85+
tup.0.paul();
86+
tup.0.d(); // expected-error{{value of type 'some P' has no member 'd'}}
87+
}

tools/swift-ide-test/swift-ide-test.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -767,10 +767,9 @@ DisableImplicitConcurrencyImport("disable-implicit-concurrency-module-import",
767767
llvm::cl::desc("Disable implicit import of _Concurrency module"),
768768
llvm::cl::init(false));
769769

770-
static llvm::cl::opt<bool> EnableExperimentalOpaqueReturnTypes(
771-
"enable-experimental-opaque-return-types",
772-
llvm::cl::desc(
773-
"Enable experimental extensions to opaque return type support"),
770+
static llvm::cl::opt<bool> EnableExperimentalNamedOpaqueTypes(
771+
"enable-experimental-named-opaque-types",
772+
llvm::cl::desc("Enable experimental support for named opaque result types"),
774773
llvm::cl::Hidden, llvm::cl::cat(Category), llvm::cl::init(false));
775774

776775
static llvm::cl::opt<bool>
@@ -3875,8 +3874,8 @@ int main(int argc, char *argv[]) {
38753874
if (options::DisableImplicitConcurrencyImport) {
38763875
InitInvok.getLangOptions().DisableImplicitConcurrencyModuleImport = true;
38773876
}
3878-
if (options::EnableExperimentalOpaqueReturnTypes) {
3879-
InitInvok.getLangOptions().EnableExperimentalOpaqueReturnTypes = true;
3877+
if (options::EnableExperimentalNamedOpaqueTypes) {
3878+
InitInvok.getLangOptions().EnableExperimentalNamedOpaqueTypes = true;
38803879
}
38813880

38823881
if (options::EnableExperimentalDistributed) {

0 commit comments

Comments
 (0)