Thread overview
Struct ctor called with cast
Feb 27, 2018
Radu
Feb 27, 2018
ag0aep6g
Feb 27, 2018
Radu
Feb 27, 2018
ag0aep6g
Feb 27, 2018
Radu
February 27, 2018
I have this:

>>>
enum Type { a };
struct S(Type t = Type.a)
{
    this(Type)(Type t)
    {
        import std.stdio;
        writeln("ctor called.");
    }
}
void main()
{
   auto x = S!(Type.a)(Type.a);
   void* y = &x;
   auto z = (cast(S!(Type.a)) y);
}
>>>

Surprisingly the cast will actually call the ctor. Is this to be expected? Sure looks like a bug to me, as a non templated S will complain about the cast.
February 27, 2018
On 02/27/2018 09:30 PM, Radu wrote:
>>>>
> enum Type { a };
> struct S(Type t = Type.a)
> {
>      this(Type)(Type t)
>      {
>          import std.stdio;
>          writeln("ctor called.");
>      }
> }
> void main()
> {
>     auto x = S!(Type.a)(Type.a);
>     void* y = &x;
>     auto z = (cast(S!(Type.a)) y);
> }
>>>>
> 
> Surprisingly the cast will actually call the ctor. Is this to be expected? Sure looks like a bug to me, as a non templated S will complain about the cast.

Not a bug. The spec says [1]: "Casting a value v to a struct S, when value is not a struct of the same type, is equivalent to: S(v)"

Templates have nothing to do with it. Your code boils down to this:

----
struct S
{
    this(void* t)
    {
        import std.stdio;
        writeln("ctor called.");
    }
}
void main()
{
   void* y;
   auto z = cast(S) y;
}
----


[1] https://dlang.org/spec/expression.html#cast_expressions
February 27, 2018
On Tuesday, 27 February 2018 at 20:51:25 UTC, ag0aep6g wrote:
> On 02/27/2018 09:30 PM, Radu wrote:
>>>>>
>> enum Type { a };
>> struct S(Type t = Type.a)
>> {
>>      this(Type)(Type t)
>>      {
>>          import std.stdio;
>>          writeln("ctor called.");
>>      }
>> }
>> void main()
>> {
>>     auto x = S!(Type.a)(Type.a);
>>     void* y = &x;
>>     auto z = (cast(S!(Type.a)) y);
>> }
>>>>>
>> 
>> Surprisingly the cast will actually call the ctor. Is this to be expected? Sure looks like a bug to me, as a non templated S will complain about the cast.
>
> Not a bug. The spec says [1]: "Casting a value v to a struct S, when value is not a struct of the same type, is equivalent to: S(v)"
>
> Templates have nothing to do with it. Your code boils down to this:
>
> ----
> struct S
> {
>     this(void* t)
>     {
>         import std.stdio;
>         writeln("ctor called.");
>     }
> }
> void main()
> {
>    void* y;
>    auto z = cast(S) y;
> }
> ----
>
>
> [1] https://dlang.org/spec/expression.html#cast_expressions

OK, got it - thanks.

But this:

>>>
struct S
{
    this(int t)
    {
        import std.stdio;
        writeln("ctor called.");
    }
}
void main()
{
   auto x = S(1);
   void* y = &x;
   auto z = (cast(S) y);
}
>>>

Produces:
Error: cannot cast expression y of type void* to S

Which is kinda correct as I don't have any ctor in S taking a void*.

Adding

>>>

    this(void* t)
    {
        import std.stdio;
        writeln("ctor called.");
    }
>>>

Will make the error go away.


So the bug is that somehow the templated version makes it so there is an implicit void* ctor.
February 27, 2018
On 02/27/2018 09:59 PM, Radu wrote:
> On Tuesday, 27 February 2018 at 20:51:25 UTC, ag0aep6g wrote:
>> On 02/27/2018 09:30 PM, Radu wrote:
[...]
>>> enum Type { a };
>>> struct S(Type t = Type.a)
>>> {
>>>      this(Type)(Type t)
>>>      {
>>>          import std.stdio;
>>>          writeln("ctor called.");
>>>      }
>>> }
[...]
> So the bug is that somehow the templated version makes it so there is an implicit void* ctor.

In your original code (quoted above), you've got a templated constructor. The `Type` in `this(Type)(Type t)` is not the enum. It's a template parameter of the constructor.

To get a non-templated constructor that takes a `Type` (the enum), you have to write:

----
this(Type t) /* NOTE: Only one set of parentheses. */
{
    /* ... */
}
----
February 27, 2018
On 2/27/18 3:59 PM, Radu wrote:
> On Tuesday, 27 February 2018 at 20:51:25 UTC, ag0aep6g wrote:
>> On 02/27/2018 09:30 PM, Radu wrote:
>>>>>>
>>> enum Type { a };
>>> struct S(Type t = Type.a)
>>> {
>>>      this(Type)(Type t)
>>>      {
>>>          import std.stdio;
>>>          writeln("ctor called.");
>>>      }
>>> }
>>> void main()
>>> {
>>>     auto x = S!(Type.a)(Type.a);
>>>     void* y = &x;
>>>     auto z = (cast(S!(Type.a)) y);
>>> }

[snip]

> So the bug is that somehow the templated version makes it so there is an implicit void* ctor.

Look at your constructor. You actually have a TEMPLATED constructor inside a TEMPLATED type.

In other words, inside your constructor, `Type` is not an enum Type, it's actually a void *.

It becomes clearer if you change the name of the second template parameter:

struct S(Type t = Type.a)
{
   this(T)(T t)
   {
       import std.stdio;
       writeln("ctor called.");
   }
}

-Steve
February 27, 2018
On Tuesday, 27 February 2018 at 21:04:59 UTC, ag0aep6g wrote:
> On 02/27/2018 09:59 PM, Radu wrote:
>> On Tuesday, 27 February 2018 at 20:51:25 UTC, ag0aep6g wrote:
>>> On 02/27/2018 09:30 PM, Radu wrote:
> [...]
>>>> [...]
> [...]
>> So the bug is that somehow the templated version makes it so there is an implicit void* ctor.
>
> In your original code (quoted above), you've got a templated constructor. The `Type` in `this(Type)(Type t)` is not the enum. It's a template parameter of the constructor.
>
> To get a non-templated constructor that takes a `Type` (the enum), you have to write:
>
> ----
> this(Type t) /* NOTE: Only one set of parentheses. */
> {
>     /* ... */
> }
> ----

Understood, make sense now, thanks!