@@ -167,15 +167,18 @@ namespace sst
167
167
unsolvedtargets.insert (i);
168
168
});
169
169
170
+ // record which optionals we passed, for a better error message.
171
+ std::set<std::string> providedOptionals;
170
172
171
- size_t last_arg = std::min (target.size () + (fvararg ? -1 : 0 ), given.size ());
172
173
174
+ size_t last_arg = std::min (target.size () + (fvararg ? -1 : 0 ), given.size ());
173
175
174
176
// we used to do this check in the parser, but to support more edge cases (like passing varargs)
175
177
// we moved it here so we can actually check stuff.
176
178
bool didNames = false ;
177
179
178
180
size_t positionalCounter = 0 ;
181
+ size_t varArgStart = last_arg;
179
182
for (size_t i = 0 ; i < last_arg; i++)
180
183
{
181
184
const ArgType* targ = 0 ;
@@ -210,24 +213,85 @@ namespace sst
210
213
didNames = true ;
211
214
positionalCounter++;
212
215
}
216
+ else
217
+ {
218
+ providedOptionals.insert (given[i].name );
219
+ }
213
220
}
214
221
else
215
222
{
223
+ /*
224
+ we didn't pass a name. if the function is variadic, we might have wanted to pass the following argument(s)
225
+ variadically. so, instead of assuming we made a mistake (like not passing the optional by name), assume we
226
+ wanted to pass it to the vararg.
227
+
228
+ so, `positionalCounter` counts the paramters on the declaration-side. thus, once we encounter a default value,
229
+ it must mean that the rest of the parameters will be optional as well.
230
+
231
+ * ie. we've passed all the positional arguments already, leaving the optional ones, which means every argument from
232
+ * here onwards (including this one) must be named. since this is *not* named, we just skip straight to the varargs if
233
+ * it was present.
234
+ */
235
+
216
236
targ = &target[positionalCounter];
217
- unsolvedtargets.erase (positionalCounter);
218
237
238
+ if (fvararg && targ->optional )
239
+ {
240
+ varArgStart = i;
241
+ break ;
242
+ }
243
+
244
+ unsolvedtargets.erase (positionalCounter);
219
245
positionalCounter++;
220
246
}
221
247
248
+ /*
249
+ TODO: not sure if there's a way to get around this, but if we have a function like this:
250
+
251
+ fn foo(a: int, b: int, c: int, x: int = 9, y: int = 8, z: int = 7) { ... }
252
+
253
+ then calling it wrongly like this: foo(x: 4, 1, 2, 5, z: 6, 3)
254
+
255
+ results in an error at the last argument ('3') saying taht optional argument 'x' must be passed by name.
256
+ the problem is that we can't really tell what argument you wanted to pass; after seeing '1', '2', and '5',
257
+ the positionalCounter now points to the 4th argument, 'x'.
258
+
259
+ even though you already passed x prior, we don't really know that? and we assume you wanted to pass x (again)
260
+ */
261
+
222
262
if (given[i].name .empty ())
223
263
{
224
264
if (didNames)
225
265
return SimpleError::make (given[i].loc , " positional arguments cannot appear after named arguments" );
226
266
227
267
else if (targ->optional )
228
- return SimpleError::make (given[i].loc , " optional argument '%s' must be passed by name" , targ->name );
229
- }
268
+ {
269
+ std::string probablyIntendedArgumentName;
270
+ for (const auto & a : target)
271
+ {
272
+ if (!a.optional )
273
+ continue ;
274
+
275
+ if (auto it = providedOptionals.find (a.name ); it == providedOptionals.end ())
276
+ {
277
+ probablyIntendedArgumentName = a.name ;
278
+ break ;
279
+ }
280
+ };
230
281
282
+ if (probablyIntendedArgumentName.empty ())
283
+ {
284
+ // * this shouldn't happen, because we only get here if we're not variadic, but if we weren't
285
+ // * variadic, then we would've errored out if the argument count was wrong to begin with.
286
+ return SimpleError::make (given[i].loc , " extraneous argument without corresponding parameter" );
287
+ }
288
+ else
289
+ {
290
+ return SimpleError::make (given[i].loc , " optional argument '%s' must be passed by name" ,
291
+ probablyIntendedArgumentName);
292
+ }
293
+ }
294
+ }
231
295
232
296
iceAssert (targ);
233
297
auto err = solveSingleType (soln, targ->toFLT (), given[i].toFLT ());
@@ -284,7 +348,7 @@ namespace sst
284
348
auto varty = target.back ()->toArraySliceType ()->getArrayElementType ();
285
349
auto ltvarty = fir::LocatedType (varty, target.back ().loc );
286
350
287
- for (size_t i = last_arg ; i < given.size (); i++)
351
+ for (size_t i = varArgStart ; i < given.size (); i++)
288
352
{
289
353
auto err = solveSingleType (soln, ltvarty, given[i].toFLT ());
290
354
if (err) return err->append (SimpleError::make (MsgType::Note, target.back ().loc , " in argument of variadic parameter" ));
0 commit comments