Bug 2025 – Inconsistent rules for instantiating templates with a tuple parameter

Status
RESOLVED
Resolution
FIXED
Severity
enhancement
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2008-04-23T01:01:00Z
Last change time
2014-03-28T21:01:13Z
Keywords
pull, spec
Assigned to
nobody
Creator
samukha
Blocks
3109

Comments

Comment #0 by samukha — 2008-04-23T01:01:05Z
The template with non-tuple parameter is preferred if that parameter is a value (Case 1). The template with tuple parameter is preferred if the non-tuple parameter is a type (Case 2). Case 1 ---- template Foo(int i) { pragma(msg, "One"); } template Foo(A...) { pragma(msg, "Many"); } alias Foo!(1) foo; ---- Outputs: One Case 2 ---- template Foo(T) { pragma(msg, "One"); } template Foo(A...) { pragma(msg, "Many"); } alias Foo!(int) foo; ---- Outputs: Many Probably both cases should result in an ambiguity error.
Comment #1 by smjg — 2008-11-21T20:55:41Z
This is either wrong-code or accepts-invalid, but we need a clarification in the spec first. Compiler behaviour is most certainly wrong in case 2 - it leaves no way to instantiate the Foo(T) version.
Comment #2 by bugzilla — 2012-01-22T01:30:32Z
Here's what's happening: Case 1 ------ 1 is an "exact" match with (int i) and (A...). Since (int i) is more specialized, it goes with (int i). Case 2 ------ int is a "convert" match with T (so that a better match would be Foo(T:int) and an "exact" match with (A...). The exact match wins.
Comment #3 by timon.gehr — 2012-01-22T12:14:01Z
(T:int) wins against (A...). Why can't the same be done for (T:int) vs (T) ? (A...) is certainly less specialized than (T), and int matches both equally well, therefore I think it is strange that (A...) would be chosen over (T).
Comment #4 by smjg — 2012-01-31T18:03:36Z
(In reply to comment #2) > int is a "convert" match with T (so that a better match would be Foo(T:int) and > an "exact" match with (A...). The exact match wins. You've lost me here. How is int matching T not exact?
Comment #5 by yebblies — 2012-01-31T19:17:20Z
(In reply to comment #4) > (In reply to comment #2) > > int is a "convert" match with T (so that a better match would be Foo(T:int) and > > an "exact" match with (A...). The exact match wins. > > You've lost me here. How is int matching T not exact? As Walter said, so that T:int would be considered a better match. It is a hack in the compiler that I'm not particularly fond of. See issue 4953 for another case it causes problems.
Comment #6 by smjg — 2012-02-01T04:16:53Z
(In reply to comment #5) > As Walter said, so that T:int would be considered a better match. It is a hack > in the compiler that I'm not particularly fond of. What has a "better match" to do with anything? (int) matches the pattern (T) perfectly - no conversion, implicit or explicit. True, (T: int) is a more specialised match, but that doesn't magically render the match with (T) inexact. > See issue 4953 for another case it causes problems. That's different - 5 is an int, so it doesn't exactly match the pattern (short x).
Comment #7 by yebblies — 2012-02-01T04:32:47Z
(In reply to comment #6) > What has a "better match" to do with anything? (int) matches the pattern (T) > perfectly - no conversion, implicit or explicit. True, (T: int) is a more > specialised match, > inexact. > more specialized == better match. That is how template type deduction works. > but that doesn't magically render the match with (T) That's why I called it a hack. > > See issue 4953 for another case it causes problems. > > That's different - 5 is an int, so it doesn't exactly match the pattern (short > x). I know it's different, I wrote the patch for it. The hack causes problems there too.
Comment #8 by smjg — 2012-02-01T04:43:27Z
(In reply to comment #7) > more specialized == better match. That is how template type deduction works. But there's no (T : int) version of this template. We have two templates to match: Foo(T) and Foo(A...). Both patterns match Foo!(int) exactly, and Foo(T) matches a proper subset of everything that Foo(A...) matches. So Foo(T) is the most specialised match that the code has supplied.
Comment #9 by yebblies — 2012-02-01T04:55:06Z
(In reply to comment #8) > (In reply to comment #7) > > more specialized == better match. That is how template type deduction works. > > But there's no (T : int) version of this template. > It doesn't matter. The compiler does template argument deduction through a function called matchArg in template.c. It will generate one of the match levels (MATCHnomatch, MATCHconvert, MATCHconst, MATCHexact) based on what the template parameter, spectype, and argument are. matchArg does not know or care what the other overloads of the template are, or even what the other arguments are. For (T : int) to be preferred over (T), it must return a worse match level for the unspecialized parameter. Currently it will return MATCHconvert for these parameters, and return MATCHexact only when there is a (matching) spec type. Which leaves you with a non-exact match for (T) and (T...), even if it's the only overload.
Comment #10 by smjg — 2012-02-01T05:27:34Z
(In reply to comment #9) > For (T : int) to be preferred over (T), it must return a worse > match level for the unspecialized parameter. Currently it will > return MATCHconvert for these parameters, and return MATCHexact > only when there is a (matching) spec type. So MATCHexact means "the pattern can be matched only by this exact set of arguments", and MATCHconvert means "this is one of various sets of arguments that can match this pattern". But then, surely, it would be rejecting the instantiation as ambiguous, not picking the (A...) overload. Really, it's a fundamental flaw in the compiler logic whereby it conflates two essentially orthogonal concepts: exactness and specialisation. A correct template instantiation algorithm would: - see which template patterns match the instantiation exactly - if none, see which template patterns match with implicit conversions - of all the matches, determine which is most specialised
Comment #11 by yebblies — 2012-02-01T05:42:20Z
(In reply to comment #10) > So MATCHexact means "the pattern can be matched only by this exact set of > arguments", and MATCHconvert means "this is one of various sets of arguments > that can match this pattern". > No, MATCHconvert means 'match with implicit conversions'. These are the same match levels used for normal function argument matching. > But then, surely, it would be rejecting the instantiation as ambiguous, not > picking the (A...) overload. > Actually, (A...) is an alias parameter so it comes up as MATCHexact. (There is a bug report about this somewhere). Otherwise you would get an ambiguity error. This is not what should be happening, non-variadic templates should be preferred over variadic ones. > Really, it's a fundamental flaw in the compiler logic whereby it conflates two > essentially orthogonal concepts: exactness and specialisation. > > A correct template instantiation algorithm would: > - see which template patterns match the instantiation exactly > - if none, see which template patterns match with implicit conversions > - of all the matches, determine which is most specialised It's not that simple when you take multiple parameters, overloads, mixing explicit template parameters and deduced ones, etc The ordering (exact match, const match, convert match, deduced match, variadic match, alias match, specialized match, etc) is complicated and the match system needs a redesign to cope with it. Expanding the current match system will not necessarily solve the problem, as the different levels interact in weird ways. This is probably what will end up happening if a better solution is not found.
Comment #12 by smjg — 2012-02-01T06:39:13Z
(In reply to comment #11) > No, MATCHconvert means 'match with implicit conversions'. These are the same > match levels used for normal function argument matching. int is a type. T as a template parameter denotes exactly that - a type. So what implicit conversion is being performed?
Comment #13 by yebblies — 2012-02-01T06:51:55Z
(In reply to comment #12) > (In reply to comment #11) > > No, MATCHconvert means 'match with implicit conversions'. These are the same > > match levels used for normal function argument matching. > > int is a type. T as a template parameter denotes exactly that - a type. So > what implicit conversion is being performed? None. That's why it's a hack to use it to represent deduced args with no spec type. That's why it causes problems... If you really want to understand how dmd does template deduction and overload matching have a poke around in template.c (deduceFunctionTemplateMatch and matchArgs) and mtype.c (callMatch).
Comment #14 by k.hara.pg — 2014-01-26T01:24:27Z
Comment #15 by timon.gehr — 2014-01-31T00:04:20Z
Why would this be an enhancement rather than a bug fix?
Comment #16 by k.hara.pg — 2014-02-01T06:30:50Z
(In reply to comment #15) > Why would this be an enhancement rather than a bug fix? I agree. The current behavior "type argument prefers tuple parameter than type parameter" is contrary to the "template specialization" concept.
Comment #17 by k.hara.pg — 2014-02-25T19:53:42Z
Comment #18 by github-bugzilla — 2014-03-01T12:25:00Z
Commit pushed to master at https://github.com/D-Programming-Language/dlang.org https://github.com/D-Programming-Language/dlang.org/commit/fe3b50c1a3db01acc88ce9525b0494126e0dcf31 fix Issue 2025 - Inconsistent rules for instantiating templates with a tuple parameter
Comment #19 by k.hara.pg — 2014-03-28T21:01:13Z
*** Issue 12416 has been marked as a duplicate of this issue. ***