Skip to content

Commit 2393a7c

Browse files
committed
[CSBindings] Represent literal requirements as a struct instead of a tuple
Doing so streamlines access to the information associated with literal protocol requirements and allows to add more helpers. Also cache default type in the struct itself for easy access.
1 parent e676e05 commit 2393a7c

File tree

4 files changed

+83
-67
lines changed

4 files changed

+83
-67
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4723,17 +4723,51 @@ class ConstraintSystem {
47234723
}
47244724
};
47254725

4726+
struct LiteralRequirement {
4727+
/// The source of the literal requirement.
4728+
Constraint *Source;
4729+
/// The default type associated with this literal (if any).
4730+
Type DefaultType;
4731+
/// Determines whether this literal is a direct requirement
4732+
/// of the current type variable.
4733+
bool IsDirectRequirement;
4734+
4735+
/// If the literal is covered by existing type binding,
4736+
/// this points to the source of the binding.
4737+
mutable Constraint *CoveredBy = nullptr;
4738+
4739+
Constraint *getSource() const { return Source; }
4740+
4741+
ProtocolDecl *getProtocol() const { return Source->getProtocol(); }
4742+
4743+
bool isCovered() const { return bool(CoveredBy); }
4744+
4745+
bool isDirectRequirement() const { return IsDirectRequirement; }
4746+
4747+
bool hasDefaultType() const { return bool(DefaultType); }
4748+
4749+
Type getDefaultType() const {
4750+
assert(hasDefaultType());
4751+
return DefaultType;
4752+
}
4753+
4754+
void setCoveredBy(Constraint *coveredBy) {
4755+
assert(!isCovered());
4756+
CoveredBy = coveredBy;
4757+
}
4758+
4759+
bool isCoveredBy(Type type, DeclContext *useDC) const;
4760+
4761+
/// Determines whether literal protocol associated with this
4762+
/// meta-information is viable for inclusion as a defaultable binding.
4763+
bool viableAsBinding() const { return !isCovered() && hasDefaultType(); }
4764+
};
4765+
4766+
private:
47264767
struct PotentialBindings {
47274768
using BindingScore =
47284769
std::tuple<bool, bool, bool, bool, bool, unsigned char, int>;
47294770

4730-
/// - Constraint * - The source of the literal requirement;
4731-
/// - bool - Determines whether this literal is a direct requirement
4732-
/// of the current type variable;
4733-
/// - Constraint * - If the literal is covered, this points to the
4734-
/// source of the binding;
4735-
using LiteralInfo = std::tuple<Constraint *, bool, Constraint *>;
4736-
47374771
/// The constraint system this type variable and its bindings belong to.
47384772
ConstraintSystem &CS;
47394773

@@ -4755,7 +4789,7 @@ class ConstraintSystem {
47554789
/// Note that ordering is important when it comes to bindings, we'd
47564790
/// like to add any "direct" default types first to attempt them
47574791
/// before transitive ones.
4758-
llvm::SmallMapVector<ProtocolDecl *, LiteralInfo, 2> Literals;
4792+
llvm::SmallMapVector<ProtocolDecl *, LiteralRequirement, 2> Literals;
47594793

47604794
/// The set of constraints which would be used to infer default types.
47614795
llvm::SmallDenseMap<CanType, Constraint *, 2> Defaults;
@@ -4970,15 +5004,9 @@ class ConstraintSystem {
49705004
/// \param canBeNil The flag that determines whether given type
49715005
/// variable requires all of its bindings to be optional.
49725006
///
4973-
/// \param isDirectRequirement The flag that determines whether
4974-
/// this literal conformance requirement is associated with the
4975-
/// current type variable or it's inferred.
4976-
///
49775007
/// \returns true if binding covers given literal protocol.
4978-
bool isLiteralCoveredBy(ProtocolDecl *literal,
4979-
PotentialBinding &binding,
4980-
bool canBeNil,
4981-
bool isDirectRequirement) const;
5008+
bool isLiteralCoveredBy(const LiteralRequirement &literal,
5009+
PotentialBinding &binding, bool canBeNil) const;
49825010

49835011
/// Add a potential binding to the list of bindings,
49845012
/// coalescing supertype bounds when we are able to compute the meet.

lib/Sema/CSBindings.cpp

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ void ConstraintSystem::PotentialBindings::inferTransitiveBindings(
319319
// `ExpressibleByStringLiteral` conformance, we'd end up picking
320320
// `T` with only one type `Any?` which is incorrect.
321321
for (const auto &literal : bindings.Literals)
322-
addLiteral(std::get<0>(literal.second));
322+
addLiteral(literal.second.getSource());
323323

324324
// Infer transitive defaults.
325325
for (const auto &def : bindings.Defaults)
@@ -476,7 +476,8 @@ void ConstraintSystem::PotentialBindings::addDefault(Constraint *constraint) {
476476
Defaults.insert({defaultTy->getCanonicalType(), constraint});
477477
}
478478

479-
static bool isCoveredBy(ProtocolDecl *protocol, Type type, DeclContext *useDC) {
479+
bool ConstraintSystem::LiteralRequirement::isCoveredBy(
480+
Type type, DeclContext *useDC) const {
480481
auto coversDefaultType = [](Type type, Type defaultType) -> bool {
481482
if (!defaultType->hasUnboundGenericType())
482483
return type->isEqual(defaultType);
@@ -493,17 +494,15 @@ static bool isCoveredBy(ProtocolDecl *protocol, Type type, DeclContext *useDC) {
493494
return nominal == type->getAnyNominal();
494495
};
495496

496-
if (auto defaultType = TypeChecker::getDefaultType(protocol, useDC)) {
497-
if (coversDefaultType(type, defaultType))
498-
return true;
499-
}
497+
if (hasDefaultType() && coversDefaultType(type, getDefaultType()))
498+
return true;
500499

501-
return bool(TypeChecker::conformsToProtocol(type, protocol, useDC));
500+
return bool(TypeChecker::conformsToProtocol(type, getProtocol(), useDC));
502501
}
503502

504503
bool ConstraintSystem::PotentialBindings::isLiteralCoveredBy(
505-
ProtocolDecl *literal, PotentialBinding &binding, bool canBeNil,
506-
bool isDirectRequirement) const {
504+
const LiteralRequirement &literal, PotentialBinding &binding,
505+
bool canBeNil) const {
507506
auto type = binding.BindingType;
508507
switch (binding.Kind) {
509508
case AllowedBindingKind::Exact:
@@ -521,7 +520,7 @@ bool ConstraintSystem::PotentialBindings::isLiteralCoveredBy(
521520

522521
bool requiresUnwrap = false;
523522
do {
524-
if (isCoveredBy(literal, type, CS.DC)) {
523+
if (literal.isCoveredBy(type, CS.DC)) {
525524
// FIXME: Side-effect like this is not great (to say the least),
526525
// but this is an artifact of the binding collection which could
527526
// be fixed separately.
@@ -538,7 +537,7 @@ bool ConstraintSystem::PotentialBindings::isLiteralCoveredBy(
538537
// If this literal protocol is not a direct requirement it
539538
// would not be possible to change optionality while inferring
540539
// bindings for a supertype, so this hack doesn't apply.
541-
if (!isDirectRequirement)
540+
if (!literal.isDirectRequirement())
542541
return false;
543542

544543
// If we're allowed to bind to subtypes, look through optionals.
@@ -613,12 +612,11 @@ void ConstraintSystem::PotentialBindings::addPotentialBinding(
613612
KnownProtocolKind::ExpressibleByNilLiteral))
614613
continue;
615614

616-
auto isDirectRequirement = std::get<1>(literal.second);
617-
auto *&coveredBy = std::get<2>(literal.second);
615+
auto &info = literal.second;
618616

619-
if (!coveredBy &&
620-
isLiteralCoveredBy(protocol, binding, allowsNil, isDirectRequirement))
621-
coveredBy = binding.getSource();
617+
if (info.viableAsBinding() &&
618+
isLiteralCoveredBy(info, binding, allowsNil))
619+
info.setCoveredBy(binding.getSource());
622620
}
623621
}
624622

@@ -661,32 +659,38 @@ void ConstraintSystem::PotentialBindings::addLiteral(Constraint *constraint) {
661659
return;
662660

663661
bool isDirect = isDirectRequirement(constraint);
664-
Constraint *coveredBy = nullptr;
665662

666663
// Coverage is not applicable to `ExpressibleByNilLiteral` since it
667664
// doesn't have a default type.
668665
if (protocol->isSpecificProtocol(
669666
KnownProtocolKind::ExpressibleByNilLiteral)) {
670-
Literals.insert(
671-
{protocol, std::make_tuple(constraint, isDirect, coveredBy)});
667+
Literals.insert({protocol,
668+
{.Source = constraint,
669+
.DefaultType = Type(),
670+
.IsDirectRequirement = isDirect}});
672671
return;
673672
}
674673

675674
// Check whether any of the existing bindings covers this literal
676675
// protocol.
676+
LiteralRequirement literal{.Source = constraint,
677+
.DefaultType =
678+
TypeChecker::getDefaultType(protocol, CS.DC),
679+
.IsDirectRequirement = isDirect};
680+
677681
{
678682
bool allowsNil = canBeNil();
679683

680684
for (auto &binding : Bindings) {
681-
if (!coveredBy &&
682-
isLiteralCoveredBy(protocol, binding, allowsNil, isDirect)) {
683-
coveredBy = binding.getSource();
685+
if (!literal.isCovered() &&
686+
isLiteralCoveredBy(literal, binding, allowsNil)) {
687+
literal.setCoveredBy(binding.getSource());
684688
break;
685689
}
686690
}
687691
}
688692

689-
Literals.insert({protocol, std::make_tuple(constraint, isDirect, coveredBy)});
693+
Literals.insert({protocol, std::move(literal)});
690694
}
691695

692696
bool ConstraintSystem::PotentialBindings::isViable(
@@ -1180,10 +1184,10 @@ ConstraintSystem::PotentialBindings::getLiteralKind() const {
11801184

11811185
for (const auto &literal : Literals) {
11821186
auto *protocol = literal.first;
1183-
auto *coveredBy = std::get<2>(literal.second);
1187+
const auto &info = literal.second;
11841188

11851189
// Only uncovered defaultable literal protocols participate.
1186-
if (coveredBy || !TypeChecker::getDefaultType(protocol, CS.DC))
1190+
if (!info.viableAsBinding())
11871191
continue;
11881192

11891193
switch (*protocol->getKnownProtocolKind()) {
@@ -1210,8 +1214,7 @@ ConstraintSystem::PotentialBindings::getLiteralKind() const {
12101214
unsigned
12111215
ConstraintSystem::PotentialBindings::getNumViableLiteralBindings() const {
12121216
return llvm::count_if(Literals, [&](const auto &literal) {
1213-
auto *coveredBy = std::get<2>(literal.second);
1214-
return !(coveredBy || !TypeChecker::getDefaultType(literal.first, CS.DC));
1217+
return literal.second.viableAsBinding();
12151218
});
12161219
}
12171220

lib/Sema/CSSolver.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2301,12 +2301,8 @@ void DisjunctionChoice::propagateConversionInfo(ConstraintSystem &cs) const {
23012301
conversionType = bindings.Bindings[0].BindingType;
23022302
} else {
23032303
for (const auto &literal : bindings.Literals) {
2304-
auto *coveredBy = std::get<2>(literal.second);
2305-
if (coveredBy)
2306-
continue;
2307-
2308-
if (auto defaultTy = TypeChecker::getDefaultType(literal.first, cs.DC)) {
2309-
conversionType = defaultTy;
2304+
if (literal.second.viableAsBinding()) {
2305+
conversionType = literal.second.getDefaultType();
23102306
break;
23112307
}
23122308
}

lib/Sema/ConstraintSystem.cpp

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5368,30 +5368,19 @@ TypeVarBindingProducer::TypeVarBindingProducer(
53685368
}
53695369

53705370
// Infer defaults based on "uncovered" literal protocol requirements.
5371-
for (const auto &literal : bindings.Literals) {
5372-
Constraint *constraint = nullptr;
5373-
bool isDirectRequirement = false;
5374-
Constraint *coveredBy = nullptr;
5371+
for (const auto &info : bindings.Literals) {
5372+
const auto &literal = info.second;
53755373

5376-
std::tie(constraint, isDirectRequirement, coveredBy) = literal.second;
5377-
5378-
// Can't be defaulted because it's already covered by an
5379-
// existing direct or transitive binding which is always
5380-
// better.
5381-
if (coveredBy)
5382-
continue;
5383-
5384-
auto defaultType = TypeChecker::getDefaultType(literal.first, CS.DC);
5385-
if (!defaultType)
5374+
if (!literal.viableAsBinding())
53865375
continue;
53875376

53885377
// We need to figure out whether this is a direct conformance
53895378
// requirement or inferred transitive one to identify binding
53905379
// kind correctly.
5391-
addBinding(
5392-
{defaultType,
5393-
isDirectRequirement ? BindingKind::Subtypes : BindingKind::Supertypes,
5394-
constraint});
5380+
addBinding({literal.getDefaultType(),
5381+
literal.isDirectRequirement() ? BindingKind::Subtypes
5382+
: BindingKind::Supertypes,
5383+
literal.getSource()});
53955384
}
53965385

53975386
// Let's always consider `Any` to be a last resort binding because

0 commit comments

Comments
 (0)