Thread overview
[Issue 2339] New: Inconsistent behaviour referring to template mixin member at compile time
Sep 06, 2008
d-bugmail
Apr 22, 2009
d-bugmail
Apr 23, 2009
d-bugmail
Apr 23, 2009
d-bugmail
September 06, 2008
http://d.puremagic.com/issues/show_bug.cgi?id=2339

           Summary: Inconsistent behaviour referring to template mixin
                    member at compile time
           Product: D
           Version: 1.035
          Platform: All
        OS/Version: All
            Status: NEW
          Keywords: rejects-valid
          Severity: blocker
          Priority: P2
         Component: DMD
        AssignedTo: bugzilla@digitalmars.com
        ReportedBy: matti.niemenmaa+dbugzilla@iki.fi


All four cases in the code below should not assert, yet the first two do and the fourth doesn't even compile. I'm marking this as "blocker" because there's no workaround that I can find and I really need the fourth case to work.

It may be that this is a combination of two bugs: the string mixin apparently mangles the identifier into "this.x", which may explain the fourth case. The first two don't make any sense in light of the third, though.

template Check(alias name) { const Check = is(typeof(name)); }

template FooTemplate() { int x; }

class X {
        mixin FooTemplate FooMixin;

        // false:
        // static assert (is(typeof(FooMixin.x)));
        // static assert (is(typeof(mixin("FooMixin.x"))));

        // true:
        static assert (Check!(FooMixin.x));

        // doesn't compile:
        // asdf.d(20): Error: 'this' is only defined in non-static member
functions, not asdf
        // asdf.d(20): Error: this for x needs to be type X not type int
        // asdf.d(20): template instance Check!(this.x) does not match any
template declaration
        // asdf.d(20): static assert  (Check!(this.x)) is not evaluatable at
compile time

        static assert (Check!(mixin("FooMixin.x")));
}


-- 

April 22, 2009
http://d.puremagic.com/issues/show_bug.cgi?id=2339





------- Comment #1 from matti.niemenmaa+dbugzilla@iki.fi  2009-04-22 03:51 -------
I've been investigating this a bit and the issue seems to be that VarDeclaration::needThis() returns true for x; if I make it return false instead, the code compiles fine. Unfortunately, I can't think of a test that would cause that and related cases to return false without breaking something else.

Alternatively, needThis() is correct but some places should be checking for
something else as well.

Here's another testcase, in case it's any help. In this one, it's FuncDeclaration::needThis() that we don't want returning true.


template ReturnTypeOf(alias f) {
   alias typeof(f()) ReturnTypeOf;
}

template FooIn(char[] s) {
   mixin (
      `template Run() {
         const Run =
            "static if (is(ReturnTypeOf!(mixin(\"`~s~`.foo\")) == void))
               `~s~`.foo;
            else
               static assert (false);
            ";
      }`
   );
}

template Mixin() { void foo() { throw new Exception("Success!"); } }

class C {
   mixin Mixin!() M;

   void runFoo() {
      // Works
      static assert (is(ReturnTypeOf!(M.foo) == void));

      // Fails
      // static assert (is(ReturnTypeOf!(mixin("M.foo")) == void));

      // Fails at an incorrect line number
      // mixin (FooIn!("M").Run!());

      // Fails like the 2nd case above
      // static if (is(ReturnTypeOf!(mixin("M.foo")) == void))
      //    M.foo;
      // else
      //    static assert (false);
   }
}
void main() { (new C).runFoo(); }


I tried returning false from needThis() if scope->parent->isTemplateMixin(),
but that breaks code like this (an LDC testcase):

extern(C) int printf(char*, ...);
template Foo() { void test() { printf("test\n"); typeof(this).whee(); } }
class Bar { void whee() { printf("whee\n"); } mixin Foo!(); }
void main() { (new Bar).test(); }


-- 

April 23, 2009
http://d.puremagic.com/issues/show_bug.cgi?id=2339





------- Comment #2 from matti.niemenmaa+dbugzilla@iki.fi  2009-04-23 09:53 -------
I think the problem in the fourth case might be in how it gets parsed. Here's a snippet from the frontend's normally-commented-out printf() and logging calls on the first, working case:

parseStaticAssert()
p = 0xf97c4a, *p = ' '
p = 0xf97c4b, *p = '('
p = 0xf97c4c, *p = 'C'
t->value = 120
parsePrimaryExp(): loc = 13
p = 0xf97c51, *p = '!'
p = 0xf97c52, *p = '('
Dsymbol::Dsymbol(0xf98df0, ident <NULL>)
TemplateInstance(this = 0xf98df0, ident = 'Check')
Parser::parseTemplateArgumentList()
p = 0xf97c53, *p = 'F'
t->value = 120
isDeclaration(needId = 0)
p = 0xf97c5b, *p = '.'
p = 0xf97c5c, *p = 'x'
t->value = 120
p = 0xf97c5d, *p = ')'
is
Parser::isDeclarator()
parseBasicType()
parseDeclarator(tpl = (nil))
parseBasicType2()
p = 0xf97c5e, *p = ')'
Expression::Expression(op = 156) this = 0xf990f0
ObjectToCBuffer()
        t: FooMixin.x ty = 6
ScopeExp::ScopeExp(pkg = 'Check!(FooMixin.x)')

As far as I can tell, when the whole expression is recognized as a template instantiation it goes into parseTemplateArgumentList() and makes it into an expression of type TOKimport (??). The whole thing is then one big ScopeExp.

Here's how the contents of the string mixin are parsed:

Lexer::Lexer(0xf99430,0,10)
lexer.mod = 0xf97770, 0xf97ab0
Parser::Parser()
p = 0xf99430, *p = 'F'
t->value = 120
p.loc.linnum = 21
Parser::parseExpression() loc = 21
parsePrimaryExp(): loc = 21
p = 0xf99438, *p = '.'
Expression::Expression(op = 120) this = 0xfcded0
p = 0xf99439, *p = 'x'
t->value = 120
p = 0xf9943a, *p = '^@'
Expression::Expression(op = 101) this = 0xfcdf20
DotIdExp::toCBuffer()
DotIdExp::semantic(this = 0xfcdf20, 'FooMixin.x')

This time, the inner expression becomes a DotIdExp, which I think has something to do with why the semantic phases diverge later on. Here's the working one, starting from just before a Scope::search call:

TypeIdentifier::resolve(sc = 0xfca6f0, idents = 'FooMixin.x')
Scope::search(0xfca6f0, 'FooMixin')
        looking in scopesym 'X', kind = 'class'
X.ClassDeclaration::search('FooMixin')
X->ScopeDsymbol::search(ident='FooMixin', flags=x0)
        s = 'X.FooTemplate!()'
        found X.FooTemplate!(), kind = 'mixin'
TypeQualified::resolveHelper(sc = 0xfca6f0, idents = 'FooMixin.x')
        scopesym = 'X'
        1: s = 'FooTemplate!()' 0xf98c30, kind = 'mixin'

And here's the non-working one:

UnaExp::semantic('FooMixin.x')
IdentifierExp::semantic('FooMixin')
Scope::search(0xfca6f0, 'FooMixin')
        looking in scopesym 'X', kind = 'class'
X.ClassDeclaration::search('FooMixin')
X->ScopeDsymbol::search(ident='FooMixin', flags=x0)
        s = 'X.FooTemplate!()'
        found X.FooTemplate!(), kind = 'mixin'
Expression::Expression(op = 42) this = 0xfce010
DsymbolExp::semantic('FooTemplate!()')
DsymbolExp:: 0xfce010 'FooTemplate!()' is a symbol

For a solution, I considered setting a flag in TemplateInstance::semanticTiargs, just prior to the latter Expression::semantic call, which would tell CompileExp (the one that handles string mixins) that we're actually looking for template arguments. It would then do something like what Parser::parseTemplateArgumentList does, instead of just looking at the expression in isolation. But I don't actually have any clue whether this would help or not. Also, I think it would require some special handling for comma expressions, or manually wrapping the contents of the mixin'd string in brackets before passing it to the parser.


-- 

April 23, 2009
http://d.puremagic.com/issues/show_bug.cgi?id=2339


matti.niemenmaa+dbugzilla@iki.fi changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Severity|blocker                     |normal




------- Comment #3 from matti.niemenmaa+dbugzilla@iki.fi  2009-04-23 12:38 -------
Well, at least there was some use in spending time on this now: I realized the obvious workaround, which makes it get parsed correctly. While the following doesn't work:

static assert (Check!(mixin("FooMixin.x")));

The following does:

static assert (mixin("Check!(FooMixin.x")));

Lowered severity to "normal" as this is now no longer a blocker for me.


--