On Wednesday, 14 July 2021 at 22:59:38 UTC, someone wrote:
Please, go to the bottom of the unittest block and uncomment
one of those lines (DMD version here is DMD64 D Compiler
v2.096.1):
so, these lines:
```d
stringUGC32 lugcSequence3 = stringUGC32(cast(char) 'x');
stringUGC32 lugcSequence4 = stringUGC32(1);
```
which call stringUGC32, an alias:
```d
alias stringUGC32 = gudtUGC!(stringUTF32);
alias stringUTF32 = dstring; /// same as immutable(dchar)[];
```
which explicitly instantiates gudtUGC with the type dstring,
```d
enum isTypeSupported(type) = is(type == stringUTF08) || is(type
== stringUTF16)
|| is(type == stringUTF32);
...
public struct gudtUGC(typeStringUTF) {
static assert(isTypeSupported!(typeStringUTF),
r"ooops … gudtUGC structure requires
[string|wstring|dstring] ≠ ["d
~ typeStringUTF.stringof ~ r"]"d);
```
which passes the assert.
You're explicitly instantiating the struct with dstring, *not
getting an assertion error because dstring passes the test*, and
are then passing the constructor a different type, which gets you
a type error at that time.
However, even if you avoid that alias and try either implicit or
explicit instantiation of the template with bad types, the
template instantiation fails first in random other places, like a
default param of " " not making sense as a char:
```d
staticif1.d(369): Error: cannot implicitly convert expression `"
"` of type `string` to `const(char)`
staticif1.d(387): Error: cannot implicitly convert expression `"
"` of type `string` to `const(char)`
staticif1.d(545): Error: template instance
`fw.types.UniCode.gudtUGC!char` error instantiating
```
So, yeah, even with no static ifs in the file, you're still not
getting these static asserts to fail before other parts of the
template. Conclusion: you shouldn't rely on static asserts
providing a good UI in the case of an error. They still *work*,
the struct still wouldn't instantiate with a `char` or the like
if all the other char-incompatibilities were removed, but for UI
purposes they're not great because you can't rely on them firing
according to normal control flow.
You're using aliases though, and not instantiating the struct
directly. If you make those function templates you can use
template constraints with them, which does result in a nice UI:
```d
gudtUGC!stringUTF08 thing8(T)(T arg) if (isTypeSupported!T) {
return gudtUGC!T(arg); }
gudtUGC!stringUTF16 thing16(T)(T arg) if (isTypeSupported!T) {
return gudtUGC!T(arg); }
gudtUGC!stringUTF32 thing32(T)(T arg) if (isTypeSupported!T) {
return gudtUGC!T(arg); }
...
stringUGC32 lugcSequence3 = thing32(cast(char) 'x');
stringUGC32 lugcSequence4 = thing32(1);
```
(introducing new 'thing' functions as stringUGC32 used is
elsewhere as a type and not just a function. So you still need
the aliases.)
Which fails in this manner:
```d
staticif1.d(548): Error: template `fw.types.UniCode.thing32`
cannot deduce function from argument types `!()(char)`,
candidates are:
staticif1.d(46): `thing32(T)(T arg)`
with `T = char`
must satisfy the following constraint:
` isTypeSupported!T`
```
and would fail more verbosely if the three functions had the same
name, instead of numbered names to follow the stringUGC32 pattern.
`isTypeSupported` isn't great here, but you can pick a more
precise name. Or in the case of already numbered functions like
this, you could use more precise tests... or just drop the tests
entirely and have non-templated functions:
```d
gudtUGC!stringUTF08 thing8(stringUTF08 arg) { return
typeof(return)(arg); }
gudtUGC!stringUTF16 thing16(stringUTF16 arg) { return
typeof(return)(arg); }
gudtUGC!stringUTF32 thing32(stringUTF32 arg) { return
typeof(return)(arg); }
```
which results in normal type errors:
```
staticif2.d(548): Error: function
`fw.types.UniCode.thing32(dstring arg)` is not callable using
argument types `(char)`
staticif2.d(548): cannot pass argument `'x'` of type
`char` to parameter `dstring arg`
```