www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Verbose checking of range category

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Hello,


Following Craig's questions about sort(), I was thinking of the 
following idea. How about defining a flag rangeCheckVerbose that emits 
explanatory messages during compilation whenever a range check fails?

Consider this prototype:

import std.stdio;

version = rangeCheckVerbose;

template isRandomAccessRange(T) {
     enum isRandomAccessRange = isRandomAccessRangeImpl!T.value;
}

template isRandomAccessRangeImpl(T) {
     enum hasEmpty = is(typeof(T.init.empty));
     enum hasFront = is(typeof(T.init.front));
     enum hasPopFront = is(typeof(T.init.popFront()));
     enum hasIndexing = is(typeof(T.init[1]));
     enum hasSlicing = is(typeof(T.init[1]));
     enum value = hasEmpty && hasFront && hasPopFront && hasIndexing && 
hasSlicing;
     version (rangeCheckVerbose) {
         static if (!value) {
             pragma(msg, "Type " ~ T.stringof ~ " is not a random access 
range because:");
             static if (!hasEmpty)
                 pragma(msg, "  no empty property");
             static if (!hasFront)
                 pragma(msg, "  no front property");
             static if (!hasPopFront)
                 pragma(msg, "  no popFront method");
             static if (!hasIndexing)
                 pragma(msg, "  no indexing");
             static if (!hasSlicing)
                 pragma(msg, "  no slicing");
         }
     }
}

void main()
{
     writeln(isRandomAccessRange!int);
}

This program will generate a valid executable, but will also print 
during compilation:

Type int is not a random access range because:
   no empty property
   no front property
   no popFront method
   no indexing
   no slicing

When a programmer has an odd issue with a range check, turning 
verboseness of checks could help.

What do you think?


Andrei
Dec 11 2010
next sibling parent Lutger Blijdestijn <lutger.blijdestijn gmail.com> writes:
It would be a welcome improvement. One downside is that the relation between 
the mismatched template and the range check is not there. I worry that it 
will be hard to understand when facing a lot of errors, but will have to try 
to find out. The error message dmd produces in the face of ambiguous 
overloading is the most model (foo matches both bar and baz).

Template constraints are great, but have taken away the possibility of very 
good custom error messages that static if / else / assert have. unaryFun and 
binaryFun of std.functional for example give really helpful messages. I'm 
hesitant to suggest another feature, but maybe an 'else' counterpart to 
template constraint checks could go a long way.
Dec 11 2010
prev sibling next sibling parent reply Jesse Phillips <jessekphillips+D gmail.com> writes:
Andrei Alexandrescu Wrote:

 This program will generate a valid executable, but will also print 
 during compilation:
 
 Type int is not a random access range because:
    no empty property
    no front property
    no popFront method
    no indexing
    no slicing
 
 When a programmer has an odd issue with a range check, turning 
 verboseness of checks could help.
 
 What do you think?
 
 
 Andrei
I think it would be best if DMD would emit which constraint failed. Better support for automatically say why would be good too. Basically I like bearophile's suggestion to give them more structure. But until then, yes do this. It should be everywhere, but ranges a big, and need them now!
Dec 11 2010
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday 11 December 2010 11:06:45 Jesse Phillips wrote:
 Andrei Alexandrescu Wrote:
 This program will generate a valid executable, but will also print
 during compilation:
 
 Type int is not a random access range because:
    no empty property
    no front property
    no popFront method
    no indexing
    no slicing
 
 When a programmer has an odd issue with a range check, turning
 verboseness of checks could help.
 
 What do you think?
 
 
 Andrei
I think it would be best if DMD would emit which constraint failed. Better support for automatically say why would be good too. Basically I like bearophile's suggestion to give them more structure. But until then, yes do this. It should be everywhere, but ranges a big, and need them now!
It would be great if dmd said which constraint failed, but since you're dealing with an arbitary boolean expression, in many cases, would not be particularly straightforward to say what failed, and I expect that it would be a _big_ change for the compiler. - Jonathan M Davis
Dec 11 2010
next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Jonathan M Davis <jmdavisProg gmx.com> wrote:

 It would be great if dmd said which constraint failed, but since you're  
 dealing
 with an arbitary boolean expression, in many cases, would not be  
 particularly
 straightforward to say what failed, and I expect that it would be a  
 _big_ change
 for the compiler.
Not to mention SFINAE, which would lead to a wall of text when multiple specializations of a template exists (15 templates failed, each for 3 different reasons = 60 lines of error messages, none of which are likely to be useful). And in such a case, no error messages are to be printed if one match is found, right? -- Simen
Dec 11 2010
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 12/11/10 1:32 PM, Jonathan M Davis wrote:
 On Saturday 11 December 2010 11:06:45 Jesse Phillips wrote:
 Andrei Alexandrescu Wrote:
 This program will generate a valid executable, but will also print
 during compilation:

 Type int is not a random access range because:
     no empty property
     no front property
     no popFront method
     no indexing
     no slicing

 When a programmer has an odd issue with a range check, turning
 verboseness of checks could help.

 What do you think?


 Andrei
I think it would be best if DMD would emit which constraint failed. Better support for automatically say why would be good too. Basically I like bearophile's suggestion to give them more structure. But until then, yes do this. It should be everywhere, but ranges a big, and need them now!
It would be great if dmd said which constraint failed, but since you're dealing with an arbitary boolean expression, in many cases, would not be particularly straightforward to say what failed, and I expect that it would be a _big_ change for the compiler.
Exactly. Oftentimes certain tests are supposed to not fail. All we can do is to be verbose and let the programmer see the last messages emitted prior to failure. Andrei
Dec 11 2010
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 12/11/10, Jonathan M Davis <jmdavisProg gmx.com> wrote:
 On Saturday 11 December 2010 11:06:45 Jesse Phillips wrote:
 Andrei Alexandrescu Wrote:
 This program will generate a valid executable, but will also print
 during compilation:

 Type int is not a random access range because:
    no empty property
    no front property
    no popFront method
    no indexing
    no slicing

 When a programmer has an odd issue with a range check, turning
 verboseness of checks could help.

 What do you think?


 Andrei
I think it would be best if DMD would emit which constraint failed. Better support for automatically say why would be good too. Basically I like bearophile's suggestion to give them more structure. But until then, yes do this. It should be everywhere, but ranges a big, and need them now!
It would be great if dmd said which constraint failed, but since you're dealing with an arbitary boolean expression, in many cases, would not be particularly straightforward to say what failed, and I expect that it would be a _big_ change for the compiler. - Jonathan M Davis
That's why I think would be great to have constraint blocks. I threw the idea in the air a few days ago, but since it would be a big change I doubt it will be thought about, at least not for D2.
Dec 11 2010
prev sibling next sibling parent reply "Robert Jacques" <sandford jhu.edu> writes:
On Sat, 11 Dec 2010 12:15:31 -0500, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:
[snip]
 This program will generate a valid executable, but will also print  
 during compilation:

 Type int is not a random access range because:
    no empty property
    no front property
    no popFront method
    no indexing
    no slicing

 When a programmer has an odd issue with a range check, turning  
 verboseness of checks could help.

 What do you think?


 Andrei
An issue with this is that failed template type checks are both extremely common and expected in template code: i.e. static if(isRandomAccessRange!T) {...}. So, you'll get a lot of spurious error messages slowly. Add in parallel builds, and the last error message won't even be the one you're looking for. So, while I think the concept is usefully, I'd view this implementation as an intermediate stepping stone at best (and a distraction from you fixing other bugs at worse). I'd recommend, as an alternative, to have specific debug versions of the checks, i.e. isRandomAccessRangeDebug, as a way to avoid false positives.
Dec 11 2010
next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Why not just add an extra string parameter to constraint functions
that will show a custom error message? This kind of works (although
you still get standard errors):

import std.conv;
import std.stdio;

alias int cool;

bool isCool(T, string X)()
{
    static if (is(T : cool))
    {
        return true;
    }
    else
    {
        pragma(msg, "Constraint failure: " ~ X);
        return false;
    }
}

void foo(X)(X x) if (isCool!(X, "foo(X) argument must be cool!")())
{
}

void main()
{
    cool myvar;
    double myvar2;
    foo!()(myvar);
    foo!()(myvar2);
}

On 12/11/10, Robert Jacques <sandford jhu.edu> wrote:
 On Sat, 11 Dec 2010 12:15:31 -0500, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 [snip]
 This program will generate a valid executable, but will also print
 during compilation:

 Type int is not a random access range because:
    no empty property
    no front property
    no popFront method
    no indexing
    no slicing

 When a programmer has an odd issue with a range check, turning
 verboseness of checks could help.

 What do you think?


 Andrei
An issue with this is that failed template type checks are both extremely common and expected in template code: i.e. static if(isRandomAccessRange!T) {...}. So, you'll get a lot of spurious error messages slowly. Add in parallel builds, and the last error message won't even be the one you're looking for. So, while I think the concept is usefully, I'd view this implementation as an intermediate stepping stone at best (and a distraction from you fixing other bugs at worse). I'd recommend, as an alternative, to have specific debug versions of the checks, i.e. isRandomAccessRangeDebug, as a way to avoid false positives.
Dec 11 2010
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
And to further hijack the topic, a hypothetical piece of code with
constraint blocks might look something like this:

http://dpaste.org/xB2N/

On 12/11/10, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:
 Why not just add an extra string parameter to constraint functions
 that will show a custom error message? This kind of works (although
 you still get standard errors):

 import std.conv;
 import std.stdio;

 alias int cool;

 bool isCool(T, string X)()
 {
     static if (is(T : cool))
     {
         return true;
     }
     else
     {
         pragma(msg, "Constraint failure: " ~ X);
         return false;
     }
 }

 void foo(X)(X x) if (isCool!(X, "foo(X) argument must be cool!")())
 {
 }

 void main()
 {
     cool myvar;
     double myvar2;
     foo!()(myvar);
     foo!()(myvar2);
 }

 On 12/11/10, Robert Jacques <sandford jhu.edu> wrote:
 On Sat, 11 Dec 2010 12:15:31 -0500, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 [snip]
 This program will generate a valid executable, but will also print
 during compilation:

 Type int is not a random access range because:
    no empty property
    no front property
    no popFront method
    no indexing
    no slicing

 When a programmer has an odd issue with a range check, turning
 verboseness of checks could help.

 What do you think?


 Andrei
An issue with this is that failed template type checks are both extremely common and expected in template code: i.e. static if(isRandomAccessRange!T) {...}. So, you'll get a lot of spurious error messages slowly. Add in parallel builds, and the last error message won't even be the one you're looking for. So, while I think the concept is usefully, I'd view this implementation as an intermediate stepping stone at best (and a distraction from you fixing other bugs at worse). I'd recommend, as an alternative, to have specific debug versions of the checks, i.e. isRandomAccessRangeDebug, as a way to avoid false positives.
Dec 11 2010
prev sibling parent Gerrit Wichert <gwichert yahoo.com> writes:
Hello,

    maybe the template resolver can provide some kind of buffer where
the constrains can emit their messages to.
    If finally a match is found the buffer can be discarded. If not the
messages can be printed.
    So we only get messages for failed template instantiations.


Am 11.12.2010 18:15, schrieb Andrei Alexandrescu:
 Hello,


 Following Craig's questions about sort(), I was thinking of the
 following idea. How about defining a flag rangeCheckVerbose that emits
 explanatory messages during compilation whenever a range check fails?

 Consider this prototype:

 import std.stdio;

 version = rangeCheckVerbose;

 template isRandomAccessRange(T) {
     enum isRandomAccessRange = isRandomAccessRangeImpl!T.value;
 }

 template isRandomAccessRangeImpl(T) {
     enum hasEmpty = is(typeof(T.init.empty));
     enum hasFront = is(typeof(T.init.front));
     enum hasPopFront = is(typeof(T.init.popFront()));
     enum hasIndexing = is(typeof(T.init[1]));
     enum hasSlicing = is(typeof(T.init[1]));
     enum value = hasEmpty && hasFront && hasPopFront && hasIndexing &&
 hasSlicing;
     version (rangeCheckVerbose) {
         static if (!value) {
             pragma(msg, "Type " ~ T.stringof ~ " is not a random
 access range because:");
             static if (!hasEmpty)
                 pragma(msg, "  no empty property");
             static if (!hasFront)
                 pragma(msg, "  no front property");
             static if (!hasPopFront)
                 pragma(msg, "  no popFront method");
             static if (!hasIndexing)
                 pragma(msg, "  no indexing");
             static if (!hasSlicing)
                 pragma(msg, "  no slicing");
         }
     }
 }

 void main()
 {
     writeln(isRandomAccessRange!int);
 }

 This program will generate a valid executable, but will also print
 during compilation:

 Type int is not a random access range because:
   no empty property
   no front property
   no popFront method
   no indexing
   no slicing

 When a programmer has an odd issue with a range check, turning
 verboseness of checks could help.

 What do you think?


 Andrei
Dec 12 2010