@@ -124,7 +124,14 @@ void ConstraintSystem::PotentialBindings::inferDefaultTypes(
124
124
// binding that provides a type that conforms to that literal protocol. In
125
125
// such cases, don't add the default binding suggestion because the existing
126
126
// 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
+ }
128
135
129
136
for (auto &binding : Bindings) {
130
137
Type type;
@@ -144,21 +151,21 @@ void ConstraintSystem::PotentialBindings::inferDefaultTypes(
144
151
continue ;
145
152
146
153
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 ;
150
157
151
- auto *protocol = constraint->getProtocol ();
152
-
153
- assert (protocol);
154
-
155
- if (coveredLiteralProtocols.count (protocol))
158
+ if (isCovered)
156
159
continue ;
157
160
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.
158
165
do {
159
166
// If the type conforms to this protocol, we're covered.
160
167
if (TypeChecker::conformsToProtocol (type, protocol, cs.DC )) {
161
- coveredLiteralProtocols. insert (protocol) ;
168
+ isCovered = true ;
162
169
break ;
163
170
}
164
171
@@ -182,11 +189,32 @@ void ConstraintSystem::PotentialBindings::inferDefaultTypes(
182
189
binding.BindingType = type;
183
190
}
184
191
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
+
185
199
for (auto *constraint : Protocols) {
186
200
auto *protocol = constraint->getProtocol ();
187
- if (coveredLiteralProtocols.count (protocol))
201
+
202
+ if (isUnviableForDefaulting (protocol))
188
203
continue ;
189
204
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
+
190
218
auto defaultType = TypeChecker::getDefaultType (protocol, cs.DC );
191
219
if (!defaultType)
192
220
continue ;
0 commit comments