Re: Struct copy constructor with inout

2023-11-16 Thread Nick Treleaven via Digitalmars-d-learn

On Tuesday, 14 November 2023 at 13:58:17 UTC, Paul Backus wrote:
On Tuesday, 14 November 2023 at 13:41:32 UTC, Steven 
Schveighoffer wrote:

```
Error: copy constructor `testinoutctor.S1.this(ref const(S1) 
s) const` is not callable using argument types `(const(S1))`

```

I'm not sure what this means. There shouldn't be a copy being 
made here, as the thing is already const. I don't understand 
this error, and it looks like a bug to me.


The error is saying that the copy constructor expects a `const` 
`this` argument, but you're passing a mutable `this` argument.


Thanks for explaining, it's a confusing error.

The error when the constructor is changed to be immutable is:
```
Error: `immutable` method `immutable_ctor.S1.this` is not 
callable using a mutable object

```
(I've made a fix to say constructor instead of method for that.)
Now I've made a fix to produce a similar error for const:
https://issues.dlang.org/show_bug.cgi?id=24248


Re: Struct copy constructor with inout

2023-11-14 Thread dhs via Digitalmars-d-learn

On Tuesday, 14 November 2023 at 16:51:07 UTC, Paul Backus wrote:


There's no assignment. The value is constructed in-place, in 
`ss2`'s memory.


The reason the compiler allows you to construct a `const(S2)` 
value inside of an `S2` variable is that `const(S2)` implicitly 
converts to `S2`.



On Tuesday, 14 November 2023 at 16:58:25 UTC, Steven 
Schveighoffer wrote:


That being said, I still consider this a bug, if the inout 
version works, the const version should work as well. I don't 
see the difference.




Ok so to summarize:
- "inout" and "const" behaving differently here is probably 
wrong, and
- in structs without reference fields, a const constructor can be 
used to construct mutable values, due to "implicit qualifier 
conversions".


Thanks.


Re: Struct copy constructor with inout

2023-11-14 Thread Steven Schveighoffer via Digitalmars-d-learn

On Tuesday, 14 November 2023 at 13:58:17 UTC, Paul Backus wrote:

It's easier to see if you compare the actual and expected 
argument lists side-by-side


Expected: (ref const(S1) s) const
Actual:   (const(S1)  )
^
 Mismatched 'this' argument


This would be a much better output. Is this something you made up 
or did you get it from one of the compilers? LDC2, which is what 
I tested with, reported in the format that I mentioned.


It might be something to add to the compiler that mismatches in 
`this` qualifiers should be reported separately, especially since 
the mutable form has no explicit qualifier. But I guess this is 
only an issue for constructors, because normal const functions 
can be called with mutable objects.


That being said, I still consider this a bug, if the inout 
version works, the const version should work as well. I don't see 
the difference.


So an interesting thing, if I change the `int i` to `int *i` in 
`S2`, instead of compiling I get the error:


```
Error: `inout` constructor `testinoutctor.S2.this` creates const 
object, not mutable

```

Which gives a much nicer error message!

-Steve


Re: Struct copy constructor with inout

2023-11-14 Thread dhs via Digitalmars-d-learn

On Tuesday, 14 November 2023 at 14:58:21 UTC, Paul Backus wrote:


```d
struct S2
{
int* p;
this(const int* p) const
{
// Ok - counts as initialization
this.p = p;
}
}

immutable int answer = 42;

void main()
{
S2 s2;
// If this were allowed to compile...
s2.__ctor(&answer);
// ...then this could happen
*s2.p = 12345;
}
```


Thanks for this explanation, it all makes perfect sense.

Regarding implicit qualifier conversion: in my code, S2 has a 
copy constructor, so it takes a "ref inout S2" as input. Since 
the copy constructor is in itself qualified as inout, the 
implicit "this" parameter is "ref inout S2" too.


We then have "const(S2) s2;" and "S2 ss2 = s2;". The implicit 
qualifier conversions *for references* do not allow conversion 
from "ref const(S2)" to "ref S2", so I assume that the "inout" in 
the copy constructor translates to "const". In that case, the 
copy constructor creates a "const S2", not an S2.


Does this "const S2" then get implicitly converted to "S2", 
before being assigned to "ss2"? I tried adding an opAssign() to 
S2 - it's not getting called. So I'm not sure what is actually 
going on here.




Re: Struct copy constructor with inout

2023-11-14 Thread Paul Backus via Digitalmars-d-learn

On Tuesday, 14 November 2023 at 14:36:57 UTC, dhs wrote:
Just to clarify some more: isn't "s1 = ss1" similar to 
something like:


```d
const(S1) s1;
S1 ss1; // ss1 is now S1.init
S1_copy_construct_const_in_const_out(ss1, s1);
```

If this is the case, the compile error is expected, but 
why/how/where do "implicit qualifer conversions" apply here?


The real answer is that constructors are special, and constructor 
calls follow different type-checking rules from normal function 
calls.


Here's an example:

```d
struct S
{
int n;
this(int n) const { this.n = n; }
void fun() const {}
}

void main()
{
S s;
s.__ctor(123); // error
s.fun(); // ok
}

```

Normally, it's fine to call a `const` method on a mutable object, 
because mutable implicitly converts to `const`. But when that 
method is a constructor, it's not allowed.


Why? Because constructors have a special "superpower" that no 
other functions in D have: they're allowed to write to `const` 
and `immutable` variables. This is documented in the spec under 
["Field initialization inside a constructor"][1].


If you could call a `const` constructor on a mutable object, it 
would be possible to use that constructor to violate the type 
system by storing a pointer to `immutable` data in a mutable 
field:


```d
struct S2
{
int* p;
this(const int* p) const
{
// Ok - counts as initialization
this.p = p;
}
}

immutable int answer = 42;

void main()
{
S2 s2;
// If this were allowed to compile...
s2.__ctor(&answer);
// ...then this could happen
*s2.p = 12345;
}
```

[1]: https://dlang.org/spec/struct.html#field-init


Re: Struct copy constructor with inout

2023-11-14 Thread dhs via Digitalmars-d-learn

On Tuesday, 14 November 2023 at 14:36:57 UTC, dhs wrote:

Just to clarify some more: isn't "s1 = ss1" similar to

I meant "ss1 = s1" here, sorry.


Re: Struct copy constructor with inout

2023-11-14 Thread dhs via Digitalmars-d-learn

On Tuesday, 14 November 2023 at 13:58:17 UTC, Paul Backus wrote:

On Tuesday, 14 November 2023 at 13:41:32 UTC, Steven

The error is saying that the copy constructor expects a `const` 
`this` argument, but you're passing a mutable `this` argument.




Thanks you both very much for answering.

Just to clarify some more: isn't "s1 = ss1" similar to something 
like:


```d
const(S1) s1;
S1 ss1; // ss1 is now S1.init
S1_copy_construct_const_in_const_out(ss1, s1);
```

If this is the case, the compile error is expected, but 
why/how/where do "implicit qualifer conversions" apply here?


Thanks again,
dhs


Re: Struct copy constructor with inout

2023-11-14 Thread Paul Backus via Digitalmars-d-learn
On Tuesday, 14 November 2023 at 13:41:32 UTC, Steven 
Schveighoffer wrote:

```
Error: copy constructor `testinoutctor.S1.this(ref const(S1) s) 
const` is not callable using argument types `(const(S1))`

```

I'm not sure what this means. There shouldn't be a copy being 
made here, as the thing is already const. I don't understand 
this error, and it looks like a bug to me.


The error is saying that the copy constructor expects a `const` 
`this` argument, but you're passing a mutable `this` argument.


It's confusing because (a) the `this` argument is hidden and 
doesn't appear in the parameter list, and (b) there's no explicit 
"mutable" qualifier. So when it prints out the list of argument 
types that were actually passed, the `this` argument (`S1 ss1`) 
gets printed as an empty string.


It's easier to see if you compare the actual and expected 
argument lists side-by-side


Expected: (ref const(S1) s) const
Actual:   (const(S1)  )
^
 Mismatched 'this' argument


Re: Struct copy constructor with inout

2023-11-14 Thread Paul Backus via Digitalmars-d-learn

On Tuesday, 14 November 2023 at 08:50:34 UTC, dhs wrote:

```d
struct S2
{
this(ref inout S2 s) inout { writeln("copy"); }
int i;
}

void test()
{
const(S1) s1;
S1 ss1 = s1; // error, ss1 not qualified as const

const(S2) s2;
S2 ss2 = s2; // fine, why?
}
```

Isn't "inout" supposed to copy the const-ness of its parameter 
to the constructor's attribute? In other words: why doesn't 
ss2=s2 fail here?


Because `S2` is a pure value type and contains no pointers or 
references, the compiler allows `const(S2)` to implicitly convert 
to `S2`. This is documented in the spec under ["Implicit 
Qualifier Conversions"][1], and you can verify it with a `static 
assert`:


```d
static assert(is(const(S2) : S2)); // ok
```

In order to prevent this conversion, you can replace `int i` with 
`int* p`:


```d
struct S3
{
this(ref inout S2 s) inout { writeln("copy"); }
int *p;
}

void test()
{
const(S3) s3;
S3 ss3 = s3;
// Error: cannot implicitly convert expression
// `s3` of type `const(S3)` to `S3`
}
```

[1]: 
https://dlang.org/spec/const3.html#implicit_qualifier_conversions


Re: Struct copy constructor with inout

2023-11-14 Thread Steven Schveighoffer via Digitalmars-d-learn

On Tuesday, 14 November 2023 at 08:50:34 UTC, dhs wrote:


I am using following code:

```d
struct S1
{
this(ref const S1 s) const { writeln("copy"); }
int i;
}

struct S2
{
this(ref inout S2 s) inout { writeln("copy"); }
int i;
}

void test()
{
const(S1) s1;
S1 ss1 = s1; // error, ss1 not qualified as const

const(S2) s2;
S2 ss2 = s2; // fine, why?
}
```

Isn't "inout" supposed to copy the const-ness of its parameter 
to the constructor's attribute? In other words: why doesn't 
ss2=s2 fail here?



`ss2 = s2` does not fail because the type is implicitly 
convertible to non-const (a const int can be converted to a 
mutable int). Change `i` to `int *` and it will fail.


IMO, the first should succeed as well. And I will note that the 
error looks different from what you say:


```
Error: copy constructor `testinoutctor.S1.this(ref const(S1) s) 
const` is not callable using argument types `(const(S1))`

```

I'm not sure what this means. There shouldn't be a copy being 
made here, as the thing is already const. I don't understand this 
error, and it looks like a bug to me.


-Steve


Re: Struct copy constructor with inout

2023-11-14 Thread dhs via Digitalmars-d-learn
On Tuesday, 14 November 2023 at 09:07:24 UTC, Alexandru Ermicioi 
wrote:


Seems like it isn't called at all, your copy constructor with 
inout. Could be a bug.


My assumption is that default copy constructors are generated 
alongside inout one, and then picked up for your initialization 
instead of inout one.


Best regards,
Alexandru.


When I run test() it outputs the string "copy", so I am not sure 
why you're saying this.




Re: Struct copy constructor with inout

2023-11-14 Thread Alexandru Ermicioi via Digitalmars-d-learn

On Tuesday, 14 November 2023 at 08:50:34 UTC, dhs wrote:

In other words: why doesn't ss2=s2 fail here?

Thanks,
dhs


Seems like it isn't called at all, your copy constructor with 
inout. Could be a bug.


My assumption is that default copy constructors are generated 
alongside inout one, and then picked up for your initialization 
instead of inout one.


Best regards,
Alexandru.


Struct copy constructor with inout

2023-11-14 Thread dhs via Digitalmars-d-learn

Hello D experts,

I have a question regarding inout in struct copy constructors.
From the spec:

"The copy constructor can be overloaded with different qualifiers 
applied to the parameter (copying from a qualified source) or to 
the copy constructor itself (copying to a qualified destination)"


I am using following code:

```d
struct S1
{
this(ref const S1 s) const { writeln("copy"); }
int i;
}

struct S2
{
this(ref inout S2 s) inout { writeln("copy"); }
int i;
}

void test()
{
const(S1) s1;
S1 ss1 = s1; // error, ss1 not qualified as const

const(S2) s2;
S2 ss2 = s2; // fine, why?
}
```

Isn't "inout" supposed to copy the const-ness of its parameter to 
the constructor's attribute? In other words: why doesn't ss2=s2 
fail here?


Thanks,
dhs