Bug 12573 – Implicit immutable cast for ref/out argument of pure functions

Status
NEW
Severity
enhancement
Priority
P4
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2014-04-13T11:57:28Z
Last change time
2024-12-13T18:19:48Z
Assigned to
No Owner
Creator
bearophile_hugs
Moved to GitHub: dmd#18815 →

Comments

Comment #0 by bearophile_hugs — 2014-04-13T11:57:28Z
Currently this code compiles and works correctly, it performs an implicit immutable cast thanks to purity: string foo1(in string s) pure nothrow { auto s2 = s.dup; s2[0] = 'a'; return s2; // OK. } void main() {} But in some cases I'd like to return the result through an argument (perhaps because the function return value is used for some other purpose, like a boolean error message). So perhaps it's possible and good to allow code like this too: void foo2(in string s, ref string sOut) pure nothrow { auto s2 = s.dup; s2[0] = 'a'; sOut = s2; // Error: cannot implicitly convert } void foo3(in string s, out string sOut) pure nothrow { auto s2 = s.dup; s2[0] = 'a'; sOut = s2; // Error: cannot implicitly convert } void main() {} In dmd 2.066alpha it gives: test.d(4,12): Error: cannot implicitly convert expression (s2) of type char[] to string test.d(9,12): Error: cannot implicitly convert expression (s2) of type char[] to string
Comment #1 by schveiguy — 2014-04-14T15:34:00Z
I think it is dangerous to allow this. Allowing the implicit casting of a return is OK, since you cannot modify the return via the mutable reference, but allowing arbitrary assignment inside the function allows modifying the mutable reference, breaking immutability. If we consider the trivial case: string foo2(in string s, ref string sout) pure nothrow { auto s2 = s.dup; sout = s2; // this would potentially be allowed auto s3 = sout.idup; // copy the data s2[0] = 'a'; // now modified immutable data referenced by sout. return sout.idup; // could be changed to return s3? } Basically, the compiler can make the legal assumption that since s3 and sout are immutable, and have not changed, calling idup on sout will reasonably result in the same value that s3 has. It would be a legal optimization. However, on return, sout has changed from what s3 contains, so the return value may not be equivalent to sout. A return does not have this vulnerability, since the function ends at a return statement, and the cast is effectively occurring after the return. In fact, you have no access to the return, so it's not possible to use it in a pure manner inside the function. I would recommend not allowing this, unless you could make more restrictive rules. I'm not sure if it's worth it. May be better to focus on multiple return values.
Comment #2 by yebblies — 2014-04-15T17:50:02Z
Yeah, doesn't make much sense for 'ref'. But what about 'out'?
Comment #3 by schveiguy — 2014-04-15T18:48:21Z
(In reply to yebblies from comment #2) > Yeah, doesn't make much sense for 'ref'. But what about 'out'? out parameters are addressable just like ref ones. Only difference is they are initialized upon entry. In other words, they have the same issue. Only return values aren't directly addressable. If out variables were write-only, then it might make sense.
Comment #4 by robert.schadek — 2024-12-13T18:19:48Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/18815 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB