| Thread overview | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
February 14, 2015 Template constraints | ||||
|---|---|---|---|---|
| ||||
There's been recurring discussion about failing constraints not generating nice error messages.
void fun(T)(T x) if (complicated_condition) { ... }
struct Type(T)(T x) if (complicated_condition) { ... }
If complicated_condition is not met, the symbol simply disappears and the compiler error message just lists is as a possible, but not viable, candidate.
I think one simple step toward improving things is pushing the condition in a static_assert inside type definitions:
void fun(T)(T x) if (complicated_condition) { ... } // no change
struct Type(T)(T x)
{
static assert(complicated_condition, "Informative message.");
...
}
This should improve error messages for types (only). The rationale is that it's okay for types to refuse compilation because types, unlike functions, don't overload. The major reason for template constraints in functions is allowing for good overloading.
Andrei
| ||||
February 14, 2015 Re: Template constraints | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Saturday, 14 February 2015 at 17:00:33 UTC, Andrei Alexandrescu wrote: > There's been recurring discussion about failing constraints not generating nice error messages. > > void fun(T)(T x) if (complicated_condition) { ... } > struct Type(T)(T x) if (complicated_condition) { ... } > > If complicated_condition is not met, the symbol simply disappears and the compiler error message just lists is as a possible, but not viable, candidate. > > I think one simple step toward improving things is pushing the condition in a static_assert inside type definitions: > > void fun(T)(T x) if (complicated_condition) { ... } // no change > struct Type(T)(T x) > { > static assert(complicated_condition, "Informative message."); > ... > } > > This should improve error messages for types (only). The rationale is that it's okay for types to refuse compilation because types, unlike functions, don't overload. The major reason for template constraints in functions is allowing for good overloading. > > > Andrei I agree, but also for function should conditions that do not effect overloading go into static asserts. For example if I were to write an algorithm that works with forward ranges and can be optimized for random access ranges but needs assignable elements in any case: void foo(R)(R r) if(isForwardRange!R && !isRandomAccessRange!R) { static assert(hasAssignableElements!R, "informative error msg"); } void foo(R)(R r) if(isRandomAccessRange!R) { static assert(hasAssignableElements!R, "informative error msg"); } | |||
February 14, 2015 Re: Template constraints | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Tobias Pankrath | On 2/14/15 9:06 AM, Tobias Pankrath wrote:
> I agree, but also for function should conditions that do not effect
> overloading go into static asserts.
>
> For example if I were to write an algorithm that works with forward
> ranges and can be optimized for random access ranges but needs
> assignable elements in any case:
>
> void foo(R)(R r) if(isForwardRange!R && !isRandomAccessRange!R)
> {
> static assert(hasAssignableElements!R, "informative error msg");
> }
>
> void foo(R)(R r) if(isRandomAccessRange!R)
> {
> static assert(hasAssignableElements!R, "informative error msg");
> }
That would impact cross-module overloading. -- Andrei
| |||
February 14, 2015 Re: Template constraints | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Saturday, 14 February 2015 at 17:00:33 UTC, Andrei Alexandrescu wrote:
> ...
> This should improve error messages for types (only). The rationale is that it's okay for types to refuse compilation because types, unlike functions, don't overload.
There was talk about enabling IFTI for constructors. If that is done, some types _will_ overload. But for the remaining ones (i.e. those that we don't want to overloadable), I think it's a good strategy.
| |||
February 14, 2015 Re: Template constraints | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Saturday, 14 February 2015 at 17:00:33 UTC, Andrei Alexandrescu wrote:
> There's been recurring discussion about failing constraints not generating nice error messages.
>
> void fun(T)(T x) if (complicated_condition) { ... }
> struct Type(T)(T x) if (complicated_condition) { ... }
>
> If complicated_condition is not met, the symbol simply disappears and the compiler error message just lists is as a possible, but not viable, candidate.
>
> I think one simple step toward improving things is pushing the condition in a static_assert inside type definitions:
>
> void fun(T)(T x) if (complicated_condition) { ... } // no change
> struct Type(T)(T x)
> {
> static assert(complicated_condition, "Informative message.");
> ...
> }
>
> This should improve error messages for types (only). The rationale is that it's okay for types to refuse compilation because types, unlike functions, don't overload. The major reason for template constraints in functions is allowing for good overloading.
>
>
> Andrei
I've done this myself for the same reason. I may split up the condition into multiple 'static assert' statements which gives a more specific error message. However, I wonder if there's a better solution that we can incorporate in to D. The trouble with template constraints is that, if you have complex conditions, there's no easy way to fall back to a default state. You would have to duplicate all the conditions and write "not this and not this and not this and ...".
Template specializations can fall back to a default state, but not template constraints. So what if we were to add a feature that would allow us to do just that?
void test(Range)(Range r) if(isRandomAccessRange!Range)
{
...
}
void test(Range)(Range r) default
{
static assert(false, "descriptive error message");
}
I'm not claiming this is a better solution; I'm simply putting the idea out there.
| |||
February 15, 2015 Re: Template constraints | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Even then this "hides" some errors and debugging isn't easy (figuring out why the template constraint failed). I've been planning on creating a DIP addressing this for ages, I should probably get around to that.
Atila
On Saturday, 14 February 2015 at 17:00:33 UTC, Andrei Alexandrescu wrote:
> There's been recurring discussion about failing constraints not generating nice error messages.
>
> void fun(T)(T x) if (complicated_condition) { ... }
> struct Type(T)(T x) if (complicated_condition) { ... }
>
> If complicated_condition is not met, the symbol simply disappears and the compiler error message just lists is as a possible, but not viable, candidate.
>
> I think one simple step toward improving things is pushing the condition in a static_assert inside type definitions:
>
> void fun(T)(T x) if (complicated_condition) { ... } // no change
> struct Type(T)(T x)
> {
> static assert(complicated_condition, "Informative message.");
> ...
> }
>
> This should improve error messages for types (only). The rationale is that it's okay for types to refuse compilation because types, unlike functions, don't overload. The major reason for template constraints in functions is allowing for good overloading.
>
>
> Andrei
| |||
February 16, 2015 Re: Template constraints | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On 2/14/15 12:00 PM, Andrei Alexandrescu wrote: > There's been recurring discussion about failing constraints not > generating nice error messages. > > void fun(T)(T x) if (complicated_condition) { ... } > struct Type(T)(T x) if (complicated_condition) { ... } > > If complicated_condition is not met, the symbol simply disappears and > the compiler error message just lists is as a possible, but not viable, > candidate. > > I think one simple step toward improving things is pushing the condition > in a static_assert inside type definitions: > > void fun(T)(T x) if (complicated_condition) { ... } // no change > struct Type(T)(T x) > { > static assert(complicated_condition, "Informative message."); > ... > } > > This should improve error messages for types (only). The rationale is > that it's okay for types to refuse compilation because types, unlike > functions, don't overload. The major reason for template constraints in > functions is allowing for good overloading. > > > Andrei Wait, isn't this OK? struct S(T) if(is(T == int)) { ... } struct S(T) if(is(T == double)) { ... } I mean we could do it like this: struct S(T) { static if(is(T == int)) { ... // int mode } else static if(is(T == double)) { ... // double mode } else static assert(0); } but that defeats the purpose of being able to split the "int mode" stuff from the "double mode" stuff. They may even be in multiple modules. Is this not "overloading" of types? I don't think this should be frowned upon. I think instead of this, we should try and just make the messages better. Note that something like you are suggesting requires much rewriting of code. Have you considered something like this? forum.dlang.org/post/m4nnrk$1ml5$1@digitalmars.com -Steve | |||
February 17, 2015 Re: Template constraints | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Saturday, 14 February 2015 at 17:00:33 UTC, Andrei Alexandrescu wrote:
> There's been recurring discussion about failing constraints not generating nice error messages.
>
> void fun(T)(T x) if (complicated_condition) { ... }
> struct Type(T)(T x) if (complicated_condition) { ... }
>
> If complicated_condition is not met, the symbol simply disappears and the compiler error message just lists is as a possible, but not viable, candidate.
>
> I think one simple step toward improving things is pushing the condition in a static_assert inside type definitions:
>
> void fun(T)(T x) if (complicated_condition) { ... } // no change
> struct Type(T)(T x)
> {
> static assert(complicated_condition, "Informative message.");
> ...
> }
>
> This should improve error messages for types (only). The rationale is that it's okay for types to refuse compilation because types, unlike functions, don't overload. The major reason for template constraints in functions is allowing for good overloading.
>
>
> Andrei
This seems like a lot of manual labor to cover a compiler limitation that is going to affect not just Phobos, but every D project which uses constraints (or template type specialization?) instead of interfaces. Ideally, the compiler would be able to say, "Well the function name and the parameter list exactly match these possibilities, so let's run through each constraint applied in the set of possibilities and explicitly dump the boolean result for each constraint."
| |||
February 19, 2015 Re: Template constraints | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On 16/02/2015 19:31, Steven Schveighoffer wrote: > Wait, isn't this OK? > > struct S(T) if(is(T == int)) > { > ... > } > > struct S(T) if(is(T == double)) > { > ... > } ... > They may even be in multiple modules. > > Is this not "overloading" of types? I don't think this should be frowned > upon. OK, so the idea might not be appropriate for types. What about for methods though - if a method doesn't have overloads, static assert might be better than a constraint. Even where there are overloads, (part of) the constraint may be OK to push into static assert instead for better error messages. > I think instead of this, we should try and just make the messages > better. Note that something like you are suggesting requires much > rewriting of code. True, but until the compiler improves perhaps we could use this technique for methods e.g. in new code. | |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply