D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 10926 - Wrong expression printed when ternary operator used as lvalue
Summary: Wrong expression printed when ternary operator used as lvalue
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: diagnostic, pull
Depends on:
Blocks:
 
Reported: 2013-08-30 04:51 UTC by bearophile_hugs
Modified: 2020-12-13 20:00 UTC (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description bearophile_hugs 2013-08-30 04:51:36 UTC
void main() {
    const(int)[] a, b;
    int[] c, d;
    (true ? a : b) ~= 10; // OK
    pragma(msg, typeof(true ? a : c)); // const(int)[]
    (true ? a : c) ~= 20; // line 6, Error: a is not an lvalue
    (true ? c : d) ~= 30; // OK
}


DMD 2.064alpha gives:

test.d(6): Error: a is not an lvalue
Comment 1 bearophile_hugs 2013-08-30 07:49:31 UTC
See also Issue 10315
Comment 2 yebblies 2013-11-12 07:20:32 UTC
No rejects-valid here, it simply prints the wrong expression.

https://github.com/D-Programming-Language/dmd/pull/2750
Comment 3 bearophile_hugs 2013-11-12 10:00:09 UTC
(In reply to comment #2)
> No rejects-valid here, it simply prints the wrong expression.
> 
> https://github.com/D-Programming-Language/dmd/pull/2750

Please help me understand. This is a reduced test code:


void main() {
    const(int)[] a;
    int[] c;
    (true ? a : c) ~= 20;
}


Before this fix it gave:
temp.d(4): Error: a is not an lvalue

Now it gives:
temp.d(4): Error: cast(const(int)[])c is not an lvalue

How is the error message improved? c is a fully mutable array.

Why isn't this output a rejects-valid still? Both a and c can be appended. The common type between a and c should be const(int)[], that is appendable.

(If I receive no good answer I'll reopen this issue later.)
Comment 4 yebblies 2013-11-12 10:14:59 UTC
(In reply to comment #3)
> (In reply to comment #2)
> > No rejects-valid here, it simply prints the wrong expression.
> > 
> > https://github.com/D-Programming-Language/dmd/pull/2750
> 
> Please help me understand. This is a reduced test code:
> 
> 
> void main() {
>     const(int)[] a;
>     int[] c;
>     (true ? a : c) ~= 20;
> }
> 
> 
> Before this fix it gave:
> temp.d(4): Error: a is not an lvalue
> 
> Now it gives:
> temp.d(4): Error: cast(const(int)[])c is not an lvalue
> 
> How is the error message improved? c is a fully mutable array.
> 
> Why isn't this output a rejects-valid still? Both a and c can be appended. The
> common type between a and c should be const(int)[], that is appendable.
> 
> (If I receive no good answer I'll reopen this issue later.)

c can be appended to, but cast(const(int)[])c cannot, because it is an rvalue.

Would this make any sense?

(cast(const(int)[])c) ~= 20;
Comment 5 bearophile_hugs 2013-11-12 10:29:09 UTC
(In reply to comment #4)

> Would this make any sense?
> 
> (cast(const(int)[])c) ~= 20;


You are probably right, but it's quite surprising:


void main() {
    const(int)[] a, b;
    int[] c, d;
    (true ? a : b) ~= 10; // OK
    (true ? c : d) ~= 20; // OK
    (true ? a : c) ~= 30; // Error: cast(const(int)[])c is not an lvalue
    if (true)
        a ~= 40; // OK
    else
        c ~= 40; // OK
}


So if both are fully mutable, or both have const items then you can append to them, but if one of them is mutable and the other has const items, then it can't append and you have to use a regular if statement.

Thank you for your answer, I'll keep this issue closed.
Comment 6 yebblies 2013-11-12 20:12:14 UTC
(In reply to comment #5)
> (In reply to comment #4)
> 
> > Would this make any sense?
> > 
> > (cast(const(int)[])c) ~= 20;
> 
> 
> You are probably right, but it's quite surprising:
> 
> 
> void main() {
>     const(int)[] a, b;
>     int[] c, d;
>     (true ? a : b) ~= 10; // OK
>     (true ? c : d) ~= 20; // OK
>     (true ? a : c) ~= 30; // Error: cast(const(int)[])c is not an lvalue
>     if (true)
>         a ~= 40; // OK
>     else
>         c ~= 40; // OK
> }
> 
> 
> So if both are fully mutable, or both have const items then you can append to
> them, but if one of them is mutable and the other has const items, then it
> can't append and you have to use a regular if statement.
> 
> Thank you for your answer, I'll keep this issue closed.


For a more involved answer:

Appending modifies length, so it must be done on lvalues.

`b ? a : c` can be transformed into an lvalue like this:
`*(b ? &a : &c)`

But if the types are different, it would have to end up as
`*(b ? &a : cast(const(int)[]*)&c)`

Which would allow
`(b ? a : c) = a;`

And that could cause 'c' to point to the data in 'a', which could be immutable.

=======================================

This could be possible, if a new re-write was introduced:

b ? a : c op ...   ->   b ? (a op ...) : (c op ...)

But I'm not entirely sure we want that.