November 13, 2009 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Yigal Chripun | Yigal Chripun wrote:
> Bill Baxter wrote:
>> On Thu, Nov 12, 2009 at 1:00 PM, Walter Bright
>> <newshound1@digitalmars.com> wrote:
>>> Walter Bright wrote:
>>>> Bill Baxter wrote:
>>>>> Any other thoughts about how to get the failure info? This is
>>>>> probably the main complaint against __traits(compiles), that there's
>>>>> no way to find out what went wrong if the code doesn't compile. Often
>>>>> it can just be a typo. I know I've spent plenty of time looking at
>>>>> static if(__traits(compiles, ...)) checks that weren't working only to
>>>>> discover I switched an x for a y somewhere. Or passed the wrong
>>>>> number of arguments.
>>>> I agree it's a problem. Perhaps we can do:
>>>>
>>>> __traits(compiles_or_msg, ...)
>>>>
>>>> which would print the error messages, at least making it easier to track
>>>> down.
>>> Eh, scratch that dumb idea. Just remove the __traits(compiles, ...) and
>>> replace it with ..., and you'll get the message!
>>
>> Maybe that is enough combined with a const code snippet
>>
>> enum code = q{
>> R r; // can define a range object
>> if (r.empty) {} // can test for empty
>> r.popFront; // can invoke next
>> auto h = r.front; // can get the front of the range
>> }
>> static if (__traits(compiles, mixin(code))) {
>> mixin(code);
>> }
>> else {
>> pragma(msg, "Unable to instantiate code for type
>> T=`"~T.stringof~"`:\n "~ code);
>> pragma(msg, "Compiler reports:" );
>> mixin(code);
>> }
>>
>> But I was really hoping for a separation of Interface definition and
>> Interface verification. With the above you'll have to have two
>> templates for every interface, like isForwardRange!(T) (evals to
>> bool) and assertIsForwardRange!(T) (reports the compiler error or is
>> silent). Hmm.... unless
>>
>> template assertIsInputRange(T, bool noisy=true) {
>> enum code = q{
>> R r; // can define a range object
>> if (r.empty) {} // can test for empty
>> r.popFront; // can invoke next
>> auto h = r.front; // can get the front of the range
>> };
>> static if (!__traits(compiles, mixin(code))) {
>> static if (noisy) {
>> pragma(msg, "Type T=`"~T.stringof~"` doesn't support
>> interface:\n "~ code);
>> pragma(msg, "Compiler reports:" );
>> }
>> mixin(code);
>> }
>> }
>>
>> template isInputRange(T) {
>> enum bool isInputRange = __traits(compiles, assertIsInputRange!(T, false));
>> }
>>
>> And then we could wrap the whole shebang in a fancy code-generating
>> string mixin and define things like the above using:
>>
>> mixin(DefineInterface(
>> "InputRange",
>> q{
>> R r; // can define a range object
>> if (r.empty) {} // can test for empty
>> r.popFront; // can invoke next
>> auto h = r.front; // can get the front of the range
>> }));
>>
>> mixin(DefineInterface!(assertIsInputRange)(
>> "ForwardRange",
>> q{
>> R r1;
>> R r2 = r1; // can copy a range object
>> })));
>>
>> Writing DefineInterface is left as an exercise for the reader. :-)
>> But it looks do-able.
>> And DefineInterface could take a variadic list of assertIsXXX template
>> aliases and generate code to check each one.
>>
>> --bb
>
> I really wish this was folded into the language by allowing structs to implement interfaces.
>
> interface Range(T) {
> bool empty();
> void popFront();
> T front();
> }
>
> struct MyRange(T) : Range!(T) { ... } // checked by compiler
>
One problem with this was that arrays wouldn't automagically be ranges anymore. Right now, "int[] a; a.popFront();" works, because std.array has a global function popFront. Some old language hack turns a.popFront into popFront(a).
| |||
November 13, 2009 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to grauzone | grauzone wrote:
> Yigal Chripun wrote:
>>
>> I really wish this was folded into the language by allowing structs to implement interfaces.
>>
>> interface Range(T) {
>> bool empty();
>> void popFront();
>> T front();
>> }
>>
>> struct MyRange(T) : Range!(T) { ... } // checked by compiler
>>
>
> One problem with this was that arrays wouldn't automagically be ranges anymore. Right now, "int[] a; a.popFront();" works, because std.array has a global function popFront. Some old language hack turns a.popFront into popFront(a).
you're talking about this construct:
int[] arr;
void foo(int[], params) {} => arr.foo(params);
This should not be considered a hack but rather should be a feature extended for all types. see extension methods in C#.
I also think that arrays (containers) must be distinct from slices/ranges (views).
| |||
November 13, 2009 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Yigal Chripun | Yigal Chripun wrote: > grauzone wrote: >> Yigal Chripun wrote: >>> >>> I really wish this was folded into the language by allowing structs to implement interfaces. >>> >>> interface Range(T) { >>> bool empty(); >>> void popFront(); >>> T front(); >>> } >>> >>> struct MyRange(T) : Range!(T) { ... } // checked by compiler >>> >> >> One problem with this was that arrays wouldn't automagically be ranges anymore. Right now, "int[] a; a.popFront();" works, because std.array has a global function popFront. Some old language hack turns a.popFront into popFront(a). > > you're talking about this construct: > int[] arr; > void foo(int[], params) {} => arr.foo(params); > > This should not be considered a hack but rather should be a feature extended for all types. see extension methods in C#. I'd rather have some sort of open types, which are the clean version of this hack. > I also think that arrays (containers) must be distinct from slices/ranges (views). | |||
November 13, 2009 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Don | On Fri, Nov 13, 2009 at 12:09 AM, Don <nospam@nospam.com> wrote: > Bill Baxter wrote: >> >> We can pretty much already use __traits(compiles,{...}) to implement >> static interface checking. >> >> template checkInterface(T) { >> enum bool checkInterface = >> __traits(compiles, >> { >> T x; >> // some code exercising various aspects of the interface >> }); >> } >> >> The main problem with this is that a static "implements interface" check is done like so: >> >> static assert(checkInterface!(myType)); >> >> And all it tells you is a single yes or no. It would help in debugging if you could somehow get the reason for the failure. >> >> So how to do it? All that comes to mind is something like the evil >> Errno from C. A global that gets set by the last failure. >> Let's call it __errmsg, but it could be a pragma, or a __traits thing. >> If you had that then you could write this: >> >> assertImplements!(checkInterface!(myType)); >> >> With: >> template assertImplements(bool v) >> { >> static if (!v) { >> pragma(msg, __errmsg); >> static assert(false); >> } >> } >> >> There are lots of ways you could provide such a concept-checker, but they all require the basic ability to get the reason for the __traits(compiles) failure. >> >> Any other thoughts about how to get the failure info? This is probably the main complaint against __traits(compiles), that there's no way to find out what went wrong if the code doesn't compile. Often it can just be a typo. I know I've spent plenty of time looking at static if(__traits(compiles, ...)) checks that weren't working only to discover I switched an x for a y somewhere. Or passed the wrong number of arguments. >> >> --bb > > I think you might be asking for: > > static try { > xxx; > } > catch( CompilerError[] errors){ > pragma(msg, "Failure in Frobozz!"); > pragma(msg, errors[0].msg); > } Heh heh. Well I proposed something along those lines before, and it didn't catch on, so I decided to avoid mentioning it this time. :-) http://digitalmars.com/d/archives/digitalmars/D/static_try_catch_construct_would_be_helpful_66794.html But yeh, actually now that you mention it that handles both problems I brought up recently. 1) how to run code only if it compiles and avoid repeating yourself. (the original reason I proposed it) 2) how to get and report errors related to failure to compile some code. (this one I hadn't thought of back then) But yeh, it looks like that could solve both problems. --bb | |||
November 13, 2009 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Bill Baxter | Bill Baxter:
> 2) how to get and report errors related to failure to compile some
> code. (this one I hadn't thought of back then)
I'd like a "static foreach" too. Eventually most statements will have a static version. At that point people will start seeing this little duplication in the language and someone may invent a way to throw away all the static versions and allow normal D code to be used at compile time, maybe with a 2-stage compilation or something.
Design is often like that, with phases of expansion of functionality followed by phases of synthesis and generalization.
Bye,
bearophile
| |||
November 13, 2009 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to bearophile | On Fri, Nov 13, 2009 at 8:40 AM, bearophile <bearophileHUGS@lycos.com> wrote:
> Bill Baxter:
>> 2) how to get and report errors related to failure to compile some
>> code. (this one I hadn't thought of back then)
>
> I'd like a "static foreach" too. Eventually most statements will have a static version. At that point people will start seeing this little duplication in the language and someone may invent a way to throw away all the static versions and allow normal D code to be used at compile time, maybe with a 2-stage compilation or something.
A static switch would be nice too. static if (is(type == xxx)) {}
else static if (is(type==yyy)) {} else static if ... gets kinda
tedious.
The kind of unification you're talking about is one thing I like about Nemerle's 2-phase macros-as-plugins. The code you execute at compile time is written in exactly the same language as what you execute at runtime. And no CTFE engine is required to make it work. Only one new construct required, the macro facility itself.
But I don't think that leads to elimination static if, etc. It just means that such things become implementable as macros, rather than language constructs.
On the other hand, having macros doesn't mean that you don't want constant folding. And if you can fold the constant 2+3, why not the constant add(2,3)? So desire to fold as many constants as possible naturally leads to a desire to do CTFE and be able to execute your entire language at compile time.
And once you're there -- yeh, I guess you're right. Ultimately it's not really necessary to specify static if vs regular if. It's yet another extension of constant folding -- if the condition is a compile time constant, then it can act as a static if. Same goes for loops. But like regular loop unrolling optimizations, the compiler should decide if it's prudent to unroll that 10,000 static foreach loop or not.
So in short. I think you're right. "static if" should go away. But "2-stage" compilation isn't really necessary, just more extensions to the constant folding engine. (Or perhaps you could say constant folding is already a separate stage of a 2-stage process)
--bb
| |||
November 13, 2009 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Bill Baxter | Bill Baxter wrote:
> On Fri, Nov 13, 2009 at 8:40 AM, bearophile <bearophileHUGS@lycos.com> wrote:
>> Bill Baxter:
>>> 2) how to get and report errors related to failure to compile some
>>> code. (this one I hadn't thought of back then)
>> I'd like a "static foreach" too. Eventually most statements will have a static version. At that point people will start seeing this little duplication in the language and someone may invent a way to throw away all the static versions and allow normal D code to be used at compile time, maybe with a 2-stage compilation or something.
>
> A static switch would be nice too. static if (is(type == xxx)) {}
> else static if (is(type==yyy)) {} else static if ... gets kinda
> tedious.
>
>
> The kind of unification you're talking about is one thing I like about
> Nemerle's 2-phase macros-as-plugins. The code you execute at compile
> time is written in exactly the same language as what you execute at
> runtime. And no CTFE engine is required to make it work. Only one
> new construct required, the macro facility itself.
>
> But I don't think that leads to elimination static if, etc. It just
> means that such things become implementable as macros, rather than
> language constructs.
>
> On the other hand, having macros doesn't mean that you don't want
> constant folding. And if you can fold the constant 2+3, why not the
> constant add(2,3)? So desire to fold as many constants as possible
> naturally leads to a desire to do CTFE and be able to execute your
> entire language at compile time.
>
> And once you're there -- yeh, I guess you're right. Ultimately it's
> not really necessary to specify static if vs regular if. It's yet
> another extension of constant folding -- if the condition is a compile
> time constant, then it can act as a static if. Same goes for loops.
> But like regular loop unrolling optimizations, the compiler should
> decide if it's prudent to unroll that 10,000 static foreach loop or
> not.
>
> So in short. I think you're right. "static if" should go away. But
> "2-stage" compilation isn't really necessary, just more extensions to
> the constant folding engine. (Or perhaps you could say constant
> folding is already a separate stage of a 2-stage process)
"static if" is different from "if" in the way it handles scopes. Also, the former is a declaration, the latter is a statement.
Andrei
| |||
November 13, 2009 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Fri, Nov 13, 2009 at 9:38 AM, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote: > Bill Baxter wrote: >> >> On Fri, Nov 13, 2009 at 8:40 AM, bearophile <bearophileHUGS@lycos.com> wrote: >>> >>> Bill Baxter: >>>> >>>> 2) how to get and report errors related to failure to compile some >>>> code. (this one I hadn't thought of back then) >>> >>> I'd like a "static foreach" too. Eventually most statements will have a static version. At that point people will start seeing this little duplication in the language and someone may invent a way to throw away all the static versions and allow normal D code to be used at compile time, maybe with a 2-stage compilation or something. >> >> A static switch would be nice too. static if (is(type == xxx)) {} >> else static if (is(type==yyy)) {} else static if ... gets kinda >> tedious. >> >> >> The kind of unification you're talking about is one thing I like about Nemerle's 2-phase macros-as-plugins. The code you execute at compile time is written in exactly the same language as what you execute at runtime. And no CTFE engine is required to make it work. Only one new construct required, the macro facility itself. >> >> But I don't think that leads to elimination static if, etc. It just means that such things become implementable as macros, rather than language constructs. >> >> On the other hand, having macros doesn't mean that you don't want constant folding. And if you can fold the constant 2+3, why not the constant add(2,3)? So desire to fold as many constants as possible naturally leads to a desire to do CTFE and be able to execute your entire language at compile time. >> >> And once you're there -- yeh, I guess you're right. Ultimately it's not really necessary to specify static if vs regular if. It's yet another extension of constant folding -- if the condition is a compile time constant, then it can act as a static if. Same goes for loops. But like regular loop unrolling optimizations, the compiler should decide if it's prudent to unroll that 10,000 static foreach loop or not. >> >> So in short. I think you're right. "static if" should go away. But >> "2-stage" compilation isn't really necessary, just more extensions to >> the constant folding engine. (Or perhaps you could say constant >> folding is already a separate stage of a 2-stage process) > > > "static if" is different from "if" in the way it handles scopes. Good point. It's not really an essential distinction, though. If the language had no-scope blocks in addition to scope blocks then you could have no-scope dynamic if() also. I've always found it unpleasing anyway that one word (somewhat separated from the {}) changes whether {} creates a scope or not. > Also, the > former is a declaration, the latter is a statement. That mostly just means you can use "static if" at the top level, right? If the language eliminated "static if" then clearly all if's at the top level would be required to have static conditions and use no-scope blocks. But ok. These things probably make it too big a change for D to swallow. --bb | |||
November 13, 2009 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Bill Baxter | On 13/11/2009 19:30, Bill Baxter wrote:
> On Fri, Nov 13, 2009 at 8:40 AM, bearophile<bearophileHUGS@lycos.com>
> wrote:
>> Bill Baxter:
>>> 2) how to get and report errors related to failure to compile
>>> some code. (this one I hadn't thought of back then)
>>
>> I'd like a "static foreach" too. Eventually most statements will
>> have a static version. At that point people will start seeing this
>> little duplication in the language and someone may invent a way to
>> throw away all the static versions and allow normal D code to be
>> used at compile time, maybe with a 2-stage compilation or
>> something.
>
> A static switch would be nice too. static if (is(type == xxx)) {}
> else static if (is(type==yyy)) {} else static if ... gets kinda
> tedious.
>
>
> The kind of unification you're talking about is one thing I like
> about Nemerle's 2-phase macros-as-plugins. The code you execute at
> compile time is written in exactly the same language as what you
> execute at runtime. And no CTFE engine is required to make it work.
> Only one new construct required, the macro facility itself.
>
> But I don't think that leads to elimination static if, etc. It just
> means that such things become implementable as macros, rather than
> language constructs.
>
> On the other hand, having macros doesn't mean that you don't want
> constant folding. And if you can fold the constant 2+3, why not the
> constant add(2,3)? So desire to fold as many constants as possible
> naturally leads to a desire to do CTFE and be able to execute your
> entire language at compile time.
>
> And once you're there -- yeh, I guess you're right. Ultimately
> it's not really necessary to specify static if vs regular if. It's
> yet another extension of constant folding -- if the condition is a
> compile time constant, then it can act as a static if. Same goes
> for loops. But like regular loop unrolling optimizations, the
> compiler should decide if it's prudent to unroll that 10,000 static
> foreach loop or not.
>
> So in short. I think you're right. "static if" should go away.
> But "2-stage" compilation isn't really necessary, just more
> extensions to the constant folding engine. (Or perhaps you could say
> constant folding is already a separate stage of a 2-stage process)
>
> --bb
I don't follow your logic regarding CTFE.
with 2 phase macros a-la nemerle:
macro foo() {
int res = 2 + 3;
return res;
}
macro bar() {
return q{2 + 3};
}
foo's addition is done at compile time so the constant folding was implemented in the macro body
bar return the AST for the expression "2 + 3". Compiler optimizations like constant folding will apply just as if you wrote that expression yourself instead of generating it by calling a macro.
static if is not supposed to be implemented with macros, rather the equivalent of a static if would be using a regular if *inside* the body of the macro.
| |||
November 13, 2009 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Yigal Chripun | On Fri, Nov 13, 2009 at 11:50 AM, Yigal Chripun <yigal100@gmail.com> wrote: > On 13/11/2009 19:30, Bill Baxter wrote: >> >> On Fri, Nov 13, 2009 at 8:40 AM, bearophile<bearophileHUGS@lycos.com> wrote: >>> >>> Bill Baxter: >>>> >>>> 2) how to get and report errors related to failure to compile >>>> some code. (this one I hadn't thought of back then) >>> >>> I'd like a "static foreach" too. Eventually most statements will have a static version. At that point people will start seeing this little duplication in the language and someone may invent a way to throw away all the static versions and allow normal D code to be used at compile time, maybe with a 2-stage compilation or something. >> >> A static switch would be nice too. static if (is(type == xxx)) {} >> else static if (is(type==yyy)) {} else static if ... gets kinda >> tedious. >> >> >> The kind of unification you're talking about is one thing I like about Nemerle's 2-phase macros-as-plugins. The code you execute at compile time is written in exactly the same language as what you execute at runtime. And no CTFE engine is required to make it work. Only one new construct required, the macro facility itself. >> >> But I don't think that leads to elimination static if, etc. It just means that such things become implementable as macros, rather than language constructs. >> >> On the other hand, having macros doesn't mean that you don't want constant folding. And if you can fold the constant 2+3, why not the constant add(2,3)? So desire to fold as many constants as possible naturally leads to a desire to do CTFE and be able to execute your entire language at compile time. >> >> And once you're there -- yeh, I guess you're right. Ultimately it's not really necessary to specify static if vs regular if. It's yet another extension of constant folding -- if the condition is a compile time constant, then it can act as a static if. Same goes for loops. But like regular loop unrolling optimizations, the compiler should decide if it's prudent to unroll that 10,000 static foreach loop or not. >> >> So in short. I think you're right. "static if" should go away. But "2-stage" compilation isn't really necessary, just more extensions to the constant folding engine. (Or perhaps you could say constant folding is already a separate stage of a 2-stage process) >> >> --bb > > I don't follow your logic regarding CTFE. > > with 2 phase macros a-la nemerle: > > macro foo() { > int res = 2 + 3; > return res; > } > > macro bar() { > return q{2 + 3}; > } > > foo's addition is done at compile time so the constant folding was implemented in the macro body > > bar return the AST for the expression "2 + 3". Compiler optimizations like constant folding will apply just as if you wrote that expression yourself instead of generating it by calling a macro. Right, which is why I'm saying you still want constant folding/CTFE even if you have a macro system. But then if you're going to have CTFE sitting around anyway, you might as well use it to implement macros instead of going to this funky two-phase thing. That was one point I was making, though not so clearly. > static if is not supposed to be implemented with macros, rather the equivalent of a static if would be using a regular if *inside* the body of the macro. But that's how you would implement a static if with macros, no? (pardon the incorrect nemerle syntax) macro static_if(bool cond, a, b) { if (cond) { <| a |> } else { <| b |> } } --bb | |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply