digitalmars.D - Getting the error from __traits(compiles, ...)
- Bill Baxter (39/39) Nov 12 2009 We can pretty much already use __traits(compiles,{...}) to implement
- Walter Bright (5/12) Nov 12 2009 I agree it's a problem. Perhaps we can do:
- Walter Bright (3/18) Nov 12 2009 Eh, scratch that dumb idea. Just remove the __traits(compiles, ...) and
- Denis Koroskin (10/25) Nov 12 2009 He was asking for a string so that it would be possible to parse and/or ...
- Walter Bright (2/11) Nov 12 2009 Makes sense.
- Bill Baxter (65/85) Nov 12 2009 n
- Yigal Chripun (9/99) Nov 12 2009 I really wish this was folded into the language by allowing structs to
- grauzone (5/110) Nov 13 2009 One problem with this was that arrays wouldn't automagically be ranges
- Yigal Chripun (8/26) Nov 13 2009 you're talking about this construct:
- grauzone (3/31) Nov 13 2009 I'd rather have some sort of open types, which are the clean version of
- Don (9/58) Nov 13 2009 I think you might be asking for:
- Justin Johansson (6/29) Nov 13 2009 This is very interesting.
- bearophile (4/13) Nov 13 2009 Good. I'd like to have a handy&clean way to verify that some static asse...
- Bill Baxter (13/72) Nov 13 2009 Heh heh. Well I proposed something along those lines before, and it
- bearophile (5/7) Nov 13 2009 I'd like a "static foreach" too. Eventually most statements will have a ...
- Bill Baxter (34/38) Nov 13 2009 tatic version. At that point people will start seeing this little duplic...
- Andrei Alexandrescu (4/43) Nov 13 2009 "static if" is different from "if" in the way it handles scopes. Also,
- Bill Baxter (16/68) Nov 13 2009 all
- Yigal Chripun (18/58) Nov 13 2009 I don't follow your logic regarding CTFE.
- Bill Baxter (19/88) Nov 13 2009 e
- Yigal Chripun (23/62) Nov 14 2009 let's start from the end,
- Don (7/83) Nov 14 2009 There's only one backend. The interpreter is basically just constant
- Yigal Chripun (7/89) Nov 14 2009 Don,
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
Nov 12 2009
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.
Nov 12 2009
Walter Bright wrote:Bill Baxter wrote:Eh, scratch that dumb idea. Just remove the __traits(compiles, ...) and replace it with ..., and you'll get the message!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.
Nov 12 2009
On Fri, 13 Nov 2009 00:00:42 +0300, Walter Bright <newshound1 digitalmars.com> wrote:Walter Bright wrote:He was asking for a string so that it would be possible to parse and/or output. More like: enum s = __traits(compiles_or_msg, ...); static if (s.length == 0) { // no error } else { // do stuff with error message }Bill Baxter wrote:Eh, scratch that dumb idea. Just remove the __traits(compiles, ...) and replace it with ..., and you'll get the message!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.
Nov 12 2009
Denis Koroskin wrote:He was asking for a string so that it would be possible to parse and/or output. More like: enum s = __traits(compiles_or_msg, ...); static if (s.length == 0) { // no error } else { // do stuff with error message }Makes sense.
Nov 12 2009
On Thu, Nov 12, 2009 at 1:00 PM, Walter Bright <newshound1 digitalmars.com> wrote:Walter Bright wrote:nBill Baxter wrote:Any other thoughts about how to get the failure info? =A0 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. =A0Ofte=Maybe that is enough combined with a const code snippet enum code =3D q{ R r; // can define a range object if (r.empty) {} // can test for empty r.popFront; // can invoke next auto h =3D 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=3D`"~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=3Dtrue) { enum code =3D q{ R r; // can define a range object if (r.empty) {} // can test for empty r.popFront; // can invoke next auto h =3D r.front; // can get the front of the range }; static if (!__traits(compiles, mixin(code))) { static if (noisy) { pragma(msg, "Type T=3D`"~T.stringof~"` doesn't support interface:\n "~ code); pragma(msg, "Compiler reports:" ); } mixin(code); } } template isInputRange(T) { enum bool isInputRange =3D __traits(compiles, assertIsInputRange!(T, f= alse)); } 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 =3D r.front; // can get the front of the range })); mixin(DefineInterface!(assertIsInputRange)( "ForwardRange", q{ R r1; R r2 =3D 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. --bbEh, scratch that dumb idea. Just remove the __traits(compiles, ...) and replace it with ..., and you'll get the message!it can just be a typo. =A0I 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. =A0Or passed the wrong number of arguments.I agree it's a problem. Perhaps we can do: =A0 __traits(compiles_or_msg, ...) which would print the error messages, at least making it easier to track down.
Nov 12 2009
Bill Baxter wrote:On Thu, Nov 12, 2009 at 1:00 PM, Walter Bright <newshound1 digitalmars.com> 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 compilerWalter Bright wrote: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. --bbBill Baxter wrote:Eh, scratch that dumb idea. Just remove the __traits(compiles, ...) and replace it with ..., and you'll get the message!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.
Nov 12 2009
Yigal Chripun wrote:Bill Baxter wrote: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).On Thu, Nov 12, 2009 at 1:00 PM, Walter Bright <newshound1 digitalmars.com> 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 compilerWalter Bright wrote: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. --bbBill Baxter wrote:Eh, scratch that dumb idea. Just remove the __traits(compiles, ...) and replace it with ..., and you'll get the message!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.
Nov 13 2009
grauzone wrote:Yigal Chripun wrote: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 I also think that arrays (containers) must be distinct from slices/ranges (views).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 compilerOne 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).
Nov 13 2009
Yigal Chripun wrote:grauzone wrote:I'd rather have some sort of open types, which are the clean version of this hack.Yigal Chripun wrote: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 featureI 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 compilerOne 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).I also think that arrays (containers) must be distinct from slices/ranges (views).
Nov 13 2009
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. --bbI think you might be asking for: static try { xxx; } catch( CompilerError[] errors){ pragma(msg, "Failure in Frobozz!"); pragma(msg, errors[0].msg); }
Nov 13 2009
This is very interesting. Given previous/recent discussion about scopes, should there be, or are there, analogous static scope statements so that one could make use of scope(exit), scope(failure), et. al. in a static context? beers, JustinThere 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. --bbI think you might be asking for: static try { xxx; } catch( CompilerError[] errors){ pragma(msg, "Failure in Frobozz!"); pragma(msg, errors[0].msg); }
Nov 13 2009
Don:I think you might be asking for: static try { xxx; } catch( CompilerError[] errors){ pragma(msg, "Failure in Frobozz!"); pragma(msg, errors[0].msg); }Good. I'd like to have a handy&clean way to verify that some static asserts throw inside my unit tests. Bye, bearophile
Nov 13 2009
On Fri, Nov 13, 2009 at 12:09 AM, Don <nospam nospam.com> wrote:Bill Baxter wrote: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. --bbWe can pretty much already use __traits(compiles,{...}) to implement static interface checking. template checkInterface(T) { =A0 =A0enum bool checkInterface =3D =A0 =A0__traits(compiles, =A0 =A0{ =A0 =A0 =A0 T x; =A0 =A0 =A0 // some code exercising various aspects of the interface =A0 =A0}); } The main problem with this is that a static "implements interface" check is done like so: =A0 static assert(checkInterface!(myType)); And all it tells you is a single yes or no. =A0It would help in debugging if you could somehow get the reason for the failure. So how to do it? =A0All that comes to mind is something like the evil Errno from C. =A0A global that gets set by the last failure. Let's call it __errmsg, but it could be a pragma, or a __traits thing. =A0If you had that then you could write this: =A0 assertImplements!(checkInterface!(myType)); With: template assertImplements(bool v) { =A0 =A0 =A0 static if (!v) { =A0 =A0 =A0 =A0 =A0 =A0pragma(msg, __errmsg); =A0 =A0 =A0 =A0 =A0 =A0static assert(false); =A0 =A0 =A0 } } 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? =A0 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. =A0Often it can just be a typo. =A0I 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. =A0Or passed the wrong number of arguments. --bbI think you might be asking for: static try { =A0 xxx; } catch( CompilerError[] errors){ =A0 pragma(msg, "Failure in Frobozz!"); =A0 pragma(msg, errors[0].msg); }
Nov 13 2009
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
Nov 13 2009
On Fri, Nov 13, 2009 at 8:40 AM, bearophile <bearophileHUGS lycos.com> wrot= e:Bill Baxter:tatic version. At that point people will start seeing this little duplicati= on in the language and someone may invent a way to throw away all the stati= c 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 =3D=3D xxx)) {} else static if (is(type=3D=3Dyyy)) {} 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) --bb2) 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 s=
Nov 13 2009
Bill Baxter wrote:On Fri, Nov 13, 2009 at 8:40 AM, bearophile <bearophileHUGS lycos.com> wrote:"static if" is different from "if" in the way it handles scopes. Also, the former is a declaration, the latter is a statement. AndreiBill Baxter: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)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.
Nov 13 2009
On Fri, Nov 13, 2009 at 9:38 AM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Bill Baxter wrote:allOn 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 =}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. =A0 static if (is(type =3D=3D xxx)) {==A0Butelse static if (is(type=3D=3Dyyy)) {} 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. =A0The code you execute at compile time is written in exactly the same language as what you execute at runtime. =A0And no CTFE engine is required to make it work. =A0Only one new construct required, the macro facility itself. But I don't think that leads to elimination static if, etc. =A0It 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. =A0And if you can fold the constant 2+3, why not the constant add(2,3)? =A0So 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. =A0 Ultimately it's not really necessary to specify static if vs regular if. =A0 It's yet another extension of constant folding -- if the condition is a compile time constant, then it can act as a static if. =A0 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. =A0I think you're right. =A0"static if" =A0should go away. =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."2-stage" compilation isn't really necessary, just more extensions to the constant folding engine. =A0(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.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
Nov 13 2009
On 13/11/2009 19:30, Bill Baxter wrote:On Fri, Nov 13, 2009 at 8:40 AM, bearophile<bearophileHUGS lycos.com> wrote: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.Bill Baxter: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) --bb2) 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.
Nov 13 2009
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:A static switch would be nice too. =A0 static if (is(type =3D=3D xxx)) {=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.eelse static if (is(type=3D=3Dyyy)) {} 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. =A0The code you execute at compile time is written in exactly the same language as what you execute at runtime. =A0And 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. =A0It 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. =A0And if you can fold the constant 2+3, why not the constant add(2,3)? =A0So 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. =A0 Ultimately it's not really necessary to specify static if vs regular if. =A0 It's yet another extension of constant folding -- if the condition is a compile time constant, then it can act as a static if. =A0 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. =A0I think you're right. =A0"static if" =A0should go away. But "2-stage" compilation isn't really necessary, just more extensions to the constant folding engine. =A0(Or perhaps you could say constant folding is already a separate stage of a 2-stage process) --bbI don't follow your logic regarding CTFE. with 2 phase macros a-la nemerle: macro foo() { =A0int res =3D 2 + 3; =A0return res; } macro bar() { =A0return 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 lik=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 o=fthe 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
Nov 13 2009
On 13/11/2009 22:05, Bill Baxter wrote:On Fri, Nov 13, 2009 at 11:50 AM, Yigal Chripun<yigal100 gmail.com> wrote:let's start from the end, yes, you can implement static if that way but I don't see why would you want to do that. regarding constant folding and 2 phase compilation: from what I know, DMD currently contains inside two backends, the regular one generates the executable *and* an interpreter which does CTFE. Constant folding is an optimization used by both. basically we already have two phases of compilation but they are done internally by DMD. This means that DMD has two separate backends instead of just one and that you need separate syntax to target the different phases. The major problems I see with the current system: - unnecessary duplication of syntax - two backends instead of just one which complicates the compiler implementation - unclear separation of phase in code: auto arr = [1.0, 2.0, bar(5.0)]; // when is bar executed? also, how can I control when it's executed? This is needlessly confusing and doesn't provide enough control to the programmer. it's analogous to structs vs. classes. I'm sure everyone in the D community agree that this separation of concerns is much better than the bug-prone c++ way.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
Nov 14 2009
Yigal Chripun wrote:On 13/11/2009 22:05, Bill Baxter wrote:On Fri, Nov 13, 2009 at 11:50 AM, Yigal Chripun<yigal100 gmail.com> wrote:let's start from the end, yes, you can implement static if that way but I don't see why would you want to do that. regarding constant folding and 2 phase compilation: from what I know, DMD currently contains inside two backends, the regular one generates the executable *and* an interpreter which does CTFEI 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 |> } } --bbConstant folding is an optimization used by both. basically we already have two phases of compilation but they are done internally by DMD. This means that DMD has two separate backends instead of just one and that you need separate syntax to target the different phases. The major problems I see with the current system: - unnecessary duplication of syntax - two backends instead of just one which complicates the compiler implementationThere's only one backend. The interpreter is basically just constant folding, with a small amount of interpreting of statements. 90% of the CTFE complexity is in the constant folding. The interpreter itself is only about 200 lines of code. But the real backend is huge.- unclear separation of phase in code: auto arr = [1.0, 2.0, bar(5.0)]; // when is bar executed? also, how can I control when it's executed? This is needlessly confusing and doesn't provide enough control to the programmer.That particular example is caused by array literals not being immutable, which I am certain is a mistake.it's analogous to structs vs. classes. I'm sure everyone in the D community agree that this separation of concerns is much better than the bug-prone c++ way.
Nov 14 2009
On 14/11/2009 13:32, Don wrote:Yigal Chripun wrote:Don, what's your opinion regarding two phase compilation a-la Nemerle vs. the current D model? btw, another benefit I forgot to mention regarding this is that in Nemerle compile time code is precompiled which solves a lot of problems with c++ style template codeOn 13/11/2009 22:05, Bill Baxter wrote:On Fri, Nov 13, 2009 at 11:50 AM, Yigal Chripun<yigal100 gmail.com> wrote:let's start from the end, yes, you can implement static if that way but I don't see why would you want to do that. regarding constant folding and 2 phase compilation: from what I know, DMD currently contains inside two backends, the regular one generates the executable *and* an interpreter which does CTFEI 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 |> } } --bbConstant folding is an optimization used by both. basically we already have two phases of compilation but they are done internally by DMD. This means that DMD has two separate backends instead of just one and that you need separate syntax to target the different phases. The major problems I see with the current system: - unnecessary duplication of syntax - two backends instead of just one which complicates the compiler implementationThere's only one backend. The interpreter is basically just constant folding, with a small amount of interpreting of statements. 90% of the CTFE complexity is in the constant folding. The interpreter itself is only about 200 lines of code. But the real backend is huge.- unclear separation of phase in code: auto arr = [1.0, 2.0, bar(5.0)]; // when is bar executed? also, how can I control when it's executed? This is needlessly confusing and doesn't provide enough control to the programmer.That particular example is caused by array literals not being immutable, which I am certain is a mistake.it's analogous to structs vs. classes. I'm sure everyone in the D community agree that this separation of concerns is much better than the bug-prone c++ way.
Nov 14 2009