Bug 24688 – Parameter by-value keeps const (only in templates)

Status
RESOLVED
Resolution
WONTFIX
Severity
enhancement
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2024-07-30T05:36:01Z
Last change time
2024-08-24T13:20:24Z
Assigned to
No Owner
Creator
Dominikus Dittes Scherkl

Comments

Comment #0 by dominikus — 2024-07-30T05:36:01Z
```d int fun(int x) { return ++x; } T tpl(T)(T x) { return ++x; } main { const int i = 1; const range r; // some type that cannot create mutable copies int z = i; // works, the compiler makes a mutable copy range s = r; // error ** z = fun(i); // works, the compiler makes a mutable copy z = tpl(i); // error } ``` The compiler should at least try to make a mutable copy also for templates. If that is not possible, it should give the same error as in the line with **. I'm ok if it instead tries a constant copy, but that should not be the default.
Comment #1 by nick — 2024-07-30T15:35:56Z
To do that, the compiler would have to analyse the body of the template function. Because T may be used to declare something that needs to be const in order for the function to compile, e.g. T*, T[].
Comment #2 by dominikus — 2024-07-30T19:11:00Z
(In reply to Nick Treleaven from comment #1) > To do that, the compiler would have to analyse the body of the template > function. No. The strategy is - create a mutable copy (as it is done for normal functions) - only if this is not possible, create a const copy - if a const copy is neccessary, declare the template to take a const T (or, what I would recommend, take it by const ref T)
Comment #3 by razvan.nitu1305 — 2024-08-19T13:02:04Z
What you are asking for is not a reasonable strategy. The type deduction mechanism works by looking at the parameter type and binding T to 'const int'. It's an elegant and simple to understand strategy. If you want tpl to be instantiated with a mutable parameter you can just specify that at the call site: `z = tpl!int(i);`. Having the compiler deduce constraints from the template body (or worse: just guess) is something that will complicate the implementation of the compiler and it's gonna be a behavior that is hard to understand (it's not intuitive). There will be a lot of gotcha moments for users when they have to guess what was the type with which the compiler instantiated the template.
Comment #4 by dominikus — 2024-08-24T12:28:01Z
So, instead of ```d T fun(T)(T x) { ... } ``` I have to write ```d T fun(T)(const T x) { return fun(cast(T)n); } T fun(T)(T x) if(!is(T==const)) { ... } ``` For all functions that used to get a mutable copy of x, just to allow the compiler to create a const copy of x where this is never useful. And I thought D wants the developer to write *less* boilerplate. sigh. This is really only a tiny bit better than to write multiple copies of fun() for each type it can take and renders templates much less useful for me :-(
Comment #5 by dominikus — 2024-08-24T13:03:52Z
of course I meant 'cast(T)x', as there is no parameter 'n'. This is not only boilerplate, you also have to be careful with the parameter names, as this now occurs three times instead of one time. Really, I hate this.
Comment #6 by dominikus — 2024-08-24T13:20:24Z
Ok, it's possible to optimize it to one extra line: T tpl(T)(T x) { ... } becomes T tpl(T)(T x) { static if(is(T==const)) return tpl(cast(T)x); ... } But I have to add this to pretty much *every* template in my code, as this is a pattern that I use almost always. And I can only hope, the compiler is smart enough to optimize this extra call and additional copy of x away.