[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()) if (lhs->hasHole() || rhs->hasHole())
return true; 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 // If requirement is something like `T == [Int]` let's let
// type matcher a chance to match generic parameters before // type matcher a chance to match generic parameters before
// recording a fix, because then we'll know exactly how many // 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"); llvm_unreachable("type variables should have already been handled by now");
case TypeKind::DependentMember: { 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 // Nothing we can solve yet, since we need to wait until
// type variables will get resolved. // type variables will get resolved.
return formUnsolvedResult(); return formUnsolvedResult();

View File

@@ -2454,8 +2454,17 @@ Type ConstraintSystem::simplifyTypeImpl(Type type,
auto *proto = assocType->getProtocol(); auto *proto = assocType->getProtocol();
auto conformance = DC->getParentModule()->lookupConformance( auto conformance = DC->getParentModule()->lookupConformance(
lookupBaseType, proto); 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); return DependentMemberType::get(lookupBaseType, assocType);
}
auto subs = SubstitutionMap::getProtocolSubstitutions( auto subs = SubstitutionMap::getProtocolSubstitutions(
proto, lookupBaseType, conformance); proto, lookupBaseType, conformance);
@@ -2895,10 +2904,9 @@ static bool diagnoseConflictingGenericArguments(ConstraintSystem &cs,
static bool static bool
diagnoseAmbiguityWithEphemeralPointers(ConstraintSystem &cs, diagnoseAmbiguityWithEphemeralPointers(ConstraintSystem &cs,
ArrayRef<Solution> solutions) { ArrayRef<Solution> solutions) {
bool allSolutionsHaveFixes = true; unsigned numSolutionsWithFixes = 0;
for (const auto &solution : solutions) { for (const auto &solution : solutions) {
if (solution.Fixes.empty()) { if (solution.Fixes.empty()) {
allSolutionsHaveFixes = false;
continue; continue;
} }
@@ -2906,11 +2914,14 @@ diagnoseAmbiguityWithEphemeralPointers(ConstraintSystem &cs,
return fix->getKind() == FixKind::TreatEphemeralAsNonEphemeral; return fix->getKind() == FixKind::TreatEphemeralAsNonEphemeral;
})) }))
return false; 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. // let `diagnoseAmbiguityWithFixes` diagnose the problem.
if (allSolutionsHaveFixes) if (numSolutionsWithFixes == 0 ||
numSolutionsWithFixes == solutions.size())
return false; return false;
// If only some of the solutions have ephemeral pointer fixes // 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 // Deduction for member operators
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
protocol Addable { // expected-note {{where 'Self' = 'U'}} protocol Addable {
static func +(x: Self, y: Self) -> Self static func +(x: Self, y: Self) -> Self
} }
func addAddables<T : Addable, U>(_ x: T, y: T, u: U) -> T { 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 return x+y
} }

View File

@@ -44,4 +44,3 @@ var x = X()
x ~> \X.y > { a in a += 1; return 3 } 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@-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@-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'}}