Skip to content

Commit c919ef7

Browse files
authored
Merge pull request swiftlang#35141 from xedin/adjust-protocol-coverage
[CSBindings] A couple of minor adjustments to inferring default bindings from literal protocols
2 parents 8ef504a + c4c401b commit c919ef7

File tree

1 file changed

+56
-31
lines changed

1 file changed

+56
-31
lines changed

lib/Sema/CSBindings.cpp

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -401,12 +401,45 @@ void ConstraintSystem::PotentialBindings::inferDefaultTypes(
401401
// Key is a literal protocol requirement, Value indicates whether (first)
402402
// given protocol is a direct requirement, and (second) whether it has been
403403
// covered by an existing binding.
404+
bool canBeNil = false;
404405
llvm::SmallMapVector<ProtocolDecl *, std::pair<bool, bool>, 4>
405406
literalProtocols;
406407
for (auto *constraint : Protocols) {
407-
if (constraint->getKind() == ConstraintKind::LiteralConformsTo)
408-
literalProtocols.insert({constraint->getProtocol(),
409-
{isDirectRequirement(constraint), false}});
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}});
410443
}
411444

412445
for (auto &binding : Bindings) {
@@ -435,19 +468,20 @@ void ConstraintSystem::PotentialBindings::inferDefaultTypes(
435468
if (isCovered)
436469
continue;
437470

438-
// FIXME: This is a hack and it's incorrect because it depends
439-
// on ordering of the literal procotols e.g. if `ExpressibleByNilLiteral`
440-
// appears before e.g. `ExpressibleByIntegerLiteral` we'd drop
441-
// optionality although that would be incorrect.
442471
do {
443472
// If the type conforms to this protocol, we're covered.
444473
if (TypeChecker::conformsToProtocol(type, protocol, cs.DC)) {
445474
isCovered = true;
446475
break;
447476
}
448477

478+
// Can't unwrap optionals if there is `ExpressibleByNilLiteral`
479+
// conformance requirement placed on the type variable.
480+
if (canBeNil)
481+
break;
482+
449483
// If this literal protocol is not a direct requirement it
450-
// would be possible to change optionality while inferring
484+
// would not be possible to change optionality while inferring
451485
// bindings for a supertype, so this hack doesn't apply.
452486
if (!isDirectRequirement)
453487
break;
@@ -472,31 +506,23 @@ void ConstraintSystem::PotentialBindings::inferDefaultTypes(
472506
binding.BindingType = type;
473507
}
474508

475-
// If this is not a literal protocol or it has been "covered" by an existing
476-
// binding it can't provide a default type.
477-
auto isUnviableForDefaulting = [&literalProtocols](ProtocolDecl *protocol) {
478-
auto literal = literalProtocols.find(protocol);
479-
return literal == literalProtocols.end() || literal->second.second;
480-
};
481-
482509
for (auto *constraint : Protocols) {
483510
auto *protocol = constraint->getProtocol();
484511

485-
if (isUnviableForDefaulting(protocol))
512+
auto literal = literalProtocols.find(protocol);
513+
if (literal == literalProtocols.end())
486514
continue;
487515

488-
// Let's try to coalesce integer and floating point literal protocols
489-
// if they appear together because the only possible default type that
490-
// could satisfy both requirements is `Double`.
491-
if (protocol->isSpecificProtocol(
492-
KnownProtocolKind::ExpressibleByIntegerLiteral)) {
493-
auto *floatLiteral = cs.getASTContext().getProtocol(
494-
KnownProtocolKind::ExpressibleByFloatLiteral);
495-
// If `ExpressibleByFloatLiteral` is a requirement and it isn't
496-
// covered, let's skip `ExpressibleByIntegerLiteral` requirement.
497-
if (!isUnviableForDefaulting(floatLiteral))
498-
continue;
499-
}
516+
bool isDirectRequirement = false;
517+
bool isCovered = false;
518+
519+
std::tie(isDirectRequirement, isCovered) = literal->second;
520+
521+
// Can't be defaulted because it's already covered by an
522+
// existing direct or transitive binding which is always
523+
// better.
524+
if (isCovered)
525+
continue;
500526

501527
auto defaultType = TypeChecker::getDefaultType(protocol, cs.DC);
502528
if (!defaultType)
@@ -509,9 +535,8 @@ void ConstraintSystem::PotentialBindings::inferDefaultTypes(
509535
// requirement or inferred transitive one to identify binding
510536
// kind correctly.
511537
addPotentialBinding({defaultType,
512-
isDirectRequirement(constraint)
513-
? AllowedBindingKind::Subtypes
514-
: AllowedBindingKind::Supertypes,
538+
isDirectRequirement ? AllowedBindingKind::Subtypes
539+
: AllowedBindingKind::Supertypes,
515540
constraint});
516541
}
517542
}

0 commit comments

Comments
 (0)