http://d.puremagic.com/issues/show_bug.cgi?id=5906

           Summary: Just pre-conditions at compile-time when arguments are
                    static
           Product: D
           Version: D2
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: enhancement
          Priority: P2
         Component: DMD
        AssignedTo: nob...@puremagic.com
        ReportedBy: bearophile_h...@eml.cc


--- Comment #0 from bearophile_h...@eml.cc 2011-04-28 16:19:57 PDT ---
This enhancement proposal was getting lost in a thread, so I have added it here
too.

DMD currently runs functions at compile time only if their call is in a context
where a compile-time value is expected, to avoid troubles (C++0X uses a keyword
to avoid those problems). This is OK.


Note: DMD is able to run contracts too at compile-time, as you see in this
program (at the r1 enum):


import std.ctype: isdigit;
int foo(string text, int x)
in {
    assert(x >= 0 && x < text.length);
    foreach (c; text[0 .. x])
        assert(isdigit(c));
} body {
    return 0;
}
enum r1 = foo("123xxx", 4); // Error: assert(isdigit(cast(dchar)c)) failed
void main(string[] args) {
    auto r2 = foo(args[2], (cast(int)args.length) - 5);
    auto r3 = foo("123xxx", 4);
}


DMD 2.052 shows:
test.d(3): Error: assert(x > 0) failed
test.d(7): Error: cannot evaluate foo(-1) at compile time
test.d(7): Error: cannot evaluate foo(-1) at compile time


This is my idea: when you call a function where all its arguments are known at
compile-time (as at the r3 variable) the compiler runs just the pre-condition
of that function at compile-time. Uf a pre-condition can't run at compile-time,
the compiler just ignores it silently, and later this pre-condition will run
normally at run-time.

Note that I am not suggesting to run the whole function, and not its
post-condition, just its pre-condition.

Running just the pre-condition is different from running the whole function
because:
- Pre-conditions are usually small or smaller than function bodies;
- Pre-conditions are usually meant to be fast (and not slower than the function
body), so they are probably not too much heavy to run. pre-conditions, unlike
debug{} code are meant to run often, sometimes even in normal usage of
programs;
- My pre-conditions are often pure. If this enhancement request gets accepted,
D programmers will be encouraged to write more pure preconditions. Even if a
function is not marked as "pure", what matters in this discussion is to its
pre-condition to be CTFE-pure. (Functions can run at compile-time even if they
aren't pure. They need to be pure just in the code path run at compile time).


Walter:

> Instead, we opted for a design that either must run at compile time, or
> must run at run time. Not one that decides one way or the other in an
> arbitrary, silent, and ever-changing manner.
> 
> The user must make an explicit choice if code is to be run at compile
> time or run time.

This problem is important for the normally run CT functions, and I agree with
this decision. But it's much less important the idea presented here, because
finding bug is almost never a deterministic process, it's usually
probabilistic. People find only some bugs (people today find new bugs even in
old very-high-quality C code used by everyone), lint tools (including the
static analysis flag done by Clang) find only some other bugs, and usually
different lints find different bugs. One important thing for those tools is to
reduce false positives as much as possible (even if this increases false
negatives a little), and the idea presented here doesn't produce false alarms
(if the pre-conditions are correct).


This feature is useful because in your code I often have struct literals like
(I assume their constructor has a pre-condition):

Foo f1 = Foo(10, -20);

The compiler is able at compile-time to tell this line of code is wrong because
for example -20 is not acceptable. This is useful in many situations. This
feature works only if the arguments are known at compile-time, this is a strong
limitation of the idea presented here, but I think it's better to have it
anyway.

Even if this feature sometimes gets "disabled" by turning a CTFE-pure function
pre-condition into not CTFE-pure code, the programmer doesn't need to worry a
lot about this, because even if this change doesn't allow to catch this bug in
the code, other bugs too are not found by the compiler. All static analysis
tools do the same, they sometimes find a bug, but if you change the code in a
way they don't fully understand, they don't find the bug any more.

The feature I have proposed here is not a language feature, it's a compiler
feature (the only change is in user code, that's encouraged to create CTFE-pure
pre-conditions). This means that even if DMD doesn't want this idea, future D
compilers will be free to adopt it. I think this is a cheap but useful compiler
feature to add. It's a cheap feature because the compiler is kept simple (to
catch contract bugs when arguments are not statically known the compiler needs
some kind of constraint solver, that is not a simple thing).

A disadvantage of the idea presented here is that compilation times may become
longer.

In the program shown the variable r2 is a situation where not all arguments of
foo() are known at compile-time, so here the foo pre-condition is not run. The
variable r2 is a situation where one argument of foo is known at run-time, and
in this case the pre-condition contains a part (assert(x>=0&&x<text.length))
that's able to use this information to catch a bug. So an improvement of the
idea is to perform this partial test. This looks less easy to implement, there
is no need to implement this second idea too.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------

Reply via email to