Skip to content

Commit b959f6b

Browse files
authored
Merge pull request #6105 from jmdavis/changelog
Add changelog entry for changes to isSomeString. merged-on-behalf-of: Jack Stouffer <jack@jackstouffer.com>
2 parents 2a6df1f + b5d6aac commit b959f6b

1 file changed

Lines changed: 88 additions & 0 deletions

File tree

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
isSomeString and isNarrowString are now `false` for enums
2+
3+
Previously, enums whose base type was a string type were `true` for
4+
$(REF isSomeString, std, traits) and $(REF isNarrowString, std, traits).
5+
Occasionally, this was useful, but in general, it was a source of bugs, because
6+
code that works with strings does not necessarily work with enums whose base
7+
type is a string, making it easy to write code where an enum would pass the
8+
template constraint and then the template would fail to compile. For instance,
9+
enums of base type string are `false` for
10+
$(REF `isInputRange`, std, range, primitives). As such, it really doesn't make
11+
sense for $(REF isSomeString, std, traits) and
12+
$(REF isNarrowString, std, traits) to be `true` for enums.
13+
14+
For some code, this will be a breaking change, but most code will either be
15+
unaffected, or it will then fail with enums at the template constraint instead
16+
of inside the function. So, the risk of code breakage is minimal but does exist.
17+
Other code will now be able to remove stuff like `!is(T == enum)` from its
18+
template constraints, since that is now part of
19+
$(REF isSomeString, std, traits) and $(REF isNarrowString, std, traits), but
20+
it will continue to compile with the now unnecessary check for enums.
21+
22+
Code that uses $(REF isSomeString, std, traits) or
23+
$(REF isNarrowString, std, traits) in a template constraint and has no other
24+
conditions which would prevent enums from passing the constraint but then would
25+
fail to compile inside the template if an enum were passed to it will now fail
26+
to compile at the template constraint instead of inside the template. So, such
27+
code is fixed rather than broken.
28+
29+
The rare code that does break because of these changes is code that uses
30+
$(REF isSomeString, std, traits) or $(REF isNarrowString, std, traits) in a
31+
template constraint or static if and does not use other conditions to prevent enums
32+
from passing and actually has code in the template which compiles with both strings
33+
and enums with a base type of string. Such code will need to be changed to use
34+
$(REF OriginalType, std, traits), $(REF asOriginalType, std, conv), or
35+
$(REF StringTypeOf, std, traits) instead. e.g. for enums to pass the constraint,
36+
instead of
37+
38+
---
39+
auto foo(S)(S str)
40+
if (isSomeString!S)
41+
{
42+
...
43+
}
44+
---
45+
46+
the code would be
47+
48+
---
49+
auto foo(S)(S str)
50+
if (isSomeString!(OriginalType!S))
51+
{
52+
...
53+
}
54+
---
55+
56+
As a rule of thumb, generic code should either disallow implicit conversions
57+
and force the caller to do the conversion explicitly (which generally is the
58+
least error-prone approach), or it should force the conversion to the desired
59+
type before the parameter is used in the function so that the function is
60+
definitely operating on the desired type and not just on one that implicitly
61+
converts to it and thus will work regardless of whether the argument was the
62+
exact type or implicitly converted to it.
63+
64+
However, great care should be taken if the implicit conversion will result in
65+
slicing the parameter or otherwise referring to its address, since that makes
66+
it very easy for a reference to local memory to escape the function, whereas if
67+
the conversion is done at the call site, then the slicing is done at the call
68+
site, so the dynamic array is still valid when the function returns.
69+
Fortunately, enums with a base type of string do not have that problem, but
70+
other types which implicitly convert to dynamic arrays (such as static arrays)
71+
do have that problem, which is a big reason why it's generally recommended to
72+
not have generic functions accept types based on implicit conversions.
73+
74+
It is recommended that if a function is supposed to accept both strings and
75+
types which implicitly convert to a string type, then it should explicitly
76+
accept dynamic arrays but be templated on the element type. e.g.
77+
78+
---
79+
auto foo(C)(C[] str)
80+
if (isSomeChar!C)
81+
{
82+
...
83+
}
84+
---
85+
86+
That way, any implicit conversions are done at the call site and do not
87+
introduce @safety problems inside the function. It also tends to result in
88+
template constraints which are shorter and more easily understood.

0 commit comments

Comments
 (0)