www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 7355] New: inout incorrectly resolved if the same type has both mutable and immutable parts

reply d-bugmail puremagic.com writes:
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



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
next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355




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
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355


Steven Schveighoffer <schveiguy yahoo.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |schveiguy yahoo.com



07:56:53 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'.
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
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355






 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.
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: -------
Jan 26 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355





 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
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355




09:36:01 PST ---


 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
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355






 
 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.
No.
 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
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355




10:21:28 PST ---


 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.
Oh yeah :) Stupid me, for some reason in my head this made sense because there was a mutable part.
 
 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.
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: -------
Jan 26 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355




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
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355




12:07:29 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)
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
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355






 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)) 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; }
 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
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355




03:24:52 PST ---

 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 three
This 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
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355






 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.]
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)[]
 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.
 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
This case has no relevance, there is no inout return value. Who cares what inout resolves to?
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.
 
 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
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355




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
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355




15:59:19 PST ---
Er... messed up that graph a smidge


 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
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355




16:04:11 PST ---

 Er... messed up that graph a smidge
 

 mutable -----------------------+
                                |
 immutable --+--> inout(const) -+-> const 
             |                  |
 inout ------+------------------+
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: -------
Feb 03 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355





 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
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355




16:54:12 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.
 
OK. What measures can I take to less obfuscate my code? =)
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 }
 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
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355







 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? =)
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 }
 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.
Indeed, it gives the same guarantees as const.
 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.
 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.
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: -------
Feb 03 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355




17:18:54 PST ---


 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.
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: -------
Feb 03 2012
prev sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=7355







 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.
Example? How can immutable match "better" than mutable? I guess it would depend on the definition of "better".
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: -------
Feb 04 2012