D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 7318 - Cannot concatenate arrays of super- and subtype
Summary: Cannot concatenate arrays of super- and subtype
Status: REOPENED
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: All All
: P4 enhancement
Assignee: No Owner
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-01-19 01:57 UTC by timon.gehr
Modified: 2024-12-13 17:57 UTC (History)
3 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description timon.gehr 2012-01-19 01:57:30 UTC
The recent fixes regarding array covariance had some side-effects:

void main(){
    auto a = [new Object()];
    auto b = [new Exception("")];
    auto y = a~b; // error
}

This code should still work, even though Exception[] does not implicitly convert to Object[] anymore. (the code works if a is declared const)
Comment 1 Walter Bright 2012-01-19 02:19:55 UTC
To make it work would require an implicit conversion, and that implicit conversion is not valid.
Comment 2 timon.gehr 2012-01-19 02:36:35 UTC
It is valid. Exception implicitly converts to Object. Since the resulting array from the concatenation is unique, there is no chance mutable aliasing occurs (which is the only reason covariant arrays are unsound in general).

To further back up my point, this compiles, and it is not a bug:

void main(){
    auto a = [new Object()];
    auto b = [new Exception("")];
    a~=b;
}
Comment 3 Andrei Alexandrescu 2012-01-19 12:01:56 UTC
More general: a ~= b for arrays is valid if element type of b implicitly converts to element type of a.

This is an addition to the language. A good one, but a net addition. It used to work because of a related bug. Let's keep this open for now and think more about it.
Comment 4 Steven Schveighoffer 2012-01-19 15:05:30 UTC
(In reply to comment #2)
> To further back up my point, this compiles, and it is not a bug:
> 
> void main(){
>     auto a = [new Object()];
>     auto b = [new Exception("")];
>     a~=b;
> }

It only is not a bug because of the special case of arrays.  That is, even though the array function takes two mutable arguments, the compiler can deduce that neither array will be molested (same for concatenation).

The same is not true for normal functions, you could not write the append function in user code that accepted a derived array type without applying const/inout to the second argument.

I agree it's not a bug, but is an enhancement.  It would also be a good brainstorming session to figure out how to employ the same assumptions on normal functions.
Comment 5 timon.gehr 2012-01-20 07:23:55 UTC
(In reply to comment #4)
> (In reply to comment #2)
> > To further back up my point, this compiles, and it is not a bug:
> > 
> > void main(){
> >     auto a = [new Object()];
> >     auto b = [new Exception("")];
> >     a~=b;
> > }
> 
> It only is not a bug because of the special case of arrays.  That is, even
> though the array function takes two mutable arguments, the compiler can deduce
> that neither array will be molested (same for concatenation).
> 
> The same is not true for normal functions, you could not write the append
> function in user code that accepted a derived array type without applying
> const/inout to the second argument.

I could use a template to do the job:

ref A[] append(A,B)(ref A[] x, B[] y) if(is(B:A)) {
    foreach(e; y){
        x.length++;
        x[$-1] = e;
    }
    return x;
}

(it is more general than built-in append, but that could be fixed with a better constraint)

> 
> I agree it's not a bug, but is an enhancement.  It would also be a good
> brainstorming session to figure out how to employ the same assumptions on
> normal functions.

Actually it is quite simple. If final means head-const, then final(T)[] is covariant. The second argument to append is conceptually final(T)[] as are both arguments to concat. With templates, those semantics can be emulated in user code, therefore the language can be fixed consistently without introducing another qualifier.
Comment 6 Andrei Alexandrescu 2012-01-20 07:43:29 UTC
> I could use a template to do the job:
> 
> ref A[] append(A,B)(ref A[] x, B[] y) if(is(B:A)) {
>     foreach(e; y){
>         x.length++;
>         x[$-1] = e;
>     }
>     return x;
> }
> 
> (it is more general than built-in append, but that could be fixed with a better
> constraint)

This suggests that the compiler should simply translate e1 ~= e2 into .object.append(e1, e2) and let druntime take care of the rest. One up for moving decisions from the compiler to the runtime.

In case of compile-time operation, the compiler should continue doing what it now does.
Comment 7 Steven Schveighoffer 2012-01-20 07:50:06 UTC
(In reply to comment #6)
> > I could use a template to do the job:
> > 
> > ref A[] append(A,B)(ref A[] x, B[] y) if(is(B:A)) {
> >     foreach(e; y){
> >         x.length++;
> >         x[$-1] = e;
> >     }
> >     return x;
> > }
> > 
> > (it is more general than built-in append, but that could be fixed with a better
> > constraint)
> 
> This suggests that the compiler should simply translate e1 ~= e2 into
> .object.append(e1, e2) and let druntime take care of the rest. One up for
> moving decisions from the compiler to the runtime.

This would also solve the problem, but would possibly generate unnecessary template bloat.  However, inlining should take care of that problem.

I'm all for the compiler translating things into expressions that can be hooked instead of functions that can be hooked.  It makes things much more upgradable/flexible.
Comment 8 dlangBugzillaToGithub 2024-12-13 17:57:47 UTC
THIS ISSUE HAS BEEN MOVED TO GITHUB

https://github.com/dlang/dmd/issues/18400

DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB