Skip to content

Commit 248341a

Browse files
committed
[CSBindings] Adjust optionality of the compute bindings as a final step
If type variable is expected to conform to `ExpressibleByNilLiteral` adjust optionality of the inferred bindings only after all of the bindings have been collected otherwise transitive supertype bindings are going to stay non-optional which is incorrect.
1 parent 3e74a44 commit 248341a

File tree

2 files changed

+43
-81
lines changed

2 files changed

+43
-81
lines changed

lib/Sema/CSBindings.cpp

Lines changed: 42 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,41 @@ void ConstraintSystem::PotentialBindings::finalize(
104104
existingTypes.insert(binding.BindingType->getCanonicalType());
105105

106106
inferTransitiveBindings(cs, existingTypes, inferredBindings);
107+
108+
// Adjust optionality of existing bindings based on presence of
109+
// `ExpressibleByNilLiteral` requirement.
110+
if (llvm::any_of(Protocols, [](Constraint *constraint) {
111+
auto *protocol = constraint->getProtocol();
112+
return protocol->isSpecificProtocol(
113+
KnownProtocolKind::ExpressibleByNilLiteral);
114+
})) {
115+
for (auto &binding : Bindings) {
116+
bool wrapInOptional = false;
117+
if (binding.Kind == AllowedBindingKind::Supertypes) {
118+
auto type = binding.BindingType->getRValueType();
119+
// If the type doesn't conform to ExpressibleByNilLiteral,
120+
// produce an optional of that type as a potential binding. We
121+
// overwrite the binding in place because the non-optional type
122+
// will fail to type-check against the nil-literal conformance.
123+
bool conformsToExprByNilLiteral = false;
124+
if (auto *nominalBindingDecl = type->getAnyNominal()) {
125+
SmallVector<ProtocolConformance *, 2> conformances;
126+
conformsToExprByNilLiteral = nominalBindingDecl->lookupConformance(
127+
cs.DC->getParentModule(),
128+
cs.getASTContext().getProtocol(
129+
KnownProtocolKind::ExpressibleByNilLiteral),
130+
conformances);
131+
}
132+
wrapInOptional = !conformsToExprByNilLiteral;
133+
} else if (binding.isDefaultableBinding() &&
134+
binding.BindingType->isAny()) {
135+
wrapInOptional = true;
136+
}
137+
138+
if (wrapInOptional)
139+
binding.BindingType = OptionalType::get(binding.BindingType);
140+
}
141+
}
107142
}
108143

109144
Optional<ConstraintSystem::PotentialBindings>
@@ -114,11 +149,8 @@ ConstraintSystem::determineBestBindings() {
114149

115150
// First, let's collect all of the possible bindings.
116151
for (auto *typeVar : getTypeVariables()) {
117-
if (typeVar->getImpl().hasRepresentativeOrFixed())
118-
continue;
119-
120-
if (auto bindings = getPotentialBindings(typeVar))
121-
cache.insert({typeVar, std::move(bindings)});
152+
if (!typeVar->getImpl().hasRepresentativeOrFixed())
153+
cache.insert({typeVar, getPotentialBindings(typeVar)});
122154
}
123155

124156
// Now let's see if we could infer something for related type
@@ -132,6 +164,9 @@ ConstraintSystem::determineBestBindings() {
132164

133165
bindings.finalize(*this, cache);
134166

167+
if (!bindings)
168+
continue;
169+
135170
if (isDebugMode()) {
136171
bindings.dump(typeVar, llvm::errs(), solverState->depth * 2);
137172
}
@@ -283,31 +318,11 @@ bool ConstraintSystem::PotentialBindings::favoredOverDisjunction(
283318
return !InvolvesTypeVariables;
284319
}
285320

286-
static bool hasNilLiteralConstraint(TypeVariableType *typeVar,
287-
const ConstraintSystem &CS) {
288-
// Look for a literal-conformance constraint on the type variable.
289-
auto constraints =
290-
CS.getConstraintGraph().gatherConstraints(
291-
typeVar, ConstraintGraph::GatheringKind::EquivalenceClass,
292-
[](Constraint *constraint) -> bool {
293-
return constraint->getKind() == ConstraintKind::LiteralConformsTo &&
294-
constraint->getProtocol()->isSpecificProtocol(
295-
KnownProtocolKind::ExpressibleByNilLiteral);
296-
});
297-
298-
for (auto constraint : constraints)
299-
if (CS.simplifyType(constraint->getFirstType())->isEqual(typeVar))
300-
return true;
301-
302-
return false;
303-
}
304-
305321
Optional<ConstraintSystem::PotentialBinding>
306322
ConstraintSystem::getPotentialBindingForRelationalConstraint(
307323
PotentialBindings &result, Constraint *constraint,
308324
bool &hasDependentMemberRelationalConstraints,
309-
bool &hasNonDependentMemberRelationalConstraints,
310-
bool &addOptionalSupertypeBindings) const {
325+
bool &hasNonDependentMemberRelationalConstraints) const {
311326
assert(constraint->getClassification() ==
312327
ConstraintClassification::Relational &&
313328
"only relational constraints handled here");
@@ -427,15 +442,6 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint(
427442
result.SubtypeOf.insert(bindingTypeVar);
428443
}
429444

430-
// If we've already set addOptionalSupertypeBindings, or we aren't
431-
// allowing supertype bindings, we're done.
432-
if (addOptionalSupertypeBindings || kind != AllowedBindingKind::Supertypes)
433-
return None;
434-
435-
// If the bound is a 'nil' literal type, add optional supertype bindings.
436-
if (hasNilLiteralConstraint(bindingTypeVar, *this))
437-
addOptionalSupertypeBindings = true;
438-
439445
return None;
440446
}
441447

@@ -498,7 +504,6 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const {
498504
llvm::SmallPtrSet<CanType, 4> exactTypes;
499505
SmallVector<Constraint *, 2> defaultableConstraints;
500506
SmallVector<PotentialBinding, 4> literalBindings;
501-
bool addOptionalSupertypeBindings = false;
502507
bool hasNonDependentMemberRelationalConstraints = false;
503508
bool hasDependentMemberRelationalConstraints = false;
504509
for (auto constraint : constraints) {
@@ -528,8 +533,7 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const {
528533

529534
auto binding = getPotentialBindingForRelationalConstraint(
530535
result, constraint, hasDependentMemberRelationalConstraints,
531-
hasNonDependentMemberRelationalConstraints,
532-
addOptionalSupertypeBindings);
536+
hasNonDependentMemberRelationalConstraints);
533537
if (!binding)
534538
break;
535539

@@ -654,13 +658,6 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const {
654658
// this is useful to use for the binding later.
655659
result.Protocols.push_back(constraint);
656660

657-
// If there is a 'nil' literal constraint, we might need optional
658-
// supertype bindings.
659-
if (constraint->getProtocol()->isSpecificProtocol(
660-
KnownProtocolKind::ExpressibleByNilLiteral)) {
661-
addOptionalSupertypeBindings = true;
662-
}
663-
664661
// If there is a default literal type for this protocol, it's a
665662
// potential binding.
666663
auto defaultType = TypeChecker::getDefaultType(constraint->getProtocol(), DC);
@@ -886,40 +883,6 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const {
886883
binding.Kind == AllowedBindingKind::Subtypes;
887884
});
888885

889-
// If we're supposed to add optional supertype bindings, do so now.
890-
if (addOptionalSupertypeBindings) {
891-
for (unsigned i : indices(result.Bindings)) {
892-
auto &binding = result.Bindings[i];
893-
bool wrapInOptional = false;
894-
895-
if (binding.Kind == AllowedBindingKind::Supertypes) {
896-
// If the type doesn't conform to ExpressibleByNilLiteral,
897-
// produce an optional of that type as a potential binding. We
898-
// overwrite the binding in place because the non-optional type
899-
// will fail to type-check against the nil-literal conformance.
900-
auto nominalBindingDecl =
901-
binding.BindingType->getRValueType()->getAnyNominal();
902-
bool conformsToExprByNilLiteral = false;
903-
if (nominalBindingDecl) {
904-
SmallVector<ProtocolConformance *, 2> conformances;
905-
conformsToExprByNilLiteral = nominalBindingDecl->lookupConformance(
906-
DC->getParentModule(),
907-
getASTContext().getProtocol(
908-
KnownProtocolKind::ExpressibleByNilLiteral),
909-
conformances);
910-
}
911-
wrapInOptional = !conformsToExprByNilLiteral;
912-
} else if (binding.isDefaultableBinding() &&
913-
binding.BindingType->isAny()) {
914-
wrapInOptional = true;
915-
}
916-
917-
if (wrapInOptional) {
918-
binding.BindingType = OptionalType::get(binding.BindingType);
919-
}
920-
}
921-
}
922-
923886
// If there were both dependent-member and non-dependent-member relational
924887
// constraints, consider this "fully bound"; we don't want to touch it.
925888
if (hasDependentMemberRelationalConstraints) {

lib/Sema/ConstraintSystem.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4645,8 +4645,7 @@ class ConstraintSystem {
46454645
getPotentialBindingForRelationalConstraint(
46464646
PotentialBindings &result, Constraint *constraint,
46474647
bool &hasDependentMemberRelationalConstraints,
4648-
bool &hasNonDependentMemberRelationalConstraints,
4649-
bool &addOptionalSupertypeBindings) const;
4648+
bool &hasNonDependentMemberRelationalConstraints) const;
46504649
PotentialBindings getPotentialBindings(TypeVariableType *typeVar) const;
46514650

46524651
private:

0 commit comments

Comments
 (0)