www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Does D allow paradoxical code?

reply Jerry Quinn <jlquinn optonline.net> writes:
What should happen when the following code is compiled?

mixin(vs);
version(v1) {
  const string vs = "version = v2;";
  pragma(msg, "yes");
}
 else {
  const string vs = "version = v1;";
  pragma(msg, "no");
 }

Currently, there's an error saying that vs must be a string.  However, as far
as I can tell, the spec says nothing about order affecting module-scope
declarations.  So vs should be valid, and once it substitutes in the mixin, you
have a cycle where no matter what vs is, vs should be the other value.

If you don't have the conditional compilation and put the declaration of vs
after the mixin, it works as expected.

This generally raises the question of what should be the order of evaluation
for constructs like mixins, conditional compilation, and CTFE.   Each has the
potential to modify the other.

What should be the rule to break the ambiguity?  Or do we leave it
implementation-defined?

Thoughts?
Jerry
Mar 25 2010
next sibling parent BCS <none anon.com> writes:
Hello Jerry,

 once it substitutes in the mixin, you have a cycle where no matter
 what vs is, vs should be the other value.
 

I think (but am not sure) that this is an inevitable side effect of D being Turing compleat at compile time. As a corollary, there is no way to enforce a rule against it. I'd say it's invalid but the compiler need not detect it in any or all cases. -- ... <IXOYE><
Mar 25 2010
prev sibling next sibling parent "Nick Sabalausky" <a a.a> writes:
"Jerry Quinn" <jlquinn optonline.net> wrote in message 
news:hohgnr$2333$1 digitalmars.com...
 What should happen when the following code is compiled?

 mixin(vs);
 version(v1) {
  const string vs = "version = v2;";
  pragma(msg, "yes");
 }
 else {
  const string vs = "version = v1;";
  pragma(msg, "no");
 }

 Currently, there's an error saying that vs must be a string.  However, as 
 far as I can tell, the spec says nothing about order affecting 
 module-scope declarations.  So vs should be valid, and once it substitutes 
 in the mixin, you have a cycle where no matter what vs is, vs should be 
 the other value.

 If you don't have the conditional compilation and put the declaration of 
 vs after the mixin, it works as expected.

 This generally raises the question of what should be the order of 
 evaluation for constructs like mixins, conditional compilation, and CTFE. 
 Each has the potential to modify the other.

 What should be the rule to break the ambiguity?  Or do we leave it 
 implementation-defined?

 Thoughts?
 Jerry

It's useful in robotics: void blowUp() { mixin(vs); version(v1) { const string vs = "version = v2;"; pragma(msg, "yes"); } else { const string vs = "version = v1;"; pragma(msg, "no"); } }
Mar 25 2010
prev sibling next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Jerry Quinn wrote:
 What should happen when the following code is compiled?
 
 mixin(vs);
 version(v1) {
   const string vs = "version = v2;";
   pragma(msg, "yes");
 }
  else {
   const string vs = "version = v1;";
   pragma(msg, "no");
  }
 
 Currently, there's an error saying that vs must be a string.  However, as far
as I can tell, the spec says nothing about order affecting module-scope
declarations.  So vs should be valid, and once it substitutes in the mixin, you
have a cycle where no matter what vs is, vs should be the other value.
 
 If you don't have the conditional compilation and put the declaration of vs
after the mixin, it works as expected.
 
 This generally raises the question of what should be the order of evaluation
for constructs like mixins, conditional compilation, and CTFE.   Each has the
potential to modify the other.
 
 What should be the rule to break the ambiguity?  Or do we leave it
implementation-defined?
 
 Thoughts?
 Jerry

The D spec, as far as I know, says nothing on this. Then again, the D spec says nothing about a lot of things it should. Personally, I just ignore the spec; DMD is the only reliable reference for the language. Now, being as I'm supposed to go off and do my walk around the block now, I'll be lazy and start making educated guesses. I don't think the DMD frontend is smart enough to handle this; nor do I think it should. If you were to allow this sort of thing, you'd never be able to bloody well compile it. The simplest solution is to simply make canonical what I think DMD already does: static code [1] can be arranged in any order [2], but meta code [3] is processed strictly in lexical order. Indeed, version actually already does this: you're not allowed to set a version after you've already used it. [1] being code which doesn't contain any meta code. [2] this statement, of course, this is complete garbage; work with enums for any length of time and you'll quickly discover "D supports forward references" is a dream at best. [3] being anything which generates static code; static if, foreach over a tuple, templates, mixins, etc.
Mar 25 2010
parent Jerry Quinn <jlquinn optonline.net> writes:
Daniel Keep Wrote:
 
 This generally raises the question of what should be the order of evaluation
for constructs like mixins, conditional compilation, and CTFE.   Each has the
potential to modify the other.
 
 What should be the rule to break the ambiguity?  Or do we leave it
implementation-defined?
 
 Thoughts?
 Jerry

The D spec, as far as I know, says nothing on this. Then again, the D spec says nothing about a lot of things it should. Personally, I just ignore the spec; DMD is the only reliable reference for the language.

That's why I'm bringing this issue up. The spec needs to become the reliable reference rather than the DMD implementation. Otherwise, D will be difficult to use seriously because you'll never know exactly what the language really allows.
 Now, being as I'm supposed to go off and do my walk around the block
 now, I'll be lazy and start making educated guesses.  I don't think the
 DMD frontend is smart enough to handle this; nor do I think it should.
 
 If you were to allow this sort of thing, you'd never be able to bloody
 well compile it.  The simplest solution is to simply make canonical what
 I think DMD already does: static code [1] can be arranged in any order
 [2], but meta code [3] is processed strictly in lexical order.
 
 Indeed, version actually already does this: you're not allowed to set a
 version after you've already used it.

I agree with you that this is a bit silly and I'm not looking to have the compiler "properly" run forever. I'm raising the issue to get the spec to be firmer about how these meta constructs get evaluated at compile time. Lexical order makes sense to me. Another variant of the problem I described is if you were to mixin a protection attribute where the attribute makes the string that builds it no longer visible. Jerry
Mar 27 2010
prev sibling parent Rainer Schuetze <r.sagitario gmx.de> writes:
With the patch in bugzilla #461 ( 
http://d.puremagic.com/issues/show_bug.cgi?id=461 ) applied, you get the 
  expected output:

test.d(1): Error: version v1 defined after use
no

version and debug conditions are analyzed and expanded before any other 
semantic analysis in a module. I think, versions are defined and limited 
enough, so you get what you can expect.

But your point holds, if you replace version in your example with static 
if conditions. These need semantic analysis and this is done (mostly) in 
lexical order. A forward reference to an identifier defined inside the 
static if fails, because the identifiers inside a static if are only 
known in the given scope after evaluating the condition. There are also 
entries to that effect in bugzilla (e.g. 
http://d.puremagic.com/issues/show_bug.cgi?id=3743 ) Even worse, 
declarations inside a static if can change an overload-set, so you might 
not get an error, but unexpected calls that are very hard to track down.

Similar problems can occur with mixins. I'm not sure CTFE falls into the 
same category, as it does not generate declarations by itself, it helps 
evaluating conditions or strings for mixins.

Here's my current guess, how to allow forward references in most cases: 
when the compiler "visits" a scope (module, class, etc.) due to normal 
semantic analysis or identifier lookup, compile-time conditions and 
mixins at the same nesting-level must be evaluated *before anything 
else* in this scope. A static if condition or mixin must not reference 
an identifier declared inside a condition or mixin later.

Rainer

Jerry Quinn wrote:
 What should happen when the following code is compiled?
 
 mixin(vs);
 version(v1) {
   const string vs = "version = v2;";
   pragma(msg, "yes");
 }
  else {
   const string vs = "version = v1;";
   pragma(msg, "no");
  }
 
 Currently, there's an error saying that vs must be a string.  However, as far
as I can tell, the spec says nothing about order affecting module-scope
declarations.  So vs should be valid, and once it substitutes in the mixin, you
have a cycle where no matter what vs is, vs should be the other value.
 
 If you don't have the conditional compilation and put the declaration of vs
after the mixin, it works as expected.
 
 This generally raises the question of what should be the order of evaluation
for constructs like mixins, conditional compilation, and CTFE.   Each has the
potential to modify the other.
 
 What should be the rule to break the ambiguity?  Or do we leave it
implementation-defined?
 
 Thoughts?
 Jerry

Mar 27 2010