Skip to content

Commit c88064b

Browse files
authored
Merge pull request #7875 from jckarter/assoc-type-inference-same-type-candidate
Sema: Accept tautological assoc type inference candidates when same-typed.
2 parents d6cc431 + cdc3c1b commit c88064b

File tree

2 files changed

+137
-10
lines changed

2 files changed

+137
-10
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 90 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
#include "llvm/Support/Compiler.h"
4040
#include "llvm/Support/SaveAndRestore.h"
4141

42+
#define DEBUG_TYPE "protocol-conformance-checking"
43+
#include "llvm/Support/Debug.h"
44+
4245
using namespace swift;
4346

4447
namespace {
@@ -3064,6 +3067,9 @@ ConformanceChecker::inferTypeWitnessesViaValueWitnesses(
30643067
};
30653068

30663069
for (auto witness : lookupValueWitnesses(req, /*ignoringNames=*/nullptr)) {
3070+
DEBUG(llvm::dbgs() << "Inferring associated types from decl:\n";
3071+
witness->dump(llvm::dbgs()));
3072+
30673073
// If the potential witness came from an extension, and our `Self`
30683074
// type can't use it regardless of what associated types we end up
30693075
// inferring, skip the witness.
@@ -3084,18 +3090,27 @@ ConformanceChecker::inferTypeWitnessesViaValueWitnesses(
30843090
}
30853091
auto &result = witnessResult.Inferred[i];
30863092

3093+
DEBUG(llvm::dbgs() << "Considering whether " << result.first->getName()
3094+
<< " can infer to:\n";
3095+
result.second->dump(llvm::dbgs()));
3096+
30873097
// Filter out errors.
3088-
if (result.second->hasError())
3098+
if (result.second->hasError()) {
3099+
DEBUG(llvm::dbgs() << "-- has error type\n");
30893100
REJECT;
3101+
}
30903102

30913103
// Filter out duplicates.
30923104
if (!known.insert({result.first, result.second->getCanonicalType()})
3093-
.second)
3105+
.second) {
3106+
DEBUG(llvm::dbgs() << "-- duplicate\n");
30943107
REJECT;
3108+
}
30953109

30963110
// Filter out circular possibilities, e.g. that
30973111
// AssocType == S.AssocType or
30983112
// AssocType == Foo<S.AssocType>.
3113+
bool canInferFromOtherAssociatedType = false;
30993114
bool containsTautologicalType =
31003115
result.second.findIf([&](Type t) -> bool {
31013116
auto dmt = t->getAs<DependentMemberType>();
@@ -3107,11 +3122,63 @@ ConformanceChecker::inferTypeWitnessesViaValueWitnesses(
31073122
if (!dmt->getBase()->isEqual(Conformance->getType()))
31083123
return false;
31093124

3125+
// If this associated type is same-typed to another associated type
3126+
// on `Self`, then it may still be an interesting candidate if we find
3127+
// an answer for that other type.
3128+
auto witnessContext = witness->getDeclContext();
3129+
if (witnessContext->getAsProtocolExtensionContext()
3130+
&& witnessContext->getGenericSignatureOfContext()) {
3131+
auto selfTy = witnessContext->getSelfInterfaceType();
3132+
auto selfAssocTy = DependentMemberType::get(selfTy,
3133+
dmt->getAssocType());
3134+
for (auto &reqt : witnessContext->getGenericSignatureOfContext()
3135+
->getRequirements()) {
3136+
switch (reqt.getKind()) {
3137+
case RequirementKind::Conformance:
3138+
case RequirementKind::Superclass:
3139+
case RequirementKind::Layout:
3140+
break;
3141+
3142+
case RequirementKind::SameType:
3143+
Type other;
3144+
if (reqt.getFirstType()->isEqual(selfAssocTy)) {
3145+
other = reqt.getSecondType();
3146+
} else if (reqt.getSecondType()->isEqual(selfAssocTy)) {
3147+
other = reqt.getFirstType();
3148+
} else {
3149+
break;
3150+
}
3151+
3152+
if (auto otherAssoc = other->getAs<DependentMemberType>()) {
3153+
if (otherAssoc->getBase()->isEqual(selfTy)) {
3154+
auto otherDMT = DependentMemberType::get(dmt->getBase(),
3155+
otherAssoc->getAssocType());
3156+
3157+
// We may be able to infer one associated type from the
3158+
// other.
3159+
result.second = result.second.transform([&](Type t) -> Type{
3160+
if (t->isEqual(dmt))
3161+
return otherDMT;
3162+
return t;
3163+
});
3164+
canInferFromOtherAssociatedType = true;
3165+
DEBUG(llvm::dbgs() << "++ we can same-type to:\n";
3166+
result.second->dump(llvm::dbgs()));
3167+
return false;
3168+
}
3169+
}
3170+
break;
3171+
}
3172+
}
3173+
}
3174+
31103175
return true;
31113176
});
31123177

3113-
if (containsTautologicalType)
3178+
if (containsTautologicalType) {
3179+
DEBUG(llvm::dbgs() << "-- tautological\n");
31143180
REJECT;
3181+
}
31153182

31163183
// Check that the type witness doesn't contradict an
31173184
// explicitly-given type witness. If it does contradict, throw out the
@@ -3128,19 +3195,27 @@ ConformanceChecker::inferTypeWitnessesViaValueWitnesses(
31283195
auto newWitness = result.second->getCanonicalType();
31293196
if (!newWitness->hasTypeParameter()
31303197
&& !existingWitness->isEqual(newWitness)) {
3198+
DEBUG(llvm::dbgs() << "** contradicts explicit type witness, "
3199+
"rejecting inference from this decl\n");
31313200
goto next_witness;
31323201
}
31333202
}
31343203

3135-
// Check that the type witness meets the
3136-
// requirements on the associated type.
3137-
if (auto failed = checkTypeWitness(TC, DC, result.first,
3138-
result.second)) {
3139-
witnessResult.NonViable.push_back(
3140-
std::make_tuple(result.first,result.second,failed));
3141-
REJECT;
3204+
// If we same-typed to another unresolved associated type, we won't
3205+
// be able to check conformances yet.
3206+
if (!canInferFromOtherAssociatedType) {
3207+
// Check that the type witness meets the
3208+
// requirements on the associated type.
3209+
if (auto failed = checkTypeWitness(TC, DC, result.first,
3210+
result.second)) {
3211+
witnessResult.NonViable.push_back(
3212+
std::make_tuple(result.first,result.second,failed));
3213+
DEBUG(llvm::dbgs() << "-- doesn't fulfill requirements\n");
3214+
REJECT;
3215+
}
31423216
}
31433217

3218+
DEBUG(llvm::dbgs() << "++ seems legit\n");
31443219
++i;
31453220
}
31463221
#undef REJECT
@@ -3657,6 +3732,8 @@ void ConformanceChecker::resolveTypeWitnesses() {
36573732

36583733
// Infer type witnesses from value witnesses.
36593734
auto inferred = inferTypeWitnessesViaValueWitnesses(unresolvedAssocTypes);
3735+
DEBUG(llvm::dbgs() << "Candidates for inference:\n";
3736+
dumpInferredAssociatedTypes(inferred));
36603737

36613738
// Compute the set of solutions.
36623739
SmallVector<std::pair<ValueDecl *, ValueDecl *>, 4> valueWitnesses;
@@ -3847,6 +3924,9 @@ void ConformanceChecker::resolveTypeWitnesses() {
38473924
Type replaced = known->first.transform(foldDependentMemberTypes);
38483925
if (replaced.isNull())
38493926
return true;
3927+
3928+
if (checkTypeWitness(TC, DC, assocType, replaced))
3929+
return true;
38503930

38513931
known->first = replaced;
38523932
}

test/Constraints/associated_types.swift

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,50 @@ func owl3() {
3737
func spoon<S: Spoon>(_ s: S) {
3838
let _: S.Runcee?
3939
}
40+
41+
// SR-4143
42+
43+
protocol SameTypedDefault {
44+
associatedtype X
45+
associatedtype Y
46+
static var x: X { get }
47+
static var y: Y { get }
48+
}
49+
extension SameTypedDefault where Y == X {
50+
static var x: X {
51+
return y
52+
}
53+
}
54+
55+
struct UsesSameTypedDefault: SameTypedDefault {
56+
static var y: Int {
57+
return 0
58+
}
59+
}
60+
61+
protocol XReqt {}
62+
protocol YReqt {}
63+
64+
protocol SameTypedDefaultWithReqts {
65+
associatedtype X: XReqt // expected-note{{}}
66+
associatedtype Y: YReqt // expected-note{{}}
67+
static var x: X { get }
68+
static var y: Y { get }
69+
}
70+
extension SameTypedDefaultWithReqts where Y == X {
71+
static var x: X {
72+
return y
73+
}
74+
}
75+
76+
struct XYType: XReqt, YReqt {}
77+
struct YType: YReqt {}
78+
79+
struct UsesSameTypedDefaultWithReqts: SameTypedDefaultWithReqts {
80+
static var y: XYType { return XYType() }
81+
}
82+
83+
// expected-error@+1{{does not conform}}
84+
struct UsesSameTypedDefaultWithoutSatisfyingReqts: SameTypedDefaultWithReqts {
85+
static var y: YType { return YType() }
86+
}

0 commit comments

Comments
 (0)