digitalmars.D.bugs - [Issue 7355] New: inout incorrectly resolved if the same type has both mutable and immutable parts
- d-bugmail puremagic.com (23/23) Jan 23 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (21/21) Jan 26 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (16/22) Jan 26 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (7/25) Jan 26 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (16/34) Jan 26 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (30/31) Jan 26 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (19/58) Jan 26 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (25/52) Jan 26 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (23/23) Jan 27 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (17/24) Feb 02 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (29/52) Feb 02 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (21/42) Feb 03 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (51/92) Feb 03 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (30/30) Feb 03 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (7/12) Feb 03 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (8/16) Feb 03 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (41/74) Feb 03 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (45/83) Feb 03 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (14/108) Feb 03 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (8/15) Feb 03 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
- d-bugmail puremagic.com (19/30) Feb 04 2012 http://d.puremagic.com/issues/show_bug.cgi?id=7355
http://d.puremagic.com/issues/show_bug.cgi?id=7355 Summary: inout incorrectly resolved if the same type has both mutable and immutable parts Product: D Version: D2 Platform: All OS/Version: All Status: NEW Severity: normal Priority: P2 Component: DMD AssignedTo: nobody puremagic.com ReportedBy: timon.gehr gmx.ch --- Comment #0 from timon.gehr gmx.ch 2012-01-23 16:41:58 PST --- DMD 2.058head inout(int*)* foo(inout(int*)* x){return x;} immutable(int)** x; static assert(is(typeof(foo(x))==const(int*)*)); Error: static assert (is(typeof(foo((__error))) == const(int*)*)) is false The assertion should pass. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jan 23 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #1 from Kenji Hara <k.hara.pg gmail.com> 2012-01-26 04:30:30 PST --- My understanding is, each inout deduction from a function argument just like pattern matching. Parameter type: inout( int *)* Argument type: mutable(immutable(int)*)* // mutable(...) is pseudo modifier --> 'inout' is deduced to 'mutable'. I think if we allow this kind of deduction, there is an ambiguous case: inout(int) foo(inout(int**) x){ return 0; } void main() { immutable(int*)* x; foo(x); // inout is deduced to imuutable or const? Both conversions // immutable(int*)* to immutable(int**) // immutable(int*)* to const(int**) // are valid, so it is ambiguous. } -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jan 26 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 Steven Schveighoffer <schveiguy yahoo.com> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |schveiguy yahoo.com --- Comment #2 from Steven Schveighoffer <schveiguy yahoo.com> 2012-01-26 07:56:53 PST --- (In reply to comment #1)My understanding is, each inout deduction from a function argument just like pattern matching. Parameter type: inout( int *)* Argument type: mutable(immutable(int)*)* // mutable(...) is pseudo modifier --> 'inout' is deduced to 'mutable'.The compiler should either fail to match, since inout wildcard is not applying to the immutable, or if it does match, it should treat foo as: int** foo(int **x) { return x; } This should fail to be able to be called with immutable(int)**. The assert should fail because the typeof should resolve to Error. I think this bug is invalid. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jan 26 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #3 from timon.gehr gmx.ch 2012-01-26 09:07:23 PST --- (In reply to comment #2)(In reply to comment #1)The typeof resolves to error because inout resolves to immutable. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------My understanding is, each inout deduction from a function argument just like pattern matching. Parameter type: inout( int *)* Argument type: mutable(immutable(int)*)* // mutable(...) is pseudo modifier --> 'inout' is deduced to 'mutable'.The compiler should either fail to match, since inout wildcard is not applying to the immutable, or if it does match, it should treat foo as: int** foo(int **x) { return x; } This should fail to be able to be called with immutable(int)**. The assert should fail because the typeof should resolve to Error. I think this bug is invalid.
Jan 26 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #4 from timon.gehr gmx.ch 2012-01-26 09:20:32 PST --- (In reply to comment #1)My understanding is, each inout deduction from a function argument just like pattern matching. Parameter type: inout( int *)* Argument type: mutable(immutable(int)*)* // mutable(...) is pseudo modifier --> 'inout' is deduced to 'mutable'.The compiler deduces inout to _immutable_ in this case. Other than that, it does not make much sense to talk about a mutable pseudo modifier: inout is transitive, but such a pseudo modifier cannot be transitive.I think if we allow this kind of deduction, there is an ambiguous case: inout(int) foo(inout(int**) x){ return 0; } void main() { immutable(int*)* x; foo(x); // inout is deduced to imuutable or const? Both conversions // immutable(int*)* to immutable(int**) // immutable(int*)* to const(int**) // are valid, so it is ambiguous. }The same ambiguity is already resolved at other points in the compiler: inout(int) foo(inout(int) x, inout(int)* y){ return 0; } void main(){ immutable(int)* y; foo(1, y); } inout is resolved to const, even though immutable would be a far better choice. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jan 26 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #5 from Steven Schveighoffer <schveiguy yahoo.com> 2012-01-26 09:36:01 PST --- (In reply to comment #3)The typeof resolves to error because inout resolves to immutable.As I said, it should fail to match or match mutable and fail to call. I'm not sure which is correct, but I feel either way that the assert should fail. If it's resolving to immutable, I think it's a bug, not because it's not passing, but because it's failing for the wrong reason. I think your expectations would be a violation of const. Let's assume inout did resolve to const for foo, and the function could be called: immutable int x = 5; immutable(int)* xp = &x; immutable(int)** xpp = &xp; const(int *)* y = foo(xpp); int z = 2; *y = &z; // this should pass, since I can assign int* to const(int*). assert(*xp == 2); z = 3; assert(*xp == 3); // oops! changed data perceived as immutable! Is there an error in my example? I think it comes down to this: immutable(int *)* foo(immutable(int *)* x) // inout == immutable const(int *)* foo( const(int *)* x) // inout == const int ** foo( int ** x) // inout == mutable none of these can be called with immutable(int)** because there is no implicit cast to the parameter. I don't think const(immutable(int)*)* reduces to const(int *)*. This does take some mental effort, so I may have made a mistake :) I hate double pointers... -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jan 26 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #6 from timon.gehr gmx.ch 2012-01-26 09:55:37 PST --- (In reply to comment #5)(In reply to comment #3)No.The typeof resolves to error because inout resolves to immutable.As I said, it should fail to match or match mutable and fail to call. I'm not sure which is correct, but I feel either way that the assert should fail. If it's resolving to immutable, I think it's a bug, not because it's not passing, but because it's failing for the wrong reason. I think your expectations would be a violation of const.Let's assume inout did resolve to const for foo, and the function could be called: immutable int x = 5; immutable(int)* xp = &x; immutable(int)** xpp = &xp; const(int *)* y = foo(xpp); int z = 2; *y = &z; // this should pass, since I can assign int* to const(int*).You cannot assign anything to const(int*), that is the point of const.assert(*xp == 2); z = 3; assert(*xp == 3); // oops! changed data perceived as immutable! Is there an error in my example? I think it comes down to this: immutable(int *)* foo(immutable(int *)* x) // inout == immutable const(int *)* foo( const(int *)* x) // inout == const int ** foo( int ** x) // inout == mutable none of these can be called with immutable(int)** because there is no implicit cast to the parameter. I don't think const(immutable(int)*)* reduces to const(int *)*.It does. The second version is callable with immutable(int)**. Not fixing this would mean there are cases where code duplication is more expressive than inout.This does take some mental effort, so I may have made a mistake :) I hate double pointers...We have: immutable(T) is a subtype of const(T). => immutable(int) :< const(int) const(T*) :< const(S*) iff const(T) :< const(S) => const(immutable(int)*) :< const(int*) const(T)* :< const(S)* iff const(T) :< const(S) => const(immutable(int)*)* : const(int*)* qed -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jan 26 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #7 from Steven Schveighoffer <schveiguy yahoo.com> 2012-01-26 10:21:28 PST --- (In reply to comment #6)(In reply to comment #5)Oh yeah :) Stupid me, for some reason in my head this made sense because there was a mutable part.Let's assume inout did resolve to const for foo, and the function could be called: immutable int x = 5; immutable(int)* xp = &x; immutable(int)** xpp = &xp; const(int *)* y = foo(xpp); int z = 2; *y = &z; // this should pass, since I can assign int* to const(int*).You cannot assign anything to const(int*), that is the point of const.You are right. So we need to come up with some rules for inout as to how it matches. What about this idea? for each inout parameter, we try as a substitute in order: mutable, immutable, inout, const. See if the argument can be implicitly converted to the substituted parameter. If none of the possible substitutes can be implicitly converted to for a single parameter, the function fails to be called, and an error is generated "inout cannot be resolved [for parameter x]" If substitutes can be found for all of the inout parameters, and they all match (i.e. all mutable, all immutable, all inout or all const), then that is used as the substitute and the function is called. If substitutes are different (e.g. one is mutable, but another is immutable), then const is used as the substitute. The parameters are then re-checked to see that they all implicitly convert to the substituted const type. If this is not possible, an error is generated "common inout substitute cannot be found". I think this should be as capable as duplicate functions. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------immutable(int *)* foo(immutable(int *)* x) // inout == immutable const(int *)* foo( const(int *)* x) // inout == const int ** foo( int ** x) // inout == mutable none of these can be called with immutable(int)** because there is no implicit cast to the parameter. I don't think const(immutable(int)*)* reduces to const(int *)*.It does. The second version is callable with immutable(int)**. Not fixing this would mean there are cases where code duplication is more expressive than inout.
Jan 26 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #8 from timon.gehr gmx.ch 2012-01-27 14:44:19 PST --- This rule should work satisfactory: - The compiler is required to resolve inout such that the behavior is as if there were four (five, with inout(const(T)) enhancement in place) separate overloads. This could be implemented similar to how you propose it, by adding all of the versions to the overload set, or by using some insights to speed up the process (not very hard) - If there would be an ambiguity between the different pseudo overloads: -- If one of the remaining multiple possibilities is the const version, drop it -- If one of the possibilities is the inout version, drop it (-- If one of the remaining multiple possibilities is the inout const version, drop it) -- If mutable and immutable are remaining possible matches for inout, resolve inout to mutable. -- otherwise resolve inout to what is left. - Allow direct conversion of the return type to any of the versions of inout that still match after implicit conversions of the arguments in place. - An inout function is considered to be less specialized than a corresponding non-inout one. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jan 27 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #9 from Steven Schveighoffer <schveiguy yahoo.com> 2012-02-02 12:07:29 PST --- (In reply to comment #8)This rule should work satisfactory: - The compiler is required to resolve inout such that the behavior is as if there were four (five, with inout(const(T)) enhancement in place) separate overloads. This could be implemented similar to how you propose it, by adding all of the versions to the overload set, or by using some insights to speed up the process (not very hard)This sounds fine. However, inout(const(T)) is not a substitute for inout(T), so it should be four. inout(const(T)) is special in what can implicitly convert to it. But the inout is the only wildcard there. The rest is not necessary. The normal overload rules already should handle which one is chosen. Since inout, mutable, and immutable do not implicitly convert to each other, it's not possible for there to be an ambiguity, is there? immutable and mutable (and by extension inout) should be preferred over const. It's important to note that the inout overload we are talking about is not the wildcard inout, but the local const-like inout. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Feb 02 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #10 from timon.gehr gmx.ch 2012-02-02 13:05:31 PST --- (In reply to comment #9)(In reply to comment #8)inout(const(T)) should be its own type. And as soon as that is the case, inout const will have to be a valid substitute for inout. For example, this should type check: immutable(int)[] arr = [2,3,4]; inout(const(int))[] foo(inout(int)[] x){ return uniform(0,2) ? arr : x; } inout(int)[] id(inout(int)[] x){ return x; } inout(int)[] bar(inout(int)[] x){ inout(int)[] y = [1,2,3]; inout(const(int))[] z = id(foo(y)); // inout const substitute for inout return z~x; }This rule should work satisfactory: - The compiler is required to resolve inout such that the behavior is as if there were four (five, with inout(const(T)) enhancement in place) separate overloads. This could be implemented similar to how you propose it, by adding all of the versions to the overload set, or by using some insights to speed up the process (not very hard)This sounds fine. However, inout(const(T)) is not a substitute for inout(T), so it should be four.inout(const(T)) is special in what can implicitly convert to it. But the inout is the only wildcard there.True, but that does not mean it should not be a valid substitute for inout.The rest is not necessary. The normal overload rules already should handle which one is chosen. Since inout, mutable, and immutable do not implicitly convert to each other, it's not possible for there to be an ambiguity, is there? immutable and mutable (and by extension inout) should be preferred over const.There are indeed corner cases, for example: void foo(immutable(int) x, float y){} void foo(const(int) x, float y){} void foo(int x, float y){} void main(){foo(1,1);} // error, matches all three A different solution would be to refine the overload rules.It's important to note that the inout overload we are talking about is not the wildcard inout, but the local const-like inout.Exactly. The same holds for inout(const(T)). -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Feb 02 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #11 from Steven Schveighoffer <schveiguy yahoo.com> 2012-02-03 03:24:52 PST --- (In reply to comment #10)For example, this should type check: immutable(int)[] arr = [2,3,4]; inout(const(int))[] foo(inout(int)[] x){ return uniform(0,2) ? arr : x; } inout(int)[] id(inout(int)[] x){ return x; } inout(int)[] bar(inout(int)[] x){ inout(int)[] y = [1,2,3]; inout(const(int))[] z = id(foo(y)); // inout const substitute for inout return z~x; }This typechecks even if we don't have inout(const) as an option: inout matches inout (we need a new term for this 'local' inout!) foo(y) => inout(int)[] inout(int)[] => inout(const(int))[] => z Besides, you are assuming here that the lvalue side of the expression plays a role in determining the inout match. It doesn't. The return type is solely determined by the argument expressions.There are indeed corner cases, for example: void foo(immutable(int) x, float y){} void foo(const(int) x, float y){} void foo(int x, float y){} void main(){foo(1,1);} // error, matches all threeThis case has no relevance, there is no inout return value. Who cares what inout resolves to? Not to be nit picky, but we should consider that any polysemous value type is not going to play a vital role in an inout function, since it's implicitly convertible to any modifier. It's references or contained references which make a difference. If it comes down to supporting this, choosing any inout match arbitrarily is good enough. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Feb 03 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #12 from timon.gehr gmx.ch 2012-02-03 14:42:21 PST --- (In reply to comment #11)(In reply to comment #10)My point was that it does not _if_ we have it as an option (and it has to be an option if we want inout to be powerful enough to replace identical overloads on const). Furthermore, foo cannot type check without inout(const) and provide the same guarantees. Maybe my example was not illustrative enough, second try: immutable(int)[] arr = [2,3,4]; inout(const(int))[] foo(inout(int)[] x){ return uniform(0,2) ? arr : x; } inout(int)[] id(inout(int)[] x){ return x; } inout(const(int))[] bar(inout(int)[] x){ inout(int)[] y = [1,2,3]; return id(foo(y)); // !!!! } void main(){ auto a = new int[10]; auto b = bar(a); static assert(is(typeof(b)==const(int)[])); // !!! auto c = new immutable(int)[10]; immutable(int)[] d = bar(c); // !!! } foo(y) => inout(const(int))[] id(foo(y)) =>! inout(const(int))[]// has to use inout const as inout substitute! resolve inout to mutable or const: return type inout(const(int))[] => const(int)[] resolve inout to immutable: return type inout(const(int))[] => immutable(int)[]For example, this should type check: immutable(int)[] arr = [2,3,4]; inout(const(int))[] foo(inout(int)[] x){ return uniform(0,2) ? arr : x; } inout(int)[] id(inout(int)[] x){ return x; } inout(int)[] bar(inout(int)[] x){ inout(int)[] y = [1,2,3]; inout(const(int))[] z = id(foo(y)); // inout const substitute for inout return z~x; }This typechecks even if we don't have inout(const) as an option: [snip.]Besides, you are assuming here that the lvalue side of the expression plays a role in determining the inout match. It doesn't. The return type is solely determined by the argument expressions.I don't see why you think I am.Nobody. You claimed that the overload resolution rules are good enough to uniquely determine what inout should resolve to in all cases, and I provided a minimal counter-example. If you want something a little bit more concrete, consider this: immutable(int)[] foo(immutable(int)[] x, float f){return x;} int[] foo(int[] x, float f){return x;} void main(){foo([1,2,3],4);} // error I am becoming quite confident that the overload resolution rules are the culprit. A parameter type should be considered more specialized than another one if it better matches the argument type. That would render the disambiguation steps in my sketch unnecessary.There are indeed corner cases, for example: void foo(immutable(int) x, float y){} void foo(const(int) x, float y){} void foo(int x, float y){} void main(){foo(1,1);} // error, matches all threeThis case has no relevance, there is no inout return value. Who cares what inout resolves to?Not to be nit picky, but we should consider that any polysemous value type is not going to play a vital role in an inout function, since it's implicitly convertible to any modifier. It's references or contained references which make a difference.I was making a statement about the overload resolution rules. Those also apply to reference types.If it comes down to supporting this, choosing any inout match arbitrarily is good enough.In this exact case, yes. In general, no. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Feb 03 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #13 from Steven Schveighoffer <schveiguy yahoo.com> 2012-02-03 15:46:53 PST --- Once again you are right Timon! I was neglecting to see in the original example that the call was id(foo(y)) instead of just id(y)! I sometimes cannot penetrate your inadvertent obfuscation :) Your original example was (should have been) sufficient. I see now that inout(const) must be a consideration for substituting inout. Now, regarding overload resolution as it pertains to resolving what inout means, I still think a simple algorithm as I previously stated should work. We just need to add inout(const) as a possible substitute. I think the ordering should be something like: mutable, immutable, inout, inout(const), const Note that inout and inout(const) are the local version (not the wildcard version). Also note that mutable, immutable, and inout could be in any order. The algorithm I stated earlier needs to be modified. The easiest way to state it is, try each of the above substitutes for inout in order, and the first one that type-checks, wins. I think it works because there are no cycles in the implicit conversion graph: mutable ----+------------------+ | | immutable --+--> inout(const) -+-> const | | inout ------+------------------+ But I don't see why we need complex overload rules as you stated. Can you show a counter-example to my simple design? I'd like to keep things simple, because inout is already difficult to understand and making it simple to explain is a huge benefit. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Feb 03 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #14 from Steven Schveighoffer <schveiguy yahoo.com> 2012-02-03 15:59:19 PST --- Er... messed up that graph a smidge (In reply to comment #13)mutable -----------------------+ | immutable --+--> inout(const) -+-> const | | inout ------+------------------+-- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Feb 03 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #15 from Steven Schveighoffer <schveiguy yahoo.com> 2012-02-03 16:04:11 PST --- (In reply to comment #14)Er... messed up that graph a smidge (In reply to comment #13)Gah! I suck at ASCII graphs, immutable converts to const too, but you get my point though :) -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------mutable -----------------------+ | immutable --+--> inout(const) -+-> const | | inout ------+------------------+
Feb 03 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #16 from timon.gehr gmx.ch 2012-02-03 16:26:32 PST --- (In reply to comment #13)Once again you are right Timon! I was neglecting to see in the original example that the call was id(foo(y)) instead of just id(y)! I sometimes cannot penetrate your inadvertent obfuscation :) Your original example was (should have been) sufficient.OK. What measures can I take to less obfuscate my code? =)I see now that inout(const) must be a consideration for substituting inout. Now, regarding overload resolution as it pertains to resolving what inout means, I still think a simple algorithm as I previously stated should work. We just need to add inout(const) as a possible substitute. I think the ordering should be something like: mutable, immutable, inout, inout(const), const Note that inout and inout(const) are the local version (not the wildcard version). Also note that mutable, immutable, and inout could be in any order. The algorithm I stated earlier needs to be modified. The easiest way to state it is, try each of the above substitutes for inout in order, and the first one that type-checks, wins. I think it works because there are no cycles in the implicit conversion graph: mutable -----------------------+ | immutable --+--> inout(const) -+-> const | | inout ------+------------------+ But I don't see why we need complex overload rules as you stated. Can you show a counter-example to my simple design?I think what you propose would work. But the overload rule I want to add (see issue 7431) is actually quite intuitive and I think it is a good move to make the overload rules consistent enough so that we could re-use them for inout matching. It benefits code that does not use inout too. Not fixing the overload rules would result in inout being _more_ powerful than three overloads => inout could not be replaced by three overloads transparently, if it was later determined that the different const versions need different function bodies. immutable(int)[] foo(immutable(int)[] x,float){return x;} const(int)[] foo(const(int)[] x, float){return x;} int[] foo(int[] x,float){return x;} inout(int)[] bar(inout(int)[] x,float){return x;} void main(){ foo([1,2,3],4); // would not work bar([1,2,3],4); // would work! }I'd like to keep things simple, because inout is already difficult to understand and making it simple to explain is a huge benefit.I think 'create pseudo-overloads and use the rules for overloading' is simplest (with the additional overloading rule, so that it works), but what you propose should work too. The only difference between using the repaired overload resolution and your proposal I can see is that yours introduces possible disambiguation in the case of multiple alias this. I don't know if this is good or bad (my initial proposal had the same characteristic). inout(int)[] foo(inout(int)[] x){return x;} class C{ int[] x(){writeln("x!");return new int[10];} immutable int[] y(){writeln("y!");return new immutable(int)[10];} alias x this; alias y this; } void main(){ C c = new C; foo(c); // should this work or fail? } -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Feb 03 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #17 from Steven Schveighoffer <schveiguy yahoo.com> 2012-02-03 16:54:12 PST --- (In reply to comment #16)(In reply to comment #13)hehe, use temporaries to demonstrate what types should be. Or more inline comments maybe. I just missed seeing the double call in one expression. For example, this could have been more effective: // your definition for foo inout(int)[] id(inout(int)[] x) { return x;} inout(const(int))[] bar(inout(int)[] x) { inout(const(int))[] tmp = foo(x); return id(tmp); // need to be able to call }Once again you are right Timon! I was neglecting to see in the original example that the call was id(foo(y)) instead of just id(y)! I sometimes cannot penetrate your inadvertent obfuscation :) Your original example was (should have been) sufficient.OK. What measures can I take to less obfuscate my code? =)I think what you propose would work. But the overload rule I want to add (see issue 7431) is actually quite intuitive and I think it is a good move to make the overload rules consistent enough so that we could re-use them for inout matching. It benefits code that does not use inout too. Not fixing the overload rules would result in inout being _more_ powerful than three overloads => inout could not be replaced by three overloads transparently, if it was later determined that the different const versions need different function bodies.inout is *already* more powerful. It guarantees no molestation, even for mutable args. But I see your point. I'm not opposed to fixing both, but this way of explaining inout is simple to me, and to someone who doesn't want to get into the complexities of understanding overload resolution. In other words, one doesn't have to be able to understand overload resolution to understand inout. Consequently, if the way it gets implemented is that overload resolution is fixed, and then inout uses that, it's not any different, but it's easier to explain this way (IMO).The only difference between using the repaired overload resolution and your proposal I can see is that yours introduces possible disambiguation in the case of multiple alias this. I don't know if this is good or bad (my initial proposal had the same characteristic). inout(int)[] foo(inout(int)[] x){return x;} class C{ int[] x(){writeln("x!");return new int[10];} immutable int[] y(){writeln("y!");return new immutable(int)[10];} alias x this; alias y this; } void main(){ C c = new C; foo(c); // should this work or fail? }I think you give me too many headaches :) My gut says this should fail, because the call is not just ambiguously typed, but what you *pass* to the call is ambiguous. Consider this less benign example: struct S { int[] x; immutable(int)[] y; alias x this; alias y this; } x and y are not just generated temporaries, so the data you pass could be different depending on the compiler choice of what order to try the first three type constructors. My rules depend on the assumption that the argument type is already decided. In the case of literals, that's ok, because an arbitrary choice doesn't change code paths, just the type of the expression. In this case, we have to cry ambiguity, and fail to compile, in the name of consistency. So how to amend my algorithm? I suppose something like: try at a minimum immutable, mutable, and inout. If more than one of these typechecks, the call is ambiguous. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Feb 03 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #18 from timon.gehr gmx.ch 2012-02-03 17:14:50 PST --- (In reply to comment #17)(In reply to comment #16)Indeed, it gives the same guarantees as const.(In reply to comment #13)hehe, use temporaries to demonstrate what types should be. Or more inline comments maybe. I just missed seeing the double call in one expression. For example, this could have been more effective: // your definition for foo inout(int)[] id(inout(int)[] x) { return x;} inout(const(int))[] bar(inout(int)[] x) { inout(const(int))[] tmp = foo(x); return id(tmp); // need to be able to call }Once again you are right Timon! I was neglecting to see in the original example that the call was id(foo(y)) instead of just id(y)! I sometimes cannot penetrate your inadvertent obfuscation :) Your original example was (should have been) sufficient.OK. What measures can I take to less obfuscate my code? =)I think what you propose would work. But the overload rule I want to add (see issue 7431) is actually quite intuitive and I think it is a good move to make the overload rules consistent enough so that we could re-use them for inout matching. It benefits code that does not use inout too. Not fixing the overload rules would result in inout being _more_ powerful than three overloads => inout could not be replaced by three overloads transparently, if it was later determined that the different const versions need different function bodies.inout is *already* more powerful. It guarantees no molestation, even for mutable args.But I see your point. I'm not opposed to fixing both, but this way of explaining inout is simple to me, and to someone who doesn't want to get into the complexities of understanding overload resolution. In other words, one doesn't have to be able to understand overload resolution to understand inout.One goal of overload resolution is to be as intuitive as possible. Few programmers who use overloading are actually intimately familiar with the rules that govern the resolution process.Consequently, if the way it gets implemented is that overload resolution is fixed, and then inout uses that, it's not any different, but it's easier to explain this way (IMO).That is probably true.What if one is a better match than the other? If it should fail I think we should use the repaired overload resolution. It has exactly the required semantics. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------The only difference between using the repaired overload resolution and your proposal I can see is that yours introduces possible disambiguation in the case of multiple alias this. I don't know if this is good or bad (my initial proposal had the same characteristic). inout(int)[] foo(inout(int)[] x){return x;} class C{ int[] x(){writeln("x!");return new int[10];} immutable int[] y(){writeln("y!");return new immutable(int)[10];} alias x this; alias y this; } void main(){ C c = new C; foo(c); // should this work or fail? }I think you give me too many headaches :) My gut says this should fail, because the call is not just ambiguously typed, but what you *pass* to the call is ambiguous. Consider this less benign example: struct S { int[] x; immutable(int)[] y; alias x this; alias y this; } x and y are not just generated temporaries, so the data you pass could be different depending on the compiler choice of what order to try the first three type constructors. My rules depend on the assumption that the argument type is already decided. In the case of literals, that's ok, because an arbitrary choice doesn't change code paths, just the type of the expression. In this case, we have to cry ambiguity, and fail to compile, in the name of consistency. So how to amend my algorithm? I suppose something like: try at a minimum immutable, mutable, and inout. If more than one of these typechecks, the call is ambiguous.
Feb 03 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #19 from Steven Schveighoffer <schveiguy yahoo.com> 2012-02-03 17:18:54 PST --- (In reply to comment #18)(In reply to comment #17)Example? How can immutable match "better" than mutable? I guess it would depend on the definition of "better". -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------try at a minimum immutable, mutable, and inout. If more than one of these typechecks, the call is ambiguous.What if one is a better match than the other? If it should fail I think we should use the repaired overload resolution. It has exactly the required semantics.
Feb 03 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7355 --- Comment #20 from timon.gehr gmx.ch 2012-02-04 10:05:05 PST --- (In reply to comment #19)(In reply to comment #18)class A{ A x()const{return new A;} alias x this; } int foo(immutable(A) x){return 1;} int foo(A x){return 2;} static assert(foo(new immutable(A)) == 1); The overload is resolved to the first function because it is an exact match whereas the second overload is a match with implicit conversions. Your proposed rules would always use the alias this when matching an inout(A) parameter, and that is probably not what is wanted. Interestingly, the compiler currently seems to explore the alias this of the alias this of the alias this... and never terminates. (Also see issue 7437). -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------(In reply to comment #17)Example? How can immutable match "better" than mutable? I guess it would depend on the definition of "better".try at a minimum immutable, mutable, and inout. If more than one of these typechecks, the call is ambiguous.What if one is a better match than the other? If it should fail I think we should use the repaired overload resolution. It has exactly the required semantics.
Feb 04 2012