Comment #0 by ali.akhtarzada — 2018-07-29T13:14:42Z
From here: https://forum.dlang.org/post/[email protected]
import std.stdio;
struct W(T) {
T t;
}
auto wrap0(T)(auto ref inout(T) t) {
return inout(W!T)(t);
}
auto wrap1(T)(auto ref inout(T) t) {
return t;
}
void main() {
pragma(msg, typeof(wrap0("foo"))); // removes it
pragma(msg, typeof(wrap1("bar"))); // keeps it
immutable int i0;
pragma(msg, typeof(wrap1(i0))); // keeps it
string s0 = "baz";
pragma(msg, typeof(wrap0(s0))); // keeps it
}
prints:
immutable(W!(char[]))
immutable(string)
immutable(int)
W!string
Comment #1 by schveiguy — 2018-07-30T13:16:55Z
The issue here is that IFTI applies inout to the head when it shouldn't
If you have a function like this:
struct S(T)
{
T t;
}
inout(S!T) makeS(T)(inout(T) t)
{
return inout S!T(t);
}
One expects the type parameter of S to match the type parameter passed in. inout is just saying "I'm not going to change anything"
However, in the case of types that have tail-mutability modifier capability (pointers, arrays), IFTI is pulling the mutability OUT from the tail and applying it to the entire parameter:
auto s1 = makeS("hello");
assert(is(typeof(s1) == immutable(S!(char[])));
auto s2 = makeS("hello".ptr);
assert(is(typeof(s2) == immutable(S!(char *));
But tail-modified values are distinctly more capable in terms of mutability than fully modified values. So IFTI I believe should NOT perform this adjustment, and just match T as string (and therefore the type of t should be inout(string)).
If one wishes to actually match inout to the modifier of the tail, one can do so with a specialization:
auto makeS(T)(inout(T)[] t)
{
return inout S!(T[])(t);
}
Note that the use of auto ref in the original code is affecting what is inferred, because of the double-indirection rule. This makes it doubly confusing (see the cases for wrap0("foo") and wrap0(s0), which one might expect to be identical ).
Comment #2 by schveiguy — 2019-07-17T15:36:39Z
I have another simpler case which is absolutely unacceptable:
Nullable!string s;
string x = s.get(""); // error
This is because Nullable.get is taking the above input as an inout(char[])!
It should be inout(string).
Checking all versions, looks like this worked in 2.065 and prior. Well, the concept worked anyway, since I don't think that form of get was added to Nullable until later.
An equivalent test:
struct S
{
string boo;
auto get(U)(inout(U) u) inout
{
return boo.length == 0 ? u : boo;
}
}
void main()
{
S s;
string x = s.get("");
}
This fails from 2.066 on. Changing to regression.
Comment #3 by ali.akhtarzada — 2019-07-18T10:24:29Z
*** Issue 19749 has been marked as a duplicate of this issue. ***
Comment #4 by ali.akhtarzada — 2019-07-18T10:26:16Z
Yes. This is super annoying.
It also messes up type inference where you need the types to be inferred properly:
struct S(T) {
T value = T.init;
}
auto ref make(T)(inout auto ref T value) {
// T == char[] when infact it's string
return inout(S!T)(value);
}
auto ref f(T)(inout auto ref S!T s) {
return make(s.value);
}
auto a = [make("hello"), S!string("hello")];
(plucked from duplicate issue)
Comment #5 by robert.schadek — 2024-12-13T18:59:58Z