Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SE-0352] Implicit opening of existentials by default #41996

Merged
merged 1 commit into from
Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,28 @@ _**Note:** This is in reverse chronological order, so newer entries are added to

## Swift 5.7

* [SE-0352][]:

It's now possible to call a generic function with a value of protocol type
in places that would previously fail because `any` types do not conform
to their protocols. For example:

```
protocol P {
associatedtype A
func getA() -> A
}

func takeP<T: P>(_ value: T) { }

func test(p: any P) {
takeP(p) // was an error "type 'any P' cannot conform to 'P'", now accepted
}
```

This operates by "opening" the value of protocol type and passing the
underlying type directly to the generic function.

* [SE-0347][]:

It's now possible to use a default value expression with a generic parameter type
Expand Down Expand Up @@ -9162,6 +9184,7 @@ Swift 1.0
[SE-0345]: <https://github.com/apple/swift-evolution/blob/main/proposals/0345-if-let-shorthand.md>
[SE-0326]: <https://github.com/apple/swift-evolution/blob/main/proposals/0326-extending-multi-statement-closure-inference.md>
[SE-0347]: <https://github.com/apple/swift-evolution/blob/main/proposals/0347-type-inference-from-default-exprs.md>
[SE-0352]: <https://github.com/apple/swift-evolution/blob/main/proposals/0352-implicit-open-existentials.md>

[SR-75]: <https://bugs.swift.org/browse/SR-75>
[SR-106]: <https://bugs.swift.org/browse/SR-106>
Expand Down
2 changes: 1 addition & 1 deletion lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
Opts.EnableOpenedExistentialTypes =
Args.hasFlag(OPT_enable_experimental_opened_existential_types,
OPT_disable_experimental_opened_existential_types,
false);
true);

Opts.EnableExperimentalVariadicGenerics |=
Args.hasArg(OPT_enable_experimental_variadic_generics);
Expand Down
2 changes: 1 addition & 1 deletion test/Generics/existential_restrictions.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-typecheck-verify-swift -enable-objc-interop
// RUN: %target-typecheck-verify-swift -enable-objc-interop -disable-experimental-opened-existential-types

protocol P { }
@objc protocol OP { }
Expand Down
4 changes: 2 additions & 2 deletions test/RemoteAST/existentials.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@ printDynamicTypeAndAddressForExistential(q)
// Case five: existential metatypes.
// CHECK-NEXT: Any.Type
let metatype : Any.Type = Any.self
printDynamicTypeAndAddressForExistential(metatype)
printDynamicTypeAndAddressForExistential(metatype as Any.Type)

stopRemoteAST()
stopRemoteAST()
6 changes: 3 additions & 3 deletions test/SILGen/builtins.swift
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ func class_archetype_to_native_object<T : C>(_ t: T) -> Builtin.NativeObject {
// CHECK-NEXT: [[PTR:%[0-9]+]] = unchecked_ref_cast [[REF]] : $@opened({{.*}}) ClassProto to $Builtin.NativeObject
// CHECK-NEXT: return [[PTR]]
func class_existential_to_native_object(_ t:ClassProto) -> Builtin.NativeObject {
return Builtin.unsafeCastToNativeObject(t)
return Builtin.unsafeCastToNativeObject(t as ClassProto)
}

// CHECK-LABEL: sil hidden [ossa] @$s8builtins24class_from_native_object{{[_0-9a-zA-Z]*}}F
Expand Down Expand Up @@ -718,7 +718,7 @@ func refcast_class_any(_ o: A) -> AnyObject {
// CHECK-LABEL: sil hidden [ossa] @$s8builtins20refcast_punknown_any{{[_0-9a-zA-Z]*}}F
// CHECK: unchecked_ref_cast_addr PUnknown in %{{.*}} : $*PUnknown to AnyObject in %{{.*}} : $*AnyObject
func refcast_punknown_any(_ o: PUnknown) -> AnyObject {
return Builtin.castReference(o)
return Builtin.castReference(o as PUnknown)
}

// CHECK-LABEL: sil hidden [ossa] @$s8builtins18refcast_pclass_anyyyXlAA6PClass_pF :
Expand All @@ -728,7 +728,7 @@ func refcast_punknown_any(_ o: PUnknown) -> AnyObject {
// CHECK: return [[ARG_CAST]]
// CHECK: } // end sil function '$s8builtins18refcast_pclass_anyyyXlAA6PClass_pF'
func refcast_pclass_any(_ o: PClass) -> AnyObject {
return Builtin.castReference(o)
return Builtin.castReference(o as PClass)
}

// CHECK-LABEL: sil hidden [ossa] @$s8builtins20refcast_any_punknown{{[_0-9a-zA-Z]*}}F
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ protocol Q: P { }

@inline(never) func sink<T>(_ x: T) {}

func p<T: Q>(_ x: T) { sink(x.p()) }
func p<T: Q>(_ x: T) { sink(x.p() as Any.Type) }

class Foo<T>: Q { func p() -> Any.Type { return T.self } }
class Bar<T>: Foo<T> {}
Expand Down
18 changes: 10 additions & 8 deletions test/decl/protocol/conforms/error_self_conformance.swift
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
// RUN: %target-typecheck-verify-swift

func wantsError<T: Error>(_: T) {}
// expected-note@-1 {{required by global function 'wantsError' where 'T' = 'any ErrorRefinement'}}
// expected-note@-2 {{required by global function 'wantsError' where 'T' = 'any Error & OtherProtocol'}}
// expected-note@-3 {{required by global function 'wantsError' where 'T' = 'any C & Error'}}
func opensError<T: Error>(_: T) {}
func wantsError<T: Error>(_: T, _: T) {}
// expected-note@-1 3{{required by global function 'wantsError' where 'T' = 'any}}

func testSimple(error: Error) {
wantsError(error)
opensError(error)
}

protocol ErrorRefinement : Error {}
func testErrorRefinment(error: ErrorRefinement) {
wantsError(error) // expected-error {{type 'any ErrorRefinement' cannot conform to 'Error'}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}}
opensError(error) // okay
wantsError(error, error) // expected-error {{type 'any ErrorRefinement' cannot conform to 'Error'}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}}
}

protocol OtherProtocol {}
func testErrorComposition(error: Error & OtherProtocol) {
wantsError(error) // expected-error {{type 'any Error & OtherProtocol' cannot conform to 'Error'}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}}
opensError(error) // okay
wantsError(error, error) // expected-error {{type 'any Error & OtherProtocol' cannot conform to 'Error'}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}}
}

class C {}
func testErrorCompositionWithClass(error: Error & C) {
wantsError(error) // expected-error {{type 'any C & Error' cannot conform to 'Error'}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}}
opensError(error) // okay
wantsError(error, error) // expected-error {{type 'any C & Error' cannot conform to 'Error'}} expected-note {{only concrete types such as structs, enums and classes can conform to protocols}}
}
2 changes: 1 addition & 1 deletion test/stdlib/Runtime.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ Runtime.test("abstraction barrier on casting generic param bound to existential"
dynamicWithErasedType,
dynamicExistentialWithErasedType,
dynamicDoubleWrappedExistentialWithErasedType)
= castWithAbstractionBarrier(x)
= castWithAbstractionBarrier(x as SomeProtocol)

expectTrue(staticWithConcreteType == SomeProtocol.self)
expectTrue(dynamicWithConcreteType == SomeProtocol.self)
Expand Down
3 changes: 0 additions & 3 deletions test/type/subclass_composition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,6 @@ func dependentMemberTypes<T : BaseIntAndP2>(
func conformsToAnyObject<T : AnyObject>(_: T) {}
// expected-note@-1 {{where 'T' = 'any P1'}}
func conformsToP1<T : P1>(_: T) {}
// expected-note@-1 {{required by global function 'conformsToP1' where 'T' = 'any P1'}}
func conformsToP2<T : P2>(_: T) {}
func conformsToBaseIntAndP2<T : Base<Int> & P2>(_: T) {}
// expected-note@-1 {{where 'T' = 'FakeDerived'}}
Expand Down Expand Up @@ -428,8 +427,6 @@ func conformsTo<T1 : P2, T2 : Base<Int> & P2>(
// expected-error@-1 {{global function 'conformsToAnyObject' requires that 'any P1' be a class type}}

conformsToP1(p1)
// expected-error@-1 {{type 'any P1' cannot conform to 'P1'}}
// expected-note@-2 {{only concrete types such as structs, enums and classes can conform to protocols}}

// FIXME: Following diagnostics are not great because when
// `conformsTo*` methods are re-typechecked, they loose information
Expand Down
3 changes: 1 addition & 2 deletions test/type/subclass_composition_objc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ func testSelfConformance(c: ObjCClass, p: ObjCProtocol, cp: ObjCClass & ObjCProt
func takesStaticObjCProtocol<T : StaticObjCProtocol>(_: T) {}

func testSelfConformance(cp: ObjCClass & StaticObjCProtocol) {
takesStaticObjCProtocol(cp)
// expected-error@-1 {{'any ObjCClass & StaticObjCProtocol' cannot be used as a type conforming to protocol 'StaticObjCProtocol' because 'StaticObjCProtocol' has static requirements}}
takesStaticObjCProtocol(cp) // okay because the type is opened
}

func testMetatypeSelfConformance(m1: (ObjCClass & ObjCProtocol).Protocol,
Expand Down
2 changes: 1 addition & 1 deletion validation-test/Reflection/existentials.swift
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ protocol Q {}
protocol Composition : P, Q {}
struct S : Composition {}
func getComposition() -> P & Q { return S() }
reflect(any: getComposition())
reflect(any: getComposition() as P & Q)
// CHECK-64: Mangled name: $s12existentials1P_AA1Qp
// CHECK-64: Demangled name: existentials.P & existentials.Q
// CHECK-32: Mangled name: $s12existentials1P_AA1Qp
Expand Down