Re: static assert not printing out along the error diagnostic

2021-07-13 Thread Paul Backus via Digitalmars-d-learn

On Wednesday, 14 July 2021 at 02:28:07 UTC, someone wrote:

If I add in main():

```d
auto g = MyType!string();
g.doWork();
```

I get:

...: Error: undefined identifier `ResultType`
...: Error: template instance `...MyType!string` error 
instantiating


But nothing like:

```d
T.stringof ~ " is not supported"
```


Weirdly, adding a dummy `ResultType` declaration to the `else` 
branch makes the assert message show up:


} else {
alias ResultType = void;
static assert(false, T.stringof ~ " is not supported");
}

https://run.dlang.io/is/68sM0Z

Somehow the compiler is processing the declaration of `doWork` 
before the `static assert` statement, even though it comes later 
in the source code.


According to run.dlang.io this behavior was introduced in DMD 
2.066.0, so the example in the book must not have been updated 
for quite a while.


Re: static assert not printing out along the error diagnostic

2021-07-13 Thread someone via Digitalmars-d-learn

On Wednesday, 14 July 2021 at 02:42:21 UTC, Paul Backus wrote:

Weirdly, adding a dummy `ResultType` declaration to the `else`  
branch makes the assert message show up:


My code is like following:

```d
public struct gudtUGC(typeStringUTF) {

   static if (! (is (typeStringUTF == string) || is 
(typeStringUTF == wstring) || is (typeStringUTF == dstring))) {


  static assert(false, r"ooops … gudtUGC structure requires 
[string|wstring|dstring] ≠ ["d ~ typeStringUTF.stringof ~ r"]"d);


  /// meaning: this will halt compilation if this UDT was 
instantiated with a type other than the ones intended


   } else {

   ... actual structure code

   }

}
```

... and it never fired the assert when I added something like:

```d
gudtUGC(int) something = gudtUGC(1); /// invalid of course
```

in main() ... so then I went to the D docs and to Ali's book 
afterward and there I tested his example to same results.


Re: static assert not printing out along the error diagnostic

2021-07-13 Thread SealabJaster via Digitalmars-d-learn

On Wednesday, 14 July 2021 at 03:06:06 UTC, someone wrote:

...


I've ran into this rarely. At a guess, it seems something 
triggers the compiler to either evaluate something in non-lexical 
order; doesn't realise it needs to error out at the static assert 
instead of doing it later, or maybe it somehow ends up gagging 
the more relevant static assert error.


The fix usually involves changing the code in a way to make the 
compiler evaluate things differently.


For your example:

```d
import std.stdio;

struct MyType(T) {

template ResultTypeT()
{
static if (is (T == float)) {
alias ResultTypeT = double;

} else static if (is (T == double)) {
alias ResultTypeT = real;

} else {
static assert(false, T.stringof ~ " is not 
supported");

}
}
alias ResultType = ResultTypeT!();

ResultType doWork() {
writefln("The return type for %s is %s.",
 T.stringof, ResultType.stringof);
ResultType result;
// ...
return result;
}
}

void main() {
auto f = MyType!float();
f.doWork();

auto d = MyType!double();
d.doWork();

auto g = MyType!string();
g.doWork();
}
```


Re: static assert not printing out along the error diagnostic

2021-07-13 Thread SealabJaster via Digitalmars-d-learn

On Wednesday, 14 July 2021 at 05:59:19 UTC, SealabJaster wrote:
I've ran into this rarely. At a guess, it seems something 
triggers the compiler to either evaluate something in 
non-lexical order; doesn't realise it needs to error out at the 
static assert instead of doing it later, or maybe it somehow 
ends up gagging the more relevant static assert error.


To put this more coherently, I think it's to do with the fact the 
compiler sometimes doesn't immediately stop compilation on error, 
usually so it can try to produce even more errors to help aid you.


So it might be storing the `static assert` error, chokes on 
`doWork` since `ResultType` isn't defined in this case, and for 
some odd reason just completely drops the `static assert`:


```d
// Obviously this isn't valid D, but mostly to show what I think 
happens.

struct MyType!
{
// `else` branch does not define a `ResultType`
//
// Compiler sees this static assert, goes into 'error 
collection' mode I guess we could call it.

static assert(false, "bad type");

// Error collection mode sees that `ResultType` isn't defined.
// But for some reason, it chokes up, and doesn't produce the 
static assert error in the output.

ResultType doWork(){...}
}
```




Re: static assert not printing out along the error diagnostic

2021-07-13 Thread jfondren via Digitalmars-d-learn

On Wednesday, 14 July 2021 at 03:06:06 UTC, someone wrote:
in main() ... so then I went to the D docs and to Ali's book 
afterward and there I tested his example to same results.


The current behavior seems like it could be taken for a bug, or 
at least room for improvement in letting static assertions fail 
faster than other template instantiation, but the workaround 
seems to be to require all branches of a static if to be valid 
from the perspective of the rest of the code. Which is very easy 
to do if you just don't have these branches at all.


case 1:

```d
public struct gudtUGC(typeStringUTF) {
   static if (! (is (typeStringUTF == string) || is 
(typeStringUTF == wstring) || is (typeStringUTF == dstring))) {
  static assert(false, r"ooops … gudtUGC structure requires 
[string|wstring|dstring] ≠ ["d ~ typeStringUTF.stringof ~ r"]"d);

   } else {
  // actual structure code
   }
}
```

alternate 1:
- pull tests out into a named enum template, like std.traits
- always static assert enum, rather than conditionally asserting 
false

- always have the rest of the code

```d
enum isString(T) = is(T == string) || is(T == wstring) || is(T == 
dstring);

// very similar to std.traits.isSomeString

struct gudtUGC(T) {
static assert(isString!T, "error message");
// unconditional structure code
}
```

case 2:

```d
struct MyType(T) {
static if (is (T == float)) {
alias ResultType = double;

} else static if (is (T == double)) {
alias ResultType = real;

} else {
static assert(false, T.stringof ~ " is not supported");
}
// code relying on some ResultType
}
```

alternate 2a:
- create a type function with std.meta

```d
enum SmallFloat(T) = is(T == float) || is(T == double);

template largerFloat(T) {
import std.meta : AliasSeq, staticIndexOf;

static assert(SmallFloat!T, T.stringof ~ " is not supported");

alias largerFloat = AliasSeq!(void, double, 
real)[1+staticIndexOf!(T, AliasSeq!(float, double))];

}

struct MyType(T) {
alias ResultType = largerFloat!T;
// code relying on some ResultType
}
```

alternate 2b:
- at least make all the branches valid

```d
enum SmallFloat(T) = is(T == float) || is(T == double);

struct MyType(T) {
static assert(SmallFloat!T, T.stringof ~ " is not supported");
static if (is(T == float)) {
alias ResultType = double;
} else static if (is(T == double)) {
alias ResultType = real;
} else {
alias ResultType = void; // dummy
}
// code relying on ResultType
}
```

A benefit of having tests like SmallFloat is that you can use 
them with template constraints...


```d
void moreWork(T)() if (SmallFloat!T) { }

unittest {
moreWork!float; // this is fine
moreWork!real; // !
}
```

... and get readable error messages out of the box, without 
having to write static assert messages:


```
example.d(23): Error: template instance `example.moreWork!real` 
does not match template declaration `moreWork(T)()`

  with `T = real`
  must satisfy the following constraint:
`   SmallFloat!T`
```


Re: static assert not printing out along the error diagnostic

2021-07-14 Thread someone via Digitalmars-d-learn

On Wednesday, 14 July 2021 at 06:28:37 UTC, jfondren wrote:


alternate 1:
- pull tests out into a named enum template, like std.traits
- always static assert enum, rather than conditionally 
asserting false

- always have the rest of the code

```d
enum isString(T) = is(T == string) || is(T == wstring) || is(T 
== dstring);

// very similar to std.traits.isSomeString

struct gudtUGC(T) {
static assert(isString!T, "error message");
// unconditional structure code
}
```


... is it me or this isn't triggering the assert either ?


Re: static assert not printing out along the error diagnostic

2021-07-14 Thread jfondren via Digitalmars-d-learn

On Wednesday, 14 July 2021 at 18:04:44 UTC, someone wrote:

On Wednesday, 14 July 2021 at 06:28:37 UTC, jfondren wrote:


alternate 1:
- pull tests out into a named enum template, like std.traits
- always static assert enum, rather than conditionally 
asserting false

- always have the rest of the code

```d
enum isString(T) = is(T == string) || is(T == wstring) || is(T 
== dstring);

// very similar to std.traits.isSomeString

struct gudtUGC(T) {
static assert(isString!T, "error message");
// unconditional structure code
}
```


... is it me or this isn't triggering the assert either ?


This isn't a complete example. The same problem elsewhere in your 
program can cause dmd to exit before getting to the static assert 
here, for the same reason that the static assert in your original 
code wasn't got to.


Here's a complete example:

```d
enum isString(T) = is(T == string) || is(T == wstring) || is(T == 
dstring);


struct gudtUGC(T) {
static assert(isString!T, "error message");
// unconditional structure code
}

unittest {
gudtUGC!int;
}
```

which fails with

```
example.d(4): Error: static assert:  "error message"
example.d(9):instantiated from here: `gudtUGC!int`
```

If you have the static assert there but then still follow up with 
static ifs that only conditionally produce an alias, then you 
have the same problem still.


Re: static assert not printing out along the error diagnostic

2021-07-14 Thread someone via Digitalmars-d-learn

On Wednesday, 14 July 2021 at 19:00:08 UTC, jfondren wrote:

On Wednesday, 14 July 2021 at 18:04:44 UTC, someone wrote:

On Wednesday, 14 July 2021 at 06:28:37 UTC, jfondren wrote:


alternate 1:
- pull tests out into a named enum template, like std.traits
- always static assert enum, rather than conditionally 
asserting false

- always have the rest of the code

```d
enum isString(T) = is(T == string) || is(T == wstring) || 
is(T == dstring);

// very similar to std.traits.isSomeString

struct gudtUGC(T) {
static assert(isString!T, "error message");
// unconditional structure code
}
```


... is it me or this isn't triggering the assert either ?


This isn't a complete example. The same problem elsewhere in 
your program can cause dmd to exit before getting to the static 
assert here, for the same reason that the static assert in your 
original code wasn't got to.


Here's a complete example:

```d
enum isString(T) = is(T == string) || is(T == wstring) || is(T 
== dstring);


struct gudtUGC(T) {
static assert(isString!T, "error message");
// unconditional structure code
}

unittest {
gudtUGC!int;
}
```

which fails with

```
example.d(4): Error: static assert:  "error message"
example.d(9):instantiated from here: `gudtUGC!int`
```

If you have the static assert there but then still follow up 
with static ifs that only conditionally produce an alias, then 
you have the same problem still.


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):

```d
/// implementation-bugs [-] using foreach (with this structure) 
always misses the last grapheme‐cluster … possible phobos bug # 
20483 @ unittest's testUTFcommon() last line

/// implementation-bugs [-] static assert not firing

/// implementation‐tasks [✓] for the time being input parameters 
are declared const instead of in; eventually they'll be back to 
in when the related DIP was setted once and for all; but, 
definetely—not scope const
/// implementation‐tasks [✓] reconsider excessive cast usage as 
suggested by Ali: bypassing compiler checks could be potentially 
harmful … cast and integer promotion @ 
http://ddili.org/ders/d.en/cast.html
/// implementation‐tasks [-] reconsider making this whole UDT 
zero‐based as suggested by ag0aep6g—has a good point


/// implementation‐tasks‐possible [-] pad[L|R]
/// implementation‐tasks‐possible [-] replicate/repeat
/// implementation‐tasks‐possible [-] replace(string, string)
/// implementation‐tasks‐possible [-] translate(string, string) … 
same‐size strings matching one‐to‐one


/// usage: array slicing can be used for usual things like: 
left() right() substr() etc … mainly when grapheme‐clusters are 
not expected at all
/// usage: array slicing needs a zero‐based first range argument 
and a second one one‐based (or one‐past‐beyond; which it is 
somehow … counter‐intuitive


module fw.types.UniCode;

import std.algorithm : map, joiner;
import std.array : array;
import std.conv : to;
import std.range : walkLength, take, tail, drop, dropBack; /// 
repeat, padLeft, padRight

import std.stdio;
import std.uni : Grapheme, byGrapheme;

/// within this file: gudtUGC



shared static this() { }  /// the following will be executed 
only‐once per‐app:
static this() { } /// the following will be executed 
only‐once per‐thread:
static ~this() { }/// the following will be executed 
only‐once per‐thread:
shared static ~this() { } /// the following will be executed 
only‐once per‐app:




alias stringUGC = Grapheme;
alias stringUGC08 = gudtUGC!(stringUTF08);
alias stringUGC16 = gudtUGC!(stringUTF16);
alias stringUGC32 = gudtUGC!(stringUTF32);
alias stringUTF08 = string;  /// same as immutable(char )[];
alias stringUTF16 = wstring; /// same as immutable(wchar)[];
alias stringUTF32 = dstring; /// same as immutable(dchar)[];

enum isTypeSupported(type) =
   is(type == stringUTF08) ||
   is(type == stringUTF16) ||
   is(type == stringUTF32)
   ;

/// mixin templateUGC!(stringUTF08, r"gudtUGC08"d);
/// mixin templateUGC!(stringUTF16, r"gudtUGC16"d);
/// mixin templateUGC!(stringUTF32, r"gudtUGC32"d);
/// template templateUGC (typeStringUTF, alias lstrStructureID) { 
/// if these were possible there will be no need for stringUGC## 
aliases in main()


public struct gudtUGC(typeStringUTF) { /// UniCode 
grapheme‐cluster‐aware string manipulation (implemented for 
one‐based operations)


   static assert(
  isTypeSupported!(typeStringUTF),
  r"ooops … gudtUGC structure requires 
[string|wstring|dstring] ≠ ["d ~ typeStringUTF.stringof ~ r"]"d
  ); /// meaning: this will halt compilation if this UDT 
was instantiated with a type other than the ones intended


   /// provides: public property size_t count

   /// provides: public size_t decode(typeStringUTF 
strSequence)

   /// provides: public typeStringUTF encode()

   /// provides: public gudtUGC!(type

Re: static assert not printing out along the error diagnostic

2021-07-14 Thread jfondren via Digitalmars-d-learn

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`

```


Re: static assert not printing out along the error diagnostic

2021-07-14 Thread someone via Digitalmars-d-learn

On Wednesday, 14 July 2021 at 23:44:18 UTC, jfondren wrote:

On Wednesday, 14 July 2021 at 22:59:38 UTC, someone wrote:

[...]


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.


I can't believe I completely forgot about my alias; I am a stupid 
to the say the least, I raised this issue without thinking much :(


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.


ACK for the static asserts.

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:


A few days ago when I started coding this UDT (and started 
thinking of *actually using it*) the first thing that came to 
mind was that to get a simple, say, strWhatever.left(1) I would 
end up writing gudtUGC!(dstring)(strWhatever).left(1).encode() or 
gudtUGC!(dstring)(strWhatever).leftAsUTF(1) or something akin to 
these ones, so I played a bit with the aliases and ended up with 
gudtUGC[08|16|32] which seemed a bit more friendly and 
crystal-clear on their types. The thing with aliases is that they 
are really useful *until* you forgot you're using aliases and end 
up biting yourself on a situation like this one. Again, sorry for 
being too silly on my part.



```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`

```


I will explore this approach.

Thank you very much for your time and your detailed step-by-step 
reply. I guess you were LoL when you firs