www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - const(Rvalue) resolved to different overloads

reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
I'm working on understanding how different qualifiers of the same type, 
the kinds of indirections that its members may have, and the expressions 
being lvalue versus rvalue affect function overload resolution.

For example, the following program has

- struct S with a member having const indirection

- Two overloads of the same function taking S and immutable(S)

- Two calls made with an Rvalue and a const Rvalue

import std.stdio;

struct S {
     const(int)[] a;
}

void foo(S s) {
     writefln("foo(S) called");
}

void foo(immutable(S) s) {
     writefln("foo(immutable(S)) called");
}

void main() {
     writefln("calling with Rvalue");
     foo(S());

     writefln("calling with const Rvalue");
     foo(const(S)());
}

The peculiar thing is that the const Rvalue case is resolved to the 
immutable(S) overload:

calling with Rvalue
foo(S) called
calling with const Rvalue
foo(immutable(S)) called    <-- ?

Can you explain that behavior?

Well... I already see that for that to happen, the argument must have 
the S.init value (or be constructed explicitly as const(S)(null)). When 
initialized with something else, it is resolved to the foo(S) overload:

     const(int)[] a = [ 42 ];
     foo(const(S)(a));

calling with const Rvalue
foo(S) called               <-- Now different; surprising

Bug or not? If not, then I don't think there can ever be more than a 
total of about 0.75 people who fully know D overload resolution. :o)

Ali
Dec 29 2016
next sibling parent reply Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Thursday, December 29, 2016 14:54:35 Ali Çehreli via Digitalmars-d wrote:
 I'm working on understanding how different qualifiers of the same type,
 the kinds of indirections that its members may have, and the expressions
 being lvalue versus rvalue affect function overload resolution.

 For example, the following program has

 - struct S with a member having const indirection

 - Two overloads of the same function taking S and immutable(S)

 - Two calls made with an Rvalue and a const Rvalue

 import std.stdio;

 struct S {
      const(int)[] a;
 }

 void foo(S s) {
      writefln("foo(S) called");
 }

 void foo(immutable(S) s) {
      writefln("foo(immutable(S)) called");
 }

 void main() {
      writefln("calling with Rvalue");
      foo(S());

      writefln("calling with const Rvalue");
      foo(const(S)());
 }

 The peculiar thing is that the const Rvalue case is resolved to the
 immutable(S) overload:

 calling with Rvalue
 foo(S) called
 calling with const Rvalue
 foo(immutable(S)) called    <-- ?

 Can you explain that behavior?

 Well... I already see that for that to happen, the argument must have
 the S.init value (or be constructed explicitly as const(S)(null)). When
 initialized with something else, it is resolved to the foo(S) overload:

      const(int)[] a = [ 42 ];
      foo(const(S)(a));

 calling with const Rvalue
 foo(S) called               <-- Now different; surprising

 Bug or not? If not, then I don't think there can ever be more than a
 total of about 0.75 people who fully know D overload resolution. :o)
I very much doubt that it's a bug, since it seems like the sort of thing that would require extra logic to make happen. That being said, I think that an argument could definitely be made that it was a bad decision from the standpoint of consistency (and it's quite possible that it's an accident that it works the way it does thanks to some weird combination of features). It wouldn't entirely surprise me though if the current behavior is a result of someone wanting a particular piece of code to work that wouldn't work otherwise. In practice, I wouldn't expect it to matter - and usually if you've overloaded on mutable and immutable, you're going to want to overload on const as well anyway - but the current behavior does make it entertaining to figure out what's going to happen in corner cases, which isn't exactly a good thing and goes counter to a lot of the reasoning behind why D's overloading rules generally work the way they work. - Jonathan M Davis
Dec 29 2016
next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 12/29/2016 11:59 PM, Jonathan M Davis via Digitalmars-d wrote:

 In practice, I wouldn't expect it to matter - and usually if
 you've overloaded on mutable and immutable, you're going to
 want to overload on const as well anyway
Coming up with such guidelines is exactly the reason why I was experimenting with different combinations of overloads.
 but the current behavior does make it entertaining to figure
 out what's going to happen in corner cases
There are too many corner cases when one considers rvalue versus lvalue, const or immutable member indirections, const or immutable parameters, const or immutable expressions. :) Then there is 'auto ref' which competes with some of these cases. Ali
Dec 30 2016
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 30.12.2016 08:59, Jonathan M Davis via Digitalmars-d wrote:
 Bug or not? If not, then I don't think there can ever be more than a
 total of about 0.75 people who fully know D overload resolution. :o)
It's a bug. https://dlang.org/spec/function.html#function-overloading The following case is essentially identical: import std.stdio; int foo(int x){ return 1; } int foo(immutable(int) x){ return 2; } void main(){ const int x=2; writeln(foo(x)); // error } The reason why rvalue vs. lvalue can make a difference is implicit conversion to immutable for provably unique references: struct S { const(int)[] a; } void main(){ immutable s = const(S)(null); // ok const t = const(S)(null); immutable u = t; // error } (I.e., the lvalue does not match the immutable overload at all.)
 I very much doubt that it's a bug, since it seems like the sort of thing
 that would require extra logic to make happen.
Bugs are wrong logic, often found within unnecessarily convoluted extra logic. Similar case: class C{} struct S{ const(C) c; } int foo(S c){ return 1; } int foo(immutable(S) c){ return 2; } void main(){ writeln(foo(const(S)(new C))); // 2 } Note that the behaviour changes if the type of 'c' is changed to C or immutable(C) instead of const(C). The rule that DMD applies seems to be: if there is at least one const indirection and all other indirections are either const or immutable, pick the immutable overload. Otherwise, error. This is not the kind of detail that should matter for overload resolution (especially given that fields can be private).
Dec 31 2016
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 12/31/2016 06:22 AM, Timon Gehr wrote:

 This is not the kind of detail that should matter for overload
 resolution (especially given that fields can be private).
Filed: https://issues.dlang.org/show_bug.cgi?id=17050 Ali
Dec 31 2016
prev sibling parent reply safety0ff <safety0ff.dev gmail.com> writes:
On Thursday, 29 December 2016 at 22:54:35 UTC, Ali Çehreli wrote:
 Can you explain that behavior?
What about: http://dlang.org/spec/const3.html#implicit_conversions "An expression may be converted from mutable or shared to immutable if the expression is unique and all expressions it transitively refers to are either unique or immutable."
Dec 31 2016
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 12/31/2016 09:18 PM, safety0ff wrote:
 On Thursday, 29 December 2016 at 22:54:35 UTC, Ali Çehreli wrote:
 Can you explain that behavior?
What about: http://dlang.org/spec/const3.html#implicit_conversions "An expression may be converted from mutable or shared to immutable if the expression is unique and all expressions it transitively refers to are either unique or immutable."
Good find but its effect in overload resolution is an oversight. The uniqueness of an expression should not interfere with overload resolution because overload resolution does not involve expressions but their types. Otherwise, a simple change in an object initialization takes us to a different function. A different kind of hijacking indeed... Ali
Dec 31 2016