Skip to content

Commit 094ca80

Browse files
authored
Merge pull request #34737 from xedin/potential-bindings-refactor
[ConstraintSystem] Infer some of the bindings attributes on demand
2 parents 9dad3af + afa1198 commit 094ca80

File tree

3 files changed

+123
-94
lines changed

3 files changed

+123
-94
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 60 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4736,26 +4736,11 @@ class ConstraintSystem {
47364736
/// Whether the bindings of this type involve other type variables.
47374737
bool InvolvesTypeVariables = false;
47384738

4739-
/// Whether this type variable is considered a hole in the constraint system.
4740-
bool IsHole = false;
4741-
4742-
/// Whether the bindings represent (potentially) incomplete set,
4743-
/// there is no way to say with absolute certainty if that's the
4744-
/// case, but that could happen when certain constraints like
4745-
/// `bind param` are present in the system.
4746-
bool PotentiallyIncomplete = false;
4747-
47484739
ASTNode AssociatedCodeCompletionToken = ASTNode();
47494740

47504741
/// Whether this type variable has literal bindings.
47514742
LiteralBindingKind LiteralBinding = LiteralBindingKind::None;
47524743

4753-
/// Whether this type variable is only bound above by existential types.
4754-
bool SubtypeOfExistentialType = false;
4755-
4756-
/// The number of defaultable bindings.
4757-
unsigned NumDefaultableBindings = 0;
4758-
47594744
/// Tracks the position of the last known supertype in the group.
47604745
Optional<unsigned> lastSupertypeIndex;
47614746

@@ -4767,49 +4752,88 @@ class ConstraintSystem {
47674752
llvm::SmallMapVector<TypeVariableType *, Constraint *, 4> SupertypeOf;
47684753
llvm::SmallMapVector<TypeVariableType *, Constraint *, 4> EquivalentTo;
47694754

4770-
PotentialBindings(TypeVariableType *typeVar)
4771-
: TypeVar(typeVar), PotentiallyIncomplete(isGenericParameter()) {}
4755+
PotentialBindings(TypeVariableType *typeVar) : TypeVar(typeVar) {}
47724756

47734757
/// Determine whether the set of bindings is non-empty.
47744758
explicit operator bool() const { return !Bindings.empty(); }
47754759

4776-
/// Whether there are any non-defaultable bindings.
4777-
bool hasNonDefaultableBindings() const {
4778-
return Bindings.size() > NumDefaultableBindings;
4760+
/// Whether the bindings represent (potentially) incomplete set,
4761+
/// there is no way to say with absolute certainty if that's the
4762+
/// case, but that could happen when certain constraints like
4763+
/// `bind param` are present in the system.
4764+
bool isPotentiallyIncomplete() const;
4765+
4766+
/// If there is only one binding and it's to a hole type, consider
4767+
/// this type variable to be a hole in a constraint system regardless
4768+
/// of where hole type originated.
4769+
bool isHole() const {
4770+
if (Bindings.size() != 1)
4771+
return false;
4772+
4773+
auto &binding = Bindings.front();
4774+
return binding.BindingType->is<HoleType>();
4775+
}
4776+
4777+
/// Determine if the bindings only constrain the type variable from above
4778+
/// with an existential type; such a binding is not very helpful because
4779+
/// it's impossible to enumerate the existential type's subtypes.
4780+
bool isSubtypeOfExistentialType() const {
4781+
if (Bindings.empty())
4782+
return false;
4783+
4784+
return llvm::all_of(Bindings, [](const PotentialBinding &binding) {
4785+
return binding.BindingType->isExistentialType() &&
4786+
binding.Kind == AllowedBindingKind::Subtypes;
4787+
});
4788+
}
4789+
4790+
unsigned getNumDefaultableBindings() const {
4791+
return llvm::count_if(Bindings, [](const PotentialBinding &binding) {
4792+
return binding.isDefaultableBinding();
4793+
});
47794794
}
47804795

47814796
static BindingScore formBindingScore(const PotentialBindings &b) {
4782-
return std::make_tuple(b.IsHole,
4783-
!b.hasNonDefaultableBindings(),
4797+
auto numDefaults = b.getNumDefaultableBindings();
4798+
auto hasNoDefaultableBindings = b.Bindings.size() > numDefaults;
4799+
4800+
return std::make_tuple(b.isHole(),
4801+
!hasNoDefaultableBindings,
47844802
b.FullyBound,
4785-
b.SubtypeOfExistentialType,
4803+
b.isSubtypeOfExistentialType(),
47864804
b.InvolvesTypeVariables,
47874805
static_cast<unsigned char>(b.LiteralBinding),
4788-
-(b.Bindings.size() - b.NumDefaultableBindings));
4806+
-(b.Bindings.size() - numDefaults));
47894807
}
47904808

47914809
/// Compare two sets of bindings, where \c x < y indicates that
47924810
/// \c x is a better set of bindings that \c y.
47934811
friend bool operator<(const PotentialBindings &x,
47944812
const PotentialBindings &y) {
4795-
if (formBindingScore(x) < formBindingScore(y))
4813+
auto xScore = formBindingScore(x);
4814+
auto yScore = formBindingScore(y);
4815+
4816+
if (xScore < yScore)
47964817
return true;
47974818

4798-
if (formBindingScore(y) < formBindingScore(x))
4819+
if (yScore < xScore)
47994820
return false;
48004821

4822+
auto xDefaults = x.Bindings.size() + std::get<6>(xScore);
4823+
auto yDefaults = y.Bindings.size() + std::get<6>(yScore);
4824+
48014825
// If there is a difference in number of default types,
48024826
// prioritize bindings with fewer of them.
4803-
if (x.NumDefaultableBindings != y.NumDefaultableBindings)
4804-
return x.NumDefaultableBindings < y.NumDefaultableBindings;
4827+
if (xDefaults != yDefaults)
4828+
return xDefaults < yDefaults;
48054829

48064830
// If neither type variable is a "hole" let's check whether
48074831
// there is a subtype relationship between them and prefer
48084832
// type variable which represents superclass first in order
48094833
// for "subtype" type variable to attempt more bindings later.
48104834
// This is required because algorithm can't currently infer
48114835
// bindings for subtype transitively through superclass ones.
4812-
if (!(x.IsHole && y.IsHole)) {
4836+
if (!(std::get<0>(xScore) && std::get<0>(yScore))) {
48134837
if (x.isSubtypeOf(y.TypeVar))
48144838
return false;
48154839

@@ -4819,7 +4843,7 @@ class ConstraintSystem {
48194843

48204844
// As a last resort, let's check if the bindings are
48214845
// potentially incomplete, and if so, let's de-prioritize them.
4822-
return x.PotentiallyIncomplete < y.PotentiallyIncomplete;
4846+
return x.isPotentiallyIncomplete() < y.isPotentiallyIncomplete();
48234847
}
48244848

48254849
void foundLiteralBinding(ProtocolDecl *proto) {
@@ -4920,18 +4944,20 @@ class ConstraintSystem {
49204944
void dump(llvm::raw_ostream &out,
49214945
unsigned indent = 0) const LLVM_ATTRIBUTE_USED {
49224946
out.indent(indent);
4923-
if (PotentiallyIncomplete)
4947+
if (isPotentiallyIncomplete())
49244948
out << "potentially_incomplete ";
49254949
if (FullyBound)
49264950
out << "fully_bound ";
4927-
if (SubtypeOfExistentialType)
4951+
if (isSubtypeOfExistentialType())
49284952
out << "subtype_of_existential ";
49294953
if (LiteralBinding != LiteralBindingKind::None)
49304954
out << "literal=" << static_cast<int>(LiteralBinding) << " ";
49314955
if (InvolvesTypeVariables)
49324956
out << "involves_type_vars ";
4933-
if (NumDefaultableBindings > 0)
4934-
out << "#defaultable_bindings=" << NumDefaultableBindings << " ";
4957+
4958+
auto numDefaultable = getNumDefaultableBindings();
4959+
if (numDefaultable > 0)
4960+
out << "#defaultable_bindings=" << numDefaultable << " ";
49354961

49364962
PrintOptions PO;
49374963
PO.PrintTypesForDebugging = true;

lib/Sema/CSBindings.cpp

Lines changed: 62 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,64 @@
2222
using namespace swift;
2323
using namespace constraints;
2424

25+
bool ConstraintSystem::PotentialBindings::isPotentiallyIncomplete() const {
26+
// Generic parameters are always potentially incomplete.
27+
if (isGenericParameter())
28+
return true;
29+
30+
// If current type variable is associated with a code completion token
31+
// it's possible that it doesn't have enough contextual information
32+
// to be resolved to anything so let's delay considering it until everything
33+
// else is resolved.
34+
if (AssociatedCodeCompletionToken)
35+
return true;
36+
37+
auto *locator = TypeVar->getImpl().getLocator();
38+
if (!locator)
39+
return false;
40+
41+
if (locator->isLastElement<LocatorPathElt::UnresolvedMemberChainResult>()) {
42+
// If subtyping is allowed and this is a result of an implicit member chain,
43+
// let's delay binding it to an optional until its object type resolved too or
44+
// it has been determined that there is no possibility to resolve it. Otherwise
45+
// we might end up missing solutions since it's allowed to implicitly unwrap
46+
// base type of the chain but it can't be done early - type variable
47+
// representing chain's result type has a different l-valueness comparing
48+
// to generic parameter of the optional.
49+
if (llvm::any_of(Bindings, [&](const PotentialBinding &binding) {
50+
if (binding.Kind != AllowedBindingKind::Subtypes)
51+
return false;
52+
53+
auto objectType = binding.BindingType->getOptionalObjectType();
54+
return objectType && objectType->isTypeVariableOrMember();
55+
}))
56+
return true;
57+
}
58+
59+
if (isHole()) {
60+
// If the base of the unresolved member reference like `.foo`
61+
// couldn't be resolved we'd want to bind it to a hole at the
62+
// very last moment possible, just like generic parameters.
63+
if (locator->isLastElement<LocatorPathElt::MemberRefBase>())
64+
return true;
65+
66+
// Delay resolution of the code completion expression until
67+
// the very end to give it a chance to be bound to some
68+
// contextual type even if it's a hole.
69+
if (locator->directlyAt<CodeCompletionExpr>())
70+
return true;
71+
72+
// Delay resolution of the `nil` literal to a hole until
73+
// the very end to give it a change to be bound to some
74+
// other type, just like code completion expression which
75+
// relies solely on contextual information.
76+
if (locator->directlyAt<NilLiteralExpr>())
77+
return true;
78+
}
79+
80+
return false;
81+
}
82+
2583
void ConstraintSystem::PotentialBindings::inferTransitiveProtocolRequirements(
2684
const ConstraintSystem &cs,
2785
llvm::SmallDenseMap<TypeVariableType *, ConstraintSystem::PotentialBindings>
@@ -462,30 +520,23 @@ void ConstraintSystem::PotentialBindings::finalize(
462520
// If there are no bindings, typeVar may be a hole.
463521
if (cs.shouldAttemptFixes() && Bindings.empty() &&
464522
TypeVar->getImpl().canBindToHole()) {
465-
IsHole = true;
466523
// If the base of the unresolved member reference like `.foo`
467524
// couldn't be resolved we'd want to bind it to a hole at the
468525
// very last moment possible, just like generic parameters.
469526
auto *locator = TypeVar->getImpl().getLocator();
470-
if (locator->isLastElement<LocatorPathElt::MemberRefBase>())
471-
PotentiallyIncomplete = true;
472527

473528
// Delay resolution of the code completion expression until
474529
// the very end to give it a chance to be bound to some
475530
// contextual type even if it's a hole.
476-
if (locator->directlyAt<CodeCompletionExpr>()) {
531+
if (locator->directlyAt<CodeCompletionExpr>())
477532
FullyBound = true;
478-
PotentiallyIncomplete = true;
479-
}
480533

481534
// Delay resolution of the `nil` literal to a hole until
482535
// the very end to give it a change to be bound to some
483536
// other type, just like code completion expression which
484537
// relies solely on contextual information.
485-
if (locator->directlyAt<NilLiteralExpr>()) {
538+
if (locator->directlyAt<NilLiteralExpr>())
486539
FullyBound = true;
487-
PotentiallyIncomplete = true;
488-
}
489540

490541
// If this type variable is associated with a code completion token
491542
// and it failed to infer any bindings let's adjust hole's locator
@@ -513,17 +564,6 @@ void ConstraintSystem::PotentialBindings::finalize(
513564
std::rotate(AnyTypePos, AnyTypePos + 1, Bindings.end());
514565
}
515566
}
516-
517-
// Determine if the bindings only constrain the type variable from above with
518-
// an existential type; such a binding is not very helpful because it's
519-
// impossible to enumerate the existential type's subtypes.
520-
if (!Bindings.empty()) {
521-
SubtypeOfExistentialType =
522-
llvm::all_of(Bindings, [](const PotentialBinding &binding) {
523-
return binding.BindingType->isExistentialType() &&
524-
binding.Kind == AllowedBindingKind::Subtypes;
525-
});
526-
}
527567
}
528568

529569
Optional<ConstraintSystem::PotentialBindings>
@@ -678,9 +718,6 @@ void ConstraintSystem::PotentialBindings::addPotentialBinding(
678718
if (!isViable(binding))
679719
return;
680720

681-
if (binding.isDefaultableBinding())
682-
++NumDefaultableBindings;
683-
684721
Bindings.push_back(std::move(binding));
685722
}
686723

@@ -709,7 +746,7 @@ bool ConstraintSystem::PotentialBindings::isViable(
709746

710747
bool ConstraintSystem::PotentialBindings::favoredOverDisjunction(
711748
Constraint *disjunction) const {
712-
if (IsHole || FullyBound)
749+
if (isHole() || FullyBound)
713750
return false;
714751

715752
// If this bindings are for a closure and there are no holes,
@@ -889,10 +926,8 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint(
889926
// bindings and use it when forming a hole if there are no other bindings
890927
// available.
891928
if (auto *locator = bindingTypeVar->getImpl().getLocator()) {
892-
if (locator->directlyAt<CodeCompletionExpr>()) {
929+
if (locator->directlyAt<CodeCompletionExpr>())
893930
result.AssociatedCodeCompletionToken = locator->getAnchor();
894-
result.PotentiallyIncomplete = true;
895-
}
896931
}
897932

898933
switch (constraint->getKind()) {
@@ -931,24 +966,6 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint(
931966
return None;
932967
}
933968

934-
// If subtyping is allowed and this is a result of an implicit member chain,
935-
// let's delay binding it to an optional until its object type resolved too or
936-
// it has been determined that there is no possibility to resolve it. Otherwise
937-
// we might end up missing solutions since it's allowed to implicitly unwrap
938-
// base type of the chain but it can't be done early - type variable
939-
// representing chain's result type has a different l-valueness comparing
940-
// to generic parameter of the optional.
941-
if (kind == AllowedBindingKind::Subtypes) {
942-
auto *locator = typeVar->getImpl().getLocator();
943-
if (locator &&
944-
locator->isLastElement<LocatorPathElt::UnresolvedMemberChainResult>()) {
945-
auto objectType = type->getOptionalObjectType();
946-
if (objectType && objectType->isTypeVariableOrMember()) {
947-
result.PotentiallyIncomplete = true;
948-
}
949-
}
950-
}
951-
952969
if (type->is<InOutType>() && !typeVar->getImpl().canBindToInOut())
953970
type = LValueType::get(type->getInOutObjectType());
954971
if (type->is<LValueType>() && !typeVar->getImpl().canBindToLValue())
@@ -987,20 +1004,6 @@ bool ConstraintSystem::PotentialBindings::infer(
9871004
case ConstraintKind::ArgumentConversion:
9881005
case ConstraintKind::OperatorArgumentConversion:
9891006
case ConstraintKind::OptionalObject: {
990-
// If there is a `bind param` constraint associated with
991-
// current type variable, result should be aware of that
992-
// fact. Binding set might be incomplete until
993-
// this constraint is resolved, because we currently don't
994-
// look-through constraints expect to `subtype` to try and
995-
// find related bindings.
996-
// This only affects type variable that appears one the
997-
// right-hand side of the `bind param` constraint and
998-
// represents result type of the closure body, because
999-
// left-hand side gets types from overload choices.
1000-
if (constraint->getKind() == ConstraintKind::BindParam &&
1001-
constraint->getSecondType()->isEqual(TypeVar))
1002-
PotentiallyIncomplete = true;
1003-
10041007
auto binding =
10051008
cs.getPotentialBindingForRelationalConstraint(*this, constraint);
10061009
if (!binding)

lib/Sema/ConstraintGraph.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1117,7 +1117,7 @@ bool ConstraintGraph::contractEdges() {
11171117
bool isNotContractable = true;
11181118
if (auto bindings = CS.inferBindingsFor(tyvar1)) {
11191119
// Holes can't be contracted.
1120-
if (bindings.IsHole)
1120+
if (bindings.isHole())
11211121
continue;
11221122

11231123
for (auto &binding : bindings.Bindings) {

0 commit comments

Comments
 (0)