www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 2339] New: Inconsistent behaviour referring to template mixin member at compile time

reply d-bugmail puremagic.com writes:
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")));
}


-- 
Sep 06 2008
next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2339






-------
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(); }


-- 
Apr 22 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2339






-------
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.


-- 
Apr 23 2009
prev sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2339


matti.niemenmaa+dbugzilla iki.fi changed:

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





-------
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.


-- 
Apr 23 2009