Skip to content

Commit ff23268

Browse files
committed
Fix issue 19465 - improve 'cannot uniquely infer foreach argument types' diagnostic
Add supplemental error messages explaining why no opApply overload matched a foreach statement's parameters (parameter count mismatch, qualifier/mutability mismatch, or explicit type mismatch), instead of the previous generic error with no further detail. The added diagnostics are only printed when no more specific error (e.g. ambiguous overload match) was already reported for the same failure, to avoid duplicating existing diagnostics.
1 parent 002479c commit ff23268

4 files changed

Lines changed: 115 additions & 5 deletions

File tree

compiler/src/dmd/opover.d

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,6 +1564,72 @@ private FuncDeclaration findBestOpApplyMatch(Expression ethis, FuncDeclaration f
15641564
return fd_best;
15651565
}
15661566

1567+
/**
1568+
* Print supplemental diagnostics explaining why no `opApply` overload
1569+
* matched the `foreach` statement's parameters, to clarify the generic
1570+
* "cannot uniquely infer foreach argument types" error (issue 19465).
1571+
* Only call this when no more-specific error (e.g. ambiguity) was
1572+
* already printed for the same failure.
1573+
* Params:
1574+
* fstart = first opApply overload (start of overload chain)
1575+
* parameters = foreach parameters, as written (types may be missing)
1576+
* aggrMod = mutability qualifier of the foreach aggregate
1577+
*/
1578+
void explainForeachArgMismatch(FuncDeclaration fstart, Parameters* parameters, MOD aggrMod)
1579+
{
1580+
auto eSink = global.errorSink;
1581+
const nGiven = parameters.length;
1582+
1583+
overloadApply(fstart, (Dsymbol s)
1584+
{
1585+
auto f = s.isFuncDeclaration();
1586+
if (!f)
1587+
return 0;
1588+
auto tf = f.type.isTypeFunction();
1589+
if (tf.parameterList.length < 1)
1590+
return 0;
1591+
auto de = tf.parameterList[0].type.isTypeDelegate();
1592+
if (!de)
1593+
return 0;
1594+
auto tdg = de.next.isTypeFunction();
1595+
const nExpected = tdg.parameterList.length;
1596+
1597+
if (f.isThis() && !MODimplicitConv(aggrMod, tf.mod))
1598+
{
1599+
eSink.errorSupplemental(f.loc, "`%s` is not callable using a `%s` aggregate",
1600+
tf.toChars(), MODtoChars(aggrMod));
1601+
return 0;
1602+
}
1603+
1604+
if (nExpected != nGiven)
1605+
{
1606+
const(char)* plural = nExpected > 1 ? "s" : "";
1607+
eSink.errorSupplemental(f.loc, "`%s` expects %llu argument%s, not %llu",
1608+
tf.toChars(), cast(ulong) nExpected, plural, cast(ulong) nGiven);
1609+
return 0;
1610+
}
1611+
1612+
bool headerPrinted = false;
1613+
foreach (u, p; *parameters)
1614+
{
1615+
if (!p.type)
1616+
continue;
1617+
Parameter param = tdg.parameterList[u];
1618+
if (!p.type.equals(param.type))
1619+
{
1620+
if (!headerPrinted)
1621+
{
1622+
eSink.errorSupplemental(f.loc, "`%s`:", tf.toChars());
1623+
headerPrinted = true;
1624+
}
1625+
eSink.errorSupplemental(f.loc, " parameter %llu: `foreach` declares `%s`, expected `%s`",
1626+
cast(ulong)(u + 1), p.type.toErrMsg(), param.type.toErrMsg());
1627+
}
1628+
}
1629+
return 0;
1630+
});
1631+
}
1632+
15671633
/******************************
15681634
* Determine if foreach parameters match opApply parameters.
15691635
* Infer missing foreach parameter types from type of opApply delegate.

compiler/src/dmd/statementsem.d

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
864864
Dsymbol sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors
865865

866866
/* Check for inference errors and apply modifier checks inline */
867+
const errorsBeforeInfer = global.errors;
867868
if (!inferApplyArgTypes(fs, sc, sapply))
868869
{
869870
bool foundMismatch = false;
@@ -911,7 +912,13 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
911912
cast(ulong) foreachParamCount, plural, cast(ulong) dim);
912913
}
913914
else
915+
{
916+
const bool alreadyExplained = global.errors > errorsBeforeInfer;
914917
eSink.error(fs.loc, "cannot uniquely infer `foreach` argument types");
918+
if (!alreadyExplained && sapplyOld)
919+
if (auto fd = sapplyOld.isFuncDeclaration())
920+
explainForeachArgMismatch(fd, fs.parameters, fs.aggr.type.mod);
921+
}
915922

916923
return setError();
917924
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// https://issues.dlang.org/show_bug.cgi?id=19465
2+
3+
/*
4+
REQUIRED_ARGS: -verrors=context
5+
TEST_OUTPUT:
6+
---
7+
fail_compilation/test19465.d(30): Error: cannot uniquely infer `foreach` argument types
8+
foreach (string x; s) {}
9+
^
10+
fail_compilation/test19465.d(21): `int(int delegate(int) dg)`:
11+
int opApply(int delegate(int) dg)
12+
^
13+
fail_compilation/test19465.d(21): parameter 1: `foreach` declares `string`, expected `int`
14+
int opApply(int delegate(int) dg)
15+
^
16+
---
17+
*/
18+
19+
struct S
20+
{
21+
int opApply(int delegate(int) dg)
22+
{
23+
return 0;
24+
}
25+
}
26+
27+
void test()
28+
{
29+
S s;
30+
foreach (string x; s) {}
31+
}

compiler/test/fail_compilation/test24353.d

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,27 @@
44
REQUIRED_ARGS: -verrors=context
55
TEST_OUTPUT:
66
---
7-
fail_compilation/test24353.d(37): Error: mutable method `test24353.S.opApply` is not callable using a `const(S)` foreach aggregate
7+
fail_compilation/test24353.d(43): Error: mutable method `test24353.S.opApply` is not callable using a `const(S)` foreach aggregate
88
foreach (e; s) {} // mod error
99
^
10-
fail_compilation/test24353.d(28): Consider adding a method type qualifier here
10+
fail_compilation/test24353.d(34): Consider adding a method type qualifier here
1111
int opApply(int delegate(int) dg)
1212
^
13-
fail_compilation/test24353.d(40): Error: shared const method `test24353.S2.opApply` is not callable using a `const(S2)` foreach aggregate
13+
fail_compilation/test24353.d(46): Error: shared const method `test24353.S2.opApply` is not callable using a `const(S2)` foreach aggregate
1414
foreach (i, e; s2) {} // mod error
1515
^
16-
fail_compilation/test24353.d(47): Consider adding a method type qualifier here
16+
fail_compilation/test24353.d(53): Consider adding a method type qualifier here
1717
int opApply(int delegate(int, int) dg) const shared;
1818
^
19-
fail_compilation/test24353.d(42): Error: cannot uniquely infer `foreach` argument types
19+
fail_compilation/test24353.d(48): Error: cannot uniquely infer `foreach` argument types
2020
foreach (i, e; const S3()) {} // cannot infer
2121
^
22+
fail_compilation/test24353.d(58): `int(int delegate(int) dg)` is not callable using a `const` aggregate
23+
int opApply(int delegate(int) dg);
24+
^
25+
fail_compilation/test24353.d(59): `int(int delegate(int, int) dg)` is not callable using a `const` aggregate
26+
int opApply(int delegate(int, int) dg);
27+
^
2228
---
2329
*/
2430

0 commit comments

Comments
 (0)