Bug 17141 – Type Inference Incorrectly Converts Characters to Integers

Status
RESOLVED
Resolution
FIXED
Severity
critical
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2017-02-03T18:18:50Z
Last change time
2019-05-26T05:18:42Z
Keywords
pull
Assigned to
Suleyman Sahmi (سليمان السهمي)
Creator
Jack Stouffer
Blocks
17358
See also
https://issues.dlang.org/show_bug.cgi?id=5659

Comments

Comment #0 by jack — 2017-02-03T18:18:50Z
Should return dchar. This breaks range functions like chain, example: string name = "Jack"; auto app = appender!string(); app.put( chain(name, toChars(10)) ); Error: template std.array.Appender!string.Appender.put cannot deduce function from argument types !()(Result), candidates are: /usr/local/Cellar/dmd/2.073.0/include/dlang/dmd/std/array.d(2820): std.array.Appender!string.Appender.put(U)(U item) if (canPutItem!U) /usr/local/Cellar/dmd/2.073.0/include/dlang/dmd/std/array.d(2848): std.array.Appender!string.Appender.put(Range)(Range items) if (canPutConstRange!Range) /usr/local/Cellar/dmd/2.073.0/include/dlang/dmd/std/array.d(2857): std.array.Appender!string.Appender.put(Range)(Range items) if (canPutRange!Range)
Comment #1 by jack — 2017-02-07T18:40:07Z
Raising the priority of this because chain is really gimped by this bug. Every call to chain with character ranges requires an extra call to map to cast the results for it to work correctly.
Comment #2 by hsteoh — 2017-04-28T18:41:32Z
It's not just CommonType!(dchar, char); it's a whole slew of cases that are incorrectly handled: ------------ void main() { import std.range; struct CharRange(C) { C ch = 'a'; @property C front() { return ch; } @property bool empty() { return ch == C.init; } void popFront() { ch = C.init; } } CharRange!char charRange; CharRange!wchar wcharRange; CharRange!dchar dcharRange; pragma(msg, "char + char: ", ElementType!(typeof(chain(charRange, charRange)))); pragma(msg, "char + wchar: ", ElementType!(typeof(chain(charRange, wcharRange)))); pragma(msg, "wchar + char: ", ElementType!(typeof(chain(wcharRange, charRange)))); pragma(msg, "char + dchar: ", ElementType!(typeof(chain(charRange, dcharRange)))); pragma(msg, "dchar + char: ", ElementType!(typeof(chain(dcharRange, charRange)))); pragma(msg, "wchar + wchar: ", ElementType!(typeof(chain(wcharRange, wcharRange)))); pragma(msg, "wchar + dchar: ", ElementType!(typeof(chain(wcharRange, dcharRange)))); pragma(msg, "dchar + wchar: ", ElementType!(typeof(chain(dcharRange, wcharRange)))); pragma(msg, "dchar + dchar: ", ElementType!(typeof(chain(dcharRange, dcharRange)))); } ------------ Output: ------------ char + char: char char + wchar: int wchar + char: int char + dchar: uint dchar + char: uint wchar + wchar: wchar wchar + dchar: uint dchar + wchar: uint dchar + dchar: dchar ------------ Seems like the only time the correct common type is deduced, as far as character types are concerned, is when both are types are the same. All the other cases are worthy of a WAT?.
Comment #3 by hsteoh — 2017-04-28T18:43:26Z
Unfortunately, it looks like CommonType is implemented using the ?: ternary operator, meaning that it's the *compiler* that's producing these crazy results.
Comment #4 by hsteoh — 2017-04-28T18:49:27Z
Looks like this is implemented in the hairball function typeMerge() in src/ddmd/dcast.d. I'll try to trace through and see if I can find an obvious problem, but I'm not sure if I'll be able to.
Comment #5 by hsteoh — 2017-04-28T19:10:19Z
Hmph. Looks like the problem is that the very first thing typeMerge() does is to do integer promotion on the incoming types. Thus, right from the start, we've already lost the original character types. The only reason this hasn't shown up earlier is because when the two types are equal, typeMerge() is not called. So we're essentially only saved "by accident". Oddly enough, there is an `if (op != TOKquestion || ...)`, which *seems* to suggest that the intent of the code is *not* to do integer promotions if ?: is involved. However, the second clause of the if condition, `t1b.ty != t2b.ty`, appears to be always true, so the check for ?: would appear to be always irrelevant. It would seem that && was intended here rather than ||, but I've to look more carefully to make sure this isn't going to cause massive code breakage...
Comment #6 by hsteoh — 2017-04-28T19:46:48Z
Wow, this bug goes way back to issue #5659. Looks like it was partially fixed back then, but there are still cases not fixed.
Comment #7 by jack — 2017-04-28T22:27:17Z
Raising this to critical because this is actually a bug in DMD's type inference and not a Phobos bug.
Comment #8 by schveiguy — 2017-04-30T20:29:10Z
I'm not sure the cure is better than the disease. If CommonType!(dchar, char) becomes dchar, then chaining together a range of dchar with a range of char *integer promotes* the char range to dchar. It doesn't decode it!
Comment #9 by john.loughran.colvin — 2017-07-05T13:49:39Z
Comment #10 by dlang-bot — 2019-05-26T04:06:43Z
@SSoulaimane updated dlang/dmd pull request #9889 "Fix issues 17141, 17358 - Ternary operator converts characters to integers" fixing this issue: - Fix issues 17141, 17358 - Ternary operator converts characters to integers https://github.com/dlang/dmd/pull/9889
Comment #11 by dlang-bot — 2019-05-26T05:18:42Z
dlang/dmd pull request #9889 "Fix issues 17141, 17358 - Ternary operator converts characters to integers" was merged into master: - 8826be69875448bfb18ecb887e89567e75a1b48f by سليمان السهمي (Suleyman Sahmi): Fix issues 17141, 17358 - Ternary operator converts characters to integers https://github.com/dlang/dmd/pull/9889