digitalmars.D - The compiler swallows opDispatch errors
- Steven Schveighoffer (49/49) Aug 27 2021 This bit me again:
- bauss (11/59) Aug 29 2021 I'd argue that opDispatch shouldn't have any constraints on it.
- Adam D Ruppe (23/24) Aug 30 2021 Well my rule would be if opDispatch is considered, the error
- H. S. Teoh (12/48) Aug 30 2021 But what about this then:
- Steven Schveighoffer (15/63) Aug 30 2021 Let's see:
- Steven Schveighoffer (26/33) Aug 30 2021 If you mean *implicit constraints*, then yes. But not allowing
- Nicholas Wilson (4/7) Aug 31 2021 https://github.com/dlang/dmd/pull/12288#issuecomment-802852873
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 2021
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. -SteveI'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 2021
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 2021
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: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 RobinsonI'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 2021
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: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. -SteveOn Monday, 30 August 2021 at 06:13:03 UTC, bauss wrote:But what about this then: struct A { void opDispatch(string s)() if (s0934hjslfadfAaarrgghh) { return; } } Should this generate an error or not?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 2021
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 2021
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 2021