Created attachment 358
Compile and run this file
While porting a library from D1 to D2 I encountered the following DMD error:
dmd: glue.c:652: virtual void FuncDeclaration::toObjFile(int): Assertion `!v->csym' failed.
Aborted
It's hard to say what causes the error, but I've been able to narrow it down to a point where almost any change I make makes the error disappear. The source files are attached. (There's four of them, but they are very short.)
To reproduce the error, compile and run the program with rdmd:
rdmd moduleA.d
Strangely enough, running DMD directly does not reproduce the error:
dmd moduleA.d moduleB.d moduleC.d moduleD.d
./moduleA
For some changes a runtime bug is introduced instead; see the commented section in moduleC.d.
Comment #1 by bugzilla — 2009-05-11T01:57:57Z
Created attachment 359
moduleB: Imported by moduleA
Comment #2 by bugzilla — 2009-05-11T01:58:36Z
Created attachment 360
moduleC: imported by moduleB
Comment #3 by bugzilla — 2009-05-11T01:59:11Z
Created attachment 361
moduleD: imported by moduleC
Comment #4 by snake.scaly — 2009-08-13T18:52:22Z
Here is a simpler test case for what I think is the same issue:
-----8<----testa.d-----
import testb;
int foo()
{
return bar(0);
}
-----8<----testa.d-----
-----8<----testb.d-----
T bar(T)(T x)
{
return baz!(T, x)();
}
T baz(T, T x)()
{
return x;
}
-----8<----testb.d-----
Compile order matters:
> dmd -c testb.d testa.d
Assertion failure: '!v->csym' on line 563 in file 'glue.c'
abnormal program termination
> dmd -c testa.d testb.d
>
Tested this with DMD 1.030, 1.033, 1.041, 1.042, 1.046, 2.026, 2.027 and 2.031, with exactly the same results.
Comment #5 by snake.scaly — 2009-08-13T19:00:24Z
Sorry, this ICE is definitely on *invalid* code since the baz template is being parametrized on the bar's run-time argument.
Comment #6 by clugdbug — 2009-08-14T00:43:43Z
Fundamentally this is an accepts-invalid bug, it shouldn't reach the code generation stage where the ICE occurs. Here's a test case which shouldn't compile.
There's a chance this could be a regression.
---
T bar(T)(T y) {
return baz!(T, y)();
}
T baz(T, T z)() {
return z*z;
}
void main() {
assert(bar(4)!=0);
}
Comment #7 by clugdbug — 2009-08-14T06:04:11Z
It's definitely not a recent regression, D1.020 behaved the same way.
Probably the same as 2733, (but please DO NOT mark this as a duplicate, this is a much better bug report).
Even smaller test case:
void baz(int z)() {}
void main() {
int x;
baz!(x)();
}
Comment #8 by clugdbug — 2009-08-14T07:30:04Z
Actually I'm not sure if this a bug or an easter egg. The current behaviour of template value parameters is: if it is a compile-time constant, instantiate the template as a value. If it is a variable, instantiate it as an alias parameter.
That's actually quite cool.
Comment #9 by snake.scaly — 2009-08-14T08:03:44Z
By the way, the example Lars posted is not as obviously invalid, at least to
me. He passes a local variable as a template alias parameter. Docs say that
"local names" can be used as template alias parameters. This actually works
correctly:
import std.stdio;
void main() {
foo(1);
}
void foo(int a) {
bar!(a)();
writefln(a);
}
void bar(alias var)() {
var = 2;
}
Prints 2. This works even if bar is defined in a different module.
Comment #10 by jarrett.billingsley — 2009-08-14T08:05:06Z
(In reply to comment #8)
> Actually I'm not sure if this a bug or an easter egg. The current behaviour of
> template value parameters is: if it is a compile-time constant, instantiate the
> template as a value. If it is a variable, instantiate it as an alias parameter.
>
> That's actually quite cool.
Local variables have been passable as alias parameters for quite some time now. :)
Comment #11 by clugdbug — 2009-08-14T08:12:03Z
(In reply to comment #10)
> (In reply to comment #8)
> > Actually I'm not sure if this a bug or an easter egg. The current behaviour of
> > template value parameters is: if it is a compile-time constant, instantiate the
> > template as a value. If it is a variable, instantiate it as an alias parameter.
> >
> > That's actually quite cool.
>
> Local variables have been passable as alias parameters for quite some time now.
> :)
Yes, but if you declare a template parameter as 'alias', it can't accept literals.
You can actually achieve the same effect with a tuple parameter, so this behaviour should probably disappear.
Comment #12 by bugzilla — 2009-08-14T12:55:04Z
What I find very strange here is that when I compile my test case with DMD 2.031 specifying just the four attached files, it works. And it should -- I think the code is valid according to the spec of both D1 and D2.
lars@neutrino:~/tmp/bug2962$ dmd moduleA.d moduleB.d moduleC.d moduleD.d
lars@neutrino:~/tmp/bug2962$ ./moduleA
1
However, when I compile it like rdmd does, specifying every module in Phobos on the command line, it fails:
lars@neutrino:~/tmp/bug2962$ dmd 'moduleA.d' 'moduleC.d' '/usr/local/include/d/druntime/core/sys/posix/config.d' '/usr/local/include/d/druntime/core/stdc/stdio.d' '/usr/local/include/d/druntime/core/sys/posix/fcntl.d' [...]
dmd: glue.c:658: virtual void FuncDeclaration::toObjFile(int): Assertion `!v->csym' failed.
(Run rdmd with --dry-run to see the full list of files it passes to DMD.)
Comment #13 by snake.scaly — 2009-08-14T13:00:32Z
(In reply to comment #12)
> What I find very strange here is that when I compile my test case with DMD
> 2.031 specifying just the four attached files, it works. And it should -- I
> think the code is valid according to the spec of both D1 and D2.
>
> lars@neutrino:~/tmp/bug2962$ dmd moduleA.d moduleB.d moduleC.d moduleD.d
Try dmd moduleA.d moduleC.d moduleB.d moduleD.d
Comment #14 by bugzilla — 2009-08-14T13:05:26Z
Oh, my. :) Didn't even cross my mind that the order of the files matter.
But my example IS valid code, right?
Comment #15 by snake.scaly — 2009-08-14T13:34:17Z
I do think it's valid, see my comment #9. According to the obj2asm, when you pass a local var as an alias parameter to a template:
void foo() {
int i;
bar!(i)();
}
void bar(alias var)() {
var = 1;
}
compiler rewrites it as
void foo() {
int i;
void bar() {
i = 1;
}
bar();
}
Probably this rewrite is not always possible, hence the assertion failure.
Comment #16 by snake.scaly — 2009-08-14T14:05:39Z
Seems like this bug is an ice-on-VALID after all. All our examples boil down to passing local variables as alias template arguments. Also it seems like a nice candidate for the issue 340 list. Don, what do you think?
Comment #17 by clugdbug — 2009-08-15T11:41:14Z
(In reply to comment #16)
> Seems like this bug is an ice-on-VALID after all. All our examples boil down
> to passing local variables as alias template arguments. Also it seems like a
> nice candidate for the issue 340 list. Don, what do you think?
Could be. I'm busy on other stuff right now, so haven't had a good look at it. Are you able to simplify the valid code example?
It's worth all bug reporters knowing that the first step to fixing a bug is to minimize the test case that's in Bugzilla. It helps a _lot_. I found that 90% of the bugs in bugzilla can be minimized further.
Comment #18 by clugdbug — 2009-09-10T01:29:07Z
I don't have a patch, but it's easy to at least get the line number into the ICE message.
glue.c line 658:
if (v->csym)
error("Bugzilla 2962 - Internal Compiler Error");
Doing this would greatly reduce frustration, and probably allow us to close bug #3283. I'm sure it's a duplicate.
Comment #19 by clugdbug — 2009-09-13T03:39:51Z
*** Issue 3283 has been marked as a duplicate of this issue. ***
Comment #20 by clugdbug — 2009-09-21T12:14:39Z
Here's a reduced test case for the ice-on-valid case. dmd moduleC.d moduleA.d
The instantiation of funcD inside funcC is failing, if funcC is only instantiated from another module. Some kind of instantiation order problem (semantic not run, perhaps). This is definitely valid code.
Quite probably related to the other template alias ICE and bad codegen bugs.(eg, bug 3293, bug 2325, bug 2845, ...)
moduleA.d
==========
import moduleC;
void main() {
funcC!(bool)(1.0);
}
=====
moduleC.d
=======
void funcD(alias x)() {
assert(x==1.0);
}
void funcC(T)(double a){
// Case 1: ICE(glue.c)
funcD!(a)();
// Case 2: wrong code
double b = 1.0; funcD!(b)();
}
Comment #21 by clugdbug — 2009-10-13T02:54:53Z
This is really tough, it's an order-of-evaluation issue.
When generating the code for a template, which has a local variable as an alias parameter, the alias parameter MUST be created before the code for template is.
But I have no idea how the order of code generation is supposed to be enforced. It seems to always get it right if everything is in the same file.
Comment #22 by leandro.lucarella — 2009-11-05T06:26:17Z
I can confirm that this bug is dependent on the order of code generation. I just ran into this on a project I'm working on and it seems that whether this bug is exposed or not depends on the order in which I pass the files to DMD. Some permutations result in my project compiling and linking w/o errors. Others result in this bug being exposed.
Comment #24 by rayerd.wiz — 2010-12-28T19:59:09Z
Phobos unittest is broken.
I tried to build dmd, druntime, and phobos of trunk.
You should notice -unittest below.
c:\d\svnproj\phobos_trunk\phobos\std>dmd -c -of_ -unittest algorithm.d stdio.d
...
std.algorithm.canFindSorted is scheduled for deprecation. Use std.range.SortedR
ange.canFind instead.
std.algorithm.lowerBound is scheduled for deprecation. Use std.range.SortedRang
e.lowerBound instead.
std.algorithm.upperBound is scheduled for deprecation. Use std.range.SortedRang
e.upperBound instead.
std.algorithm.equalRange is scheduled for deprecation. Use std.range.SortedRang
e.equalRange instead.
C:\D\dmd2\windows\bin\..\..\src\phobos\std\conv.d(1263): Error: function std.con
v.parse!(float,LockingTextReader).parse compiler error, parameter 'p', bugzilla
2962?
Assertion failure: '0' on line 729 in file 'glue.c'
abnormal program termination
(In reply to comment #25)
> Sorry, you will encounter this issue when dmd and phobos are debug build.
> For dmd, you will build dmd with "make -f win32.mak" and "DEBUG=-g -D
> -DUNITTEST -L/detailedmap".
No problem, I was able to reproduce it from your first comment.
Comment #27 by clugdbug — 2010-12-29T00:19:59Z
A simpler command line is this:
/dmd/src/phobos/std> dmd -c -unittest conv.d stdio.d
The unittest which it's failing in, is in stdio.d, line 1630:
unittest
{
float f;
if (false) readf("%s", &f);
}
Which just shows how nasty this bug is.
Comment #28 by rayerd.wiz — 2010-12-29T08:39:15Z
(In reply to comment #27)
> A simpler command line is this:
> /dmd/src/phobos/std> dmd -c -unittest conv.d stdio.d
> The unittest which it's failing in, is in stdio.d, line 1630:
>
> unittest
> {
> float f;
> if (false) readf("%s", &f);
> }
> Which just shows how nasty this bug is.
This is a progress report.
failed:
/dmd/src/phobos/std dmd -c -unittest conv.d stdio.d
succeeded:
/dmd/src/phobos/std dmd -c -unittest stdio.d conv.d
Comment #29 by rayerd.wiz — 2010-12-29T10:14:31Z
A simpler sample is this:
// module a
import b;
void main()
{
foo!int(0);
}
// module b
void foo(T)(T p)
{
void inner(U)() {
auto p2 = p;
}
inner!int();
}
C:\Users\haru\Desktop\c>dmd b a
b.d(1): Error: function b.foo!(int).foo compiler error, parameter 'p', bugzilla
2962?
assert glue.c(729) 0
<--------------------------------- HALT
However, the following result is success.
C:\Users\haru\Desktop\c>dmd a b
Max # of fixups = 8
Comment #30 by r.sagitario — 2011-01-30T08:07:38Z
I've also hit this issue today after updating to the latest dmd and runtime from github.
As there is no hint where the problem is in my project, I took a look at the last test case, and figured out a few things:
- the template instantiation for foo is added to module a, but the instantiation of inner is added to module b.
- the error is triggered because the code for the inner function is generated before the code for the outer function. Hence the reliance on the order of modules on the command line.
- that made me think, that it might be ok to just skip the test, but the following code shows, that it does not work because the inner function needs the stack offset which is only calculated when the outer function is generated:
// module b
T foo(T)(T p, int q)
{
T inner(U)() {
return p;
}
return inner!int();
}
// module a
import b;
void main()
{
assert(foo!int(5, 2) == 5);
}
fails for "dmd b.a b.d", but not for "dmd a.d b.d".
Is there a good reason why the inner function is generated into a different module than the outer function?
Comment #31 by r.sagitario — 2011-02-06T12:22:16Z
Investigating this a little further, I noticed that when compiling "dmd b a" the problem seems to be that inner!int is *parsed* before the invocation of the outer foo, which causes a TemplateInstance to be created. This later causes the code generation of inner to come first, making the compiler bail out, because the reference to the parameter of the outer function is not known yet.
As a workaround it seems to work if you put a reference to foo!int where the parser sees it before inner!int, for example into another file c.d:
module c;
static if(__traits(compiles,foo!int))){}
and the compile it with "dmd c b a".
dmd v2.052, Ubuntu, quite short example of this bug:
$ cat main.d
import std.stdio;
void main(){}
$ cat utils.d
import std.json;
auto val = &parseJSON!string;
$ dmd main.d utils.d
/usr/include/d/dmd/phobos/std/conv.d(1301): Error: function std.conv.parse!(real,string).parse compiler error, parameter 'p', bugzilla 2962?
dmd: glue.c:734: virtual void FuncDeclaration::toObjFile(int): Assertion `0' failed.
Aborted (core dumped)
Comment #34 by clugdbug — 2011-05-23T04:20:16Z
(In reply to comment #33)
> dmd v2.052, Ubuntu, quite short example of this bug:
<snip>
No, that's a long example -- it imports from Phobos. Test cases for compiler bugs which import from Phobos are not completely reduced.
The examples in comments 20 and 29 are minimal test cases.
Comment #35 by clugdbug — 2012-02-03T02:25:33Z
*** Issue 7323 has been marked as a duplicate of this issue. ***
Comment #36 by code — 2012-03-22T11:11:55Z
cat > a.d << CODE
import b;
alias foo!() fooi;
CODE
cat > b.d << CODE
void foo()(int p)
{
int inner()() { return p; }
alias inner!() finner;
}
CODE
dmd -c b a
--------
Slightly more reduced test case.
There are two things that should get fixed.
- We push template instances into the wrong object/module.
The module a template instance is attached to is found
by following the importedFrom chain. The head of the chain
should be the template declaration module not the instantiation
module. By doing so we guarantee that all instances remain
ordered and are bundled with their dependencies.
- VarDeclaration::toSymbol creates csym lazily.
I don't see any reason why this shouldn't apply to
parameters as well, e.g. replacing the 'p' parameter
with a local variable fixes the bug. So we should
just call v->toSymbol() and remove the assertion.
Comment #37 by github-bugzilla — 2012-03-22T22:41:57Z
(In reply to comment #36)
> cat > a.d << CODE
> import b;
> alias foo!() fooi;
> CODE
>
> cat > b.d << CODE
> void foo()(int p)
> {
> int inner()() { return p; }
> alias inner!() finner;
> }
> CODE
>
> dmd -c b a
>
> --------
>
> Slightly more reduced test case.
>
> There are two things that should get fixed.
>
> - We push template instances into the wrong object/module.
> The module a template instance is attached to is found
> by following the importedFrom chain. The head of the chain
> should be the template declaration module not the instantiation
> module. By doing so we guarantee that all instances remain
> ordered and are bundled with their dependencies.
It's interesting how closely related that issue is to this one I posted yesterday:
https://github.com/D-Programming-Language/dmd/pull/824
Fundamentally we need to sort out which module owns a function. I'm not sure where the code belongs.
>
> - VarDeclaration::toSymbol creates csym lazily.
> I don't see any reason why this shouldn't apply to
> parameters as well, e.g. replacing the 'p' parameter
> with a local variable fixes the bug. So we should
> just call v->toSymbol() and remove the assertion.
Comment #39 by github-bugzilla — 2012-04-25T16:27:25Z
Comment #42 by jens.k.mueller — 2012-08-17T14:05:39Z
The following code (two modules) shows that this bug is not fixed yet.
module foo;
void baz(alias pred)()
{
pred(0);
}
---
module bar;
import foo;
void bar(int id)
{
baz!((a)
{
id = 0; // passing a delegate as template argument does not allow
// accessing its environment
})();
}
dmd foo.d bar.d
results in:
bar.d(5): Error: function bar.bar compiler error, parameter 'id', bugzilla 2962?
dmd: glue.c:717: virtual void FuncDeclaration::toObjFile(int): Assertion `0' failed.
Unfortunately I couldn't reduce the test case any further. If the delegate has no parameter I cannot reproduce the compiler assertion failure. And of course if id is not accessed from within the delegate the problem goes away. This is at the core of the problem.
As pointed out in the comments the specified order of files is important. I.e.
dmd bar.d foo.d compiles just fine.
I used DMD64 D Compiler v2.060.
I'm reopening this bug.
Comment #43 by tmn.dbugs — 2012-11-08T05:24:57Z
Using a local variable inside the delegate instead of a parameter of the calling function results in incorrect code:
----------------
module b;
auto call(alias fun)() {
return fun(0);
}
----------------
module a;
import b;
import std.stdio;
void main() {
int x = 1;
writeln(call!(a => x)); // should print the value of x
}
----------------
$ dmd b.d a.d -ofa && ./a
-771364480
(Using DMD64 D Compiler v2.060 on Linux)
Comment #44 by clugdbug — 2012-12-12T03:47:58Z
Comments 42 and 43 refer to an entirely different bug. The test cases in those comments were fixed by today's fix to bug 6395.
Comment #45 by github-bugzilla — 2013-08-27T13:20:09Z