Re: opCast in class prevents destroy
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
Re: opCast in class prevents destroy
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: ```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 Is 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'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`.
Re: opCast in class prevents destroy
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: 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. 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.
Re: opCast in class prevents destroy
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: 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. 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. ```
Re: opCast in class prevents destroy
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.
Re: opCast in class prevents destroy
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.
Re: opCast in class prevents destroy
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 compile I meant "instead", not also.
Re: opCast in class prevents destroy
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=22635 Is 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`.
opCast in class prevents destroy
```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?