digitalmars.D - Implicit conversion to mutable if no indirections?
- =?UTF-8?Q?Ali_=c3=87ehreli?= (21/21) Sep 02 2022 An issue I have is non-mutable expressions without indirections not
- rikki cattermole (15/15) Sep 02 2022 ```d
- =?UTF-8?Q?Ali_=c3=87ehreli?= (7/12) Sep 02 2022 Fair enough but that type has an indirection. I "feel" it should work
- rikki cattermole (4/8) Sep 02 2022 Yeah that's the problem isn't it, its exceptions in the type system that...
- Steven Schveighoffer (11/30) Sep 02 2022 result is a local `const int`. That's because `T` is a `const int`.
- Mathias LANG (5/10) Sep 03 2022 I think it's also done to reduce the number of template
- Daniel N (4/8) Sep 03 2022 C++ solved this... maybe we could add this feature to D?
- Steven Schveighoffer (3/14) Sep 03 2022 Yeah, this would be useful.
- Nick Treleaven (17/20) Sep 03 2022 The following works, the pragma only prints once.
- Steven Schveighoffer (6/28) Sep 03 2022 This solves the OP's problem, but I am certain that is just a toy
- Dmitry Olshansky (19/32) Sep 03 2022 My take on this is that passing by value is equivalent of shallow
- Nick Treleaven (11/14) Sep 04 2022 Another feature that would be interesting is if an `auto`
- Salih Dincer (19/25) Sep 04 2022 P
- =?UTF-8?Q?Ali_=c3=87ehreli?= (4/13) Sep 04 2022 Nick Treleaven was showing how it could be *if* 'auto' actually worked
- Mathias LANG (3/17) Sep 04 2022 This has been discussed in another thread and Walter approved it.
- Meta (3/23) Sep 04 2022 He did? This is straight out of Scott Myers' "The Last Thing D
- Quirin Schroll (53/78) Sep 06 2022 I ran into this while writing a DIP that “abused” `const`. In
- Loara (31/47) Sep 06 2022 Consider
- Quirin Schroll (11/40) Sep 06 2022 This one isn’t even vaguely a container of `T`. You could argue
- Loara (25/30) Sep 07 2022 Exactly, what you say is valid only for containers not for
- Paul Backus (14/33) Sep 06 2022 Fun fact: I actually had to work around this behavior a little
- Nick Treleaven (4/9) Sep 06 2022 There is at least a library pattern:
- Simen =?UTF-8?B?S2rDpnLDpXM=?= (11/20) Sep 07 2022 I really wish that had gone somewhere. There's not a lot of code
An issue I have is non-mutable expressions without indirections not converting to mutable. I talked about this before and mentioned Unqual at DConf 2022 to explain how it solves this. The problem happens in template code: void main() { const i = 42; // const, so foo's T will be const below foo(i); } void foo(T)(T value) { T result; ++result; // Compilation error; but should it work? } I feel like it should work because 'result' is a local int. But I am aware that we can't deduce T to be 'int' because we would be losing that qualifier and further template deductions would be wrong. :/ Anyway... That's one thing that bugs me occasionally and causes bugs like the following one, which I think is caused by a missing Unqual: https://issues.dlang.org/show_bug.cgi?id=23319 At least, there is a moral here at least for myself: Don't forget to test your templates with const, etc. Ali
Sep 02 2022
```d void main() { auto foo = const Foo("whatever"); bar(foo); } struct Foo { char[] data; } void bar(T)(T value) { T thing = value; thing.data[0] = 'a'; } ``` Do you want this to error at compile time or at runtime due to writing to read only memory?
Sep 02 2022
On 9/2/22 12:05, rikki cattermole wrote:struct Foo { char[] data; }Do you want this to error at compile time or at runtime due to writing to read only memory?Fair enough but that type has an indirection. I "feel" it should work for fundamental types, etc. without indirections but as I said, I understand it will ruin template type deduction as well as function overloading which I forgot to mention. This thread is here to remind us to test our templates for const, etc. :) Ali
Sep 02 2022
On 03/09/2022 7:19 AM, Ali Çehreli wrote:Fair enough but that type has an indirection. I "feel" it should work for fundamental types, etc. without indirections but as I said, I understand it will ruin template type deduction as well as function overloading which I forgot to mention.Yeah that's the problem isn't it, its exceptions in the type system that you have to add to get it to work for basic types only. For other types like slices, pointers, structs and classes it gets dangerous.
Sep 02 2022
On 9/2/22 2:58 PM, Ali Çehreli wrote:An issue I have is non-mutable expressions without indirections not converting to mutable. I talked about this before and mentioned Unqual at DConf 2022 to explain how it solves this. The problem happens in template code: void main() { const i = 42; // const, so foo's T will be const below foo(i); } void foo(T)(T value) { T result; ++result; // Compilation error; but should it work? } I feel like it should work because 'result' is a local int.result is a local `const int`. That's because `T` is a `const int`. IFTI uses the exact type passed, not a different type.But I am aware that we can't deduce T to be 'int' because we would be losing that qualifier and further template deductions would be wrong. :/It's not just that, there is no syntax to say to IFTI, "if you match type T, I really want you to use type U". It would actually be nice, but I don't know how it can be done. The one exception to the "exact type" rule is for arrays and pointers, which automatically convert to their tail versions. This is a special case for the benefit of range code. Perhaps the same rules should be done for value types? -Steve
Sep 02 2022
On Saturday, 3 September 2022 at 01:50:05 UTC, Steven Schveighoffer wrote:The one exception to the "exact type" rule is for arrays and pointers, which automatically convert to their tail versions. This is a special case for the benefit of range code. Perhaps the same rules should be done for value types? -SteveI think it's also done to reduce the number of template instantiations. And I agree, it should be done for value types.
Sep 03 2022
On Saturday, 3 September 2022 at 01:50:05 UTC, Steven Schveighoffer wrote:It's not just that, there is no syntax to say to IFTI, "if you match type T, I really want you to use type U". It would actually be nice, but I don't know how it can be done. -SteveC++ solved this... maybe we could add this feature to D? https://en.cppreference.com/w/cpp/language/class_template_argument_deduction#:~:text=x%7B2.0%2C%201%7D%3B-,User%2Ddefined%20deduction%20guides,-The%20syntax%20of
Sep 03 2022
On 9/3/22 7:09 AM, Daniel N wrote:On Saturday, 3 September 2022 at 01:50:05 UTC, Steven Schveighoffer wrote:Yeah, this would be useful. -SteveIt's not just that, there is no syntax to say to IFTI, "if you match type T, I really want you to use type U". It would actually be nice, but I don't know how it can be done.C++ solved this... maybe we could add this feature to D? https://en.cppreference.com/w/cpp/language/class_template_argument_deduction#:~:text=x%7B2.0%2C%201%7D%3B-,User%2Ddefined%20deduction%20guide ,-The%20syntax%20of
Sep 03 2022
On Saturday, 3 September 2022 at 01:50:05 UTC, Steven Schveighoffer wrote:It's not just that, there is no syntax to say to IFTI, "if you match type T, I really want you to use type U". It would actually be nice, but I don't know how it can be done.The following works, the pragma only prints once. ```d void f(T)(const T v) { pragma(msg, T); // int T result; ++result; } void main() { const int c; f(c); f(int()); } ```
Sep 03 2022
On 9/3/22 10:15 AM, Nick Treleaven wrote:On Saturday, 3 September 2022 at 01:50:05 UTC, Steven Schveighoffer wrote:This solves the OP's problem, but I am certain that is just a toy example. What I want to see is the *parameter* properly typed. Either left as const if it's a reference type or stripped of const if it's a value type. -SteveIt's not just that, there is no syntax to say to IFTI, "if you match type T, I really want you to use type U". It would actually be nice, but I don't know how it can be done.The following works, the pragma only prints once. ```d void f(T)(const T v) { pragma(msg, T); // int T result; ++result; } void main() { const int c; f(c); f(int()); } ```
Sep 03 2022
On Friday, 2 September 2022 at 18:58:43 UTC, Ali Çehreli wrote:An issue I have is non-mutable expressions without indirections not converting to mutable. I talked about this before and mentioned Unqual at DConf 2022 to explain how it solves this. The problem happens in template code: void main() { const i = 42; // const, so foo's T will be const below foo(i); } void foo(T)(T value) { T result; ++result; // Compilation error; but should it work? } I feel like it should work because 'result' is a local int.My take on this is that passing by value is equivalent of shallow (1 level deep) unqual. This is sadly not representable in the current language. struct A { int[] slice; } void main() { const a = A([1, 2, 3]); pass(a); // still have const a here } void pass(Unqual!(const A) value) { // value is really this: // struct A { const(int)[]slice; } value.slice ~= 4; // should work } — Dmitry Olshansky
Sep 03 2022
On Friday, 2 September 2022 at 18:58:43 UTC, Ali Çehreli wrote:But I am aware that we can't deduce T to be 'int' because we would be losing that qualifier and further template deductions would be wrong. :/Another feature that would be interesting is if an `auto` declaration stripped const/immutable where possible. After all, if the user didn't want that they could've used `const` or `immutable`. ```d const i = 4; // const int auto v = i; // int const a = [0]; // const(int[]) auto s = a; // const(int)[] ```
Sep 04 2022
On Sunday, 4 September 2022 at 11:03:53 UTC, Nick Treleaven wrote:```d const i = 4; // const int auto v = i; // int const a = [0]; // const(int[]) auto s = a; // const(int)[] ```P I got different results. Which compiler are you using? For example, the content of v cannot be changed: ```d void main() { const i = 4; // const(int) auto v = i; // const(int) /* onlineapp.d(6): Error: cannot modify `const` expression `v` --v; //*/ const a = [0]; // const(const(int)[]) auto b = a; // const(const(int)[]) const c = [i]; // const(const(int)[]) auto d = c; // const(const(int)[]) //typeid(d).writeln; } ``` SDB 79
Sep 04 2022
On 9/4/22 05:24, Salih Dincer wrote:On Sunday, 4 September 2022 at 11:03:53 UTC, Nick Treleaven wrote:Nick Treleaven was showing how it could be *if* 'auto' actually worked that way. Ali```d const i = 4; // const int auto v = i; // int const a = [0]; // const(int[]) auto s = a; // const(int)[] ```P I got different results.
Sep 04 2022
On Sunday, 4 September 2022 at 11:03:53 UTC, Nick Treleaven wrote:On Friday, 2 September 2022 at 18:58:43 UTC, Ali Çehreli wrote:This has been discussed in another thread and Walter approved it. Just need to implement it.But I am aware that we can't deduce T to be 'int' because we would be losing that qualifier and further template deductions would be wrong. :/Another feature that would be interesting is if an `auto` declaration stripped const/immutable where possible. After all, if the user didn't want that they could've used `const` or `immutable`. ```d const i = 4; // const int auto v = i; // int const a = [0]; // const(int[]) auto s = a; // const(int)[] ```
Sep 04 2022
On Sunday, 4 September 2022 at 12:33:37 UTC, Mathias LANG wrote:On Sunday, 4 September 2022 at 11:03:53 UTC, Nick Treleaven wrote:He did? This is straight out of Scott Myers' "The Last Thing D Needs" talk. He uses almost the same code as an example too.On Friday, 2 September 2022 at 18:58:43 UTC, Ali Çehreli wrote:This has been discussed in another thread and Walter approved it. Just need to implement it.But I am aware that we can't deduce T to be 'int' because we would be losing that qualifier and further template deductions would be wrong. :/Another feature that would be interesting is if an `auto` declaration stripped const/immutable where possible. After all, if the user didn't want that they could've used `const` or `immutable`. ```d const i = 4; // const int auto v = i; // int const a = [0]; // const(int[]) auto s = a; // const(int)[] ```
Sep 04 2022
On Sunday, 4 September 2022 at 14:45:15 UTC, Meta wrote:On Sunday, 4 September 2022 at 12:33:37 UTC, Mathias LANG wrote:I ran into this while writing a DIP that “abused” `const`. In C++, `const` is a far lower guarantee and it’s more like a convention than actual compiler checking. I really dislike the fact that Walter made `const(int*)` become `const(int)*` as well as `const(int[])` become `const(int)[]` when an object of the former types is passed to a function template as an argument which has its type inferred: ```D void f(T)(T x) { pragma(msg, "parameter: ", T); } void main() { const int[] xs; pragma(msg, "argument : ", typeof(xs)); f(xs); const int* p; pragma(msg, "argument : ", typeof(p)); f(p); } ``` difference between built-in stuff and user-defined stuff. (One In C++, there’s has been lot of work put in to give user-defined types the same possibilities that built-in types have. In D, there’s much more special casing around built-in types that make meta-programming tedious. Some examples: * Value-range propagation: Given `int x`, `x & 0x7FFF` converts to `short` (but `x` alone requires `cast(int)`. Nice for concrete code, but in meta-programming code, it opens the door for hard-to-understand errors appearing. * Aforementioned `const(int*)` becoming `const(int)*`. That does not happen with custom `struct S(T)` automatically even in cases where it is provably correct, and there is no way to tell the D compiler that copies of `const(S!int)` are better understood to be `S!(const int)`. * Types are keywords: With `alias` we could have `alias int = __traits(signed_integer_type, 32);` etc. There were a lot of issues in the parser that could be avoided. If you think this leads to problems, note that `string` is such an alias already. * Type constructors are hard-wired: If templates want to deconstruct a type, a lot of special cases have to be written because in `int*`, `int[]`, `int[10]`, and `int[string]`, the `*`, `[]`, `[N]`, `[T]` is not syntactic sugar for (hypothetical) `Ptr!int`, `Slice!int`, `Array!(int, 10)`, and `AssocArray!(string, int)`, respectively, so those would be matched by `void f(alias TT, T)(TT!T value)`. Technically speaking, `const`, `immutable` etc. and `function`/`delegate` are type constructors as well, but those have additional things going on: e.g. `const` methods and a function’s parameters have more on them than their type (e.g., `ref`). As in the bullet above: `alias Ptr = __traits(pointer_type_ctor);`, etc. The Last Thing D Needs is the opposite of streamlining.On Sunday, 4 September 2022 at 11:03:53 UTC, Nick Treleaven wrote:He did? This is straight out of Scott Myers' "The Last Thing D Needs" talk. He uses almost the same code as an example too.On Friday, 2 September 2022 at 18:58:43 UTC, Ali Çehreli wrote:This has been discussed in another thread and Walter approved it. Just need to implement it.But I am aware that we can't deduce T to be 'int' because we would be losing that qualifier and further template deductions would be wrong. :/Another feature that would be interesting is if an `auto` declaration stripped const/immutable where possible. After all, if the user didn't want that they could've used `const` or `immutable`. ```d const i = 4; // const int auto v = i; // int const a = [0]; // const(int[]) auto s = a; // const(int)[] ```
Sep 06 2022
On Tuesday, 6 September 2022 at 10:06:59 UTC, Quirin Schroll wrote:* Aforementioned `const(int*)` becoming `const(int)*`. That does not happen with custom `struct S(T)` automatically even in cases where it is provably correct, and there is no way to tell the D compiler that copies of `const(S!int)` are better understood to be `S!(const int)`.Consider ```d struct S(T){ T val; int idx; void inc(){ idx++; } } ``` then `s.inc()` is valid if `s` id `S!(const int)` but not if `s` is `const S!int`! Again consider ```d struct S(T){ int dat; T func(T dat){ ... } } ``` in this example conversion of `S!(const int)` to `const S!int` has much less sense. If you want to perform a such conversion then you should instead introduce a "cloning" method: ```d auto clone() const pure{ return S(...); } ```* Types are keywords: With `alias` we could have `alias int = __traits(signed_integer_type, 32);` etc. There were a lot of issues in the parser that could be avoided. If you think this leads to problems, note that `string` is such an alias already. * Type constructors are hard-wired: If templates want to deconstruct a type, a lot of special cases have to be written because in `int*`, `int[]`, `int[10]`, and `int[string]`, the `*`, `[]`, `[N]`, `[T]` is not syntactic sugar for (hypothetical) `Ptr!int`, `Slice!int`, `Array!(int, 10)`, and `AssocArray!(string, int)`, respectively, so those would be matched by `void f(alias TT, T)(TT!T value)`.Traits exists for this reason: provide a common interface for hard wired types and templated ones.
Sep 06 2022
On Tuesday, 6 September 2022 at 11:09:26 UTC, Loara wrote:On Tuesday, 6 September 2022 at 10:06:59 UTC, Quirin Schroll wrote:Of course. That’s e.g. what a reference counted pointer would do.* Aforementioned `const(int*)` becoming `const(int)*`. That does not happen with custom `struct S(T)` automatically even in cases where it is provably correct, and there is no way to tell the D compiler that copies of `const(S!int)` are better understood to be `S!(const int)`.Consider ```d struct S(T){ T val; int idx; void inc(){ idx++; } } ``` then `s.inc()` is valid if `s` id `S!(const int)` but not if `s` is `const S!int`!Again consider ```d struct S(T){ int dat; T func(T dat){ ... } } ``` in this example conversion of `S!(const int)` to `const S!int` has much less sense. If you want to perform a such conversion then you should instead introduce a "cloning" method:This one isn’t even vaguely a container of `T`. You could argue about `int*`, and the case for `int[]` is obvious. I’m not entirely sure what you want to tell me. In the cases of containers, it’s probably even possible to a large degree to use introspection to provide a wide-ranging `clone`. The relevant part is that when you clone, you have to be explicit. There’s tension between stuff being handy when using known types and known functions, and stuff being simple using templates’ type arguments and function templates.
Sep 06 2022
On Tuesday, 6 September 2022 at 17:48:53 UTC, Quirin Schroll wrote:This one isn’t even vaguely a container of `T`.Exactly, what you say is valid only for containers not for generic templated objects. It's better to provide a custom method that apply the needed conversion: ```d struct myContainer(T){ ... const(myContainer!(Unconst!T)) shareConst() const{ //do stuff } } ```I’m not entirely sure what you want to tell me. In the cases of containers, it’s probably even possible to a large degree to use introspection to provide a wide-ranging `clone`. The relevant part is that when you clone, you have to be explicit.I don't think that could be possible to implement such feature without heavy drawbacks, it's smarter to implement custom conversion methods for each templated object since "cloning an object" may assume different meaning depending of its type. For example in [this project](https://github.com/Loara/dclone) cloning an object means "create a new object that doesn't share any indirection with the original one", however this condition is often too restrictive for containers since you don't want to clone each stored object. An unique solution that works for every object doesn't exists, it's better that each programmer implements the preferred strategy in his code.
Sep 07 2022
On Tuesday, 6 September 2022 at 10:06:59 UTC, Quirin Schroll wrote:I ran into this while writing a DIP that “abused” `const`. In C++, `const` is a far lower guarantee and it’s more like a convention than actual compiler checking. I really dislike the fact that Walter made `const(int*)` become `const(int)*` as well as `const(int[])` become `const(int)[]` when an object of the former types is passed to a function template as an argument which has its type inferred: ```D void f(T)(T x) { pragma(msg, "parameter: ", T); } void main() { const int[] xs; pragma(msg, "argument : ", typeof(xs)); f(xs); const int* p; pragma(msg, "argument : ", typeof(p)); f(p); } ```Fun fact: I actually had to work around this behavior a little while ago, in order to get `SumType` working correctly with `inout`. You can see the workaround and its application in this Phobos commit: https://github.com/dlang/phobos/commit/a74fa63e6775d626850d8ebd854d9803c7ffb97d I can't find the post, but think it was Andrei who once remarked on these forums that the adjustment of `qual(T[])` to `qual(T)[]` was all upside, with essentially zero cost. Well, here's your cost. Special cases are like drugs. They can make you feel good in the short term, but sooner or later, the bill will always come due. Just Say No, kids.
Sep 06 2022
On Tuesday, 6 September 2022 at 10:06:59 UTC, Quirin Schroll wrote:* Aforementioned `const(int*)` becoming `const(int)*`. That does not happen with custom `struct S(T)` automatically even in cases where it is provably correct, and there is no way to tell the D compiler that copies of `const(S!int)` are better understood to be `S!(const int)`.There is at least a library pattern: https://dlang.org/blog/2020/06/25/a-pattern-for-head-mutable-structures/
Sep 06 2022
On Tuesday, 6 September 2022 at 17:45:50 UTC, Nick Treleaven wrote:On Tuesday, 6 September 2022 at 10:06:59 UTC, Quirin Schroll wrote:I really wish that had gone somewhere. There's not a lot of code to it, it's almost all library code that the user doesn't see, and it would provide a solution while we wait for the promised compiler support for the same thing (which I don't think would be very different - you'll need to specify the behavior for complex cases anyway). Also, it's not worth a whole lot as a library if Phobos doesn't implement it. -- Simen* Aforementioned `const(int*)` becoming `const(int)*`. That does not happen with custom `struct S(T)` automatically even in cases where it is provably correct, and there is no way to tell the D compiler that copies of `const(S!int)` are better understood to be `S!(const int)`.There is at least a library pattern: https://dlang.org/blog/2020/06/25/a-pattern-for-head-mutable-structures/
Sep 07 2022