Skip to content

Commit cdc3c1b

Browse files
committed
Sema: Accept tautological assoc type inference candidates when same-typed.
`X := Self.X` is still interesting information if it came from a witness candidate in a context where `X == Y` and we can infer a concrete witness for Y, for example: ```swift protocol SameTypedDefault { associatedtype X associatedtype Y static var x: X { get } static var y: Y { get } } extension SameTypedDefault where Y == X { static var x: X { return y } } struct UsesSameTypedDefault: SameTypedDefault { static var y: Int { return 0 } } ``` Defer checking whether such a witness candidate satisfies associated type requirements until we consider a complete solution system, since `ConformingType.X` isn't a valid type until we've settled on a witness for X, and its abilities depend on what we choose for Y. Fixes SR-4143.
1 parent a562c0e commit cdc3c1b

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
@@ -38,6 +38,9 @@
3838
#include "llvm/Support/Compiler.h"
3939
#include "llvm/Support/SaveAndRestore.h"
4040

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

4346
namespace {
@@ -3029,6 +3032,9 @@ ConformanceChecker::inferTypeWitnessesViaValueWitnesses(
30293032
};
30303033

30313034
for (auto witness : lookupValueWitnesses(req, /*ignoringNames=*/nullptr)) {
3035+
DEBUG(llvm::dbgs() << "Inferring associated types from decl:\n";
3036+
witness->dump(llvm::dbgs()));
3037+
30323038
// If the potential witness came from an extension, and our `Self`
30333039
// type can't use it regardless of what associated types we end up
30343040
// inferring, skip the witness.
@@ -3049,18 +3055,27 @@ ConformanceChecker::inferTypeWitnessesViaValueWitnesses(
30493055
}
30503056
auto &result = witnessResult.Inferred[i];
30513057

3058+
DEBUG(llvm::dbgs() << "Considering whether " << result.first->getName()
3059+
<< " can infer to:\n";
3060+
result.second->dump(llvm::dbgs()));
3061+
30523062
// Filter out errors.
3053-
if (result.second->hasError())
3063+
if (result.second->hasError()) {
3064+
DEBUG(llvm::dbgs() << "-- has error type\n");
30543065
REJECT;
3066+
}
30553067

30563068
// Filter out duplicates.
30573069
if (!known.insert({result.first, result.second->getCanonicalType()})
3058-
.second)
3070+
.second) {
3071+
DEBUG(llvm::dbgs() << "-- duplicate\n");
30593072
REJECT;
3073+
}
30603074

30613075
// Filter out circular possibilities, e.g. that
30623076
// AssocType == S.AssocType or
30633077
// AssocType == Foo<S.AssocType>.
3078+
bool canInferFromOtherAssociatedType = false;
30643079
bool containsTautologicalType =
30653080
result.second.findIf([&](Type t) -> bool {
30663081
auto dmt = t->getAs<DependentMemberType>();
@@ -3072,11 +3087,63 @@ ConformanceChecker::inferTypeWitnessesViaValueWitnesses(
30723087
if (!dmt->getBase()->isEqual(Conformance->getType()))
30733088
return false;
30743089

3090+
// If this associated type is same-typed to another associated type
3091+
// on `Self`, then it may still be an interesting candidate if we find
3092+
// an answer for that other type.
3093+
auto witnessContext = witness->getDeclContext();
3094+
if (witnessContext->getAsProtocolExtensionContext()
3095+
&& witnessContext->getGenericSignatureOfContext()) {
3096+
auto selfTy = witnessContext->getSelfInterfaceType();
3097+
auto selfAssocTy = DependentMemberType::get(selfTy,
3098+
dmt->getAssocType());
3099+
for (auto &reqt : witnessContext->getGenericSignatureOfContext()
3100+
->getRequirements()) {
3101+
switch (reqt.getKind()) {
3102+
case RequirementKind::Conformance:
3103+
case RequirementKind::Superclass:
3104+
case RequirementKind::Layout:
3105+
break;
3106+
3107+
case RequirementKind::SameType:
3108+
Type other;
3109+
if (reqt.getFirstType()->isEqual(selfAssocTy)) {
3110+
other = reqt.getSecondType();
3111+
} else if (reqt.getSecondType()->isEqual(selfAssocTy)) {
3112+
other = reqt.getFirstType();
3113+
} else {
3114+
break;
3115+
}
3116+
3117+
if (auto otherAssoc = other->getAs<DependentMemberType>()) {
3118+
if (otherAssoc->getBase()->isEqual(selfTy)) {
3119+
auto otherDMT = DependentMemberType::get(dmt->getBase(),
3120+
otherAssoc->getAssocType());
3121+
3122+
// We may be able to infer one associated type from the
3123+
// other.
3124+
result.second = result.second.transform([&](Type t) -> Type{
3125+
if (t->isEqual(dmt))
3126+
return otherDMT;
3127+
return t;
3128+
});
3129+
canInferFromOtherAssociatedType = true;
3130+
DEBUG(llvm::dbgs() << "++ we can same-type to:\n";
3131+
result.second->dump(llvm::dbgs()));
3132+
return false;
3133+
}
3134+
}
3135+
break;
3136+
}
3137+
}
3138+
}
3139+
30753140
return true;
30763141
});
30773142

3078-
if (containsTautologicalType)
3143+
if (containsTautologicalType) {
3144+
DEBUG(llvm::dbgs() << "-- tautological\n");
30793145
REJECT;
3146+
}
30803147

30813148
// Check that the type witness doesn't contradict an
30823149
// explicitly-given type witness. If it does contradict, throw out the
@@ -3093,19 +3160,27 @@ ConformanceChecker::inferTypeWitnessesViaValueWitnesses(
30933160
auto newWitness = result.second->getCanonicalType();
30943161
if (!newWitness->hasTypeParameter()
30953162
&& !existingWitness->isEqual(newWitness)) {
3163+
DEBUG(llvm::dbgs() << "** contradicts explicit type witness, "
3164+
"rejecting inference from this decl\n");
30963165
goto next_witness;
30973166
}
30983167
}
30993168

3100-
// Check that the type witness meets the
3101-
// requirements on the associated type.
3102-
if (auto failed = checkTypeWitness(TC, DC, result.first,
3103-
result.second)) {
3104-
witnessResult.NonViable.push_back(
3105-
std::make_tuple(result.first,result.second,failed));
3106-
REJECT;
3169+
// If we same-typed to another unresolved associated type, we won't
3170+
// be able to check conformances yet.
3171+
if (!canInferFromOtherAssociatedType) {
3172+
// Check that the type witness meets the
3173+
// requirements on the associated type.
3174+
if (auto failed = checkTypeWitness(TC, DC, result.first,
3175+
result.second)) {
3176+
witnessResult.NonViable.push_back(
3177+
std::make_tuple(result.first,result.second,failed));
3178+
DEBUG(llvm::dbgs() << "-- doesn't fulfill requirements\n");
3179+
REJECT;
3180+
}
31073181
}
31083182

3183+
DEBUG(llvm::dbgs() << "++ seems legit\n");
31093184
++i;
31103185
}
31113186
#undef REJECT
@@ -3622,6 +3697,8 @@ void ConformanceChecker::resolveTypeWitnesses() {
36223697

36233698
// Infer type witnesses from value witnesses.
36243699
auto inferred = inferTypeWitnessesViaValueWitnesses(unresolvedAssocTypes);
3700+
DEBUG(llvm::dbgs() << "Candidates for inference:\n";
3701+
dumpInferredAssociatedTypes(inferred));
36253702

36263703
// Compute the set of solutions.
36273704
SmallVector<std::pair<ValueDecl *, ValueDecl *>, 4> valueWitnesses;
@@ -3812,6 +3889,9 @@ void ConformanceChecker::resolveTypeWitnesses() {
38123889
Type replaced = known->first.transform(foldDependentMemberTypes);
38133890
if (replaced.isNull())
38143891
return true;
3892+
3893+
if (checkTypeWitness(TC, DC, assocType, replaced))
3894+
return true;
38153895

38163896
known->first = replaced;
38173897
}

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)