Skip to content

Commit 22b34e8

Browse files
committed
[CSBindings] Let producer record a binding for a hole
As a step towards making binding inference more incremental, let's make producer responsible for adding hole type binding instead of doing so in `finalize`.
1 parent 23b8dba commit 22b34e8

File tree

4 files changed

+66
-39
lines changed

4 files changed

+66
-39
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4725,6 +4725,9 @@ class ConstraintSystem {
47254725
using BindingScore =
47264726
std::tuple<bool, bool, bool, bool, bool, unsigned char, int>;
47274727

4728+
/// The constraint system this type variable and its bindings belong to.
4729+
ConstraintSystem &CS;
4730+
47284731
TypeVariableType *TypeVar;
47294732

47304733
/// The set of potential bindings.
@@ -4764,10 +4767,13 @@ class ConstraintSystem {
47644767
llvm::SmallMapVector<TypeVariableType *, Constraint *, 4> SupertypeOf;
47654768
llvm::SmallMapVector<TypeVariableType *, Constraint *, 4> EquivalentTo;
47664769

4767-
PotentialBindings(TypeVariableType *typeVar) : TypeVar(typeVar) {}
4770+
PotentialBindings(ConstraintSystem &cs, TypeVariableType *typeVar)
4771+
: CS(cs), TypeVar(typeVar) {}
47684772

47694773
/// Determine whether the set of bindings is non-empty.
4770-
explicit operator bool() const { return !Bindings.empty(); }
4774+
explicit operator bool() const {
4775+
return !Bindings.empty() || isDirectHole();
4776+
}
47714777

47724778
/// Determine whether attempting this type variable should be
47734779
/// delayed until the rest of the constraint system is considered
@@ -4791,17 +4797,34 @@ class ConstraintSystem {
47914797
/// `bind param` are present in the system.
47924798
bool isPotentiallyIncomplete() const;
47934799

4794-
/// If there is only one binding and it's to a hole type, consider
4795-
/// this type variable to be a hole in a constraint system regardless
4796-
/// of where hole type originated.
4800+
/// If this type variable doesn't have any viable bindings, or
4801+
/// if there is only one binding and it's a hole type, consider
4802+
/// this type variable to be a hole in a constraint system
4803+
/// regardless of where hole type originated.
47974804
bool isHole() const {
4805+
if (isDirectHole())
4806+
return true;
4807+
47984808
if (Bindings.size() != 1)
47994809
return false;
48004810

4801-
auto &binding = Bindings.front();
4811+
const auto &binding = Bindings.front();
48024812
return binding.BindingType->is<HoleType>();
48034813
}
48044814

4815+
/// Determines whether the only possible binding for this type variable
4816+
/// would be a hole type. This is different from `isHole` method because
4817+
/// type variable could also acquire a hole type transitively if one
4818+
/// of the type variables in its subtype/equivalence chain has been
4819+
/// bound to a hole type.
4820+
bool isDirectHole() const {
4821+
// Direct holes are only allowed in "diagnostic mode".
4822+
if (!CS.shouldAttemptFixes())
4823+
return false;
4824+
4825+
return Bindings.empty() && TypeVar->getImpl().canBindToHole();
4826+
}
4827+
48054828
/// Determine if the bindings only constrain the type variable from above
48064829
/// with an existential type; such a binding is not very helpful because
48074830
/// it's impossible to enumerate the existential type's subtypes.
@@ -4816,22 +4839,26 @@ class ConstraintSystem {
48164839
}
48174840

48184841
unsigned getNumDefaultableBindings() const {
4819-
return llvm::count_if(Bindings, [](const PotentialBinding &binding) {
4820-
return binding.isDefaultableBinding();
4821-
});
4842+
return isDirectHole()
4843+
? 1
4844+
: llvm::count_if(Bindings,
4845+
[](const PotentialBinding &binding) {
4846+
return binding.isDefaultableBinding();
4847+
});
48224848
}
48234849

48244850
static BindingScore formBindingScore(const PotentialBindings &b) {
48254851
auto numDefaults = b.getNumDefaultableBindings();
4826-
auto hasNoDefaultableBindings = b.Bindings.size() > numDefaults;
4852+
auto numNonDefaultableBindings =
4853+
b.isDirectHole() ? 0 : b.Bindings.size() - numDefaults;
48274854

48284855
return std::make_tuple(b.isHole(),
4829-
!hasNoDefaultableBindings,
4856+
numNonDefaultableBindings == 0,
48304857
b.isDelayed(),
48314858
b.isSubtypeOfExistentialType(),
48324859
b.involvesTypeVariables(),
48334860
static_cast<unsigned char>(b.LiteralBinding),
4834-
-(b.Bindings.size() - numDefaults));
4861+
-numNonDefaultableBindings);
48354862
}
48364863

48374864
/// Compare two sets of bindings, where \c x < y indicates that
@@ -4847,8 +4874,10 @@ class ConstraintSystem {
48474874
if (yScore < xScore)
48484875
return false;
48494876

4850-
auto xDefaults = x.Bindings.size() + std::get<6>(xScore);
4851-
auto yDefaults = y.Bindings.size() + std::get<6>(yScore);
4877+
auto xDefaults =
4878+
x.isDirectHole() ? 1 : x.Bindings.size() + std::get<6>(xScore);
4879+
auto yDefaults =
4880+
y.isDirectHole() ? 1 : y.Bindings.size() + std::get<6>(yScore);
48524881

48534882
// If there is a difference in number of default types,
48544883
// prioritize bindings with fewer of them.
@@ -4972,6 +5001,8 @@ class ConstraintSystem {
49725001
void dump(llvm::raw_ostream &out,
49735002
unsigned indent = 0) const LLVM_ATTRIBUTE_USED {
49745003
out.indent(indent);
5004+
if (isDirectHole())
5005+
out << "hole ";
49755006
if (isPotentiallyIncomplete())
49765007
out << "potentially_incomplete ";
49775008
if (isDelayed())

lib/Sema/CSBindings.cpp

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -546,26 +546,6 @@ void ConstraintSystem::PotentialBindings::finalize(
546546
inferTransitiveProtocolRequirements(cs, inferredBindings);
547547
inferTransitiveBindings(cs, existingTypes, inferredBindings);
548548
inferDefaultTypes(cs, existingTypes);
549-
550-
// If there are no bindings, typeVar may be a hole.
551-
if (cs.shouldAttemptFixes() && Bindings.empty() &&
552-
TypeVar->getImpl().canBindToHole()) {
553-
// If the base of the unresolved member reference like `.foo`
554-
// couldn't be resolved we'd want to bind it to a hole at the
555-
// very last moment possible, just like generic parameters.
556-
auto *locator = TypeVar->getImpl().getLocator();
557-
558-
// If this type variable is associated with a code completion token
559-
// and it failed to infer any bindings let's adjust hole's locator
560-
// to point to a code completion token to avoid attempting to "fix"
561-
// this problem since its rooted in the fact that constraint system
562-
// is under-constrained.
563-
if (AssociatedCodeCompletionToken) {
564-
locator = cs.getConstraintLocator(AssociatedCodeCompletionToken);
565-
}
566-
567-
addPotentialBinding(PotentialBinding::forHole(TypeVar, locator));
568-
}
569549
}
570550

571551
Optional<ConstraintSystem::PotentialBindings>
@@ -595,7 +575,7 @@ ConstraintSystem::determineBestBindings() {
595575
if (shouldAttemptFixes() && typeVar->getImpl().canBindToHole())
596576
return true;
597577

598-
return !bindings.Bindings.empty() || !bindings.Defaults.empty() ||
578+
return bindings || !bindings.Defaults.empty() ||
599579
llvm::any_of(bindings.Protocols, [&](Constraint *constraint) {
600580
return bool(
601581
TypeChecker::getDefaultType(constraint->getProtocol(), DC));
@@ -635,7 +615,7 @@ ConstraintSystem::determineBestBindings() {
635615
// If these are the first bindings, or they are better than what
636616
// we saw before, use them instead.
637617
if (!bestBindings || bindings < *bestBindings)
638-
bestBindings = bindings;
618+
bestBindings.emplace(bindings);
639619
}
640620

641621
return bestBindings;
@@ -783,7 +763,7 @@ ConstraintSystem::inferBindingsFor(TypeVariableType *typeVar, bool finalize) {
783763
"not a representative");
784764
assert(!typeVar->getImpl().getFixedType(nullptr) && "has a fixed type");
785765

786-
PotentialBindings bindings(typeVar);
766+
PotentialBindings bindings(*this, typeVar);
787767

788768
// Gather the constraints associated with this type variable.
789769
auto constraints = CG.gatherConstraints(
@@ -796,7 +776,7 @@ ConstraintSystem::inferBindingsFor(TypeVariableType *typeVar, bool finalize) {
796776

797777
// Upon inference failure let's produce an empty set of bindings.
798778
if (failed)
799-
return {typeVar};
779+
return {*this, typeVar};
800780
}
801781

802782
if (finalize) {

lib/Sema/CSSolver.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2287,7 +2287,8 @@ void DisjunctionChoice::propagateConversionInfo(ConstraintSystem &cs) const {
22872287
return;
22882288

22892289
auto bindings = cs.inferBindingsFor(typeVar);
2290-
if (bindings.involvesTypeVariables() || bindings.Bindings.size() != 1)
2290+
if (bindings.isHole() || bindings.involvesTypeVariables() ||
2291+
bindings.Bindings.size() != 1)
22912292
return;
22922293

22932294
auto conversionType = bindings.Bindings[0].BindingType;

lib/Sema/ConstraintSystem.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5340,6 +5340,21 @@ TypeVarBindingProducer::TypeVarBindingProducer(
53405340
return protocol->isSpecificProtocol(
53415341
KnownProtocolKind::ExpressibleByNilLiteral);
53425342
})) {
5343+
if (bindings.isDirectHole()) {
5344+
auto *locator = getLocator();
5345+
// If this type variable is associated with a code completion token
5346+
// and it failed to infer any bindings let's adjust hole's locator
5347+
// to point to a code completion token to avoid attempting to "fix"
5348+
// this problem since its rooted in the fact that constraint system
5349+
// is under-constrained.
5350+
if (bindings.AssociatedCodeCompletionToken) {
5351+
locator = cs.getConstraintLocator(bindings.AssociatedCodeCompletionToken);
5352+
}
5353+
5354+
Bindings.push_back(Binding::forHole(TypeVar, locator));
5355+
return;
5356+
}
5357+
53435358
// A binding to `Any` which should always be considered as a last resort.
53445359
Optional<Binding> Any;
53455360

0 commit comments

Comments
 (0)