isSomeString and isNarrowString are now `false` for enums Previously, enums whose base type was a string type were `true` for $(REF isSomeString, std, traits) and $(REF isNarrowString, std, traits). Occasionally, this was useful, but in general, it was a source of bugs, because code that works with strings does not necessarily work with enums whose base type is a string, making it easy to write code where an enum would pass the template constraint and then the template would fail to compile. For instance, enums of base type string are `false` for $(REF isInputRange, std, range, primitives). As such, it really doesn't make sense for $(REF isSomeString, std, traits) and $(REF isNarrowString, std, traits) to be `true` for enums. For some code, this will be a breaking change, but most code will either be unaffected, or it will then fail with enums at the template constraint instead of inside the function. So, the risk of code breakage is minimal but does exist. Other code will now be able to remove stuff like `!is(T == enum)` from its template constraints, since that is now part of $(REF isSomeString, std, traits) and $(REF isNarrowString, std, traits), but it will continue to compile with the now unnecessary check for enums. Code that uses $(REF isSomeString, std, traits) or $(REF isNarrowString, std, traits) in a template constraint and has no other conditions which would prevent enums from passing the constraint but then would fail to compile inside the template if an enum were passed to it will now fail to compile at the template constraint instead of inside the template. So, such code is fixed rather than broken. The rare code that does break because of these changes is code that uses $(REF isSomeString, std, traits) or $(REF isNarrowString, std, traits) in a template constraint or static if and does not use other conditions to prevent enums from passing and actually has code in the template which compiles with both strings and enums with a base type of string. Such code will need to be changed to use $(REF OriginalType, std, traits), $(REF asOriginalType, std, conv), or $(REF StringTypeOf, std, traits) instead. e.g. for enums to pass the constraint, instead of --- auto foo(S)(S str) if (isSomeString!S) { ... } --- the code would be --- auto foo(S)(S str) if (isSomeString!(OriginalType!S)) { ... } --- As a rule of thumb, generic code should either disallow implicit conversions and force the caller to do the conversion explicitly (which generally is the least error-prone approach), or it should force the conversion to the desired type before the parameter is used in the function so that the function is definitely operating on the desired type and not just on one that implicitly converts to it and thus will work regardless of whether the argument was the exact type or implicitly converted to it. However, great care should be taken if the implicit conversion will result in slicing the parameter or otherwise referring to its address, since that makes it very easy for a reference to local memory to escape the function, whereas if the conversion is done at the call site, then the slicing is done at the call site, so the dynamic array is still valid when the function returns. Fortunately, enums with a base type of string do not have that problem, but other types which implicitly convert to dynamic arrays (such as static arrays) do have that problem, which is a big reason why it's generally recommended to not have generic functions accept types based on implicit conversions. It is recommended that if a function is supposed to accept both strings and types which implicitly convert to a string type, then it should explicitly accept dynamic arrays but be templated on the element type. e.g. --- auto foo(C)(C[] str) if (isSomeChar!C) { ... } --- That way, any implicit conversions are done at the call site and do not introduce @safety problems inside the function. It also tends to result in template constraints which are shorter and more easily understood.