Mercurial > projects > ddmd
view dmd/ReturnStatement.d @ 178:e3afd1303184
Many small bugs fixed
Made all classes derive from TObject to detect memory leaks (functionality is disabled for now)
Began work on overriding backend memory allocations (to avoid memory leaks)
author | korDen |
---|---|
date | Sun, 17 Oct 2010 07:42:00 +0400 |
parents | a43c65469219 |
children | b0d41ff5e0df |
line wrap: on
line source
module dmd.ReturnStatement; import dmd.common; import dmd.Loc; import dmd.Statement; import dmd.GotoStatement; import dmd.STC; import dmd.CompoundStatement; import dmd.Id; import dmd.AssignExp; import dmd.ExpStatement; import dmd.FuncDeclaration; import dmd.IntegerExp; import dmd.ThisExp; import dmd.StructDeclaration; import dmd.TypeFunction; import dmd.CSX; import dmd.RET; import dmd.TOK; import dmd.Type; import dmd.Expression; import dmd.StructLiteralExp; import dmd.TypeStruct; import dmd.Scope; import dmd.OutBuffer; import dmd.HdrGenState; import dmd.InterState; import dmd.InlineCostState; import dmd.InlineDoState; import dmd.InlineScanState; import dmd.IRState; import dmd.TY; import dmd.WANT; import dmd.VarExp; import dmd.VarDeclaration; import dmd.GlobalExpressions; import dmd.BE; import dmd.Global; import dmd.codegen.Util; import dmd.backend.Blockx; import dmd.backend.elem; import dmd.backend.TYM; import dmd.backend.Util; import dmd.backend.OPER; import dmd.backend.mTY; import dmd.backend.BC; import core.stdc.string; class ReturnStatement : Statement { Expression exp; this(Loc loc, Expression exp) { register(); super(loc); this.exp = exp; } override Statement syntaxCopy() { Expression e = exp ? exp.syntaxCopy() : null; return new ReturnStatement(loc, e); } override void toCBuffer(OutBuffer buf, HdrGenState* hgs) { buf.printf("return "); if (exp) exp.toCBuffer(buf, hgs); buf.writeByte(';'); buf.writenl(); } override Statement semantic(Scope sc) { //printf("ReturnStatement.semantic() %s\n", toChars()); FuncDeclaration fd = sc.parent.isFuncDeclaration(); Scope scx = sc; int implicit0 = 0; if (sc.fes) { // Find scope of function foreach is in for (; 1; scx = scx.enclosing) { assert(scx); if (scx.func !is fd) { fd = scx.func; // fd is now function enclosing foreach break; } } } Type tret = fd.type.nextOf(); if (fd.tintro) { /* We'll be implicitly casting the return expression to tintro */ tret = fd.tintro.nextOf(); } Type tbret = null; if (tret) { tbret = tret.toBasetype(); } // main() returns 0, even if it returns void if (!exp && (!tbret || tbret.ty == TY.Tvoid) && fd.isMain()) { implicit0 = 1; exp = new IntegerExp(0); } if (sc.incontract || scx.incontract) error("return statements cannot be in contracts"); if (sc.tf || scx.tf) error("return statements cannot be in finally, scope(exit) or scope(success) bodies"); if (fd.isCtorDeclaration()) { // Constructors implicitly do: // return this; if (exp && exp.op != TOK.TOKthis) { error("cannot return expression from constructor"); } exp = new ThisExp(Loc(0)); } if (!exp) { fd.nrvo_can = 0; } if (exp) { fd.hasReturnExp |= 1; exp = exp.semantic(sc); exp = resolveProperties(sc, exp); exp = exp.optimize(WANT.WANTvalue); if (fd.nrvo_can && exp.op == TOK.TOKvar) { VarExp ve = cast(VarExp)exp; VarDeclaration v = ve.var.isVarDeclaration(); if ((cast(TypeFunction)fd.type).isref) { // Function returns a reference fd.nrvo_can = 0; } else if (!v || v.isOut() || v.isRef()) { fd.nrvo_can = 0; } else if (tbret.ty == TY.Tstruct && (cast(TypeStruct)tbret).sym.dtor) { // Struct being returned has destructors fd.nrvo_can = 0; } else if (fd.nrvo_var is null) { if (!v.isDataseg() && !v.isParameter() && v.toParent2() == fd) { //printf("Setting nrvo to %s\n", v.toChars()); fd.nrvo_var = v; } else { fd.nrvo_can = 0; } } else if (fd.nrvo_var != v) { fd.nrvo_can = 0; } } else { fd.nrvo_can = 0; } if (fd.returnLabel && tbret.ty != TY.Tvoid) { ; } else if (fd.inferRetType) { auto tf = cast(TypeFunction)fd.type; assert(tf.ty == TY.Tfunction); Type tfret = tf.nextOf(); if (tfret) { if (!exp.type.equals(tfret)) error("mismatched function return type inference of %s and %s", exp.type.toChars(), tfret.toChars()); /* The "refness" is determined by the first return statement, * not all of them. This means: * return 3; return x; // ok, x can be a value * return x; return 3; // error, 3 is not an lvalue */ } else { if (tf.isref) { /* Determine "refness" of function return: * if it's an lvalue, return by ref, else return by value */ if (exp.isLvalue()) { /* Return by ref * (but first ensure it doesn't fail the "check for * escaping reference" test) */ uint errors = global.errors; global.gag++; exp.checkEscapeRef(); global.gag--; if (errors != global.errors) { tf.isref = false; // return by value global.errors = errors; } } else tf.isref = false; // return by value } tf.next = exp.type; fd.type = tf.semantic(loc, sc); if (!fd.tintro) { tret = fd.type.nextOf(); tbret = tret.toBasetype(); } } } else if (tbret.ty != TY.Tvoid) { exp = exp.implicitCastTo(sc, tret); exp = exp.optimize(WANT.WANTvalue); } } else if (fd.inferRetType) { if (fd.type.nextOf()) { if (fd.type.nextOf().ty != TY.Tvoid) { error("mismatched function return type inference of void and %s", fd.type.nextOf().toChars()); } } else { (cast(TypeFunction*)fd.type).next = Type.tvoid; fd.type = fd.type.semantic(loc, sc); if (!fd.tintro) { tret = Type.tvoid; tbret = tret; } } } else if (tbret.ty != TY.Tvoid) {// if non-void return error("return expression expected"); } if (sc.fes) { Statement s; if (exp && !implicit0) { exp = exp.implicitCastTo(sc, tret); } if (!exp || exp.op == TOK.TOKint64 || exp.op == TOK.TOKfloat64 || exp.op == TOK.TOKimaginary80 || exp.op == TOK.TOKcomplex80 || exp.op == TOK.TOKthis || exp.op == TOK.TOKsuper || exp.op == TOK.TOKnull || exp.op == TOK.TOKstring) { sc.fes.cases.push(cast(void*)this); // Construct: return cases.dim+1; s = new ReturnStatement(Loc(0), new IntegerExp(sc.fes.cases.dim + 1)); } else if (fd.type.nextOf().toBasetype() == Type.tvoid) { s = new ReturnStatement(Loc(0), null); sc.fes.cases.push(cast(void*)s); // Construct: { exp; return cases.dim + 1; } Statement s1 = new ExpStatement(loc, exp); Statement s2 = new ReturnStatement(Loc(0), new IntegerExp(sc.fes.cases.dim + 1)); s = new CompoundStatement(loc, s1, s2); } else { // Construct: return vresult; if (!fd.vresult) { // Declare vresult VarDeclaration v = new VarDeclaration(loc, tret, Id.result, null); v.noauto = true; v.semantic(scx); if (!scx.insert(v)) { assert(0); } v.parent = fd; fd.vresult = v; } s = new ReturnStatement(Loc(0), new VarExp(Loc(0), fd.vresult)); sc.fes.cases.push(cast(void*)s); // Construct: { vresult = exp; return cases.dim + 1; } exp = new AssignExp(loc, new VarExp(Loc(0), fd.vresult), exp); exp.op = TOK.TOKconstruct; exp = exp.semantic(sc); Statement s1 = new ExpStatement(loc, exp); Statement s2 = new ReturnStatement(Loc(0), new IntegerExp(sc.fes.cases.dim + 1)); s = new CompoundStatement(loc, s1, s2); } return s; } if (exp) { if (fd.returnLabel && tbret.ty != TY.Tvoid) { assert(fd.vresult); VarExp v = new VarExp(Loc(0), fd.vresult); exp = new AssignExp(loc, v, exp); exp.op = TOK.TOKconstruct; exp = exp.semantic(sc); } if ((cast(TypeFunction)fd.type).isref && !fd.isCtorDeclaration()) { // Function returns a reference if (tbret.isMutable()) exp = exp.modifiableLvalue(sc, exp); else exp = exp.toLvalue(sc, exp); exp.checkEscapeRef(); } else { //exp.dump(0); //exp.print(); exp.checkEscape(); } } /* BUG: need to issue an error on: * this * { if (x) return; * super(); * } */ if (sc.callSuper & CSX.CSXany_ctor && !(sc.callSuper & (CSX.CSXthis_ctor | CSX.CSXsuper_ctor))) { error("return without calling constructor"); } sc.callSuper |= CSX.CSXreturn; // See if all returns are instead to be replaced with a goto returnLabel; if (fd.returnLabel) { GotoStatement gs = new GotoStatement(loc, Id.returnLabel); gs.label = fd.returnLabel; if (exp) { /* Replace: return exp; * with: exp; goto returnLabel; */ Statement s = new ExpStatement(Loc(0), exp); return new CompoundStatement(loc, s, gs); } return gs; } if (exp && tbret.ty == Tvoid && !implicit0) { /* Replace: * return exp; * with: * exp; return; */ Statement s = new ExpStatement(loc, exp); exp = null; s = s.semantic(sc); loc = Loc(); return new CompoundStatement(loc, s, this); } return this; } override BE blockExit() { BE result = BE.BEreturn; if (exp && exp.canThrow()) result |= BE.BEthrow; return result; } override Expression interpret(InterState istate) { version (LOG) { printf("ReturnStatement.interpret(%s)\n", exp ? exp.toChars() : ""); } mixin(START); if (!exp) return EXP_VOID_INTERPRET; version (LOG) { Expression e = exp.interpret(istate); printf("e = %p\n", e); return e; } else { return exp.interpret(istate); } } override int inlineCost(InlineCostState* ics) { // Can't handle return statements nested in if's if (ics.nested) return COST_MAX; return exp ? exp.inlineCost(ics) : 0; } override Expression doInline(InlineDoState ids) { //printf("ReturnStatement.doInline() '%s'\n", exp ? exp.toChars() : ""); return exp ? exp.doInline(ids) : null; } override Statement inlineScan(InlineScanState* iss) { //printf("ReturnStatement.inlineScan()\n"); if (exp) { exp = exp.inlineScan(iss); } return this; } override void toIR(IRState* irs) { Blockx* blx = irs.blx; incUsage(irs, loc); if (exp) { //printf("%.*s %.*s\n", exp.classinfo.name, exp.toChars()); elem *e; FuncDeclaration func = irs.getFunc(); assert(func); assert(func.type.ty == TY.Tfunction); TypeFunction tf = cast(TypeFunction)(func.type); RET retmethod = tf.retStyle(); if (retmethod == RET.RETstack) { elem* es; /* If returning struct literal, write result * directly into return value */ if (exp.op == TOK.TOKstructliteral) { StructLiteralExp se = cast(StructLiteralExp)exp; enum objectSize = __traits(classInstanceSize, StructLiteralExp); ubyte save[objectSize]; memcpy(save.ptr, cast(void*)se, objectSize); se.sym = irs.shidden; se.soffset = 0; se.fillHoles = 1; e = exp.toElem(irs); memcpy(cast(void*)se, save.ptr, objectSize); } else e = exp.toElem(irs); assert(e); if (exp.op == TOK.TOKstructliteral || (func.nrvo_can && func.nrvo_var)) { // Return value via hidden pointer passed as parameter // Write exp; return shidden; es = e; } else { // Return value via hidden pointer passed as parameter // Write *shidden=exp; return shidden; int op; tym_t ety; ety = e.Ety; es = el_una(OPER.OPind,ety,el_var(irs.shidden)); op = (tybasic(ety) == TYM.TYstruct) ? OPER.OPstreq : OPER.OPeq; es = el_bin(op, ety, es, e); if (op == OPER.OPstreq) es.Enumbytes = cast(uint)exp.type.size(); version (DMDV2) { /* Call postBlit() on *shidden */ Type tb = exp.type.toBasetype(); //if (tb.ty == TY.Tstruct) exp.dump(0); if ((exp.op == TOK.TOKvar || exp.op == TOK.TOKdotvar || exp.op == TOK.TOKstar) && tb.ty == TY.Tstruct) { StructDeclaration sd = (cast(TypeStruct)tb).sym; if (sd.postblit) { FuncDeclaration fd = sd.postblit; elem* ec = el_var(irs.shidden); ec = callfunc(loc, irs, 1, Type.tvoid, ec, tb.pointerTo(), fd, fd.type, null, null); es = el_bin(OPER.OPcomma, ec.Ety, es, ec); } static if (false) { /* It has been moved, so disable destructor */ if (exp.op == TOK.TOKvar) { VarExp ve = cast(VarExp)exp; VarDeclaration v = ve.var.isVarDeclaration(); if (v && v.rundtor) { elem* er = el_var(v.rundtor.toSymbol()); er = el_bin(OPER.OPeq, TYM.TYint, er, el_long(TYM.TYint, 0)); es = el_bin(OPER.OPcomma, TYM.TYint, es, er); } } } } } } e = el_var(irs.shidden); e = el_bin(OPER.OPcomma, e.Ety, es, e); } ///version (DMDV2) { else if (tf.isref) { // Reference return, so convert to a pointer Expression ae = exp.addressOf(null); e = ae.toElem(irs); } ///} else { e = exp.toElem(irs); assert(e); } block_appendexp(blx.curblock, e); block_next(blx, BC.BCretexp, null); } else block_next(blx, BC.BCret, null); } override ReturnStatement isReturnStatement() { return this; } }