Re: opCast in class prevents destroy

2022-03-01 Thread Paul Backus via Digitalmars-d-learn

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

2022-03-01 Thread Paul Backus via Digitalmars-d-learn

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

2022-03-01 Thread bauss via Digitalmars-d-learn

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

2022-03-01 Thread vit via Digitalmars-d-learn

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*));
}

Foo opCast(T, this This)()@safe
if(is(immutable T == immutable This)){
return Foo(2);
}

~this()@safe{
writeln("dtor(", i, "): ", cast(void*));
}
}

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

2022-03-01 Thread Mike Parker via Digitalmars-d-learn

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

2022-02-28 Thread bauss via Digitalmars-d-learn

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

2022-02-28 Thread Mike Parker via Digitalmars-d-learn

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

2022-02-28 Thread Mike Parker via Digitalmars-d-learn

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

2022-02-28 Thread cc via Digitalmars-d-learn

```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?