Bug 8899 – Erroneous delegate usage and map template
Status
RESOLVED
Resolution
WORKSFORME
Severity
normal
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2012-10-26T13:44:00Z
Last change time
2015-06-09T05:15:04Z
Assigned to
nobody
Creator
maxim
Comments
Comment #0 by maxim — 2012-10-26T13:44:49Z
Currently dmd incorrectly treats delegates declared when type of parameter is omitted:
------------------------
import std.stdio;
void main()
{
auto x = 0;
writeln(typeof(a=>x).stringof);
writeln(typeof((int a)=>x).stringof);
writeln(typeof(delegate (a) { return x; }).stringof);
writeln(typeof(delegate (int a) { return x; }).stringof);
}
-------------------------
This is available at dpaste http://dpaste.dzfl.pl/738f7301
The way map template accepts function parameter allows to hide type mismatch (if a=>c is used in other contexts, dmd will complain about inability to deduct arguments of type void).
Exploit example is below:
-----main.d--------------
version (bug)
{
import std.string;
}
import test;
void main()
{
testfun();
}
-------test.d-----------
import std.algorithm;
import std.stdio;
void testfun()
{
int c = 0;
writeln([0].map!(a=>c)[0]);
}
-------------------------
Depending on whether bug version is set or not, the program may run correctly or not. The only significant difference in generated code is in lambda function. In correct version it takes local value from -30(%rdx), in the broken version from (%rdx). It may seem wired how importing standard module can change behavior.
Fixing syntax to delegate(int a) {} or (int a) => helps.
Probably the bug comes from how dmd arranges code generation. If main.d includes std.string (or other heavily templated module) it is forced to instantiate templates when it generates code for main module even if they are irrelevant. However, because main imports test, it instantiates map template and generates delegate before code of test function is generated.
Relevant parts of compilation logs of runnable and buggy versions:
----buggy.txt-----
import object
import std.string //imported, forcing instantiation during main codegen
.....
code main
function D main
function std.array.empty!(int).empty
function std.array.popFront!(int[]).popFront
function std.array.front!(int).front
function test.testfun.MapResult!(__lambda2,int[]).MapResult.back
function test.testfun.MapResult!(__lambda2,int[]).MapResult.popBack
function test.testfun.MapResult!(__lambda2,int[]).MapResult.this
function test.testfun.MapResult!(__lambda2,int[]).MapResult.empty
function test.testfun.MapResult!(__lambda2,int[]).MapResult.popFront
function test.testfun.MapResult!(__lambda2,int[]).MapResult.front
function test.testfun.MapResult!(__lambda2,int[]).MapResult.opIndex
function test.testfun.MapResult!(__lambda2,int[]).MapResult.length
function test.testfun.MapResult!(__lambda2,int[]).MapResult.opSlice
function test.testfun.MapResult!(__lambda2,int[]).MapResult.save
....
function test.testfun.__lambda2!(int).__lambda2 // testfunc isn't yet generated
....
code test
function test.testfun
function test.testfun.map!(__lambda2).map!(int[]).map
----runnable.txt-------
code main
function D main
code test
function test.testfun
function std.array.empty!(int).empty
function std.array.popFront!(int[]).popFront
function std.array.front!(int).front
function test.testfun.map!(__lambda2).map!(int[]).map
function test.testfun.MapResult!(__lambda2,int[]).MapResult.back
function test.testfun.MapResult!(__lambda2,int[]).MapResult.popBack
function test.testfun.MapResult!(__lambda2,int[]).MapResult.this
function test.testfun.MapResult!(__lambda2,int[]).MapResult.empty
function test.testfun.MapResult!(__lambda2,int[]).MapResult.popFront
function test.testfun.MapResult!(__lambda2,int[]).MapResult.front
function test.testfun.MapResult!(__lambda2,int[]).MapResult.opIndex
function test.testfun.MapResult!(__lambda2,int[]).MapResult.length
function test.testfun.MapResult!(__lambda2,int[]).MapResult.opSlice
function test.testfun.MapResult!(__lambda2,int[]).MapResult.save
.....
function test.testfun.__lambda2!(int).__lambda2
------------------------
So, probably the problem is in generating delegate before generating function which local variables are accessed by delegate.
This issue summarizes information from following issues:
- 8514 : delegate and map leads to segfault, importing standard module affects behavior
- 8854 : as described above
- 8832 : similar code which uses map and delegate has different and incorrect behavior
- 5064 (???): program crashes when using map and delegate
- 7978 : map, take and iota functions with delegates
Comment #1 by maxim — 2012-11-02T05:04:41Z
- issue 8934 shows same problem
Comment #2 by maxim — 2012-11-10T11:56:00Z
Issue 8994 shows similar problem.
Comment #3 by k.hara.pg — 2013-11-21T23:02:59Z
(In reply to comment #0)
> Probably the bug comes from how dmd arranges code generation. If main.d
> includes std.string (or other heavily templated module) it is forced to
> instantiate templates when it generates code for main module even if they are
> irrelevant. However, because main imports test, it instantiates map template
> and generates delegate before code of test function is generated.
>
[snip]
> ------------------------
> So, probably the problem is in generating delegate before generating function
> which local variables are accessed by delegate.
Import graph dependent lambda code generation bug is mostly fixed in 2.064 (eg. bug 10857). And with 2.064, the OP code (main.d + test.d + -version=bug) does not cause any runtime problem. So the issue seems to be fixed already.
> This issue summarizes information from following issues:
> - issue 8514 : delegate and map leads to segfault, importing standard module affects
> behavior
> - issue 8854 : as described above
> - issue 8832 : similar code which uses map and delegate has different and incorrect
> behavior
> - issue 5064 (???): program crashes when using map and delegate
> - issue 7978 : map, take and iota functions with delegates
All of listed issues are fixed right now.