Difference between "can call" and "can compile"

Steven Schveighoffer schveiguy at gmail.com
Tue Sep 8 20:04:32 UTC 2020


On 9/8/20 3:48 PM, Paul Backus wrote:

> 
> writeln could just as easily use __traits(hasMember) to check for the 
> presence of toString, and print a warning message if it exists but 
> doesn't compile. In other words, the problem here is that writeln 
> assumes "toString doesn't compile" implies "toString isn't supposed to 
> compile," which in most cases is not actually true. Adding a new 
> language feature is neither necessary nor sufficient to correct that 
> assumption.

hasMember is something I've been using a lot more lately. But it's not 
the same.

For instance, if the member was toString(int x), then it has the member 
toString, but would not match a call, and therefore should not be used. 
Contrived, but possible in other contexts.

Or the "member" could be a UFCS function, where __traits(hasMember) 
doesn't work.

> I agree that it would probably be a good idea to change writeln (and 
> other similar code) so that it does not make these kinds of assumptions.

possibly with writeln, you can make the assumption that toString really 
should be picked even if it's not formed correctly. But that's not the 
case for other things.


> Let me make this concrete. Here's a contrived example program that 
> illustrates the problem from your first post in this thread:
> 
> --- example.d
> void use(alias fun)()
>      if (__traits(compiles, fun()))
> {}
> 
> void bad()()
> {
>      oops();
> }
> 
> void irrelevant()()
> {
>      oops();
> }
> 
> void test()
> {
>      enum _ = __traits(compiles, irrelevant());
>      use!bad();
> }
> ---
> 

...

> And here's what I would like the compiler to print with my proposed 
> `-verrors=spec-but-only-stuff-i-care-about` flag:
> 
> ---
> (spec:1) example.d(7): Error: undefined identifier `oops`
> (spec:1) example.d(2): Error: template instance `example.bad!()` error 
> instantiating
> (spec:1) example.d(2): Error: template instance `example.bad!()` cannot 
> resolve forward reference
> (spec:1) example.d(2): Error: template `example.bad` cannot deduce 
> function from argument types `!()()`, candidates are:
> (spec:1) example.d(5):        `bad()()`
> (spec:1) example.d(7): Error: undefined identifier `oops`
> (spec:1) example.d(2): Error: template instance `example.bad!()` error 
> instantiating
> (spec:1) example.d(2): Error: template instance `example.bad!()` cannot 
> resolve forward reference
> (spec:1) example.d(2): Error: template `example.bad` cannot deduce 
> function from argument types `!()()`, candidates are:
> (spec:1) example.d(5):        `bad()()`
> example.d(18): Error: template instance `example.use!(bad)` does not 
> match template declaration `use(alias fun)()`
>    with `fun = bad()()`
>    must satisfy the following constraint:
> `       __traits(compiles, fun())`
> ---
> 
> Notice how the above includes messages about speculative errors in 
> `bad`, but does NOT include messages about speculative errors in 
> `irrelevant`.

Here's what it says when there is no constraint (and when I actually 
call fun inside use):

onlineapp.d(9): Error: undefined identifier oops
onlineapp.d(4): Error: template instance onlineapp.bad!() error 
instantiating
onlineapp.d(20):        instantiated from here: use!(bad)

This is what I want it to do. Why can't it just do that? Why do I need 
to see 10 more or even 5 more lines of irrelevant error messages?

-Steve


More information about the Digitalmars-d mailing list