www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - idea: static scope(failure)

reply Steven Schveighoffer <schveiguy gmail.com> writes:
Just recently running into some problems where deeply nested inside some 
library code, a static assert triggers. Of course, the static assert 
doesn't give any context with which it was called from *my* code.

And the compiler helpfully says something like (names changed to protect 
the proprietary):

```
somelibrary.d(263,16):        instantiated from here: `foo!(Json[string])`
somelibrary.d(286,25):        instantiated from here: `bar!(Json[string])`
somelibrary.d(204,16):        instantiated from here: `baz!(Json)`
somelibrary.d(263,16):        ... (1 instantiations, -v to show) ...
```

OK, so it stopped telling me useful information. And on top of that, the 
information isn't always enough to determine the cause, because in one 
of these instantiations, there's probably a static foreach, where I 
don't know which element being looped over is actually the cause.

If this was runtime I'd do something like:

```d
scope(failure) writeln("Context: ", relevant, info);
```

And then when the exception is thrown, I can see some context. But I 
can't do that at compile time.

What I've done instead is:

```d
static if(!__traits(compiles, entireLineOfCode)) pragma(msg, "Context: 
", relevant, info);
entireLineOfCode;
```

Which is *OK*, but the place where I might want to print the context 
might not be for just one line of code, you might have to repeat an 
entire mess of stuff. And I was thinking, wouldn't it be cool to just do 
something like:

```d
static scope(failure) pragma(msg, stuff);
```

Then, no matter what the context, or where it's failing, I can ensure 
that on a failure to compile, I can tease out some debug info at the 
relevant spot.

another possible syntax is something like:

```d
pragma(msgOnFailure, stuff);
```

Which might even be gagged if the compiler isn't actually failing (i.e. 
this is inside a `__traits(compiles)`)

Does this (or something like it) make sense?

-Steve
Jan 25 2022
next sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
Perhaps this can be simplified somewhat.

version(compiles) {
	...
} else {
	string str = __errorMessage;
	writeln(str);
}
Jan 25 2022
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/25/22 9:07 PM, rikki cattermole wrote:
 Perhaps this can be simplified somewhat.
 
 version(compiles) {
      ...
 } else {
      string str = __errorMessage;
      writeln(str);
 }
While this might be feasible, it suffers from the same problem as manually writing out the try/catch/finally blocks that scope(...) lowers to -- You need to enblock all the covered code, add indentation, modify lines that are far away (e.g. to add a brace) etc. That being said, I'd be happy with anything that helps here. -Steve
Jan 25 2022
prev sibling next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/25/22 8:58 PM, Steven Schveighoffer wrote:
 Just recently running into some problems where deeply nested inside some 
 library code, a static assert triggers. Of course, the static assert 
 doesn't give any context with which it was called from *my* code.
 
 And the compiler helpfully says something like (names changed to protect 
 the proprietary):
 
 ```
 somelibrary.d(263,16):        instantiated from here:
`foo!(Json[string])`
 somelibrary.d(286,25):        instantiated from here:
`bar!(Json[string])`
 somelibrary.d(204,16):        instantiated from here: `baz!(Json)`
 somelibrary.d(263,16):        ... (1 instantiations, -v to show) ...
 ```
 
 OK, so it stopped telling me useful information. And on top of that, the 
 information isn't always enough to determine the cause, because in one 
 of these instantiations, there's probably a static foreach, where I 
 don't know which element being looped over is actually the cause.
 
Just ran into this again (same project, ironically). I just had a new idea that maybe could be even better -- the lines above specify the line the instantiation happened, and what the template parameters are. But what is really missing is the other static information. Specifically, which static foreach loop iteration it's in. With that, I could piece together what is happening, and no special code is necessary to be added to get more diagnostic information. Maybe only output when -v is specified? One nice thing about failed compilation, you can just do it again and it is repeatable. -Steve
Apr 19 2022
prev sibling parent max haughton <maxhaton gmail.com> writes:
On Wednesday, 26 January 2022 at 01:58:23 UTC, Steven 
Schveighoffer wrote:
 Just recently running into some problems where deeply nested 
 inside some library code, a static assert triggers. Of course, 
 the static assert doesn't give any context with which it was 
 called from *my* code.

 And the compiler helpfully says something like (names changed 
 to protect the proprietary):

 ```
 somelibrary.d(263,16):        instantiated from here: 
 `foo!(Json[string])`
 somelibrary.d(286,25):        instantiated from here: 
 `bar!(Json[string])`
 somelibrary.d(204,16):        instantiated from here: 
 `baz!(Json)`
 somelibrary.d(263,16):        ... (1 instantiations, -v to 
 show) ...
 ```

 OK, so it stopped telling me useful information. And on top of 
 that, the information isn't always enough to determine the 
 cause, because in one of these instantiations, there's probably 
 a static foreach, where I don't know which element being looped 
 over is actually the cause.

 If this was runtime I'd do something like:

 ```d
 scope(failure) writeln("Context: ", relevant, info);
 ```

 And then when the exception is thrown, I can see some context. 
 But I can't do that at compile time.

 What I've done instead is:

 ```d
 static if(!__traits(compiles, entireLineOfCode)) pragma(msg, 
 "Context: ", relevant, info);
 entireLineOfCode;
 ```

 Which is *OK*, but the place where I might want to print the 
 context might not be for just one line of code, you might have 
 to repeat an entire mess of stuff. And I was thinking, wouldn't 
 it be cool to just do something like:

 ```d
 static scope(failure) pragma(msg, stuff);
 ```

 Then, no matter what the context, or where it's failing, I can 
 ensure that on a failure to compile, I can tease out some debug 
 info at the relevant spot.

 another possible syntax is something like:

 ```d
 pragma(msgOnFailure, stuff);
 ```

 Which might even be gagged if the compiler isn't actually 
 failing (i.e. this is inside a `__traits(compiles)`)

 Does this (or something like it) make sense?

 -Steve
As steve and I have been discussing on the D discord, I've implemented this locally and had a play with it. ```d void main() { string x; int y; set(x, y); } void set(T...)(ref T x) { foreach(idx, elem; x) { elem = "a string"; static scope(failure) static assert(0, "I couldn't set tuple[" ~ idx.stringof ~ "] equal to \"string\""); } } ``` works with basically 10 lines of code added to the compiler.
Apr 19 2022