mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-08 11:56:12 +03:00
[svn r133] Fixed some problems with inlining not happening :P
Fixed problems with certain cases of deeply nested classes/functions.
This commit is contained in:
parent
f420bc1265
commit
5eb88f9e80
17 changed files with 263 additions and 59 deletions
|
@ -606,6 +606,7 @@ struct FuncDeclaration : Declaration
|
||||||
|
|
||||||
FuncDeclaration *isFuncDeclaration() { return this; }
|
FuncDeclaration *isFuncDeclaration() { return this; }
|
||||||
|
|
||||||
|
// llvmdc stuff
|
||||||
bool llvmQueued;
|
bool llvmQueued;
|
||||||
llvm::Value* llvmThisVar;
|
llvm::Value* llvmThisVar;
|
||||||
std::set<VarDeclaration*> llvmNestedVars;
|
std::set<VarDeclaration*> llvmNestedVars;
|
||||||
|
|
|
@ -247,7 +247,7 @@ int runLINK()
|
||||||
argv.push((void*)s);
|
argv.push((void*)s);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!global.params.useInline) {
|
if (!(global.params.useInline || global.params.llvmInline)) {
|
||||||
argv.push((void *)"-disable-inlining");
|
argv.push((void *)"-disable-inlining");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -264,6 +264,7 @@ int main(int argc, char *argv[])
|
||||||
global.params.useArrayBounds = 0;
|
global.params.useArrayBounds = 0;
|
||||||
global.params.useSwitchError = 0;
|
global.params.useSwitchError = 0;
|
||||||
global.params.useInline = 0; // this one messes things up to a point where codegen breaks
|
global.params.useInline = 0; // this one messes things up to a point where codegen breaks
|
||||||
|
global.params.llvmInline = 0; // use this one instead to know if inline passes should be run
|
||||||
global.params.obj = 1;
|
global.params.obj = 1;
|
||||||
global.params.Dversion = 2;
|
global.params.Dversion = 2;
|
||||||
|
|
||||||
|
@ -471,9 +472,10 @@ int main(int argc, char *argv[])
|
||||||
#endif
|
#endif
|
||||||
else if (strcmp(p + 1, "inline") == 0) {
|
else if (strcmp(p + 1, "inline") == 0) {
|
||||||
// TODO
|
// TODO
|
||||||
// the ast rewrites dmd does for inling messes up the ast
|
// the ast rewrites dmd does for inlining messes up the ast.
|
||||||
// someday maybe we can support it, for now llvm does an excellent job at inlining
|
// someday maybe we can support it, for now llvm does an excellent job at inlining
|
||||||
global.params.useInline = 0; //1
|
global.params.useInline = 0; //1
|
||||||
|
global.params.llvmInline = 1;
|
||||||
}
|
}
|
||||||
else if (strcmp(p + 1, "nofloat") == 0)
|
else if (strcmp(p + 1, "nofloat") == 0)
|
||||||
global.params.nofloat = 1;
|
global.params.nofloat = 1;
|
||||||
|
|
|
@ -129,6 +129,7 @@ struct Param
|
||||||
char *tt_os;
|
char *tt_os;
|
||||||
char *data_layout;
|
char *data_layout;
|
||||||
char disassemble;
|
char disassemble;
|
||||||
|
char llvmInline;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Global
|
struct Global
|
||||||
|
|
|
@ -540,7 +540,7 @@ void DtoDefineFunc(FuncDeclaration* fd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const llvm::StructType* nestSType = llvm::StructType::get(nestTypes);
|
const llvm::StructType* nestSType = llvm::StructType::get(nestTypes);
|
||||||
Logger::cout() << "nested var struct has type:" << '\n' << *nestSType;
|
Logger::cout() << "nested var struct has type:" << *nestSType << '\n';
|
||||||
fd->llvmNested = new llvm::AllocaInst(nestSType,"nestedvars",allocaPoint);
|
fd->llvmNested = new llvm::AllocaInst(nestSType,"nestedvars",allocaPoint);
|
||||||
if (parentNested) {
|
if (parentNested) {
|
||||||
assert(fd->llvmThisVar);
|
assert(fd->llvmThisVar);
|
||||||
|
|
|
@ -13,8 +13,6 @@ using namespace llvm;
|
||||||
void llvmdc_optimize_module(Module* m, char lvl, bool doinline)
|
void llvmdc_optimize_module(Module* m, char lvl, bool doinline)
|
||||||
{
|
{
|
||||||
assert(lvl >= 0 && lvl <= 5);
|
assert(lvl >= 0 && lvl <= 5);
|
||||||
if (lvl == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
PassManager pm;
|
PassManager pm;
|
||||||
pm.add(new TargetData(m));
|
pm.add(new TargetData(m));
|
||||||
|
@ -24,12 +22,12 @@ void llvmdc_optimize_module(Module* m, char lvl, bool doinline)
|
||||||
pm.add(createRaiseAllocationsPass());
|
pm.add(createRaiseAllocationsPass());
|
||||||
pm.add(createCFGSimplificationPass());
|
pm.add(createCFGSimplificationPass());
|
||||||
pm.add(createPromoteMemoryToRegisterPass());
|
pm.add(createPromoteMemoryToRegisterPass());
|
||||||
|
pm.add(createGlobalOptimizerPass());
|
||||||
|
pm.add(createGlobalDCEPass());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lvl >= 2)
|
if (lvl >= 2)
|
||||||
{
|
{
|
||||||
pm.add(createGlobalOptimizerPass());
|
|
||||||
pm.add(createGlobalDCEPass());
|
|
||||||
pm.add(createIPConstantPropagationPass());
|
pm.add(createIPConstantPropagationPass());
|
||||||
pm.add(createDeadArgEliminationPass());
|
pm.add(createDeadArgEliminationPass());
|
||||||
pm.add(createInstructionCombiningPass());
|
pm.add(createInstructionCombiningPass());
|
||||||
|
@ -77,5 +75,6 @@ void llvmdc_optimize_module(Module* m, char lvl, bool doinline)
|
||||||
|
|
||||||
// level 4 and 5 are linktime optimizations
|
// level 4 and 5 are linktime optimizations
|
||||||
|
|
||||||
pm.run(*m);
|
if (lvl > 0 || doinline)
|
||||||
|
pm.run(*m);
|
||||||
}
|
}
|
||||||
|
|
12
gen/toir.cpp
12
gen/toir.cpp
|
@ -1824,6 +1824,18 @@ DValue* NewExp::toElem(IRState* p)
|
||||||
idx += tc->sym->llvmIRStruct->interfaces.size();
|
idx += tc->sym->llvmIRStruct->interfaces.size();
|
||||||
DtoStore(thisval->getRVal(), DtoGEPi(emem,0,idx,"tmp"));
|
DtoStore(thisval->getRVal(), DtoGEPi(emem,0,idx,"tmp"));
|
||||||
}
|
}
|
||||||
|
else if (tc->sym->isNested())
|
||||||
|
{
|
||||||
|
size_t idx = 2;
|
||||||
|
idx += tc->sym->llvmIRStruct->interfaces.size();
|
||||||
|
llvm::Value* nest = p->func()->decl->llvmNested;
|
||||||
|
if (!nest)
|
||||||
|
nest = p->func()->decl->llvmThisVar;
|
||||||
|
assert(nest);
|
||||||
|
llvm::Value* gep = DtoGEPi(emem,0,idx,"tmp");
|
||||||
|
nest = DtoBitCast(nest, gep->getType()->getContainedType(0));
|
||||||
|
DtoStore(nest, gep);
|
||||||
|
}
|
||||||
|
|
||||||
// then call constructor
|
// then call constructor
|
||||||
if (arguments) {
|
if (arguments) {
|
||||||
|
|
171
gen/tollvm.cpp
171
gen/tollvm.cpp
|
@ -796,64 +796,143 @@ llvm::Value* DtoArgument(const llvm::Type* paramtype, Argument* fnarg, Expressio
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
llvm::Value* DtoNestedVariable(VarDeclaration* vd)
|
static void print_frame_worker(VarDeclaration* var, Dsymbol* par)
|
||||||
{
|
{
|
||||||
FuncDeclaration* fd = vd->toParent()->isFuncDeclaration();
|
if (var->toParent2() == par)
|
||||||
assert(fd != NULL);
|
{
|
||||||
|
Logger::println("parent found: '%s' kind: '%s'", par->toChars(), par->kind());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
IRFunction* fcur = gIR->func();
|
Logger::println("diving into parent: '%s' kind: '%s'", par->toChars(), par->kind());
|
||||||
FuncDeclaration* f = fcur->decl;
|
LOG_SCOPE;
|
||||||
|
print_frame_worker(var, par->toParent2());
|
||||||
|
}
|
||||||
|
|
||||||
// on this stack
|
static void print_nested_frame_list(VarDeclaration* var, Dsymbol* par)
|
||||||
if (fd == f) {
|
{
|
||||||
llvm::Value* vdv = vd->llvmValue;
|
Logger::println("PRINTING FRAME LIST FOR NESTED VAR: '%s'", var->toChars());
|
||||||
if (!vdv)
|
{
|
||||||
{
|
LOG_SCOPE;
|
||||||
Logger::println(":o null vd->llvmValue for: %s", vd->toChars());
|
print_frame_worker(var, par);
|
||||||
vdv = fd->llvmNested;
|
}
|
||||||
assert(vdv);
|
Logger::println("DONE");
|
||||||
}
|
}
|
||||||
assert(vd->llvmNestedIndex != ~0);
|
|
||||||
llvm::Value* v = DtoGEPi(vdv,0,unsigned(vd->llvmNestedIndex),"tmp");
|
static const llvm::Type* get_next_frame_ptr_type(Dsymbol* sc)
|
||||||
if (vd->isParameter() && (vd->isRef() || vd->isOut() || DtoIsPassedByRef(vd->type))) {
|
{
|
||||||
Logger::cout() << "1267 loading: " << *v << '\n';
|
assert(sc->isFuncDeclaration() || sc->isClassDeclaration());
|
||||||
v = gIR->ir->CreateLoad(v,"tmp");
|
Dsymbol* p = sc->toParent2();
|
||||||
}
|
assert(p->isFuncDeclaration() || p->isClassDeclaration());
|
||||||
|
if (FuncDeclaration* fd = p->isFuncDeclaration())
|
||||||
|
{
|
||||||
|
llvm::Value* v = fd->llvmNested;
|
||||||
|
assert(v);
|
||||||
|
return v->getType();
|
||||||
|
}
|
||||||
|
else if (ClassDeclaration* cd = p->isClassDeclaration())
|
||||||
|
{
|
||||||
|
return DtoType(cd->type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger::println("symbol: '%s' kind: '%s'", sc->toChars(), sc->kind());
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static llvm::Value* get_frame_ptr_impl(VarDeclaration* vd, Dsymbol* sc, llvm::Value* v)
|
||||||
|
{
|
||||||
|
if (vd->toParent2() == sc)
|
||||||
|
{
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
else if (FuncDeclaration* fd = sc->isFuncDeclaration())
|
||||||
|
{
|
||||||
|
Logger::println("scope is function");
|
||||||
|
v = DtoBitCast(v, get_next_frame_ptr_type(fd));
|
||||||
|
Logger::cout() << "v = " << *v << '\n';
|
||||||
|
|
||||||
// on a caller stack
|
if (fd->toParent2() == vd->toParent2())
|
||||||
llvm::Value* ptr = f->llvmThisVar;
|
return v;
|
||||||
|
|
||||||
|
if (fd->toParent2()->isFuncDeclaration())
|
||||||
|
{
|
||||||
|
v = DtoGEPi(v, 0,0, "tmp");
|
||||||
|
v = DtoLoad(v);
|
||||||
|
}
|
||||||
|
else if (ClassDeclaration* cd = fd->toParent2()->isClassDeclaration())
|
||||||
|
{
|
||||||
|
size_t idx = 2;
|
||||||
|
idx += cd->llvmIRStruct->interfaces.size();
|
||||||
|
v = DtoGEPi(v,0,idx,"tmp");
|
||||||
|
v = DtoLoad(v);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
return get_frame_ptr_impl(vd, fd->toParent2(), v);
|
||||||
|
}
|
||||||
|
else if (ClassDeclaration* cd = sc->isClassDeclaration())
|
||||||
|
{
|
||||||
|
Logger::println("scope is class");
|
||||||
|
/*size_t idx = 2;
|
||||||
|
idx += cd->llvmIRStruct->interfaces.size();
|
||||||
|
v = DtoGEPi(v,0,idx,"tmp");
|
||||||
|
Logger::cout() << "gep = " << *v << '\n';
|
||||||
|
v = DtoLoad(v);*/
|
||||||
|
return get_frame_ptr_impl(vd, cd->toParent2(), v);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger::println("symbol: '%s'", sc->toChars());
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static llvm::Value* get_frame_ptr(VarDeclaration* vd)
|
||||||
|
{
|
||||||
|
Logger::println("RESOLVING FRAME PTR FOR NESTED VAR: '%s'", vd->toChars());
|
||||||
|
LOG_SCOPE;
|
||||||
|
IRFunction* irfunc = gIR->func();
|
||||||
|
|
||||||
|
// in the parent scope already
|
||||||
|
if (vd->toParent2() == irfunc->decl)
|
||||||
|
return irfunc->decl->llvmNested;
|
||||||
|
|
||||||
|
// use the 'this' pointer
|
||||||
|
llvm::Value* ptr = irfunc->decl->llvmThisVar;
|
||||||
assert(ptr);
|
assert(ptr);
|
||||||
|
|
||||||
f = f->toParent()->isFuncDeclaration();
|
// return the fully resolved frame pointer
|
||||||
assert(f);
|
ptr = get_frame_ptr_impl(vd, irfunc->decl, ptr);
|
||||||
assert(f->llvmNested);
|
Logger::cout() << "FOUND: '" << *ptr << "'\n";
|
||||||
const llvm::Type* nesttype = f->llvmNested->getType();
|
|
||||||
assert(nesttype);
|
|
||||||
|
|
||||||
ptr = gIR->ir->CreateBitCast(ptr, nesttype, "tmp");
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
Logger::cout() << "nested var reference:" << '\n' << *ptr << *nesttype << '\n';
|
llvm::Value* DtoNestedVariable(VarDeclaration* vd)
|
||||||
|
{
|
||||||
|
// log the frame list
|
||||||
|
IRFunction* irfunc = gIR->func();
|
||||||
|
print_nested_frame_list(vd, irfunc->decl);
|
||||||
|
|
||||||
while (f) {
|
// resolve frame ptr
|
||||||
if (fd == f) {
|
llvm::Value* ptr = get_frame_ptr(vd);
|
||||||
llvm::Value* v = DtoGEPi(ptr,0,vd->llvmNestedIndex,"tmp");
|
Logger::cout() << "nested ptr = " << *ptr << '\n';
|
||||||
if (vd->isParameter() && (vd->isRef() || vd->isOut() || DtoIsPassedByRef(vd->type))) {
|
|
||||||
Logger::cout() << "1291 loading: " << *v << '\n';
|
|
||||||
v = gIR->ir->CreateLoad(v,"tmp");
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ptr = DtoGEPi(ptr,0,0,"tmp");
|
|
||||||
ptr = gIR->ir->CreateLoad(ptr,"tmp");
|
|
||||||
}
|
|
||||||
f = f->toParent()->isFuncDeclaration();
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(0 && "nested var not found");
|
// we must cast here to be sure. nested classes just have a void*
|
||||||
return NULL;
|
ptr = DtoBitCast(ptr, vd->toParent2()->isFuncDeclaration()->llvmNested->getType());
|
||||||
|
|
||||||
|
// index nested var and load (if necessary)
|
||||||
|
llvm::Value* v = DtoGEPi(ptr, 0, vd->llvmNestedIndex, "tmp");
|
||||||
|
// references must be loaded, for normal variables this IS already the variable storage!!!
|
||||||
|
if (vd->isParameter() && (vd->isRef() || vd->isOut() || DtoIsPassedByRef(vd->type)))
|
||||||
|
v = DtoLoad(v);
|
||||||
|
|
||||||
|
Logger::cout() << "FINAL RESULT: " << *v << '\n';
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -138,7 +138,7 @@ void Module::genobjfile()
|
||||||
|
|
||||||
// run optimizer
|
// run optimizer
|
||||||
if (global.params.optimize) {
|
if (global.params.optimize) {
|
||||||
llvmdc_optimize_module(ir.module, global.params.optimizeLevel, global.params.useInline);
|
llvmdc_optimize_module(ir.module, global.params.optimizeLevel, global.params.llvmInline);
|
||||||
}
|
}
|
||||||
|
|
||||||
// write bytecode
|
// write bytecode
|
||||||
|
|
|
@ -259,6 +259,7 @@ test/arrays7.d
|
||||||
test/arrays8.d
|
test/arrays8.d
|
||||||
test/arrays9.d
|
test/arrays9.d
|
||||||
test/assign.d
|
test/assign.d
|
||||||
|
test/ast1.d
|
||||||
test/b.d
|
test/b.d
|
||||||
test/bitops.d
|
test/bitops.d
|
||||||
test/bug1.d
|
test/bug1.d
|
||||||
|
@ -336,6 +337,7 @@ test/bug9.d
|
||||||
test/c.d
|
test/c.d
|
||||||
test/classes.d
|
test/classes.d
|
||||||
test/classes10.d
|
test/classes10.d
|
||||||
|
test/classes11.d
|
||||||
test/classes2.d
|
test/classes2.d
|
||||||
test/classes3.d
|
test/classes3.d
|
||||||
test/classes4.d
|
test/classes4.d
|
||||||
|
@ -406,9 +408,15 @@ test/multiarr3.d
|
||||||
test/multiarr4.d
|
test/multiarr4.d
|
||||||
test/neg.d
|
test/neg.d
|
||||||
test/nested1.d
|
test/nested1.d
|
||||||
|
test/nested10.d
|
||||||
test/nested2.d
|
test/nested2.d
|
||||||
test/nested3.d
|
test/nested3.d
|
||||||
test/nested4.d
|
test/nested4.d
|
||||||
|
test/nested5.d
|
||||||
|
test/nested6.d
|
||||||
|
test/nested7.d
|
||||||
|
test/nested8.d
|
||||||
|
test/nested9.d
|
||||||
test/pointers.d
|
test/pointers.d
|
||||||
test/pt.d
|
test/pt.d
|
||||||
test/ptrarith.d
|
test/ptrarith.d
|
||||||
|
|
|
@ -11,4 +11,9 @@ int find(char[] s, dchar c)
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
}
|
char[] hello = "hello world";
|
||||||
|
int i = find(hello, 'w');
|
||||||
|
assert(i == 6);
|
||||||
|
i = find(hello, 'z');
|
||||||
|
assert(i == -1);
|
||||||
|
}
|
||||||
|
|
20
test/nested10.d
Normal file
20
test/nested10.d
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
module nested10;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
int j = 3;
|
||||||
|
void F()
|
||||||
|
{
|
||||||
|
int i = j;
|
||||||
|
printf("F: i = %d, j = %d\n", i, j);
|
||||||
|
void G()
|
||||||
|
{
|
||||||
|
printf("G: i = %d, j = %d\n", i, j);
|
||||||
|
j += i;
|
||||||
|
}
|
||||||
|
G();
|
||||||
|
}
|
||||||
|
F();
|
||||||
|
printf("6 = %d\n", j);
|
||||||
|
assert(j == 6);
|
||||||
|
}
|
|
@ -11,9 +11,11 @@ void main()
|
||||||
void func()
|
void func()
|
||||||
{
|
{
|
||||||
printf("Hello world %d\n", i++);
|
printf("Hello world %d\n", i++);
|
||||||
|
//i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scope c = new C;
|
scope c = new C;
|
||||||
c.func();
|
c.func();
|
||||||
|
assert(i == 44);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,19 +4,19 @@ void main()
|
||||||
{
|
{
|
||||||
int i = 42;
|
int i = 42;
|
||||||
|
|
||||||
printf("Hello world %d\n", i++);
|
printf("main() %d\n", i++);
|
||||||
|
|
||||||
class C
|
class C
|
||||||
{
|
{
|
||||||
void func()
|
void func()
|
||||||
{
|
{
|
||||||
printf("Hello world %d\n", i++);
|
printf("C.func() %d\n", i++);
|
||||||
|
|
||||||
class C2
|
class C2
|
||||||
{
|
{
|
||||||
void func2()
|
void func2()
|
||||||
{
|
{
|
||||||
printf("Hello world %d\n", i++);
|
printf("C2.func2() %d\n", i++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
32
test/nested7.d
Normal file
32
test/nested7.d
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
module nested7;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
i = 52;
|
||||||
|
printf("i = %d\n", i);
|
||||||
|
|
||||||
|
void func()
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
|
||||||
|
void func2()
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
|
||||||
|
void func3()
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
func3();
|
||||||
|
}
|
||||||
|
|
||||||
|
func2();
|
||||||
|
}
|
||||||
|
|
||||||
|
func();
|
||||||
|
|
||||||
|
printf("i = %d\n", i);
|
||||||
|
assert(i == 55);
|
||||||
|
}
|
30
test/nested8.d
Normal file
30
test/nested8.d
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
module nested8;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
int i = 1;
|
||||||
|
void func()
|
||||||
|
{
|
||||||
|
printf("func()\n");
|
||||||
|
i++;
|
||||||
|
void func2()
|
||||||
|
{
|
||||||
|
printf(" func2()\n");
|
||||||
|
int j = i + 1;
|
||||||
|
void func3()
|
||||||
|
{
|
||||||
|
printf(" func3()\n");
|
||||||
|
j++;
|
||||||
|
printf(" done = %d\n", j);
|
||||||
|
}
|
||||||
|
func3();
|
||||||
|
i = j;
|
||||||
|
printf(" done = %d\n", j);
|
||||||
|
}
|
||||||
|
func2();
|
||||||
|
printf("done\n");
|
||||||
|
}
|
||||||
|
func();
|
||||||
|
printf("i == %d\n", i);
|
||||||
|
assert(i == 4);
|
||||||
|
}
|
13
test/nested9.d
Normal file
13
test/nested9.d
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
module nested9;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
int i = 42;
|
||||||
|
int func()
|
||||||
|
{
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
|
int j = func();
|
||||||
|
printf("j = %d\n", j);
|
||||||
|
assert(j == 43);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue