Skip to content

Commit 650c54b

Browse files
committed
[CSBindings] Split literals from other protocol requirements
Literal protocol requirements are handled differently from other protocols, they require additional contextual information (such as coverage, direct/transitive distinction), and participate in binding inference (could be turned into default bindings).
1 parent 949b0c0 commit 650c54b

File tree

3 files changed

+92
-92
lines changed

3 files changed

+92
-92
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4700,6 +4700,8 @@ class ConstraintSystem {
47004700
return BindingSource.get<ConstraintLocator *>();
47014701
}
47024702

4703+
Constraint *getSource() const { return BindingSource.get<Constraint *>(); }
4704+
47034705
PotentialBinding withType(Type type) const {
47044706
return {type, Kind, BindingSource};
47054707
}
@@ -4725,6 +4727,13 @@ class ConstraintSystem {
47254727
using BindingScore =
47264728
std::tuple<bool, bool, bool, bool, bool, unsigned char, int>;
47274729

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+
47284737
/// The constraint system this type variable and its bindings belong to.
47294738
ConstraintSystem &CS;
47304739

@@ -4740,6 +4749,14 @@ class ConstraintSystem {
47404749
/// subtype/conversion/equivalence relations with other type variables.
47414750
Optional<llvm::SmallPtrSet<Constraint *, 4>> TransitiveProtocols;
47424751

4752+
/// The set of unique literal protocol requirements placed on this
4753+
/// type variable or inferred transitively through subtype chains.
4754+
///
4755+
/// Note that ordering is important when it comes to bindings, we'd
4756+
/// like to add any "direct" default types first to attempt them
4757+
/// before transitive ones.
4758+
llvm::SmallMapVector<ProtocolDecl *, LiteralInfo, 2> Literals;
4759+
47434760
/// The set of constraints which would be used to infer default types.
47444761
llvm::SmallDenseMap<CanType, Constraint *, 2> Defaults;
47454762

@@ -4949,6 +4966,8 @@ class ConstraintSystem {
49494966

49504967
void addDefault(Constraint *constraint);
49514968

4969+
void addLiteral(Constraint *constraint);
4970+
49524971
/// Add a potential binding to the list of bindings,
49534972
/// coalescing supertype bounds when we are able to compute the meet.
49544973
void addPotentialBinding(PotentialBinding binding,

lib/Sema/CSBindings.cpp

Lines changed: 71 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -312,11 +312,8 @@ void ConstraintSystem::PotentialBindings::inferTransitiveBindings(
312312
// If one of the literal arguments doesn't propagate its
313313
// `ExpressibleByStringLiteral` conformance, we'd end up picking
314314
// `T` with only one type `Any?` which is incorrect.
315-
llvm::copy_if(bindings.Protocols, std::back_inserter(Protocols),
316-
[](const Constraint *protocol) {
317-
return protocol->getKind() ==
318-
ConstraintKind::LiteralConformsTo;
319-
});
315+
for (const auto &literal : bindings.Literals)
316+
addLiteral(std::get<0>(literal.second));
320317

321318
// Infer transitive defaults.
322319
for (const auto &def : bindings.Defaults)
@@ -380,67 +377,11 @@ isUnviableDefaultType(Type defaultType,
380377

381378
void ConstraintSystem::PotentialBindings::inferDefaultTypes(
382379
ConstraintSystem &cs, llvm::SmallPtrSetImpl<CanType> &existingTypes) {
383-
auto isDirectRequirement = [&](Constraint *constraint) -> bool {
384-
if (auto *typeVar = constraint->getFirstType()->getAs<TypeVariableType>()) {
385-
auto *repr = cs.getRepresentative(typeVar);
386-
return repr == TypeVar;
387-
}
388-
389-
return false;
390-
};
391-
392-
// If we have any literal constraints, check whether there is already a
393-
// binding that provides a type that conforms to that literal protocol. In
394-
// such cases, don't add the default binding suggestion because the existing
395-
// suggestion is better.
396-
//
397-
// Note that ordering is important when it comes to bindings, we'd like to
398-
// add any "direct" default types first to attempt them before transitive
399-
// ones.
400-
//
401-
// Key is a literal protocol requirement, Value indicates whether (first)
402-
// given protocol is a direct requirement, and (second) whether it has been
403-
// covered by an existing binding.
404-
bool canBeNil = false;
405-
llvm::SmallMapVector<ProtocolDecl *, std::pair<bool, bool>, 4>
406-
literalProtocols;
407-
for (auto *constraint : Protocols) {
408-
if (constraint->getKind() != ConstraintKind::LiteralConformsTo)
409-
continue;
410-
411-
auto *protocol = constraint->getProtocol();
412-
413-
// No reason to add `ExpressibleByNilLiteral` into the set
414-
// because it doesn't have a default type.
415-
if (protocol->isSpecificProtocol(
416-
KnownProtocolKind::ExpressibleByNilLiteral)) {
417-
canBeNil = true;
418-
continue;
419-
}
420-
421-
// Let's try to coalesce integer and floating point literal protocols
422-
// if they appear together because the only possible default type that
423-
// could satisfy both requirements is `Double`.
424-
{
425-
if (protocol->isSpecificProtocol(
426-
KnownProtocolKind::ExpressibleByIntegerLiteral)) {
427-
auto *floatLiteral = CS.getASTContext().getProtocol(
428-
KnownProtocolKind::ExpressibleByFloatLiteral);
429-
if (literalProtocols.count(floatLiteral))
430-
continue;
431-
}
432-
433-
if (protocol->isSpecificProtocol(
434-
KnownProtocolKind::ExpressibleByFloatLiteral)) {
435-
auto *intLiteral = CS.getASTContext().getProtocol(
436-
KnownProtocolKind::ExpressibleByIntegerLiteral);
437-
literalProtocols.erase(intLiteral);
438-
}
439-
}
440-
441-
literalProtocols.insert(
442-
{protocol, {isDirectRequirement(constraint), false}});
443-
}
380+
bool canBeNil = llvm::any_of(
381+
Literals, [](const std::pair<ProtocolDecl *, LiteralInfo> &literal) {
382+
return literal.first->isSpecificProtocol(
383+
KnownProtocolKind::ExpressibleByNilLiteral);
384+
});
444385

445386
for (auto &binding : Bindings) {
446387
Type type;
@@ -460,18 +401,24 @@ void ConstraintSystem::PotentialBindings::inferDefaultTypes(
460401
continue;
461402

462403
bool requiresUnwrap = false;
463-
for (auto &entry : literalProtocols) {
464-
auto *protocol = entry.first;
465-
bool isDirectRequirement = entry.second.first;
466-
bool &isCovered = entry.second.second;
404+
for (auto &literal : Literals) {
405+
auto *protocol = literal.first;
406+
bool isDirectRequirement = std::get<1>(literal.second);
407+
Constraint *&coveredBy = std::get<2>(literal.second);
467408

468-
if (isCovered)
409+
if (coveredBy)
410+
continue;
411+
412+
// Ignore `ExpressibleByNilLiteral` since it can't produce
413+
// a default type.
414+
if (protocol->isSpecificProtocol(
415+
KnownProtocolKind::ExpressibleByNilLiteral))
469416
continue;
470417

471418
do {
472419
// If the type conforms to this protocol, we're covered.
473420
if (TypeChecker::conformsToProtocol(type, protocol, cs.DC)) {
474-
isCovered = true;
421+
coveredBy = binding.getSource();
475422
break;
476423
}
477424

@@ -506,25 +453,20 @@ void ConstraintSystem::PotentialBindings::inferDefaultTypes(
506453
binding.BindingType = type;
507454
}
508455

509-
for (auto *constraint : Protocols) {
510-
auto *protocol = constraint->getProtocol();
511-
512-
auto literal = literalProtocols.find(protocol);
513-
if (literal == literalProtocols.end())
514-
continue;
515-
456+
for (const auto &literal : Literals) {
457+
Constraint *constraint = nullptr;
516458
bool isDirectRequirement = false;
517-
bool isCovered = false;
459+
Constraint *coveredBy = nullptr;
518460

519-
std::tie(isDirectRequirement, isCovered) = literal->second;
461+
std::tie(constraint, isDirectRequirement, coveredBy) = literal.second;
520462

521463
// Can't be defaulted because it's already covered by an
522464
// existing direct or transitive binding which is always
523465
// better.
524-
if (isCovered)
466+
if (coveredBy)
525467
continue;
526468

527-
auto defaultType = TypeChecker::getDefaultType(protocol, cs.DC);
469+
auto defaultType = TypeChecker::getDefaultType(literal.first, cs.DC);
528470
if (!defaultType)
529471
continue;
530472

@@ -584,10 +526,12 @@ ConstraintSystem::determineBestBindings() {
584526
return true;
585527

586528
return bindings ||
587-
llvm::any_of(bindings.Protocols, [&](Constraint *constraint) {
588-
return bool(
589-
TypeChecker::getDefaultType(constraint->getProtocol(), DC));
590-
});
529+
llvm::any_of(
530+
bindings.Literals,
531+
[&](const std::pair<ProtocolDecl *,
532+
PotentialBindings::LiteralInfo> &literal) {
533+
return bool(TypeChecker::getDefaultType(literal.first, DC));
534+
});
591535
};
592536

593537
// Now let's see if we could infer something for related type
@@ -716,6 +660,43 @@ void ConstraintSystem::PotentialBindings::addPotentialBinding(
716660
Bindings.push_back(std::move(binding));
717661
}
718662

663+
void ConstraintSystem::PotentialBindings::addLiteral(Constraint *constraint) {
664+
auto isDirectRequirement = [&](Constraint *constraint) -> bool {
665+
if (auto *typeVar = constraint->getFirstType()->getAs<TypeVariableType>()) {
666+
auto *repr = CS.getRepresentative(typeVar);
667+
return repr == TypeVar;
668+
}
669+
670+
return false;
671+
};
672+
673+
auto *protocol = constraint->getProtocol();
674+
675+
// Let's try to coalesce integer and floating point literal protocols
676+
// if they appear together because the only possible default type that
677+
// could satisfy both requirements is `Double`.
678+
{
679+
if (protocol->isSpecificProtocol(
680+
KnownProtocolKind::ExpressibleByIntegerLiteral)) {
681+
auto *floatLiteral = CS.getASTContext().getProtocol(
682+
KnownProtocolKind::ExpressibleByFloatLiteral);
683+
if (Literals.count(floatLiteral))
684+
return;
685+
}
686+
687+
if (protocol->isSpecificProtocol(
688+
KnownProtocolKind::ExpressibleByFloatLiteral)) {
689+
auto *intLiteral = CS.getASTContext().getProtocol(
690+
KnownProtocolKind::ExpressibleByIntegerLiteral);
691+
Literals.erase(intLiteral);
692+
}
693+
}
694+
695+
Literals.insert(
696+
{protocol, std::make_tuple(constraint, isDirectRequirement(constraint),
697+
/*coveredBy=*/nullptr)});
698+
}
699+
719700
bool ConstraintSystem::PotentialBindings::isViable(
720701
PotentialBinding &binding) const {
721702
// Prevent against checking against the same opened nominal type
@@ -1129,13 +1110,14 @@ bool ConstraintSystem::PotentialBindings::infer(
11291110
if (!protocolTy->is<ProtocolType>())
11301111
return false;
11311112

1132-
LLVM_FALLTHROUGH;
1113+
Protocols.push_back(constraint);
1114+
break;
11331115
}
11341116

11351117
case ConstraintKind::LiteralConformsTo: {
11361118
// Record constraint where protocol requirement originated
11371119
// this is useful to use for the binding later.
1138-
Protocols.push_back(constraint);
1120+
addLiteral(constraint);
11391121
break;
11401122
}
11411123

lib/Sema/ConstraintSystem.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5331,9 +5331,8 @@ TypeVarBindingProducer::TypeVarBindingProducer(
53315331
ConstraintSystem::PotentialBindings &bindings)
53325332
: BindingProducer(bindings.CS, bindings.TypeVar->getImpl().getLocator()),
53335333
TypeVar(bindings.TypeVar),
5334-
CanBeNil(llvm::any_of(bindings.Protocols, [](Constraint *constraint) {
5335-
auto *protocol = constraint->getProtocol();
5336-
return protocol->isSpecificProtocol(
5334+
CanBeNil(llvm::any_of(bindings.Literals, [](const auto &literal) {
5335+
return literal.first->isSpecificProtocol(
53375336
KnownProtocolKind::ExpressibleByNilLiteral);
53385337
})) {
53395338
if (bindings.isDirectHole()) {

0 commit comments

Comments
 (0)