Skip to content

Commit 60e8de7

Browse files
committed
[Associated type inference] Infer escaping function types, not noescape ones.
When inferring an associated type witness by matching a function signature, adjust non-escaping function types to escaping function types, because only escaping function types could be written as explicit witnesses and non-escaping function types are not permitted outside of function parameters. Addresses the first part of rdar://problem/35297911, eliminating the type-soundness issue.
1 parent 69a2e9c commit 60e8de7

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

lib/Sema/TypeCheckProtocolInference.cpp

+32-1
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,33 @@ AssociatedTypeInference::inferTypeWitnessesViaAssociatedType(
607607
return result;
608608
}
609609

610+
/// Perform any necessary adjustments to the inferred associated type to
611+
/// make it suitable for later use.
612+
static Type adjustInferredAssociatedType(Type type) {
613+
// If we have an optional type, adjust its wrapped type.
614+
if (auto optionalObjectType = type->getOptionalObjectType()) {
615+
auto newOptionalObjectType =
616+
adjustInferredAssociatedType(optionalObjectType);
617+
if (newOptionalObjectType.getPointer() == optionalObjectType.getPointer())
618+
return type;
619+
620+
return OptionalType::get(newOptionalObjectType);
621+
}
622+
623+
// If we have a noescape function type, make it escaping.
624+
if (auto funcType = type->getAs<FunctionType>()) {
625+
if (funcType->isNoEscape()) {
626+
return FunctionType::get(funcType->getParams(), funcType->getResult(),
627+
funcType->getExtInfo().withNoEscape(false));
628+
}
629+
}
630+
631+
// We can only infer materializable types.
632+
if (!type->isMaterializable()) return nullptr;
633+
634+
return type;
635+
}
636+
610637
/// Attempt to resolve a type witness via a specific value witness.
611638
InferredAssociatedTypesByWitness
612639
AssociatedTypeInference::inferTypeWitnessesViaValueWitness(ValueDecl *req,
@@ -662,10 +689,14 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitness(ValueDecl *req,
662689
if (secondType->hasError())
663690
return true;
664691

692+
Type inferredType = adjustInferredAssociatedType(secondType);
693+
if (!inferredType)
694+
return true;
695+
665696
auto proto = Conformance->getProtocol();
666697
if (auto assocType = getReferencedAssocTypeOfProtocol(firstDepMember,
667698
proto)) {
668-
Inferred.Inferred.push_back({assocType, secondType});
699+
Inferred.Inferred.push_back({assocType, inferredType});
669700
}
670701

671702
// Always allow mismatches here.

test/decl/protocol/conforms/associated_type.swift

+29
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,32 @@ class Foo: FooType {
3232
func foo(action: (Bar) -> Void) {
3333
}
3434
}
35+
36+
// rdar://problem/35297911: noescape function types
37+
protocol P1 {
38+
associatedtype A
39+
40+
func f(_: A)
41+
// expected-note@-1 {{protocol requires function 'f' with type '(X1c.A) -> ()' (aka '((Int) -> Int) -> ()'); do you want to add a stub?}}
42+
// expected-note@-2 {{protocol requires function 'f' with type '((Int) -> Int) -> ()'; do you want to add a stub?}}
43+
}
44+
45+
struct X1a : P1 {
46+
func f(_: @escaping (Int) -> Int) { }
47+
}
48+
49+
struct X1b : P1 {
50+
typealias A = (Int) -> Int
51+
52+
func f(_: @escaping (Int) -> Int) { }
53+
}
54+
55+
struct X1c : P1 { // expected-error{{type 'X1c' does not conform to protocol 'P1'}}
56+
typealias A = (Int) -> Int
57+
58+
func f(_: (Int) -> Int) { } // expected-note{{candidate has non-matching type '((Int) -> Int) -> ()'}}
59+
}
60+
61+
struct X1d : P1 { // expected-error{{type 'X1d' does not conform to protocol 'P1'}}
62+
func f(_: (Int) -> Int) { } // expected-note{{candidate has non-matching type '((Int) -> Int) -> ()' [with A = (Int) -> Int]}}
63+
}

0 commit comments

Comments
 (0)