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 by bugzilla — 2012-01-19T02:19:55Z
To make it work would require an implicit conversion, and that implicit conversion is not valid.
Comment #2 by timon.gehr — 2012-01-19T02:36:35Z
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 by andrei — 2012-01-19T12:01:56Z
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 by schveiguy — 2012-01-19T15:05:30Z
(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 by timon.gehr — 2012-01-20T07:23:55Z
(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 by andrei — 2012-01-20T07:43:29Z
> 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 by schveiguy — 2012-01-20T07:50:06Z
(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 by robert.schadek — 2024-12-13T17:57:47Z