Bug 19096 – [REG 2.061] Proper error messages are not shown for templates that go beyond two deep, wrongly says no template overload matches

Status
RESOLVED
Resolution
WORKSFORME
Severity
regression
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2018-07-18T15:02:10Z
Last change time
2021-02-23T13:25:07Z
Assigned to
No Owner
Creator
Joakim

Comments

Comment #0 by dbugz — 2018-07-18T15:02:10Z
I ran into this when refactoring std.conv.parse in Phobos and mistakenly had a variable shadowing another. Suddenly, ldc started telling me that none of the 11 overloads matched anymore, with no clue what I'd done wrong. I resorted to cutting and pasting the parse() function into a test file and removing all template arguments and constraints, after which the compiler pointed out the shadowed variable. I reduced that down to this code that shows the ins and outs: ``` import std.range.primitives, std.traits; Target parse(Target, Source)(ref Source source) if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && isFloatingPoint!Target && !is(Target == enum)) { bool anydigits = false; version(broken) { if ( anydigits ) { bool anydigits = true; } } return 3.14; } template isExactSomeString(T) { enum isExactSomeString = isSomeString!T && !is(T == enum); } private T toImpl(T, S)(S value) if (isInputRange!S && isSomeChar!(ElementEncodingType!S) && !isExactSomeString!T && is(typeof(parse!T(value)))) { bool anydigits = false; version(proper) { if ( anydigits ) { bool anydigits = true; } } return parse!T(value); } void main() { string foo = "3.14"; version(correct) parse!real(foo); else toImpl!real(foo); } ``` Ignoring all the version statements, this compiles fine with no shadowed variables with dmd 2.081.1 for linux/x64. If there's a shadowed variable in the toImpl() template called from main, enabled with `dmd -version=proper shadow.d`, I get this useful error message: frontend.d(32): Error: variable anydigits is shadowing variable frontend.toImpl!(real, string).toImpl.anydigits frontend.d(42): Error: template instance `frontend.toImpl!(real, string)` error instantiating However, if the shadowing problem goes two templates deep, enabled with `dmd -version=broken shadow.d`, I get this weird error, it claims no overloads match anymore: frontend.d(42): Error: template frontend.toImpl cannot deduce function from argument types !(real)(string), candidates are: frontend.d(23): frontend.toImpl(T, S)(S value) if (isInputRange!S && isSomeChar!(ElementEncodingType!S) && !isExactSomeString!T && is(typeof(parse!T(value)))) If I call that broken parse() template directly, enabled with `dmd -version=correct -version=broken shadow.d`, now I get a useful error again: frontend.d(12): Error: variable anydigits is shadowing variable frontend.parse!(real, string).parse.anydigits frontend.d(41): Error: template instance `frontend.parse!(real, string)` error instantiating This demonstrates how the frontend is throwing away the right error messages once templates start getting nested, which can be seen in the similar bug 9179 and bug 13340 also. run.dlang.io says this actually used to work up till dmd 2.061: https://run.dlang.io/is/DpqFeg Working with templates gets very annoying if it starts to gag these error messages. I'm not sure why it would do this: are there actually template overloads where multiple functions might work, so it just chooses the one that compiles? In this case, I think only one overload should match, so I want the error message for that one.
Comment #1 by dbugz — 2018-07-20T06:07:41Z
Ah, I finally figured out why, if I comment out the last constraint on toImpl, `is(typeof(parse!T(value)))`, I get the right error with `dmd -version=broken shadow.d`: frontend.d(12): Error: variable anydigits is shadowing variable frontend.parse!(real, string).parse.anydigits frontend.d(35): Error: template instance `frontend.parse!(real, string)` error instantiating frontend.d(42): instantiated from here: toImpl!(real, string) I'm unsure what that last constraint is supposed to accomplish, make sure parse() compiles and returns a type? It still used to print a useful error up till 2.060, but I'll leave it up to someone who knows the compiler better whether this should be considered a regression.
Comment #2 by iamthewilsonator — 2018-10-10T00:26:50Z
The usage of is(typeof(parse!T(value))) follows a common pattern in phobos: namely to specify external dependencies of the function. (e.g. `all` has `is(typeof(unaryFun!pred(range.front))` in its constraint) toImpl uses parse and so specifies that in its constraint. This has the obvious effect of invalidating the template's candidacy which is needed because D doesn't do SFINAE. This is not a regression, just an unfortunate side effect of the above. On that note, my DIP[1] would make it immediately obvious that parse!T(value) is where the problem lies. [1]: https://github.com/dlang/DIPs/pull/131
Comment #3 by razvan.nitu1305 — 2021-02-23T13:25:07Z
Compiling this with git master yields: onlineapp.d(42): Error: template onlineapp.toImpl cannot deduce function from argument types !(real)(string), candidates are: onlineapp.d(23): toImpl(T, S)(S value) with T = real, S = string must satisfy the following constraint: is(typeof(parse!T(value))) This is expected behavior from the compilers' point view. There is a constraint that is not satisfied and that is exactly what the compiler is reporting; since the constraint is an is expression, the errors are gagged and the shadowing is not reported. It could be argued that this is a phobos regression, however, as Nicholas pointed out, the purpose of that check is to make sure that you are able to instantiate `parse`, otherwise you might get errors from the innards of `parse` that will leave most of the standard lib users scratching their head.On the other hand, right now, things are very clear: you are not able to instantiate the toImpl template because you are failing the is constraint. If you want to find out more you can manually call it or compile with -verrors=spec. Closing as WORKSFORME.