D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 6510 - [CTFE] "internal error: illegal stack value" when compiled with -inline
Summary: [CTFE] "internal error: illegal stack value" when compiled with -inline
Status: RESOLVED FIXED
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: Other All
: P2 normal
Assignee: No Owner
URL:
Keywords: ice-on-valid-code
Depends on:
Blocks:
 
Reported: 2011-08-16 13:39 UTC by Dmitry Olshansky
Modified: 2011-08-22 22:47 UTC (History)
2 users (show)

See Also:


Attachments
test case, stripped down parser (8.03 KB, application/octet-stream)
2011-08-17 02:52 UTC, Dmitry Olshansky
Details

Note You need to log in before you can comment on or make changes to this issue.
Description Dmitry Olshansky 2011-08-16 13:39:07 UTC
Recently found this one in my GSOC project. As this involves compilation of regex at CTFE, I'm not yet able to produce small test case.

dmd spits this:
..\..\fred.d(2020): Error: CTFE internal error: illegal stack value stack

Assertion failure: 'isStackValueValid(newval)' on line 5452 in file 'interpret.c'

abnormal program termination

But anyway, what kind of situation is supposed to trigger that assert?

My dmd is 2.055 compiled form github with last commit being:
 Merge pull request #293 from 9rnsr/fix2246 
 4a1c9e7646ed9152f9644cd29d07968583888cb2
Comment 1 Don 2011-08-16 19:32:55 UTC
(In reply to comment #0)
> Recently found this one in my GSOC project. As this involves compilation of
> regex at CTFE, I'm not yet able to produce small test case.
> 
> dmd spits this:
> ..\..\fred.d(2020): Error: CTFE internal error: illegal stack value stack
> 
> Assertion failure: 'isStackValueValid(newval)' on line 5452 in file
> 'interpret.c'
> 
> abnormal program termination
> 
> But anyway, what kind of situation is supposed to trigger that assert?

Any kind of CTFE assignment involving a non-reference type, that wasn't tested.
The few lines involving the variable 'stack' around line 2020 will be the only ones involved in the bug.
Comment 2 Dmitry Olshansky 2011-08-17 02:52:42 UTC
Created attachment 1018 [details]
test case, stripped down parser
Comment 3 Dmitry Olshansky 2011-08-17 02:55:11 UTC
I've managed to get a resonably sized test case.
Apparently it has soemthing to do with -inline:

dmd fred.d  // Ok
dmd -inline fred.d //same error as before
Comment 4 Dmitry Olshansky 2011-08-17 02:57:06 UTC
I haven't made this point clear enough I think: the full source also fails only with -inline.
Comment 5 Don 2011-08-17 06:52:06 UTC
Partially reduced test case. The UNUSED static function seems to be necessary.

struct Stack(U) {
    struct Proxy { 
        int[] data;
        void shrink(){   data = data[0..0]; }
    }
    Proxy stack;
    void pop() {
        stack.shrink();
    }
}

int bug6510() {
    static void UNUSED() {
        Stack!(int) junk;
        junk.pop();
    }
    Stack!(int) opstk;
    opstk.pop();
    return 3;
}

 static assert(bug6510());
Comment 6 Don 2011-08-17 08:34:41 UTC
Reduced slightly further. If you uncomment the alias, everything works. So this is a bug which only happens when the semantic pass, and the inlining, is run on Stack!int *during* CTFE.
The stack.shrink() call gets translated into:
ref Proxy ths = this.stack;
which CTFE chokes on. Note that this is a local reference variable, which isn't legal D (it can only happen in compiler-generated code).

When the alias is present, the inline scan of bug6510() doesn't happen until after CTFE has finished. But interestingly an inline scan of UNUSED() still happens during CTFE.

---

struct Stack(U) {
    struct Proxy { 
        void shrink() {}
    }
    Proxy stack;
    void pop() {
        stack.shrink();
    }
}

alias Stack!int Stuck;

int bug6510() {
    static void UNUSED() {
        Stack!(int) junk;
        junk.pop();
    }
    Stack!(int) opstk;
    opstk.pop();
    return 3;
}

static assert(bug6510());
Comment 7 Dmitry Olshansky 2011-08-17 08:52:15 UTC
Thanks for getting a workaround so fast, it's just awesome.
I confirm that the full source works after I added aliases to Stack!(x).
Comment 8 Don 2011-08-19 01:38:48 UTC
(In reply to comment #7)
> Thanks for getting a workaround so fast, it's just awesome.
> I confirm that the full source works after I added aliases to Stack!(x).

I think the workaround is just luck, though, and may be quite fragile.
There seems to be something wrong at a fairly deep level, which doesn't involve CTFE. If you remove the static assert (so that CTFE isn't involved at all), then the presence or absence of the alias affects what gets inlined.

If there is no alias, semantic3() gets done on Stack before any inlining happens.
shrink and pop both get inlined in UNUSED, then pop gets inlined in bug6510.
It seems to be the inlining of 'shrink' inside 'pop' from 'unused' which is the problem.

But, if the alias is present, no inlining happens in UNUSED, but shrink and pop both get inlined in bug6510.
It seems wrong to me that the alias inhibits inlining. There also seems to be something wrong with the inlining itself.
Comment 9 Don 2011-08-19 05:53:36 UTC
Further reduced, showing that templates are not required. Seems to require an inlined member function call to a member of a nested struct, called from a nested function. No alias trick works in this case.

struct Stack6510 {
    struct Proxy { 
        void shrink() {}
    }
    Proxy stack;    
    void pop() {
        stack.shrink();        
    }
}

int bug6510() {
    static int used() {
        Stack6510 junk;
        junk.pop();
        return 3;
    }
    return used();
}

void main() {
    static assert(bug6510()==3);
}