[ConstraintSystem] Replace dependent member types with holes when the base type

doesn't conform to the associatedtype's protocol (only in diagnostic mode).

This allows the solver to find solutions for more cases involving requirement
failures for dependent member types without special cases across the solver
that check for dependent member types with no type variables.
This commit is contained in:
Holly Borla
2020-03-03 12:01:28 -08:00
parent b58ea4bb37
commit fce8738ea8
5 changed files with 43 additions and 25 deletions

View File

@@ -3560,14 +3560,6 @@ bool ConstraintSystem::repairFailures(
if (lhs->hasHole() || rhs->hasHole())
return true;
// If dependent members are present here it's because the base doesn't
// conform to the associated type's protocol. We can only get here if we
// already applied a fix for the conformance failure.
if (lhs->hasDependentMember() || rhs->hasDependentMember()) {
increaseScore(SK_Fix);
return true;
}
// If requirement is something like `T == [Int]` let's let
// type matcher a chance to match generic parameters before
// recording a fix, because then we'll know exactly how many
@@ -4202,15 +4194,6 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
llvm_unreachable("type variables should have already been handled by now");
case TypeKind::DependentMember: {
// If one of the dependent member types has no type variables, the
// dependent member can't be simplified because the base doesn't conform
// to the associated type's protocol. We can only get here if we already
// applied a fix for the conformance failure.
if (!desugar1->hasTypeVariable() || !desugar2->hasTypeVariable()) {
increaseScore(SK_Fix);
return getTypeMatchSuccess();
}
// Nothing we can solve yet, since we need to wait until
// type variables will get resolved.
return formUnsolvedResult();

View File

@@ -2454,8 +2454,17 @@ Type ConstraintSystem::simplifyTypeImpl(Type type,
auto *proto = assocType->getProtocol();
auto conformance = DC->getParentModule()->lookupConformance(
lookupBaseType, proto);
if (!conformance)
if (!conformance) {
// If the base type doesn't conform to the associatedtype's protocol,
// there will be a missing conformance fix applied in diagnostic mode,
// so the concrete dependent member type is considered a "hole" in
// order to continue solving.
if (shouldAttemptFixes() &&
getPhase() == ConstraintSystemPhase::Solving)
return getASTContext().TheUnresolvedType;
return DependentMemberType::get(lookupBaseType, assocType);
}
auto subs = SubstitutionMap::getProtocolSubstitutions(
proto, lookupBaseType, conformance);
@@ -2895,10 +2904,9 @@ static bool diagnoseConflictingGenericArguments(ConstraintSystem &cs,
static bool
diagnoseAmbiguityWithEphemeralPointers(ConstraintSystem &cs,
ArrayRef<Solution> solutions) {
bool allSolutionsHaveFixes = true;
unsigned numSolutionsWithFixes = 0;
for (const auto &solution : solutions) {
if (solution.Fixes.empty()) {
allSolutionsHaveFixes = false;
continue;
}
@@ -2906,11 +2914,14 @@ diagnoseAmbiguityWithEphemeralPointers(ConstraintSystem &cs,
return fix->getKind() == FixKind::TreatEphemeralAsNonEphemeral;
}))
return false;
numSolutionsWithFixes += 1;
}
// If all solutions have fixes for ephemeral pointers, let's
// If all or no solutions have fixes for ephemeral pointers, let's
// let `diagnoseAmbiguityWithFixes` diagnose the problem.
if (allSolutionsHaveFixes)
if (numSolutionsWithFixes == 0 ||
numSolutionsWithFixes == solutions.size())
return false;
// If only some of the solutions have ephemeral pointer fixes

View File

@@ -0,0 +1,24 @@
// RUN: %target-typecheck-verify-swift
protocol P {
associatedtype V
}
struct Person {
var name: String = ""
}
struct Row : P {
typealias V = String
init(_: V) {}
}
func foo<C: Collection, R: P>(_ collection: C, _: (C.Element.V) -> R) where C.Element: P { }
// expected-note@-1 {{where 'C.Element' = 'Person'}}
func bar(_ arr: [Person]) {
foo(arr) { person in // expected-error {{global function 'foo' requires that 'Person' conform to 'P'}}
Row(person.name)
}
}

View File

@@ -243,11 +243,12 @@ genericInheritsA(C_GI())
//===----------------------------------------------------------------------===//
// Deduction for member operators
//===----------------------------------------------------------------------===//
protocol Addable { // expected-note {{where 'Self' = 'U'}}
protocol Addable {
static func +(x: Self, y: Self) -> Self
}
func addAddables<T : Addable, U>(_ x: T, y: T, u: U) -> T {
u + u // expected-error{{referencing operator function '+' on 'Addable' requires that 'U' conform to 'Addable'}}
// FIXME(diagnostics): This should report the "no exact matches" diagnostic.
u + u // expected-error{{referencing operator function '+' on 'RangeReplaceableCollection' requires that 'U' conform to 'RangeReplaceableCollection'}}
return x+y
}

View File

@@ -44,4 +44,3 @@ var x = X()
x ~> \X.y > { a in a += 1; return 3 }
// expected-error@-1 {{referencing operator function '~>' on 'P' requires that 'M<WritableKeyPath<X, Int>, R>' conform to 'P'}}
// expected-error@-2 {{unable to infer complex closure return type; add explicit type to disambiguate}}
// expected-error@-3 {{cannot convert value of type 'X' to expected argument type 'M<WritableKeyPath<X, Int>, R>.A'}}