Skip to content

Commit c3cc968

Browse files
committed
[CSBindings] Coalesce integer/float literal protocols to produce a single default
Let's try to coalesce integer and floating point literal protocols if they appear together because the only possible default type that could satisfy both requirements is `Double`.
1 parent 7622993 commit c3cc968

File tree

1 file changed

+39
-11
lines changed

1 file changed

+39
-11
lines changed

lib/Sema/CSBindings.cpp

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,14 @@ void ConstraintSystem::PotentialBindings::inferDefaultTypes(
124124
// binding that provides a type that conforms to that literal protocol. In
125125
// such cases, don't add the default binding suggestion because the existing
126126
// suggestion is better.
127-
llvm::SmallPtrSet<ProtocolDecl *, 4> coveredLiteralProtocols;
127+
//
128+
// Note that ordering is important when it comes to bindings, we'd like to
129+
// add any "direct" default types first to attempt them before transitive ones.
130+
llvm::SmallMapVector<ProtocolDecl *, bool, 4> literalProtocols;
131+
for (auto *constraint : Protocols) {
132+
if (constraint->getKind() == ConstraintKind::LiteralConformsTo)
133+
literalProtocols.insert({constraint->getProtocol(), false});
134+
}
128135

129136
for (auto &binding : Bindings) {
130137
Type type;
@@ -144,21 +151,21 @@ void ConstraintSystem::PotentialBindings::inferDefaultTypes(
144151
continue;
145152

146153
bool requiresUnwrap = false;
147-
for (auto *constraint : Protocols) {
148-
if (constraint->getKind() != ConstraintKind::LiteralConformsTo)
149-
continue;
154+
for (auto &entry : literalProtocols) {
155+
auto *protocol = entry.first;
156+
bool &isCovered = entry.second;
150157

151-
auto *protocol = constraint->getProtocol();
152-
153-
assert(protocol);
154-
155-
if (coveredLiteralProtocols.count(protocol))
158+
if (isCovered)
156159
continue;
157160

161+
// FIXME: This is a hack and it's incorrect because it depends
162+
// on ordering of the literal procotols e.g. if `ExpressibleByNilLiteral`
163+
// appears before e.g. `ExpressibleByIntegerLiteral` we'd drop
164+
// optionality although that would be incorrect.
158165
do {
159166
// If the type conforms to this protocol, we're covered.
160167
if (TypeChecker::conformsToProtocol(type, protocol, cs.DC)) {
161-
coveredLiteralProtocols.insert(protocol);
168+
isCovered = true;
162169
break;
163170
}
164171

@@ -182,11 +189,32 @@ void ConstraintSystem::PotentialBindings::inferDefaultTypes(
182189
binding.BindingType = type;
183190
}
184191

192+
// If this is not a literal protocol or it has been "covered" by an existing
193+
// binding it can't provide a default type.
194+
auto isUnviableForDefaulting = [&literalProtocols](ProtocolDecl *protocol) {
195+
auto literal = literalProtocols.find(protocol);
196+
return literal == literalProtocols.end() || literal->second;
197+
};
198+
185199
for (auto *constraint : Protocols) {
186200
auto *protocol = constraint->getProtocol();
187-
if (coveredLiteralProtocols.count(protocol))
201+
202+
if (isUnviableForDefaulting(protocol))
188203
continue;
189204

205+
// Let's try to coalesce integer and floating point literal protocols
206+
// if they appear together because the only possible default type that
207+
// could satisfy both requirements is `Double`.
208+
if (protocol->isSpecificProtocol(
209+
KnownProtocolKind::ExpressibleByIntegerLiteral)) {
210+
auto *floatLiteral = cs.getASTContext().getProtocol(
211+
KnownProtocolKind::ExpressibleByFloatLiteral);
212+
// If `ExpressibleByFloatLiteral` is a requirement and it isn't
213+
// covered, let's skip `ExpressibleByIntegerLiteral` requirement.
214+
if (!isUnviableForDefaulting(floatLiteral))
215+
continue;
216+
}
217+
190218
auto defaultType = TypeChecker::getDefaultType(protocol, cs.DC);
191219
if (!defaultType)
192220
continue;

0 commit comments

Comments
 (0)