www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Limits of implicit conversion of class arrays

reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
Is there a reason why

```d
class Base {}
class Derived : Base {}

 safe pure nothrow unittest {
	Base b;
	Derived d;
	b = d; // pass

	Base[] bs;
	Derived[] ds;
	bs ~= ds; // pass
	bs = ds; // fail [1], should pass
	bs = cast(Base[])ds; // fail [2], should pass
}
```

fails as

[1]: cannot implicitly convert expression `ds` of type 
`Derived[]` to `Base[]`
[2]: cast from `Derived[]` to `Base[]` not allowed in safe code

?
Mar 23
parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On Saturday, 23 March 2024 at 09:08:45 UTC, Per Nordlöw wrote:
 Is there a reason why

 ```d
 class Base {}
 class Derived : Base {}

  safe pure nothrow unittest {
 	Base b;
 	Derived d;
 	b = d; // pass

 	Base[] bs;
 	Derived[] ds;
 	bs ~= ds; // pass
 	bs = ds; // fail [1], should pass
 	bs = cast(Base[])ds; // fail [2], should pass
 }
 ```

 fails as

 [1]: cannot implicitly convert expression `ds` of type 
 `Derived[]` to `Base[]`
 [2]: cast from `Derived[]` to `Base[]` not allowed in safe code

 ?
The first and second is unsound (infamously allowed in Java). Once you cast the slice you can populate it with Derived2 objects that are not Derived, hence breaking type safety of the ds slice. — Dmitry Olshansky CEO Glow labs https://olshansky.me
Mar 23
next sibling parent FeepingCreature <feepingcreature gmail.com> writes:
On Saturday, 23 March 2024 at 11:04:04 UTC, Dmitry Olshansky 
wrote:
 On Saturday, 23 March 2024 at 09:08:45 UTC, Per Nordlöw wrote:
 Is there a reason why

 ```d
 class Base {}
 class Derived : Base {}

  safe pure nothrow unittest {
 	Base b;
 	Derived d;
 	b = d; // pass

 	Base[] bs;
 	Derived[] ds;
 	bs ~= ds; // pass
 	bs = ds; // fail [1], should pass
 	bs = cast(Base[])ds; // fail [2], should pass
 }
 ```

 fails as

 [1]: cannot implicitly convert expression `ds` of type 
 `Derived[]` to `Base[]`
 [2]: cast from `Derived[]` to `Base[]` not allowed in safe code

 ?
The first and second is unsound (infamously allowed in Java). Once you cast the slice you can populate it with Derived2 objects that are not Derived, hence breaking type safety of the ds slice. — Dmitry Olshansky CEO Glow labs https://olshansky.me
Note that it works if the classes are const: ```d const(Base)[] bs; const(Derived)[] ds; bs ~= ds; // pass bs = ds; // pass bs = cast(const(Base)[])ds; // pass ``` Exactly because you can't replace existing entries.
Mar 23
prev sibling parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Saturday, 23 March 2024 at 11:04:04 UTC, Dmitry Olshansky 
wrote:
 The first and second is unsound (infamously allowed in Java).
In the general case, yes. But, do you see any errors with the code ```d class Base {} class Derived : Base {} safe pure nothrow unittest { Base b; Derived d; b = d; // pass Base[] bs; Derived[] ds; bs ~= ds; // pass bs = ds; // fail [1], should pass bs = cast(Base[])ds; // fail [2], should pass } ```
 Once you cast the slice you can populate it with Derived2 
 objects that are not Derived, hence breaking type safety of the 
 ds slice.
Again, in the general case, yes. So what is different in this code example compared to the general case? Hint: this has overlaps with a missing compiler optimization in dmd (and many other statically typed languages) enabled by a specific kind of data flow analysis. Which one?
Mar 25
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On Monday, 25 March 2024 at 07:16:35 UTC, Per Nordlöw wrote:
 On Saturday, 23 March 2024 at 11:04:04 UTC, Dmitry Olshansky 
 wrote:
 The first and second is unsound (infamously allowed in Java).
In the general case, yes. But, do you see any errors with the code ```d class Base {} class Derived : Base {} safe pure nothrow unittest { Base b; Derived d; b = d; // pass Base[] bs; Derived[] ds; bs ~= ds; // pass bs = ds; // fail [1], should pass bs = cast(Base[])ds; // fail [2], should pass } ```
Yes, it's unsafe, as you can replace an element of `ds` with something that has no relation to `Derived`.
 Once you cast the slice you can populate it with Derived2 
 objects that are not Derived, hence breaking type safety of 
 the ds slice.
Again, in the general case, yes. So what is different in this code example compared to the general case? Hint: this has overlaps with a missing compiler optimization in dmd (and many other statically typed languages) enabled by a specific kind of data flow analysis. Which one?
If there is a way to end up with a `Derived` reference to point at something that is not a `Derived` *without a cast* in system code, or *even with a cast* in safe code, then it is an error. It doesn't matter if you aren't actually doing it. If you know you are not making that mistake, change it to system, and cast to inform the compiler that you "know what you are doing". -Steve
Mar 27
parent Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Thursday, 28 March 2024 at 01:53:52 UTC, Steven Schveighoffer 
wrote:
 ```d
 class Base {}
 class Derived : Base {}

  safe pure nothrow unittest {
 	Base b;
 	Derived d;
 	b = d; // pass

 	Base[] bs;
 	Derived[] ds;
 	bs ~= ds; // pass
 	bs = ds; // fail [1], should pass
 	bs = cast(Base[])ds; // fail [2], should pass
 }
 ```
Yes, it's unsafe, as you can replace an element of `ds` with something that has no relation to `Derived`.
This is a suggested change that when _applied_ will make the code unsafe yes. But the code in its current form is safe and the compiler could be extended to prove it.
Mar 31