Skip to content

Commit 49e089c

Browse files
authored
Merge pull request #15350 from DougGregor/escaping-associated-types
2 parents 20bc656 + 44e230b commit 49e089c

File tree

5 files changed

+101
-3
lines changed

5 files changed

+101
-3
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,17 @@ static std::tuple<Type, Type, OptionalAdjustmentKind>
138138
getTypesToCompare(ValueDecl *reqt, Type reqtType, bool reqtTypeIsIUO,
139139
Type witnessType, bool witnessTypeIsIUO,
140140
VarianceKind variance) {
141+
// If the witness type is noescape but the requirement type is not,
142+
// adjust the witness type to be escaping. This permits a limited form of
143+
// covariance.
144+
bool reqNoescapeToEscaping = false;
145+
(void)adjustInferredAssociatedType(reqtType, reqNoescapeToEscaping);
146+
bool witnessNoescapeToEscaping = false;
147+
Type adjustedWitnessType =
148+
adjustInferredAssociatedType(witnessType, witnessNoescapeToEscaping);
149+
if (witnessNoescapeToEscaping && !reqNoescapeToEscaping)
150+
witnessType = adjustedWitnessType;
151+
141152
// For @objc protocols, deal with differences in the optionality.
142153
// FIXME: It probably makes sense to extend this to non-@objc
143154
// protocols as well, but this requires more testing.
@@ -152,8 +163,7 @@ getTypesToCompare(ValueDecl *reqt, Type reqtType, bool reqtTypeIsIUO,
152163
reqtType = reqtValueType;
153164
}
154165
bool witnessIsOptional = false;
155-
if (Type witnessValueType =
156-
witnessType->getOptionalObjectType()) {
166+
if (Type witnessValueType = witnessType->getOptionalObjectType()) {
157167
witnessIsOptional = true;
158168
witnessType = witnessValueType;
159169
}

lib/Sema/TypeCheckProtocol.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,13 @@ RequirementMatch matchWitness(TypeChecker &tc,
852852
AssociatedTypeDecl *getReferencedAssocTypeOfProtocol(Type type,
853853
ProtocolDecl *proto);
854854

855+
/// Perform any necessary adjustments to the inferred associated type to
856+
/// make it suitable for later use.
857+
///
858+
/// \param noescapeToEscaping Will be set \c true if this operation performed
859+
/// the noescape-to-escaping adjustment.
860+
Type adjustInferredAssociatedType(Type type, bool &noescapeToEscaping);
861+
855862
}
856863

857864
#endif // SWIFT_SEMA_PROTOCOL_H

lib/Sema/TypeCheckProtocolInference.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,28 @@ AssociatedTypeInference::inferTypeWitnessesViaAssociatedType(
607607
return result;
608608
}
609609

610+
Type swift::adjustInferredAssociatedType(Type type, bool &noescapeToEscaping) {
611+
// If we have an optional type, adjust its wrapped type.
612+
if (auto optionalObjectType = type->getOptionalObjectType()) {
613+
auto newOptionalObjectType =
614+
adjustInferredAssociatedType(optionalObjectType, noescapeToEscaping);
615+
if (newOptionalObjectType.getPointer() == optionalObjectType.getPointer())
616+
return type;
617+
618+
return OptionalType::get(newOptionalObjectType);
619+
}
620+
621+
// If we have a noescape function type, make it escaping.
622+
if (auto funcType = type->getAs<FunctionType>()) {
623+
if (funcType->isNoEscape()) {
624+
noescapeToEscaping = true;
625+
return FunctionType::get(funcType->getParams(), funcType->getResult(),
626+
funcType->getExtInfo().withNoEscape(false));
627+
}
628+
}
629+
return type;
630+
}
631+
610632
/// Attempt to resolve a type witness via a specific value witness.
611633
InferredAssociatedTypesByWitness
612634
AssociatedTypeInference::inferTypeWitnessesViaValueWitness(ValueDecl *req,
@@ -662,10 +684,17 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitness(ValueDecl *req,
662684
if (secondType->hasError())
663685
return true;
664686

687+
// Adjust the type to a type that can be written explicitly.
688+
bool noescapeToEscaping = false;
689+
Type inferredType =
690+
adjustInferredAssociatedType(secondType, noescapeToEscaping);
691+
if (!inferredType->isMaterializable())
692+
return true;
693+
665694
auto proto = Conformance->getProtocol();
666695
if (auto assocType = getReferencedAssocTypeOfProtocol(firstDepMember,
667696
proto)) {
668-
Inferred.Inferred.push_back({assocType, secondType});
697+
Inferred.Inferred.push_back({assocType, inferredType});
669698
}
670699

671700
// Always allow mismatches here.

test/SILGen/witnesses.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,3 +510,16 @@ class CrashableBase {
510510
// CHECK-NEXT: return [[RESULT]] : $()
511511

512512
class GenericCrashable<T> : CrashableBase, Crashable {}
513+
514+
// rdar://problem/35297911: allow witness with a noescape parameter to
515+
// match a requirement with an escaping paameter.
516+
protocol EscapingReq {
517+
func f(_: @escaping (Int) -> Int)
518+
}
519+
520+
// CHECK-LABEL: sil private [transparent] [thunk] @$S9witnesses18EscapingCovarianceVAA0B3ReqA2aDP1fyyS2icFTW : $@convention(witness_method: EscapingReq) (@owned @callee_guaranteed (Int) -> Int, @in_guaranteed EscapingCovariance) -> ()
521+
// CHECK-NOT: return
522+
// CHECK: convert_escape_to_noescape %0
523+
struct EscapingCovariance: EscapingReq {
524+
func f(_: (Int) -> Int) { }
525+
}

test/decl/protocol/conforms/associated_type.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,42 @@ 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+
}
42+
43+
struct X1a : P1 {
44+
func f(_: @escaping (Int) -> Int) { }
45+
}
46+
47+
struct X1b : P1 {
48+
typealias A = (Int) -> Int
49+
50+
func f(_: @escaping (Int) -> Int) { }
51+
}
52+
53+
struct X1c : P1 {
54+
typealias A = (Int) -> Int
55+
56+
func f(_: (Int) -> Int) { }
57+
}
58+
59+
struct X1d : P1 {
60+
func f(_: (Int) -> Int) { }
61+
}
62+
63+
protocol P2 {
64+
func f(_: (Int) -> Int) // expected-note{{protocol requires function 'f' with type '((Int) -> Int) -> ()'; do you want to add a stub?}}
65+
}
66+
67+
struct X2a : P2 {
68+
func f(_: (Int) -> Int) { }
69+
}
70+
71+
struct X2b : P2 { // expected-error{{type 'X2b' does not conform to protocol 'P2'}}
72+
func f(_: @escaping (Int) -> Int) { } // expected-note{{candidate has non-matching type '(@escaping (Int) -> Int) -> ()'}}
73+
}

0 commit comments

Comments
 (0)