www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Showing a user specified error message when no overloads match

reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
I have a template function with a particular constraint (in this 
case [1]). When this constraint doesn't match, I want to give the 
user a suggestion what to do instead.

The only way I know of to do this currently is to relax the 
template constraints, and adding a `static assert`. However, if 
there is another function that would match, this would lead to an 
ambiguity.

Is there a way to trigger the static assert only if there are no 
other overloads (including members and UFCS functions) that 
match? Or maybe a solution that only works for this particular 
case?

[1] https://github.com/D-Programming-Language/phobos/pull/2350
Jul 25 2014
parent reply "Trass3r" <un known.com> writes:
Yeah that's the price we pay for the simplicity.
Also most constraints directly or indirectly consist of a complex 
boolean expressions and you don't get any hint which part failed 
and why.
Jul 26 2014
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
Hmmm... thinking about it, is this possible?

1. Remove the constraints to match anything.
2. Inside the template, have some construct that enumerates all 
possible overloads and UFCS functions that are visible at the 
point of instantiation.
3. If this set contains only the current template, use a static 
assert to print the message.
4. Otherwise, make the template fail to compile somehow (for 
example, evaluate a semantically invalid expression), and hope 
that the compiler will then take the other overloads into 
consideration (SFINAE).

Could this work?
Jul 26 2014
parent reply "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
On Sat, Jul 26, 2014 at 05:14:44PM +0000, via Digitalmars-d-learn wrote:
 Hmmm... thinking about it, is this possible?
 
 1. Remove the constraints to match anything.
 2. Inside the template, have some construct that enumerates all possible
 overloads and UFCS functions that are visible at the point of instantiation.
 3. If this set contains only the current template, use a static assert
 to print the message.
 4. Otherwise, make the template fail to compile somehow (for example,
 evaluate a semantically invalid expression), and hope that the
 compiler will then take the other overloads into consideration
 (SFINAE).
 
 Could this work?
D does not have SFINAE. This has been discussed before. I proposed the following solution: - Sig constraints should match all types that the function *logically* accepts -- even if the current implementation does not support some of said types. - In the function body, use a `static if` chain to implement specializations. - In the final else clause, do a static assert(0) with a user-friendly error message. T -- In order to understand recursion you must first understand recursion.
Jul 26 2014
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Sunday, 27 July 2014 at 00:43:40 UTC, H. S. Teoh via 
Digitalmars-d-learn wrote:
 On Sat, Jul 26, 2014 at 05:14:44PM +0000, via 
 Digitalmars-d-learn wrote:
 Hmmm... thinking about it, is this possible?
 
 1. Remove the constraints to match anything.
 2. Inside the template, have some construct that enumerates 
 all possible
 overloads and UFCS functions that are visible at the point of 
 instantiation.
 3. If this set contains only the current template, use a 
 static assert
 to print the message.
 4. Otherwise, make the template fail to compile somehow (for 
 example,
 evaluate a semantically invalid expression), and hope that the
 compiler will then take the other overloads into consideration
 (SFINAE).
 
 Could this work?
D does not have SFINAE.
http://dlang.org/templates-revisited.html says otherwise. But thinking about it, I've never seen it used anywhere, nor used it myself, and even the examples in the linked article under the SFINAE section use `is` expressions instead...
 This has been discussed before. I proposed the following 
 solution:

 - Sig constraints should match all types that the function 
 *logically*
   accepts -- even if the current implementation does not 
 support some of
   said types.

 - In the function body, use a `static if` chain to implement
   specializations.

 - In the final else clause, do a static assert(0) with a 
 user-friendly
   error message.
But this prevents other people from providing overloads in their own modules :-(
Jul 27 2014
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Sunday, 27 July 2014 at 08:40:42 UTC, Marc Schütz wrote:
 On Sunday, 27 July 2014 at 00:43:40 UTC, H. S. Teoh via 
 Digitalmars-d-learn wrote:
 On Sat, Jul 26, 2014 at 05:14:44PM +0000, via 
 Digitalmars-d-learn wrote:
 Hmmm... thinking about it, is this possible?
 
 1. Remove the constraints to match anything.
 2. Inside the template, have some construct that enumerates 
 all possible
 overloads and UFCS functions that are visible at the point of 
 instantiation.
 3. If this set contains only the current template, use a 
 static assert
 to print the message.
 4. Otherwise, make the template fail to compile somehow (for 
 example,
 evaluate a semantically invalid expression), and hope that the
 compiler will then take the other overloads into consideration
 (SFINAE).
 
 Could this work?
D does not have SFINAE.
http://dlang.org/templates-revisited.html says otherwise. But thinking about it, I've never seen it used anywhere, nor used it myself, and even the examples in the linked article under the SFINAE section use `is` expressions instead...
It's poorly worded: "is" is what functionally replaces the SFINAE functionality.
 This has been discussed before. I proposed the following 
 solution:

 - Sig constraints should match all types that the function 
 *logically*
  accepts -- even if the current implementation does not 
 support some of
  said types.

 - In the function body, use a `static if` chain to implement
  specializations.

 - In the final else clause, do a static assert(0) with a 
 user-friendly
  error message.
But this prevents other people from providing overloads in their own modules :-(
Not quite: It prevents users from providing "un-ambigous" overloads. But arguably, it's a good thing. I've argued before against the "over use" of template constraints for type validation. Their initial use was really to restrict the "logical types" that can be used, and help with template overload. Not for validation. If the type then has some "flaw", that's where the static ifs come in. This as a several advantages: - Cleaner interface (*cough* http://dlang.org/phobos/std_algorithm.html#sort *cough*) - Better error messages in case of non match ("The provided range can not be stable sorted, because its elements are not lvalues"). - Prevents "overload" turning into a hijack. You can add your overload (without ambiguity) if it works on types completely un-related. That won't be ambiguous. But if you are adding a very specific variant, it might be better that your function can't be accidentally called, and create surprises later.
Jul 27 2014
parent reply "Vlad Levenfeld" <vlevenfeld gmail.com> writes:
opDispatch behaves as though it has SFINAE. When something fails 
in the definition (like I am having now, some of the symbols I 
used in it hadn't been imported) there won't ever be an error 
message, I just get "Error: no property 'bar' for type 'Foo'"

In one case I had to use static ifs and pragmas and static 
assert(0) to get error messages out of opDispatch because static 
assert messages were being suppressed.

Its very frustrating.
Jul 28 2014
next sibling parent reply "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
On Tue, Jul 29, 2014 at 03:34:22AM +0000, Vlad Levenfeld via
Digitalmars-d-learn wrote:
 opDispatch behaves as though it has SFINAE. When something fails in
 the definition (like I am having now, some of the symbols I used in it
 hadn't been imported) there won't ever be an error message, I just get
 "Error: no property 'bar' for type 'Foo'"
 
 In one case I had to use static ifs and pragmas and static assert(0)
 to get error messages out of opDispatch because static assert messages
 were being suppressed.
 
 Its very frustrating.
You're right, opDispatch behaves like SFINAE. I've had trouble debugging it before, because when it works, it works very well, but when you accidentally make a typo, it just "disappears" -- you get an error that the property is missing, but the actual error inside opDispatch has been gagged and it's almost impossible to get at the actual error message. Maybe we should file an enhancement bug to improve error reporting for opDispatch. T -- Time flies like an arrow. Fruit flies like a banana.
Jul 29 2014
parent reply "Vlad Levenfeld" <vlevenfeld gmail.com> writes:
On Tuesday, 29 July 2014 at 15:47:08 UTC, H. S. Teoh via 
Digitalmars-d-learn wrote:
 Maybe we should file an enhancement bug to improve error 
 reporting for
 opDispatch.
I found this https://issues.dlang.org/show_bug.cgi?id=8387 and cast a vote for it. I've cast a few votes in the bugtracker, don't know if that raises the visibility of the bugs or what. Maybe not since this bug had 4 votes to begin with and has been around since 2012... or maybe 4 is not that many.
Jul 29 2014
parent "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
On Tue, Jul 29, 2014 at 11:30:01PM +0000, Vlad Levenfeld via
Digitalmars-d-learn wrote:
 On Tuesday, 29 July 2014 at 15:47:08 UTC, H. S. Teoh via Digitalmars-d-learn
 wrote:
Maybe we should file an enhancement bug to improve error reporting
for opDispatch.
I found this https://issues.dlang.org/show_bug.cgi?id=8387 and cast a vote for it. I've cast a few votes in the bugtracker, don't know if that raises the visibility of the bugs or what. Maybe not since this bug had 4 votes to begin with and has been around since 2012... or maybe 4 is not that many.
Hmm. Looks like that bug is frequently encountered, judging by the number of duplicates it has. Maybe it's time to complain in the main D forum in hopes that one of the compiler devs will respond. ;-) T -- "The number you have dialed is imaginary. Please rotate your phone 90 degrees and try again."
Jul 29 2014
prev sibling next sibling parent Artur Skawina via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On 07/29/14 17:45, H. S. Teoh via Digitalmars-d-learn wrote:
 You're right, opDispatch behaves like SFINAE. I've had trouble debugging
 it before, because when it works, it works very well, but when you
 accidentally make a typo, it just "disappears" -- you get an error that
 the property is missing, but the actual error inside opDispatch has been
 gagged and it's almost impossible to get at the actual error message.
D's overloaded operators are (usually) normal (templated) functions, you can use `a.opDispatch!"blah"` instead of `a.blah` to see what's wrong. What's really nasty is the way phobos handles `toString` - if that method fails to compile then you get a usually not very helpful default, and no warning that something is wrong. It's easy to break `toString` w/o noticing anything. Figuring out later what exactly broke can be "interesting". Still doable via the above mentioned trick, but you'll need to create a mock `sink` etc. artur
Jul 29 2014
prev sibling parent "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
On Tue, Jul 29, 2014 at 06:57:00PM +0200, Artur Skawina via Digitalmars-d-learn
wrote:
 On 07/29/14 17:45, H. S. Teoh via Digitalmars-d-learn wrote:
 You're right, opDispatch behaves like SFINAE. I've had trouble
 debugging it before, because when it works, it works very well, but
 when you accidentally make a typo, it just "disappears" -- you get
 an error that the property is missing, but the actual error inside
 opDispatch has been gagged and it's almost impossible to get at the
 actual error message.
D's overloaded operators are (usually) normal (templated) functions, you can use `a.opDispatch!"blah"` instead of `a.blah` to see what's wrong.
Good idea! Though it's still limited when the opDispatch call is buried under several layers of templated generic functions. The problem is, sometimes you don't even know that the problem is inside opDispatch because failure causes it to default to something else (that generates an unrelated error message), and the actual error message is gagged. Then once you do narrow it down, it isn't always so easy to get at the code that calls opDispatch, since it could be quite deep inside generic functions. Once you pinpoint it, though, your idea is pretty good -- invoke it directly so that the errors are not gagged.
 What's really nasty is the way phobos handles `toString` - if that
 method fails to compile then you get a usually not very helpful
 default, and no warning that something is wrong. It's easy to break
 `toString` w/o noticing anything. Figuring out later what exactly
 broke can be "interesting". Still doable via the above mentioned
 trick, but you'll need to create a mock `sink` etc.
[...] Mock sinks are very easy, because a delegate is a sink. :-) import std.range; int delegate(const(char)[]) dg; static assert(isOutputRange!(typeof(dg), const(char)[])); // passes Which makes it very handy to insert arbitrary debugging code for toString via dg.formattedWrite("%s", obj). T -- "I'm running Windows '98." "Yes." "My computer isn't working now." "Yes, you already said that." -- User-Friendly
Jul 29 2014