D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 5896 - const overload matching is succumb to template parameter one
Summary: const overload matching is succumb to template parameter one
Status: RESOLVED FIXED
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: All All
: P2 normal
Assignee: No Owner
URL:
Keywords: patch, rejects-valid
Depends on:
Blocks:
 
Reported: 2011-04-27 01:54 UTC by Kenji Hara
Modified: 2012-05-12 12:07 UTC (History)
3 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Kenji Hara 2011-04-27 01:54:51 UTC
This code doesn't compile when -version=expect.
----
struct X
{
  version(expect)
  {
    T opCast(T)()      { return 10; }  // 1a
    T opCast(T)()const { return 11; }  // 1b
  }
  else
  {
    T opCast(T:int)()      { return 10; }  // 2a
    T opCast(T:int)()const { return 11; }  // 2b
  }
}
void main()
{
  auto xm = X();
  auto xc = const(X)();
  assert(cast(int)xm == 10);
  assert(cast(int)xc == 11);
}
----
test.d(18): Error: template test.X.opCast(T) opCast(T) matches more than one template declaration, test.d(5):opCast(T) and test.d(6):opCast(T)
----

Template function matching processed by template.c TemplateDeclaration::deduceFunctionTemplateMatch(), and
const overload matching is more priority than template parameter one.

T:int(2a,2b) is more specialized from T(1a,1b), then first priority is MATCHexact and second is MATCHconvert.

Next, currently const overload matching returns MATCHconst or not, but the priority of T(MATCHconvert) is less than MATCHconst, so both 1a and 1b return MATCHconvert, then make ambiguous.

It seems to me that more priority level is need:
Priority name
0 MATCHnomatch
1 MATCHconvertconst  ... const conversion + template parameter conversion (new)
2 MATCHconvert       ... template parameter conversion
3 MATCHconst         ... const conversion
4 MATCHexact         ... exact match
Comment 1 Don 2011-04-29 08:51:07 UTC
I don't understand why case 2a,2b works.
It seems to me that case 1 is identical to:

struct X{}

T  foo(T)(const(X) _this) { return 10; }

T  foo(T)(X _this) { return 11; }

void main(){
    auto xm = X();
    assert(foo!int(xm)==11);
}

Which fails in exactly the same way.
Comment 2 Kenji Hara 2011-04-29 16:53:00 UTC
(In reply to comment #1)
> I don't understand why case 2a,2b works.
> It seems to me that case 1 is identical to:
> 
> struct X{}
> 
> T  foo(T)(const(X) _this) { return 10; }
> 
> T  foo(T)(X _this) { return 11; }
> 
> void main(){
>     auto xm = X();
>     assert(foo!int(xm)==11);
> }
> 
> Which fails in exactly the same way.

I have just started reading the code around the template, template function matching may have three steps:

1. Tempate parameter vs explicit template arguments.
   ex. opCast(T)() vs opCast!X() -> T vs X
2. const overload matching on ethis
   void f(T)() vs const void f(T)() -> (mutable) vs const
3. function parameters type matching (and may inference of implict template parameters)
   void f(X x) vs void f(const(X) x) -> X vs const(X)

The cause of this issue may be step 1 and 2, and cause of your test case may be 3.

----
Re-explain of my test case. on xm (type is X) lookup...
1a matching:
  step1: opCast(T) vs opCast!(int) -> MATCHconvert
  step2: through                   -> MATCHconst
  -> afterward result: MATCHconvert
1b matching:
  step1: opCast(T) vs opCast!(int) -> MATCHconvert
  step2: MATCHconst if can         -> !!! MATCHconst is succomb to MATCHconvert
  -> afterward result: MATCHconvert
Both result of 1a and 1b are MATCHconvert, so make ambiguous.

2a matching:
  step1: opCast(T:int) vs opCast!(int) -> MATCHexact
  step2: through
  -> afterward result: MATCHexact
2b matching:
  step1: opCast(T:int) vs opCast!(int) -> MATCHexact
  step2: MATCHconst if can             -> MATCHconst
  -> afterward result: MATCHconvert
2a is more specialized than 2b, then 2a is selected.
Comment 3 Kenji Hara 2011-07-14 04:20:08 UTC
Patch and discussions:
https://github.com/D-Programming-Language/dmd/pull/45
Comment 4 SomeDude 2012-04-23 12:50:13 UTC
The 2 examples compile with 2.059
Comment 5 Kenji Hara 2012-04-23 17:45:58 UTC
More complicated test from my pull is still broken.

----
struct X5896
{
                 T opCast(T)(){ return 1; }    // L3
           const T opCast(T)(){ return 2; }    // L4
       immutable T opCast(T)(){ return 3; }
          shared T opCast(T)(){ return 4; }    // L6
    const shared T opCast(T)(){ return 5; }    // L7
}
void test5896()
{
    auto xm =              X5896  ();
    auto xc =        const(X5896) ();
    auto xi =    immutable(X5896) ();
    auto xs =       shared(X5896) ();
    auto xcs= const(shared(X5896))();
    assert(cast(int)xm == 1);    // L16
    assert(cast(int)xc == 2);
    assert(cast(int)xi == 3);    // L18
    assert(cast(int)xs == 4);    // L19
    assert(cast(int)xcs== 5);
}

Errors:
----
test.d(16): Error: template test.X5896.opCast matches more than one template declaration, test.d(3):opCast(T) and test.d(4):opCast(T)
test.d(18): Error: template test.X5896.opCast matches more than one template declaration, test.d(4):opCast(T) and test.d(7):opCast(T)
test.d(19): Error: template test.X5896.opCast matches more than one template declaration, test.d(6):opCast(T) and test.d(7):opCast(T)
Comment 6 github-bugzilla 2012-05-12 11:35:38 UTC
Commits pushed to master at https://github.com/D-Programming-Language/dmd

https://github.com/D-Programming-Language/dmd/commit/60d7fadd7100c52746a29fade983c5644a4c8cfb
fix Issue 5896 - const overload matching is succumb to template parameter one

Treat matching levels separately based on initial template arguments and inferred from function arguments.

https://github.com/D-Programming-Language/dmd/commit/f21d0cc53e2af192c8441ee8de786981d3bbca57
Merge pull request #45 from 9rnsr/fix5896

Issue 5896 - const overload matching is succumb to template parameter one