Skip to content

Commit de323b5

Browse files
committed
Sema: Update resolveType() for subclass existentials
If the -enable-experimental-subclass-existentials staging flag is on, resolveType() now allows protocol compositions to contain class types. It also diagnoses if a composition has more than one superclass requirement. Also, change diagnostics that talked about 'protocol composition' to 'protocol-constrained type'. Since such types can now contain a superclass constraint, it's not correct to call them protocol composition. "Protocol-constrained type" isn't quite accurate either because 'Any' has no protocols, and 'AnyObject' will have no protocols but a general class constraint; but those are edge cases which won't come up in these diagnostics.
1 parent 1b2252f commit de323b5

File tree

15 files changed

+369
-38
lines changed

15 files changed

+369
-38
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -699,9 +699,9 @@ ERROR(tuple_type_multiple_labels,none,
699699

700700
// Protocol Types
701701
ERROR(expected_rangle_protocol,PointsToFirstBadToken,
702-
"expected '>' to complete protocol composition type", ())
702+
"expected '>' to complete protocol-constrained type", ())
703703
ERROR(disallowed_protocol_composition,PointsToFirstBadToken,
704-
"protocol composition is neither allowed nor needed here", ())
704+
"protocol-constrained type is neither allowed nor needed here", ())
705705

706706
WARNING(deprecated_protocol_composition,none,
707707
"'protocol<...>' composition syntax is deprecated; join the protocols using '&'", ())
@@ -1361,7 +1361,7 @@ ERROR(unexpected_class_constraint,none,
13611361
NOTE(suggest_anyobject,none,
13621362
"did you mean to constrain %0 with the 'AnyObject' protocol?", (Identifier))
13631363
ERROR(expected_generics_type_restriction,none,
1364-
"expected a type name or protocol composition restricting %0",
1364+
"expected a class type or protocol-constrained type restricting %0",
13651365
(Identifier))
13661366
ERROR(requires_single_equal,none,
13671367
"use '==' for same-type requirements rather than '='", ())

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,14 +1548,19 @@ ERROR(circular_protocol_def,none,
15481548
"circular protocol inheritance %0", (StringRef))
15491549
NOTE(protocol_here,none,
15501550
"protocol %0 declared here", (Identifier))
1551-
ERROR(protocol_composition_not_protocol,none,
1552-
"non-protocol type %0 cannot be used within a protocol composition", (Type))
15531551
ERROR(objc_protocol_inherits_non_objc_protocol,none,
15541552
"@objc protocol %0 cannot refine non-@objc protocol %1", (Type, Type))
15551553
WARNING(protocol_composition_with_postfix,none,
1556-
"protocol composition with postfix '%0' is ambiguous "
1554+
"protocol-constrained type with postfix '%0' is ambiguous "
15571555
"and will be rejected in future version of Swift", (StringRef))
15581556

1557+
ERROR(invalid_protocol_composition_member,none,
1558+
"non-protocol, non-class type %0 cannot be used within a "
1559+
"protocol-constrained type", (Type))
1560+
ERROR(protocol_composition_one_class,none,
1561+
"protocol-constrained type cannot contain class %0 because it already "
1562+
"contains class %1", (Type, Type))
1563+
15591564
ERROR(requires_conformance_nonprotocol,none,
15601565
"type %0 constrained to non-protocol type %1", (TypeLoc, TypeLoc))
15611566
ERROR(requires_not_suitable_archetype,none,
@@ -3086,7 +3091,7 @@ NOTE(not_objc_empty_protocol_composition,none,
30863091
NOTE(not_objc_protocol,none,
30873092
"protocol %0 is not '@objc'", (Type))
30883093
NOTE(not_objc_error_protocol_composition,none,
3089-
"protocol composition involving 'Error' cannot be represented "
3094+
"protocol-constrained type containing 'Error' cannot be represented "
30903095
"in Objective-C", ())
30913096
NOTE(not_objc_empty_tuple,none,
30923097
"empty tuple type cannot be represented in Objective-C", ())

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ namespace swift {
162162
/// optimized custom allocator, so that memory debugging tools can be used.
163163
bool UseMalloc = false;
164164

165+
/// \brief Enable classes to appear in protocol composition types.
166+
bool EnableExperimentalSubclassExistentials = false;
167+
165168
/// \brief Enable experimental property behavior feature.
166169
bool EnableExperimentalPropertyBehaviors = false;
167170

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,10 @@ def enable_experimental_deserialization_recovery :
267267
Flag<["-"], "enable-experimental-deserialization-recovery">,
268268
HelpText<"Attempt to recover from missing xrefs (etc) in swiftmodules">;
269269

270+
def enable_experimental_subclass_existentials : Flag<["-"],
271+
"enable-experimental-subclass-existentials">,
272+
HelpText<"Enable classes to appear in protocol composition types">;
273+
270274
def enable_cow_existentials : Flag<["-"], "enable-cow-existentials">,
271275
HelpText<"Enable the copy-on-write existential implementation">;
272276

lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
871871
Opts.EnableExperimentalPropertyBehaviors |=
872872
Args.hasArg(OPT_enable_experimental_property_behaviors);
873873

874+
Opts.EnableExperimentalSubclassExistentials |=
875+
Args.hasArg(OPT_enable_experimental_subclass_existentials);
876+
874877
Opts.EnableClassResilience |=
875878
Args.hasArg(OPT_enable_class_resilience);
876879

lib/Sema/TypeCheckType.cpp

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2944,19 +2944,48 @@ Type TypeResolver::resolveCompositionType(CompositionTypeRepr *repr,
29442944
if (auto fixed = fixCompositionWithPostfix(TC, repr))
29452945
return resolveType(fixed, options);
29462946

2947+
Type SuperclassType;
29472948
SmallVector<Type, 4> ProtocolTypes;
2949+
2950+
auto checkSuperclass = [&](SourceLoc loc, Type t) -> bool {
2951+
if (SuperclassType && !SuperclassType->isEqual(t)) {
2952+
TC.diagnose(loc, diag::protocol_composition_one_class, t,
2953+
SuperclassType);
2954+
return true;
2955+
}
2956+
2957+
SuperclassType = t;
2958+
return false;
2959+
};
2960+
29482961
for (auto tyR : repr->getTypes()) {
29492962
Type ty = TC.resolveType(tyR, DC, withoutContext(options), Resolver);
29502963
if (!ty || ty->hasError()) return ty;
29512964

2952-
if (!ty->isExistentialType()) {
2953-
TC.diagnose(tyR->getStartLoc(), diag::protocol_composition_not_protocol,
2954-
ty);
2965+
auto nominalDecl = ty->getAnyNominal();
2966+
if (TC.Context.LangOpts.EnableExperimentalSubclassExistentials &&
2967+
nominalDecl && isa<ClassDecl>(nominalDecl)) {
2968+
if (checkSuperclass(tyR->getStartLoc(), ty))
2969+
continue;
2970+
2971+
ProtocolTypes.push_back(ty);
29552972
continue;
29562973
}
29572974

2958-
ProtocolTypes.push_back(ty);
2975+
if (ty->isExistentialType()) {
2976+
if (auto superclassTy = ty->getSuperclass(nullptr))
2977+
if (checkSuperclass(tyR->getStartLoc(), superclassTy))
2978+
continue;
2979+
2980+
ProtocolTypes.push_back(ty);
2981+
continue;
2982+
}
2983+
2984+
TC.diagnose(tyR->getStartLoc(),
2985+
diag::invalid_protocol_composition_member,
2986+
ty);
29592987
}
2988+
29602989
return ProtocolCompositionType::get(Context, ProtocolTypes);
29612990
}
29622991

test/Compatibility/protocol_composition.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ protocol P2 {
1515

1616
// Warning for mistakenly accepted protocol composition production.
1717
func foo(x: P1 & Any & P2.Type?) {
18-
// expected-warning @-1 {{protocol composition with postfix '.Type' is ambiguous and will be rejected in future version of Swift}} {{13-13=(}} {{26-26=)}}
18+
// expected-warning @-1 {{protocol-constrained type with postfix '.Type' is ambiguous and will be rejected in future version of Swift}} {{13-13=(}} {{26-26=)}}
1919
let _: (P1 & P2).Type? = x
2020
let _: (P1 & P2).Type = x!
2121
let _: Int = x!.p1()
@@ -25,25 +25,25 @@ func foo(x: P1 & Any & P2.Type?) {
2525
// Type expression
2626
func bar() -> ((P1 & P2)?).Type {
2727
let x = (P1 & P2?).self
28-
// expected-warning @-1 {{protocol composition with postfix '?' is ambiguous and will be rejected in future version of Swift}} {{12-12=(}} {{19-19=)}}
28+
// expected-warning @-1 {{protocol-constrained type with postfix '?' is ambiguous and will be rejected in future version of Swift}} {{12-12=(}} {{19-19=)}}
2929
return x
3030
}
3131

3232
// Non-ident type at non-last position are rejected anyway.
33-
typealias A1 = P1.Type & P2 // expected-error {{type 'P1.Type' cannot be used within a protocol composition}}
33+
typealias A1 = P1.Type & P2 // expected-error {{non-protocol, non-class type 'P1.Type' cannot be used within a protocol-constrained type}}
3434

3535
// BEGIN swift4.swift
3636

37-
func foo(x: P1 & Any & P2.Type?) { // expected-error {{non-protocol type 'P2.Type?' cannot be used within a protocol composition}}
37+
func foo(x: P1 & Any & P2.Type?) { // expected-error {{non-protocol, non-class type 'P2.Type?' cannot be used within a protocol-constrained type}}
3838
let _: (P1 & P2).Type? = x // expected-error {{cannot convert value of type 'P1' to specified type '(P1 & P2).Type?'}}
3939
let _: (P1 & P2).Type = x! // expected-error {{cannot force unwrap value of non-optional type 'P1'}}
4040
let _: Int = x!.p1() // expected-error {{cannot force unwrap value of non-optional type 'P1'}}
4141
let _: Int? = x?.p2 // expected-error {{cannot use optional chaining on non-optional value of type 'P1'}}
4242
}
4343

4444
func bar() -> ((P1 & P2)?).Type {
45-
let x = (P1 & P2?).self // expected-error {{non-protocol type 'P2?' cannot be used within a protocol composition}}
45+
let x = (P1 & P2?).self // expected-error {{non-protocol, non-class type 'P2?' cannot be used within a protocol-constrained type}}
4646
return x // expected-error {{cannot convert return expression}}
4747
}
4848

49-
typealias A1 = P1.Type & P2 // expected-error {{type 'P1.Type' cannot be used within a protocol composition}}
49+
typealias A1 = P1.Type & P2 // expected-error {{non-protocol, non-class type 'P1.Type' cannot be used within a protocol-constrained type}}

test/Generics/function_decls.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
func f0<T>(x: Int, y: Int, t: T) { }
88
func f1<T : Any>(x: Int, y: Int, t: T) { }
99
func f2<T : IteratorProtocol>(x: Int, y: Int, t: T) { }
10-
func f3<T : () -> ()>(x: Int, y: Int, t: T) { } // expected-error{{expected a type name or protocol composition restricting 'T'}}
10+
func f3<T : () -> ()>(x: Int, y: Int, t: T) { } // expected-error{{expected a class type or protocol-constrained type restricting 'T'}}
1111
func f4<T>(x: T, y: T) { }
1212

1313
// Non-protocol type constraints.

test/Parse/recovery.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ struct ErrorTypeInVarDecl7 {
333333
}
334334

335335
struct ErrorTypeInVarDecl8 {
336-
var v1 : protocol<FooProtocol // expected-error {{expected '>' to complete protocol composition type}} expected-note {{to match this opening '<'}}
336+
var v1 : protocol<FooProtocol // expected-error {{expected '>' to complete protocol-constrained type}} expected-note {{to match this opening '<'}}
337337
var v2 : Int
338338
}
339339

@@ -343,7 +343,7 @@ struct ErrorTypeInVarDecl9 {
343343
}
344344

345345
struct ErrorTypeInVarDecl10 {
346-
var v1 : protocol<FooProtocol // expected-error {{expected '>' to complete protocol composition type}} expected-note {{to match this opening '<'}}
346+
var v1 : protocol<FooProtocol // expected-error {{expected '>' to complete protocol-constrained type}} expected-note {{to match this opening '<'}}
347347
var v2 : Int
348348
}
349349

@@ -353,7 +353,7 @@ struct ErrorTypeInVarDecl11 {
353353
}
354354

355355
func ErrorTypeInPattern1(_: protocol<) { } // expected-error {{expected identifier for type name}}
356-
func ErrorTypeInPattern2(_: protocol<F) { } // expected-error {{expected '>' to complete protocol composition type}}
356+
func ErrorTypeInPattern2(_: protocol<F) { } // expected-error {{expected '>' to complete protocol-constrained type}}
357357
// expected-note@-1 {{to match this opening '<'}}
358358
// expected-error@-2 {{use of undeclared type 'F'}}
359359

test/Parse/type_expr.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -265,11 +265,11 @@ func compositionType() {
265265
_ = P1 & P2.self // expected-error {{binary operator '&' cannot be applied to operands of type 'P1.Protocol' and 'P2.Protocol'}} expected-note {{overloads for '&' exist }}
266266
_ = (P1 & P2).self // Ok.
267267
_ = (P1 & (P2)).self // FIXME: OK? while `typealias P = P1 & (P2)` is rejected.
268-
_ = (P1 & (P2, P3)).self // expected-error {{non-protocol type '(P2, P3)' cannot be used within a protocol composition}}
269-
_ = (P1 & Int).self // expected-error {{non-protocol type 'Int' cannot be used within a protocol composition}}
270-
_ = (P1? & P2).self // expected-error {{non-protocol type 'P1?' cannot be used within a protocol composition}}
268+
_ = (P1 & (P2, P3)).self // expected-error {{non-protocol, non-class type '(P2, P3)' cannot be used within a protocol-constrained type}}
269+
_ = (P1 & Int).self // expected-error {{non-protocol, non-class type 'Int' cannot be used within a protocol-constrained type}}
270+
_ = (P1? & P2).self // expected-error {{non-protocol, non-class type 'P1?' cannot be used within a protocol-constrained type}}
271271

272-
_ = (P1 & P2.Type).self // expected-error {{non-protocol type 'P2.Type' cannot be used within a protocol composition}}
272+
_ = (P1 & P2.Type).self // expected-error {{non-protocol, non-class type 'P2.Type' cannot be used within a protocol-constrained type}}
273273

274274
_ = P1 & P2 -> P3
275275
// expected-error @-1 {{single argument function types require parentheses}} {{7-7=(}} {{14-14=)}}

test/attr/attr_objc.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2024,7 +2024,7 @@ class ClassThrows1 {
20242024

20252025
@objc func fooWithErrorProtocolComposition1(x: Error & Protocol_ObjC1) { }
20262026
// expected-error@-1{{method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C}}
2027-
// expected-note@-2{{protocol composition involving 'Error' cannot be represented in Objective-C}}
2027+
// expected-note@-2{{protocol-constrained type containing 'Error' cannot be represented in Objective-C}}
20282028

20292029
// CHECK: {{^}} func fooWithErrorProtocolComposition2(x: Error & Protocol_ObjC1)
20302030
func fooWithErrorProtocolComposition2(x: Error & Protocol_ObjC1) { }

test/decl/inherit/inherit.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ struct S2 : struct { } // expected-error{{expected identifier for type name}}
2929

3030
// Protocol composition in inheritance clauses
3131
struct S3 : P, P & Q { } // expected-error {{duplicate inheritance from 'P'}}
32-
// expected-error @-1 {{protocol composition is neither allowed nor needed here}}
32+
// expected-error @-1 {{protocol-constrained type is neither allowed nor needed here}}
3333
// expected-error @-2 {{'Q' requires that 'S3' inherit from 'A'}}
3434
// expected-note @-3 {{requirement specified as 'Self' : 'A' [with Self = S3]}}
3535
struct S4 : P, P { } // expected-error {{duplicate inheritance from 'P'}}
3636
struct S6 : P & { } // expected-error {{expected identifier for type name}}
37-
// expected-error @-1 {{protocol composition is neither allowed nor needed here}}
37+
// expected-error @-1 {{protocol-constrained type is neither allowed nor needed here}}
3838
struct S7 : protocol<P, Q> { } // expected-warning {{'protocol<...>' composition syntax is deprecated; join the protocols using '&'}}
39-
// expected-error @-1 {{protocol composition is neither allowed nor needed here}}{{13-22=}} {{26-27=}}
39+
// expected-error @-1 {{protocol-constrained type is neither allowed nor needed here}}{{13-22=}} {{26-27=}}
4040
// expected-error @-2 {{'Q' requires that 'S7' inherit from 'A'}}
4141
// expected-note @-3 {{requirement specified as 'Self' : 'A' [with Self = S7]}}
4242

test/expr/expressions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ func test_lambda() {
257257
func test_lambda2() {
258258
{ () -> protocol<Int> in
259259
// expected-warning @-1 {{'protocol<...>' composition syntax is deprecated and not needed here}} {{11-24=Int}}
260-
// expected-error @-2 {{non-protocol type 'Int' cannot be used within a protocol composition}}
260+
// expected-error @-2 {{non-protocol, non-class type 'Int' cannot be used within a protocol-constrained type}}
261261
// expected-warning @-3 {{result of call is unused}}
262262
return 1
263263
}()

test/type/protocol_composition.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ typealias Any2 = protocol< > // expected-warning {{'protocol<>' syntax is deprec
2828
// Okay to inherit a typealias for Any type.
2929
protocol P5 : Any { }
3030
protocol P6 : protocol<> { } // expected-warning {{'protocol<>' syntax is deprecated; use 'Any' instead}}
31-
// expected-error@-1 {{protocol composition is neither allowed nor needed here}}
31+
// expected-error@-1 {{protocol-constrained type is neither allowed nor needed here}}
3232
typealias P7 = Any & Any1
3333

3434
extension Int : P5 { }
3535

36-
typealias Bogus = P1 & Int // expected-error{{non-protocol type 'Int' cannot be used within a protocol composition}}
36+
typealias Bogus = P1 & Int // expected-error{{non-protocol, non-class type 'Int' cannot be used within a protocol-constrained type}}
3737

3838
func testEquality() {
3939
// Remove duplicates from protocol-conformance types.
@@ -143,19 +143,19 @@ typealias H = protocol<P1>! // expected-warning {{'protocol<...>' composition sy
143143
typealias J = protocol<P1, P2>.Protocol // expected-warning {{'protocol<...>' composition syntax is deprecated; join the protocols using '&'}} {{15-31=(P1 & P2)}}
144144
typealias K = protocol<P1, P2>? // expected-warning {{'protocol<...>' composition syntax is deprecated; join the protocols using '&'}} {{15-32=(P1 & P2)?}}
145145

146-
typealias T01 = P1.Protocol & P2 // expected-error {{non-protocol type 'P1.Protocol' cannot be used within a protocol composition}}
147-
typealias T02 = P1.Type & P2 // expected-error {{non-protocol type 'P1.Type' cannot be used within a protocol composition}}
148-
typealias T03 = P1? & P2 // expected-error {{non-protocol type 'P1?' cannot be used within a protocol composition}}
149-
typealias T04 = P1 & P2! // expected-error {{non-protocol type 'P2!' cannot be used within a protocol composition}} expected-error {{implicitly unwrapped optionals}} {{24-25=?}}
146+
typealias T01 = P1.Protocol & P2 // expected-error {{non-protocol, non-class type 'P1.Protocol' cannot be used within a protocol-constrained type}}
147+
typealias T02 = P1.Type & P2 // expected-error {{non-protocol, non-class type 'P1.Type' cannot be used within a protocol-constrained type}}
148+
typealias T03 = P1? & P2 // expected-error {{non-protocol, non-class type 'P1?' cannot be used within a protocol-constrained type}}
149+
typealias T04 = P1 & P2! // expected-error {{non-protocol, non-class type 'P2!' cannot be used within a protocol-constrained type}} expected-error {{implicitly unwrapped optionals}} {{24-25=?}}
150150
typealias T05 = P1 & P2 -> P3 // expected-error {{single argument function types require parentheses}} {{17-17=(}} {{24-24=)}}
151151
typealias T06 = P1 -> P2 & P3 // expected-error {{single argument function types require parentheses}} {{17-17=(}} {{19-19=)}}
152152
typealias T07 = P1 & protocol<P2, P3> // expected-warning {{protocol<...>' composition syntax is deprecated; join the protocols using '&'}} {{22-38=P2 & P3}}
153153
func fT07(x: T07) -> P1 & P2 & P3 { return x } // OK, 'P1 & protocol<P2, P3>' is parsed as 'P1 & P2 & P3'.
154154
let _: P1 & P2 & P3 -> P1 & P2 & P3 = fT07 // expected-error {{single argument function types require parentheses}} {{8-8=(}} {{20-20=)}}
155155

156-
struct S01: P5 & P6 {} // expected-error {{protocol composition is neither allowed nor needed here}} {{none}}
156+
struct S01: P5 & P6 {} // expected-error {{protocol-constrained type is neither allowed nor needed here}} {{none}}
157157
struct S02: P5? & P6 {} // expected-error {{inheritance from non-named type 'P5?'}}
158-
struct S03: Optional<P5> & P6 {} // expected-error {{inheritance from non-protocol type 'Optional<P5>'}} expected-error {{protocol composition is neither allowed nor needed here}}
158+
struct S03: Optional<P5> & P6 {} // expected-error {{inheritance from non-protocol type 'Optional<P5>'}} expected-error {{protocol-constrained type is neither allowed nor needed here}}
159159
struct S04<T : P5 & (P6)> {} // expected-error {{inheritance from non-named type '(P6)'}}
160160
struct S05<T> where T : P5? & P6 {} // expected-error {{inheritance from non-named type 'P5?'}}
161161

0 commit comments

Comments
 (0)