digitalmars.D.learn - opCast in class prevents destroy
- cc (35/35) Feb 28 2022 ```d
- Mike Parker (14/49) Feb 28 2022 Is it a bug? It's not documented in the `opCast` documentation,
- Mike Parker (2/4) Feb 28 2022 I meant "instead", not also.
- bauss (8/10) Feb 28 2022 Personally it doesn't make sense to me. I don't think it should
- Mike Parker (15/19) Mar 01 2022 It's two template functions like the OP used: one for T to catch
- Paul Backus (6/35) Mar 01 2022 It's a bug in druntime. `destroy` needs to reinterpret the class
- Paul Backus (2/7) Mar 01 2022 https://github.com/dlang/druntime/pull/3766
```d struct A {} class B { A opCast(T : A)() { return A(); } } void main() { auto b = new B(); destroy(b); } ``` fails with ``` dmd2\windows\bin\..\..\src\druntime\import\object.d(4209): Error: template instance `opCast!(void*)` does not match template declaration `opCast(T : A)()` main.d(9): Error: template instance `object.destroy!(true, B)` error instantiating ``` Looks like a similar bug has been reported: https://issues.dlang.org/show_bug.cgi?id=22635 As a workaround, adding an additional opCast: ```d class B { A opCast(T : A)() { return A(); } auto opCast(T)() { return cast(T)super; } } ``` SEEMS to work. Is that safe? Or are consequences not what I'm intending?
Feb 28 2022
On Tuesday, 1 March 2022 at 04:29:56 UTC, cc wrote:```d struct A {} class B { A opCast(T : A)() { return A(); } } void main() { auto b = new B(); destroy(b); } ``` fails with ``` dmd2\windows\bin\..\..\src\druntime\import\object.d(4209): Error: template instance `opCast!(void*)` does not match template declaration `opCast(T : A)()` main.d(9): Error: template instance `object.destroy!(true, B)` error instantiating ``` Looks like a similar bug has been reported: https://issues.dlang.org/show_bug.cgi?id=22635Is it a bug? It's not documented in the `opCast` documentation, but it looks like when you define an `opCast` it completely replaces the default behavior, i.e., whatever type you define as the target type becomes the only type to which you can attempt to cast. It makes sense to me, and I would say the bug is that it's not documented.As a workaround, adding an additional opCast: ```d class B { A opCast(T : A)() { return A(); } auto opCast(T)() { return cast(T)super; } } ``` SEEMS to work. Is that safe? Or are consequences not what I'm intending?So what you've done here is specialized on anything convertible to `A` and then reenabled casts to all other types, i.e., the default behavior, but with a special exception for `T:A`. You could also specialize on `void*`, as that's the type that was failing to compile. Then you're restricted to `void*` and anything convertible to `A`.
Feb 28 2022
On Tuesday, 1 March 2022 at 04:59:49 UTC, Mike Parker wrote:You could also specialize on `void*`, as that's the type that was failing to compileI meant "instead", not also.
Feb 28 2022
On Tuesday, 1 March 2022 at 04:59:49 UTC, Mike Parker wrote:It makes sense to me, and I would say the bug is that it's not documented.Personally it doesn't make sense to me. I don't think it should override default behaviors, but just add onto it, so you can add an additional cast. Right now if you want to add an additional cast then you have to implement ALL the default behaviors and then add your custom cast. That doesn't seem correct to me at least. That's not how the behavior is in most other languages either.
Feb 28 2022
On Tuesday, 1 March 2022 at 07:16:11 UTC, bauss wrote:Right now if you want to add an additional cast then you have to implement ALL the default behaviors and then add your custom cast.It's two template functions like the OP used: one for T to catch everything, and one specialization.That doesn't seem correct to me at least.Depends on your perspective I guess. For the inverse, when you want to allow only one kind of cast and prevent everything else, you only have to implement one template right now. If that were not the case, then you'd have to implement an additional catch-all template that bombs out with a static assert. So either way makes sense, IMO. Though I totally understand how the current behavior can be a surprise when people expect it to behave like, e.g., C++. But D is not C++. So is `opCast` intended to expand the list of target types (like C++), or is it intended to define it? The spec says, "To define how one type can be cast to another", which doesn't really answer the question.
Mar 01 2022
On Tuesday, 1 March 2022 at 08:16:13 UTC, Mike Parker wrote:On Tuesday, 1 March 2022 at 07:16:11 UTC, bauss wrote:Now is possible this: ```d import std.stdio; struct Foo{ int i; this(int i) safe{ this.i = i; writeln("ctor(", i, "): ", cast(void*)&this); } Foo opCast(T, this This)() safe if(is(immutable T == immutable This)){ return Foo(2); } ~this() safe{ writeln("dtor(", i, "): ", cast(void*)&this); } } struct Bar{ const Foo foo; this(int i) safe{ this.foo = Foo(i); } } void main() safe{ Bar bar = Bar(1); } ``` Result: ```d ctor(1): 7FFE0D5A94A8 //dtor for Foo(1) is never called. ctor(2): 7FFE0D5A9410 dtor(2): 7FFE0D5A9470 dtor(2): 7FFE0D5A9470 //dtor for Foo(2) is called twice. ```Right now if you want to add an additional cast then you have to implement ALL the default behaviors and then add your custom cast.It's two template functions like the OP used: one for T to catch everything, and one specialization.That doesn't seem correct to me at least.Depends on your perspective I guess. For the inverse, when you want to allow only one kind of cast and prevent everything else, you only have to implement one template right now. If that were not the case, then you'd have to implement an additional catch-all template that bombs out with a static assert. So either way makes sense, IMO. Though I totally understand how the current behavior can be a surprise when people expect it to behave like, e.g., C++. But D is not C++. So is `opCast` intended to expand the list of target types (like C++), or is it intended to define it? The spec says, "To define how one type can be cast to another", which doesn't really answer the question.
Mar 01 2022
On Tuesday, 1 March 2022 at 08:16:13 UTC, Mike Parker wrote:On Tuesday, 1 March 2022 at 07:16:11 UTC, bauss wrote:Yes of course it's a matter of perspective. I think the solution would be to have two functions for opCast, maybe something like opAdditionalCast, idk, not to break current behavior I guess.Right now if you want to add an additional cast then you have to implement ALL the default behaviors and then add your custom cast.It's two template functions like the OP used: one for T to catch everything, and one specialization.That doesn't seem correct to me at least.Depends on your perspective I guess. For the inverse, when you want to allow only one kind of cast and prevent everything else, you only have to implement one template right now. If that were not the case, then you'd have to implement an additional catch-all template that bombs out with a static assert. So either way makes sense, IMO. Though I totally understand how the current behavior can be a surprise when people expect it to behave like, e.g., C++. But D is not C++. So is `opCast` intended to expand the list of target types (like C++), or is it intended to define it? The spec says, "To define how one type can be cast to another", which doesn't really answer the question.
Mar 01 2022
On Tuesday, 1 March 2022 at 04:59:49 UTC, Mike Parker wrote:On Tuesday, 1 March 2022 at 04:29:56 UTC, cc wrote:It's a bug in druntime. `destroy` needs to reinterpret the class reference as a `void*` to pass it to `rt_finalize`: https://github.com/dlang/druntime/blob/v2.098.1/src/object.d#L4209 However, `cast(void*)` is not the correct way to do this, because it fails in the presence of `opCast`.```d struct A {} class B { A opCast(T : A)() { return A(); } } void main() { auto b = new B(); destroy(b); } ``` fails with ``` dmd2\windows\bin\..\..\src\druntime\import\object.d(4209): Error: template instance `opCast!(void*)` does not match template declaration `opCast(T : A)()` main.d(9): Error: template instance `object.destroy!(true, B)` error instantiating ``` Looks like a similar bug has been reported: https://issues.dlang.org/show_bug.cgi?id=22635Is it a bug? It's not documented in the `opCast` documentation, but it looks like when you define an `opCast` it completely replaces the default behavior, i.e., whatever type you define as the target type becomes the only type to which you can attempt to cast.
Mar 01 2022
On Tuesday, 1 March 2022 at 16:40:50 UTC, Paul Backus wrote:It's a bug in druntime. `destroy` needs to reinterpret the class reference as a `void*` to pass it to `rt_finalize`: https://github.com/dlang/druntime/blob/v2.098.1/src/object.d#L4209 However, `cast(void*)` is not the correct way to do this, because it fails in the presence of `opCast`.https://github.com/dlang/druntime/pull/3766
Mar 01 2022