Bug 5666 – std.array.replace compile error (string and immutable string)

Status
RESOLVED
Resolution
FIXED
Severity
enhancement
Priority
P2
Component
phobos
Product
D
Version
D2
Platform
x86
OS
All
Creation time
2011-02-28T02:03:00Z
Last change time
2013-01-08T00:42:20Z
Assigned to
andrei
Creator
d_lang

Comments

Comment #0 by d_lang — 2011-02-28T02:03:03Z
Code ---- import std.array; void main() { immutable string r = "de"; replace("abcde", "abc", r); } ---- Compile error ---- test.d(6): Error: template std.array.replace(T,Range) if (isDynamicArray!(Range) && is(ElementType!(Range) : T)) does not match any function template declaration test.d(6): Error: template std.array.replace(T,Range) if (isDynamicArray!(Range) && is(ElementType!(Range) : T)) cannot deduce template function from argument types !()(string,string,immutable(char[])) ---- dmd version is 2.052.
Comment #1 by issues.dlang — 2011-03-10T01:46:09Z
Of course that doesn't compile. Think about it for a moment. r is an _immutable_ string. It _cannot_ change. Not only cannot you not alter any of its elements (since the elements are immutable), but the reference itself is immutable, so it can't be reassigned to another string. replace takes a reference to the string that you pass it, creates a new string with the replacement made in it, and then assigns the result to the original reference. That won't work if the original is immutable. And in this case, it is. So, this is not a bug. It is as designed. If you want to be able to use replace, you need to do it on a mutable string (the elements can be immutable - a string is immutable(char)[] after all - but the reference must be mutable).
Comment #2 by issues.dlang — 2011-03-10T01:48:29Z
Of wait. I read that code wrong. I read it like it was replace(r, "to be replaced", "to replace with"); Regardless, it's still wrong. As I said, replace takes a reference to a string - notice that its signature has ref on its first parameter. You passed it a temporary - the string literal "abcde". ref parameters _must_ refer to an actual variable, not a temporary. So, this is still invalid. I just gave the wrong reason. Sorry about that.
Comment #3 by d_lang — 2011-03-10T06:08:01Z
Sorry. My description was so bad. I want use 'R1 replace(R1, R2, R3)(R1 subject, R2 from, R3 to)' replacement for std.string.replace. ---- import std.array; void main() { string r = "de"; string replaced = replace("abcde", "abc", r); } --- This code is no problem. ---- import std.array; void main() { immutable string r = "de"; string replaced = replace("abcde", "abc", r); } --- But, this code is failed.
Comment #4 by issues.dlang — 2011-03-10T08:54:10Z
Ah, okay. I relooked at the code, at that version of replace _should_ be returning a new string instead of making the changes in place (it's the one which takes indices which makes the changes in place). The problem is still that you're giving it an immutable string. replace takes a range for its last 2 arguments. Normally, immutable ranges are useless. You can't pop their front (because they're immutable), and sometimes you can't even get their front, because their front property isn't const or immutable. So, in the general case, passing an immutable range to a function just doesn't work. The current template constraints for std.array.replace reflect that. However, arrays are a bit funny in that the compiler understands that you can get away with copying an array without altering its immutable original, so it's legal to take an immutable array (or string) and pass it to a function which takes an array with immutable elements but a mutable array (so it could have a paramater of string and yet take an immutable string). So, the template constraint _could_ be adjusted to allow for immutable arrays, even though it couldn't take immutable ranges which _aren't_ arrays. So, I'll reopen this an enhancement. However, I would point out that you probably don't want to be in the habit of using immutable ranges. While it does sometimes work with arrays, since the compiler understands them and lets them get away with some stuff that normal types can't, in the general case, immutable ranges are pretty much useless.
Comment #5 by r.sagitario — 2011-03-10T14:34:41Z
Very much related is this issue with join in dmd 2.052: import std.string; void main() { string[] a = ["1","2","3"]; join(a,std.string.newline); } test.d(6): Error: template std.array.join(RoR,R) if (isInputRange!(RoR) && isInputRange!(ElementType!(RoR)) && isForwardRange!(R)) does not match any function template declaration test.d(6): Error: template std.array.join(RoR,R) if (isInputRange!(RoR) && isInputRange!(ElementType!(RoR)) && isForwardRange!(R)) cannot deduce template function from argument types !()(string[],immutable(char[2u])) This worked in 2.051, aswell as the code with replace in the original report. I consider these regressions, because the functions should just work with immutable string arguments. The user should not be bothered with the internals of the implementation of these functions. IIRC the regression happened because both functions are now based on ranges in std.array, whereas there were some special string implementations in std.string before. The new implementations would probably work if creating a copy of a value of type "immutable char[]" (as done when passing a function argument to replace/join) would allow implicite conversion to immutable(char)[].
Comment #6 by kennytm — 2011-05-04T07:12:14Z
std.array.join doesn't work with array of const/immutable string either :\ ------------------------ import std.array; void main() { const(char[])[] d; join(d, ","); } ------------------------ x.d(5): Error: template std.array.join(RoR,R) if (isInputRange!(RoR) && isInputRange!(ElementType!(RoR)) && isForwardRange!(R)) does not match any function template declaration x.d(5): Error: template std.array.join(RoR,R) if (isInputRange!(RoR) && isInputRange!(ElementType!(RoR)) && isForwardRange!(R)) cannot deduce template function from argument types !()(const(char[])[],string) ------------------------ It works if 'd' is a 'const(char)[][]'.
Comment #7 by r.sagitario — 2012-01-21T13:47:12Z
All of the test cases work with current git-head, but the last. I am not sure if it isn't a restriction of join to not allow mixing modifiers. I wonder why this issue is marked as an enhancement, I think it is a regression in dmd 2.052.
Comment #8 by andrei — 2013-01-08T00:42:20Z
Seems to have been fixed a while ago.