Mercurial > projects > ddmd
diff dmd/ReturnStatement.d @ 0:10317f0c89a5
Initial commit
author | korDen |
---|---|
date | Sat, 24 Oct 2009 08:42:06 +0400 |
parents | |
children | cab4c37afb89 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmd/ReturnStatement.d Sat Oct 24 08:42:06 2009 +0400 @@ -0,0 +1,501 @@ +module dmd.ReturnStatement; + +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.BE; +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; + +class ReturnStatement : Statement +{ + Expression exp; + + this(Loc loc, Expression exp) + { + super(loc); + this.exp = exp; + } + + Statement syntaxCopy() + { + Expression e = exp ? exp.syntaxCopy() : null; + return new ReturnStatement(loc, e); + } + + void toCBuffer(OutBuffer buf, HdrGenState* hgs) + { + assert(false); + } + + 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) { + if (fd.type.nextOf()) { + if (!exp.type.equals(fd.type.nextOf())) + error("mismatched function return type inference of %s and %s", exp.type.toChars(), fd.type.nextOf().toChars()); + } + else + { + (cast(TypeFunction)fd.type).next = exp.type; + fd.type = fd.type.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); + + if (exp.op == TOK.TOKvar) + { + VarExp ve = cast(VarExp)exp; + VarDeclaration v = ve.var.isVarDeclaration(); + if (v && !v.isDataseg() && !(v.storage_class & (STC.STCref | STC.STCout))) { + error("escaping reference to local variable %s", v.toChars()); + } + } + } + + //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 == TY.Tvoid && !fd.isMain()) + { + /* Replace: + * return exp; + * with: + * exp; return; + */ + Statement s = new ExpStatement(loc, exp); + loc = Loc(0); + exp = null; + return new CompoundStatement(loc, s, this); + } + + return this; + } + + BE blockExit() + { + BE result = BE.BEreturn; + if (exp && exp.canThrow()) + result |= BE.BEthrow; + + return result; + } + + Expression interpret(InterState* istate) + { + assert(false); + } + + 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; + } + + Expression doInline(InlineDoState ids) + { + //printf("ReturnStatement.doInline() '%s'\n", exp ? exp.toChars() : ""); + return exp ? exp.doInline(ids) : null; + } + + Statement inlineScan(InlineScanState* iss) + { + //printf("ReturnStatement.inlineScan()\n"); + if (exp) + { + exp = exp.inlineScan(iss); + } + return this; + } + + void toIR(IRState* irs) + { + Blockx* blx = irs.blx; + + incUsage(irs, loc); + if (exp) + { + 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) + { + assert(false); + /* + StructLiteralExp se = cast(StructLiteralExp)exp; + char save[(StructLiteralExp).sizeof]; + memcpy(save, se, sizeof(StructLiteralExp)); + se.sym = irs.shidden; + se.soffset = 0; + se.fillHoles = 1; + e = exp.toElem(irs); + memcpy(se, save, sizeof(StructLiteralExp)); + */ + } + 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); + } + + ReturnStatement isReturnStatement() { return this; } +} \ No newline at end of file