Bug 19125 – IFTI and inout removes head mutability qualifier on by-val parameters

Status
NEW
Severity
regression
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2018-07-29T13:14:42Z
Last change time
2024-12-13T18:59:58Z
Assigned to
No Owner
Creator
Ali Ak
Moved to GitHub: dmd#19469 →

Comments

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
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/19469 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB