Skip to content

Commit 8afc560

Browse files
committed
[ConstraintSystem] Introduce a notion of a "hole"
A "hole" is a type variable which type couldn't be determined due to an inference failure e.g. missing member, ambiguous generic parameter which hasn't been explicitly specified. It is used to propagate information about failures and avoid recording fixes which are a consequence of earlier failures e.g. ```swift func foo<T: BinaryInteger>(_: T) {} struct S {} foo(S.bar) // Actual failure here is that `S` doesn't have a member // `bar` but a consequence of that failure is that generic // parameter `T` doesn't conform to `BinaryInteger`. ```
1 parent fb9a864 commit 8afc560

File tree

5 files changed

+86
-10
lines changed

5 files changed

+86
-10
lines changed

lib/Sema/CSBindings.cpp

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -978,8 +978,33 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const {
978978
cs.addConstraint(ConstraintKind::Bind, TypeVar, type, locator);
979979

980980
// If this was from a defaultable binding note that.
981-
if (Binding.isDefaultableBinding())
982-
cs.DefaultedConstraints.push_back(Binding.DefaultableBinding);
981+
if (Binding.isDefaultableBinding()) {
982+
auto *locator = Binding.DefaultableBinding;
983+
// If this default binding comes from a "hole"
984+
// in the constraint system, we have to propagate
985+
// this information and mark this type variable
986+
// as well as mark everything adjacent to it as
987+
// a potential "hole".
988+
//
989+
// Consider this example:
990+
//
991+
// func foo<T: BinaryInteger>(_: T) {}
992+
// foo(.bar) <- Since `.bar` can't be inferred due to
993+
// luck of information about its base type,
994+
// it's member type is going to get defaulted
995+
// to `Any` which has to be propaged to type
996+
// variable associated with `T` and vice versa.
997+
if (cs.shouldAttemptFixes() && cs.isHoleAt(locator)) {
998+
auto &CG = cs.getConstraintGraph();
999+
for (auto *constraint : CG.gatherConstraints(
1000+
TypeVar, ConstraintGraph::GatheringKind::EquivalenceClass)) {
1001+
for (auto *typeVar : constraint->getTypeVariables())
1002+
cs.recordHole(typeVar);
1003+
}
1004+
}
1005+
1006+
cs.DefaultedConstraints.push_back(locator);
1007+
}
9831008

9841009
return !cs.failedConstraint && !cs.simplify();
9851010
}

lib/Sema/CSSimplify.cpp

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3715,6 +3715,23 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
37153715
ConstraintKind kind,
37163716
ConstraintLocatorBuilder locator,
37173717
TypeMatchOptions flags) {
3718+
if (shouldAttemptFixes()) {
3719+
auto *typeVar = type->getAs<TypeVariableType>();
3720+
// If type variable, associated with this conformance check,
3721+
// has been determined to be a "hole" in constraint system,
3722+
// let's consider this check a success without recording
3723+
// a fix, because it's just a consequence of other failure
3724+
// e.g.
3725+
//
3726+
// func foo<T: BinaryInteger>(_: T) {}
3727+
// foo(Foo.bar) <- if `Foo` doesn't have `bar` there is
3728+
// no reason to complain about missing conformance.
3729+
if (typeVar && isHole(typeVar)) {
3730+
increaseScore(SK_Fix);
3731+
return SolutionKind::Solved;
3732+
}
3733+
}
3734+
37183735
// Dig out the fixed type to which this type refers.
37193736
type = getFixedTypeRecursive(type, flags, /*wantRValue=*/true);
37203737

@@ -5266,14 +5283,22 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
52665283
// fake its presence based on use, that makes it possible to diagnose
52675284
// problems related to member lookup more precisely.
52685285

5286+
auto defaultTypeVariableToAny = [&](TypeVariableType *typeVar) {
5287+
// Recording this generic parameter is a potential "hole" in
5288+
// the constraint system.
5289+
recordHole(typeVar);
5290+
// Default all of the generic parameters found in base to `Any`.
5291+
addConstraint(ConstraintKind::Defaultable, typeVar,
5292+
getASTContext().TheAnyType,
5293+
typeVar->getImpl().getLocator());
5294+
};
5295+
52695296
origBaseTy.transform([&](Type type) -> Type {
52705297
if (auto *typeVar = type->getAs<TypeVariableType>()) {
52715298
if (typeVar->getImpl().hasRepresentativeOrFixed())
52725299
return type;
5273-
// Default all of the generic parameters found in base to `Any`.
5274-
addConstraint(ConstraintKind::Defaultable, typeVar,
5275-
getASTContext().TheAnyType,
5276-
typeVar->getImpl().getLocator());
5300+
5301+
defaultTypeVariableToAny(typeVar);
52775302
}
52785303
return type;
52795304
});
@@ -5286,8 +5311,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
52865311
// Allow member type to default to `Any` to make it possible to form
52875312
// solutions when contextual type of the result cannot be deduced e.g.
52885313
// `let _ = x.foo`.
5289-
addConstraint(ConstraintKind::Defaultable, memberTy,
5290-
getASTContext().TheAnyType, locator);
5314+
if (auto *memberTypeVar = memberTy->getAs<TypeVariableType>())
5315+
defaultTypeVariableToAny(memberTypeVar);
5316+
52915317
return SolutionKind::Solved;
52925318
}
52935319
return SolutionKind::Error;

lib/Sema/CSSolver.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs)
452452
numSavedBindings = cs.solverState->savedBindings.size();
453453
numConstraintRestrictions = cs.ConstraintRestrictions.size();
454454
numFixes = cs.Fixes.size();
455+
numHoles = cs.Holes.size();
455456
numFixedRequirements = cs.FixedRequirements.size();
456457
numDisjunctionChoices = cs.DisjunctionChoices.size();
457458
numOpenedTypes = cs.OpenedTypes.size();
@@ -500,6 +501,9 @@ ConstraintSystem::SolverScope::~SolverScope() {
500501
// Remove any fixes.
501502
truncate(cs.Fixes, numFixes);
502503

504+
// Remove any holes encountered along the current path.
505+
truncate(cs.Holes, numHoles);
506+
503507
// Remove any disjunction choices.
504508
truncate(cs.DisjunctionChoices, numDisjunctionChoices);
505509

lib/Sema/ConstraintSystem.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,6 +1095,14 @@ class ConstraintSystem {
10951095

10961096
/// The set of fixes applied to make the solution work.
10971097
llvm::SmallVector<ConstraintFix *, 4> Fixes;
1098+
1099+
/// The set of "holes" in the constraint system encountered
1100+
/// along the current path identified by locator. A "hole" is
1101+
/// a type variable which type couldn't be determined due to
1102+
/// an inference failure e.g. missing member, ambiguous generic
1103+
/// parameter which hasn't been explicitly specified.
1104+
llvm::SmallSetVector<ConstraintLocator *, 4> Holes;
1105+
10981106
/// The set of type variables representing types of missing members.
10991107
llvm::SmallSetVector<ConstraintLocator *, 4> MissingMembers;
11001108

@@ -1631,6 +1639,9 @@ class ConstraintSystem {
16311639
/// The length of \c Fixes.
16321640
unsigned numFixes;
16331641

1642+
/// The length of \c Holes.
1643+
unsigned numHoles;
1644+
16341645
/// The length of \c FixedRequirements.
16351646
unsigned numFixedRequirements;
16361647

@@ -2034,6 +2045,18 @@ class ConstraintSystem {
20342045
/// subsequent solution would be worse than the best known solution.
20352046
bool recordFix(ConstraintFix *fix);
20362047

2048+
void recordHole(TypeVariableType *typeVar) {
2049+
Holes.insert(typeVar->getImpl().getLocator());
2050+
}
2051+
2052+
bool isHole(TypeVariableType *typeVar) const {
2053+
return isHoleAt(typeVar->getImpl().getLocator());
2054+
}
2055+
2056+
bool isHoleAt(ConstraintLocator *locator) const {
2057+
return bool(Holes.count(locator));
2058+
}
2059+
20372060
/// Determine whether constraint system already has a fix recorded
20382061
/// for a particular location.
20392062
bool hasFixFor(ConstraintLocator *locator) const {

test/Sema/diag_ambiguous_overloads.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,6 @@ class sr7440_Genre {
9494
return sr7440_Genre.fetch(genreID: iTunesGenre.genreID, name: iTunesGenre.name)
9595
// expected-error@-1 {{value of type 'sr7440_ITunesGenre' has no member 'genreID'; did you mean 'genre'?}}
9696
// expected-error@-2 {{cannot convert return expression of type '()' to return type 'sr7440_Genre'}}
97-
// expected-error@-3 {{protocol type 'Any' cannot conform to 'BinaryInteger' because only concrete types can conform to protocols}}
98-
// TODO(diagnostics): Last diagnostic should not be recorded but to be able to correctly handle it we need a notion of a "hole" in constraint system.
9997
}
10098
}
10199

0 commit comments

Comments
 (0)