digitalmars.D - const(Rvalue) resolved to different overloads
- =?UTF-8?Q?Ali_=c3=87ehreli?= (40/40) Dec 29 2016 I'm working on understanding how different qualifiers of the same type,
- Jonathan M Davis via Digitalmars-d (15/54) Dec 29 2016 I very much doubt that it's a bug, since it seems like the sort of thing
- =?UTF-8?Q?Ali_=c3=87ehreli?= (8/13) Dec 30 2016 Coming up with such guidelines is exactly the reason why I was
- Timon Gehr (41/45) Dec 31 2016 It's a bug.
- =?UTF-8?Q?Ali_=c3=87ehreli?= (4/6) Dec 31 2016 Filed:
- safety0ff (5/6) Dec 31 2016 What about: http://dlang.org/spec/const3.html#implicit_conversions
- =?UTF-8?Q?Ali_=c3=87ehreli?= (7/14) Dec 31 2016 Good find but its effect in overload resolution is an oversight.
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
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
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 anywayComing 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 casesThere 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
On 30.12.2016 08:59, Jonathan M Davis via Digitalmars-d wrote: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.)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.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
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
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
On 12/31/2016 09:18 PM, safety0ff wrote:On Thursday, 29 December 2016 at 22:54:35 UTC, Ali Çehreli wrote: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... AliCan 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