www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - The compiler swallows opDispatch errors

reply Steven Schveighoffer <schveiguy gmail.com> writes:
This bit me again:

```d
struct S
{
    void opDispatch(string s)() {
      writeln("you called " ~ s);
    }
}

void main()
{
    S s;
    s.foo();
}
```

The result?

```
onlineapp.d(11): Error: no property `foo` for type `onlineapp.S`
onlineapp.d(11):        potentially malformed `opDispatch`. Use an 
explicit instantiation to get a better error message
```

What? I have to tell the compiler to *explicitly instantiate opDispatch* 
in order for it to tell me the actual message?

What I expected is something like:

Error: no symbol `writeln`, please import std.stdio

What is happening here is that if `opDispatch` doesn't compile *for any 
reason*, it's not considered a valid instantiation.

This is not how any other functions templates work. If you call a 
function or template, and it doesn't compile, it tells you *why* it 
didn't compile and gives an error. With `opDispatch`, it implicitly is 
adding one of the worst template constraints `if (__traits(compiles, 
<function body>))`. This hides so much stuff, and makes it really hard 
to find out why something doesn't work, or results in calling very 
surprising UFCS functions.

Let's make it even more obscure!

```d
s.get();
```


```
onlineapp.d(11): Error: template `object.get` cannot deduce function 
from argument types `!()(S)`, candidates are:
/dlang/dmd/linux/bin64/../../src/druntime/import/object.d(3077): 
`get(K, V)(inout(V[K]) aa, K key, lazy inout(V) defaultValue)`
/dlang/dmd/linux/bin64/../../src/druntime/import/object.d(3084): 
`get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue)`
```

Yeah, can we please fix this? `opDispatch` should use the *same rules* 
as any other function template -- if it matches, compile. No implicit 
"compilation must succeed" BS.

-Steve
Aug 27
next sibling parent reply bauss <jj_1337 live.dk> writes:
On Friday, 27 August 2021 at 16:08:37 UTC, Steven Schveighoffer 
wrote:
 This bit me again:

 ```d
 struct S
 {
    void opDispatch(string s)() {
      writeln("you called " ~ s);
    }
 }

 void main()
 {
    S s;
    s.foo();
 }
 ```

 The result?

 ```
 onlineapp.d(11): Error: no property `foo` for type `onlineapp.S`
 onlineapp.d(11):        potentially malformed `opDispatch`. Use 
 an explicit instantiation to get a better error message
 ```

 What? I have to tell the compiler to *explicitly instantiate 
 opDispatch* in order for it to tell me the actual message?

 What I expected is something like:

 Error: no symbol `writeln`, please import std.stdio

 What is happening here is that if `opDispatch` doesn't compile 
 *for any reason*, it's not considered a valid instantiation.

 This is not how any other functions templates work. If you call 
 a function or template, and it doesn't compile, it tells you 
 *why* it didn't compile and gives an error. With `opDispatch`, 
 it implicitly is adding one of the worst template constraints 
 `if (__traits(compiles, <function body>))`. This hides so much 
 stuff, and makes it really hard to find out why something 
 doesn't work, or results in calling very surprising UFCS 
 functions.

 Let's make it even more obscure!

 ```d
 s.get();
 ```


 ```
 onlineapp.d(11): Error: template `object.get` cannot deduce 
 function from argument types `!()(S)`, candidates are:
 /dlang/dmd/linux/bin64/../../src/druntime/import/object.d(3077): `get(K,
V)(inout(V[K]) aa, K key, lazy inout(V) defaultValue)`
 /dlang/dmd/linux/bin64/../../src/druntime/import/object.d(3084): `get(K,
V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue)`
 ```

 Yeah, can we please fix this? `opDispatch` should use the *same 
 rules* as any other function template -- if it matches, 
 compile. No implicit "compilation must succeed" BS.

 -Steve
I'd argue that opDispatch shouldn't have any constraints on it. It's a function that's rewritten by the compiler and so any error checking should be done solely by the compiler and without any attributing such as __traits(compiles) If an opDispatch is declared that cannot be compiled then arguably it should stop the entire compilation as something is clearly wrong. It's almost equivalent to a try/catch without handling an exception, but this time in the compiler.
Aug 29
next sibling parent reply Adam D Ruppe <destructionator gmail.com> writes:
On Monday, 30 August 2021 at 06:13:03 UTC, bauss wrote:
 I'd argue that opDispatch shouldn't have any constraints on it.
Well my rule would be if opDispatch is considered, the error shoudl be printed. But you can prevent opDispatch from being considered by putting a constraint on it. So struct A { void opDispatch(string s)() { dfsdfsdf; } } A a; a.whatever; // REPORT THE FULL ERROR OMG BUT struct A { void opDispatch(string s)() if(s != "whatever") { dfsdfsdf; } } A a; a.whatever; // "no such property: whatver" so you only get "no such property" if the opDispatch is not considered at all because the outer constraint filtered it out.
Aug 30
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Aug 30, 2021 at 12:19:21PM +0000, Adam D Ruppe via Digitalmars-d wrote:
 On Monday, 30 August 2021 at 06:13:03 UTC, bauss wrote:
 I'd argue that opDispatch shouldn't have any constraints on it.
Well my rule would be if opDispatch is considered, the error shoudl be printed. But you can prevent opDispatch from being considered by putting a constraint on it. So struct A { void opDispatch(string s)() { dfsdfsdf; } } A a; a.whatever; // REPORT THE FULL ERROR OMG BUT struct A { void opDispatch(string s)() if(s != "whatever") { dfsdfsdf; } } A a; a.whatever; // "no such property: whatver" so you only get "no such property" if the opDispatch is not considered at all because the outer constraint filtered it out.
But what about this then: struct A { void opDispatch(string s)() if (s0934hjslfadfAaarrgghh) { return; } } Should this generate an error or not? T -- Why are you blatanly misspelling "blatant"? -- Branden Robinson
Aug 30
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/30/21 8:48 AM, H. S. Teoh wrote:
 On Mon, Aug 30, 2021 at 12:19:21PM +0000, Adam D Ruppe via Digitalmars-d wrote:
 On Monday, 30 August 2021 at 06:13:03 UTC, bauss wrote:
 I'd argue that opDispatch shouldn't have any constraints on it.
Well my rule would be if opDispatch is considered, the error shoudl be printed. But you can prevent opDispatch from being considered by putting a constraint on it. So struct A { void opDispatch(string s)() { dfsdfsdf; } } A a; a.whatever; // REPORT THE FULL ERROR OMG BUT struct A { void opDispatch(string s)() if(s != "whatever") { dfsdfsdf; } } A a; a.whatever; // "no such property: whatver" so you only get "no such property" if the opDispatch is not considered at all because the outer constraint filtered it out.
But what about this then: struct A { void opDispatch(string s)() if (s0934hjslfadfAaarrgghh) { return; } } Should this generate an error or not?
Let's see: ```d void foo()() if (s0934hjslfadfAaarrgghh) {} void main() { foo(); } ``` ``` onlineapp.d(1): Error: undefined identifier `s0934hjslfadfAaarrgghh` onlineapp.d(7): Error: `foo()() if (s0934hjslfadfAaarrgghh)` has no effect ``` Yes, it should, if considered. -Steve
Aug 30
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/30/21 2:13 AM, bauss wrote:

 I'd argue that opDispatch shouldn't have any constraints on it.
If you mean *implicit constraints*, then yes. But not allowing constraints would be insanely awkward. We might be saying the same thing, but I'm not sure.
 It's a function that's rewritten by the compiler and so any error 
 checking should be done solely by the compiler and without any 
 attributing such as __traits(compiles)
I'm not sure how the compiler implements the check, but the effect is the same as adding a `__traits(compiles, <function body>)` constraint implicitly.
 
 If an opDispatch is declared that cannot be compiled then arguably it 
 should stop the entire compilation as something is clearly wrong.
Determining the compilability of a declared template is complex -- usually you need an instantiation. And opDispatch doesn't have to necessarily compile for all possible instantiations. For instance, opDispatch won't be used if there is already a member of that name. I *think* the point of using non-compilation as an implicit constraint is so you can do things like: ```d auto opDispatch(string n, Args...)(Args args) { return mixin("something.", n, "(args)"); } ``` and not have to repeat the callability of that in the constraint. However, the cure is worse than the disease here IMO. For sure, if this were to be "fixed", we'd have to deprecate for a while as I'm sure some libraries utilize this "feature". As a mitigating feature, perhaps something like `__traits(compiles, template)` could be added as a constraint possibility which will mean "the thing I'm constraining here compiles as instantiated". -Steve
Aug 30
prev sibling parent Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Friday, 27 August 2021 at 16:08:37 UTC, Steven Schveighoffer 
wrote:
 This bit me again:
 What? I have to tell the compiler to *explicitly instantiate 
 opDispatch* in order for it to tell me the actual message?
https://github.com/dlang/dmd/pull/12288#issuecomment-802852873 because I couldn't figure out how to do it properly.
Aug 31