Skip to content

Commit aa887fd

Browse files
committed
[ConstraintSystem] Turn binding storage into a SetVector
Currently potential bindings are stored in a vector (`SmallVector`) and every call has to pass additional set of unique types to inference methods to unqiue the bindings. Instead let's merge these two together and use `SetVector` for binding storage, which would also be great for incremental mode that can't pass additional sets around.
1 parent db4f790 commit aa887fd

File tree

2 files changed

+83
-62
lines changed

2 files changed

+83
-62
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4623,7 +4623,10 @@ class ConstraintSystem {
46234623
/// bindings, e.g., the supertypes of a given type.
46244624
struct PotentialBinding {
46254625
/// The type to which the type variable can be bound.
4626-
Type BindingType;
4626+
///
4627+
/// Note that the type is mutable since it could be
4628+
/// adjusted as a result of type-join operation.
4629+
mutable Type BindingType;
46274630

46284631
/// The kind of bindings permitted.
46294632
AllowedBindingKind Kind;
@@ -4756,7 +4759,7 @@ class ConstraintSystem {
47564759
TypeVariableType *TypeVar;
47574760

47584761
/// The set of potential bindings.
4759-
SmallVector<PotentialBinding, 4> Bindings;
4762+
llvm::SmallSetVector<PotentialBinding, 4> Bindings;
47604763

47614764
/// The set of protocol requirements placed on this type variable.
47624765
llvm::SmallVector<Constraint *, 4> Protocols;
@@ -4986,13 +4989,21 @@ class ConstraintSystem {
49864989
/// \param canBeNil The flag that determines whether given type
49874990
/// variable requires all of its bindings to be optional.
49884991
///
4989-
/// \returns true if binding covers given literal protocol.
4990-
bool isLiteralCoveredBy(const LiteralRequirement &literal,
4991-
PotentialBinding &binding, bool canBeNil) const;
4992+
/// \returns a pair of bool and a type:
4993+
/// - bool, true if binding covers given literal protocol;
4994+
/// - type, non-null if binding type has to be adjusted
4995+
/// to cover given literal protocol;
4996+
std::pair<bool, Type> isLiteralCoveredBy(const LiteralRequirement &literal,
4997+
const PotentialBinding &binding,
4998+
bool canBeNil) const;
49924999

49935000
/// Add a potential binding to the list of bindings,
49945001
/// coalescing supertype bounds when we are able to compute the meet.
4995-
void addPotentialBinding(PotentialBinding binding,
5002+
///
5003+
/// \returns true if this binding has been added to the set,
5004+
/// false otherwise (e.g. because binding with this type is
5005+
/// already in the set).
5006+
bool addPotentialBinding(PotentialBinding binding,
49965007
bool allowJoinMeet = true);
49975008

49985009
/// Check if this binding is viable for inclusion in the set.
@@ -5035,7 +5046,6 @@ class ConstraintSystem {
50355046
/// variables in the workset.
50365047
void inferTransitiveBindings(
50375048
ConstraintSystem &cs,
5038-
llvm::SmallPtrSetImpl<CanType> &existingTypes,
50395049
const llvm::SmallDenseMap<TypeVariableType *,
50405050
ConstraintSystem::PotentialBindings>
50415051
&inferredBindings);
@@ -5050,9 +5060,7 @@ class ConstraintSystem {
50505060
&inferredBindings);
50515061

50525062
public:
5053-
bool infer(ConstraintSystem &cs,
5054-
llvm::SmallPtrSetImpl<CanType> &exactTypes,
5055-
Constraint *constraint);
5063+
bool infer(ConstraintSystem &cs, Constraint *constraint);
50565064

50575065
/// Finalize binding computation for this type variable by
50585066
/// inferring bindings from context e.g. transitive bindings.
@@ -6252,7 +6260,8 @@ struct DenseMapInfo<swift::constraints::ConstraintSystem::PotentialBinding> {
62526260
}
62536261

62546262
static unsigned getHashValue(const Binding &Val) {
6255-
return DenseMapInfo<swift::Type>::getHashValue(Val.BindingType);
6263+
return DenseMapInfo<swift::Type>::getHashValue(
6264+
Val.BindingType->getCanonicalType());
62566265
}
62576266

62586267
static bool isEqual(const Binding &LHS, const Binding &RHS) {

lib/Sema/CSBindings.cpp

Lines changed: 63 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ void ConstraintSystem::PotentialBindings::inferTransitiveProtocolRequirements(
283283
}
284284

285285
void ConstraintSystem::PotentialBindings::inferTransitiveBindings(
286-
ConstraintSystem &cs, llvm::SmallPtrSetImpl<CanType> &existingTypes,
286+
ConstraintSystem &cs,
287287
const llvm::SmallDenseMap<TypeVariableType *,
288288
ConstraintSystem::PotentialBindings>
289289
&inferredBindings) {
@@ -343,13 +343,10 @@ void ConstraintSystem::PotentialBindings::inferTransitiveBindings(
343343
if (type->isHole())
344344
continue;
345345

346-
if (!existingTypes.insert(type->getCanonicalType()).second)
347-
continue;
348-
349346
if (ConstraintSystem::typeVarOccursInType(TypeVar, type))
350347
continue;
351348

352-
addPotentialBinding(
349+
(void)addPotentialBinding(
353350
binding.withSameSource(type, BindingKind::Supertypes));
354351
}
355352
}
@@ -359,14 +356,8 @@ void ConstraintSystem::PotentialBindings::finalize(
359356
ConstraintSystem &cs,
360357
llvm::SmallDenseMap<TypeVariableType *, ConstraintSystem::PotentialBindings>
361358
&inferredBindings) {
362-
// We need to make sure that there are no duplicate bindings in the
363-
// set, otherwise solver would produce multiple identical solutions.
364-
llvm::SmallPtrSet<CanType, 4> existingTypes;
365-
for (const auto &binding : Bindings)
366-
existingTypes.insert(binding.BindingType->getCanonicalType());
367-
368359
inferTransitiveProtocolRequirements(cs, inferredBindings);
369-
inferTransitiveBindings(cs, existingTypes, inferredBindings);
360+
inferTransitiveBindings(cs, inferredBindings);
370361
}
371362

372363
Optional<ConstraintSystem::PotentialBindings>
@@ -500,8 +491,8 @@ bool ConstraintSystem::LiteralRequirement::isCoveredBy(
500491
return bool(TypeChecker::conformsToProtocol(type, getProtocol(), useDC));
501492
}
502493

503-
bool ConstraintSystem::PotentialBindings::isLiteralCoveredBy(
504-
const LiteralRequirement &literal, PotentialBinding &binding,
494+
std::pair<bool, Type> ConstraintSystem::PotentialBindings::isLiteralCoveredBy(
495+
const LiteralRequirement &literal, const PotentialBinding &binding,
505496
bool canBeNil) const {
506497
auto type = binding.BindingType;
507498
switch (binding.Kind) {
@@ -516,29 +507,24 @@ bool ConstraintSystem::PotentialBindings::isLiteralCoveredBy(
516507
}
517508

518509
if (type->isTypeVariableOrMember() || type->isHole())
519-
return false;
510+
return std::make_pair(false, Type());
520511

521512
bool requiresUnwrap = false;
522513
do {
523514
if (literal.isCoveredBy(type, CS.DC)) {
524-
// FIXME: Side-effect like this is not great (to say the least),
525-
// but this is an artifact of the binding collection which could
526-
// be fixed separately.
527-
if (requiresUnwrap)
528-
binding.BindingType = type;
529-
return true;
515+
return std::make_pair(true, requiresUnwrap ? type : binding.BindingType);
530516
}
531517

532518
// Can't unwrap optionals if there is `ExpressibleByNilLiteral`
533519
// conformance requirement placed on the type variable.
534520
if (canBeNil)
535-
return false;
521+
return std::make_pair(false, Type());
536522

537523
// If this literal protocol is not a direct requirement it
538524
// would not be possible to change optionality while inferring
539525
// bindings for a supertype, so this hack doesn't apply.
540526
if (!literal.isDirectRequirement())
541-
return false;
527+
return std::make_pair(false, Type());
542528

543529
// If we're allowed to bind to subtypes, look through optionals.
544530
// FIXME: This is really crappy special case of computing a reasonable
@@ -551,14 +537,17 @@ bool ConstraintSystem::PotentialBindings::isLiteralCoveredBy(
551537
}
552538
}
553539

554-
return false;
540+
return std::make_pair(false, Type());
555541
} while (true);
556542
}
557543

558-
void ConstraintSystem::PotentialBindings::addPotentialBinding(
544+
bool ConstraintSystem::PotentialBindings::addPotentialBinding(
559545
PotentialBinding binding, bool allowJoinMeet) {
560546
assert(!binding.BindingType->is<ErrorType>());
561547

548+
if (Bindings.count(binding))
549+
return false;
550+
562551
// If this is a non-defaulted supertype binding,
563552
// check whether we can combine it with another
564553
// supertype binding by computing the 'join' of the types.
@@ -585,7 +574,7 @@ void ConstraintSystem::PotentialBindings::addPotentialBinding(
585574
// If new binding has been joined with at least one of existing
586575
// bindings, there is no reason to include it into the set.
587576
if (joined)
588-
return;
577+
return false;
589578
}
590579

591580
// If the type variable can't bind to an lvalue, make sure the
@@ -596,7 +585,7 @@ void ConstraintSystem::PotentialBindings::addPotentialBinding(
596585
}
597586

598587
if (!isViable(binding))
599-
return;
588+
return false;
600589

601590
// Check whether the given binding covers any of the literal protocols
602591
// associated with this type variable.
@@ -614,13 +603,24 @@ void ConstraintSystem::PotentialBindings::addPotentialBinding(
614603

615604
auto &info = literal.second;
616605

617-
if (info.viableAsBinding() &&
618-
isLiteralCoveredBy(info, binding, allowsNil))
619-
info.setCoveredBy(binding.getSource());
606+
if (!info.viableAsBinding())
607+
continue;
608+
609+
bool isCovered = false;
610+
Type adjustedTy;
611+
612+
std::tie(isCovered, adjustedTy) =
613+
isLiteralCoveredBy(info, binding, allowsNil);
614+
615+
if (!isCovered)
616+
continue;
617+
618+
binding = binding.withType(adjustedTy);
619+
info.setCoveredBy(binding.getSource());
620620
}
621621
}
622622

623-
Bindings.push_back(std::move(binding));
623+
return Bindings.insert(std::move(binding));
624624
}
625625

626626
void ConstraintSystem::PotentialBindings::addLiteral(Constraint *constraint) {
@@ -675,15 +675,33 @@ void ConstraintSystem::PotentialBindings::addLiteral(Constraint *constraint) {
675675
LiteralRequirement literal(
676676
constraint, TypeChecker::getDefaultType(protocol, CS.DC), isDirect);
677677

678-
{
678+
if (literal.viableAsBinding()) {
679679
bool allowsNil = canBeNil();
680680

681-
for (auto &binding : Bindings) {
682-
if (!literal.isCovered() &&
683-
isLiteralCoveredBy(literal, binding, allowsNil)) {
684-
literal.setCoveredBy(binding.getSource());
685-
break;
681+
for (auto binding = Bindings.begin(); binding != Bindings.end();
682+
++binding) {
683+
bool isCovered = false;
684+
Type adjustedTy;
685+
686+
std::tie(isCovered, adjustedTy) =
687+
isLiteralCoveredBy(literal, *binding, allowsNil);
688+
689+
// No luck here, let's try next literal requirement.
690+
if (!isCovered)
691+
continue;
692+
693+
// If the type has been adjusted, we need to re-insert
694+
// the binding but skip all of the previous checks.
695+
//
696+
// It's okay to do this here since iteration stops after
697+
// first covering binding has been found.
698+
if (adjustedTy) {
699+
Bindings.erase(binding);
700+
Bindings.insert(binding->withType(adjustedTy));
686701
}
702+
703+
literal.setCoveredBy(binding->getSource());
704+
break;
687705
}
688706
}
689707

@@ -756,10 +774,8 @@ ConstraintSystem::inferBindingsFor(TypeVariableType *typeVar, bool finalize) {
756774
auto constraints = CG.gatherConstraints(
757775
typeVar, ConstraintGraph::GatheringKind::EquivalenceClass);
758776

759-
llvm::SmallPtrSet<CanType, 4> exactTypes;
760-
761777
for (auto *constraint : constraints) {
762-
bool failed = bindings.infer(*this, exactTypes, constraint);
778+
bool failed = bindings.infer(*this, constraint);
763779

764780
// Upon inference failure let's produce an empty set of bindings.
765781
if (failed)
@@ -979,9 +995,8 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint(
979995
/// Retrieve the set of potential type bindings for the given
980996
/// representative type variable, along with flags indicating whether
981997
/// those types should be opened.
982-
bool ConstraintSystem::PotentialBindings::infer(
983-
ConstraintSystem &cs, llvm::SmallPtrSetImpl<CanType> &exactTypes,
984-
Constraint *constraint) {
998+
bool ConstraintSystem::PotentialBindings::infer(ConstraintSystem &cs,
999+
Constraint *constraint) {
9851000
switch (constraint->getKind()) {
9861001
case ConstraintKind::Bind:
9871002
case ConstraintKind::Equal:
@@ -998,9 +1013,7 @@ bool ConstraintSystem::PotentialBindings::infer(
9981013
break;
9991014

10001015
auto type = binding->BindingType;
1001-
if (exactTypes.insert(type->getCanonicalType()).second) {
1002-
addPotentialBinding(*binding);
1003-
1016+
if (addPotentialBinding(*binding)) {
10041017
// Determines whether this type variable represents an object
10051018
// of the optional type extracted by force unwrap.
10061019
if (auto *locator = TypeVar->getImpl().getLocator()) {
@@ -1014,7 +1027,7 @@ bool ConstraintSystem::PotentialBindings::infer(
10141027
// delaying bindings for as long as possible.
10151028
if (isExpr<ForceValueExpr>(anchor) &&
10161029
TypeVar->getImpl().canBindToLValue() && !type->is<LValueType>()) {
1017-
addPotentialBinding(binding->withType(LValueType::get(type)));
1030+
(void)addPotentialBinding(binding->withType(LValueType::get(type)));
10181031
DelayedBy.push_back(constraint);
10191032
}
10201033

@@ -1025,10 +1038,9 @@ bool ConstraintSystem::PotentialBindings::infer(
10251038
// helps to avoid creating a thunk to support it.
10261039
auto voidType = cs.getASTContext().TheEmptyTupleType;
10271040
if (locator->isLastElement<LocatorPathElt::ClosureResult>() &&
1028-
binding->Kind == AllowedBindingKind::Supertypes &&
1029-
exactTypes.insert(voidType).second) {
1030-
addPotentialBinding({voidType, binding->Kind, constraint},
1031-
/*allowJoinMeet=*/false);
1041+
binding->Kind == AllowedBindingKind::Supertypes) {
1042+
(void)addPotentialBinding({voidType, binding->Kind, constraint},
1043+
/*allowJoinMeet=*/false);
10321044
}
10331045
}
10341046
}

0 commit comments

Comments
 (0)