From ce1a41305b9a43d50cb7b9a6dd738290d35dc4ce Mon Sep 17 00:00:00 2001 From: Alexey Prokhin Date: Sat, 23 Aug 2014 19:44:39 +0400 Subject: [PATCH] Set enclosingScopeExit in glue layer after semantic is done Otherwise, we may end up with a wrong enclosing statement. It could happen if a try-finally is rewritten as a try-catch (see NrvoWalker). In this case, enclosingScopeExit will still point to the old unused try-finally. --- dmd2/statement.c | 44 +------- dmd2/statement.h | 2 - gen/functions.cpp | 2 +- gen/irstate.h | 1 + gen/statements.cpp | 226 +++++++++++++++++++++++++++++++++++++++++ tests/d2/dmd-testsuite | 2 +- 6 files changed, 232 insertions(+), 45 deletions(-) diff --git a/dmd2/statement.c b/dmd2/statement.c index 7608470095..3daa9718d7 100644 --- a/dmd2/statement.c +++ b/dmd2/statement.c @@ -3153,9 +3153,8 @@ SwitchStatement::SwitchStatement(Loc loc, Expression *c, Statement *b, bool isFi this->body = b; this->isFinal = isFinal; sdefault = NULL; -#if !IN_LLVM tf = NULL; -#else +#if IN_LLVM enclosingScopeExit = NULL; #endif cases = NULL; @@ -3174,13 +3173,10 @@ Statement *SwitchStatement::syntaxCopy() Statement *SwitchStatement::semantic(Scope *sc) { //printf("SwitchStatement::semantic(%p)\n", this); + tf = sc->tf; if (cases) return this; // already run -#if IN_LLVM - enclosingScopeExit = sc->enclosingScopeExit; -#endif - condition = condition->semantic(sc); condition = resolveProperties(sc, condition); TypeEnum *te = NULL; @@ -3355,13 +3351,6 @@ Statement *CaseStatement::semantic(Scope *sc) sc = sc->endCTFE(); if (sw) { -#if IN_LLVM - enclosingScopeExit = sc->enclosingScopeExit; - if (enclosingScopeExit != sw->enclosingScopeExit) - { - error("case must be inside the same try, synchronized or volatile level as switch"); - } -#endif exp = exp->implicitCastTo(sc, sw->condition->type); exp = exp->optimize(WANTvalue); @@ -3417,10 +3406,8 @@ Statement *CaseStatement::semantic(Scope *sc) sw->gotoCases.remove(i); // remove from array } } -#if IN_DMD if (sc->sw->tf != sc->tf) error("switch and case are in different finally blocks"); -#endif } else error("case not in switch statement"); @@ -3558,12 +3545,9 @@ Statement *DefaultStatement::semantic(Scope *sc) } sc->sw->sdefault = this; -#if !IN_LLVM if (sc->sw->tf != sc->tf) error("switch and default are in different finally blocks"); -#else - enclosingScopeExit = sc->sw->enclosingScopeExit; -#endif + if (sc->sw->isFinal) error("default statement not allowed in final switch statement"); } @@ -4350,16 +4334,7 @@ Statement *SynchronizedStatement::semantic(Scope *sc) } Lbody: if (body) -#if IN_LLVM - { - Statement* oldScopeExit = sc->enclosingScopeExit; - sc->enclosingScopeExit = this; -#endif body = body->semantic(sc); -#if IN_LLVM - sc->enclosingScopeExit = oldScopeExit; - } -#endif if (body && body->isErrorStatement()) return body; return this; @@ -4682,14 +4657,7 @@ Statement *TryFinallyStatement::syntaxCopy() Statement *TryFinallyStatement::semantic(Scope *sc) { //printf("TryFinallyStatement::semantic()\n"); -#if IN_LLVM - Statement* oldScopeExit = sc->enclosingScopeExit; - sc->enclosingScopeExit = this; -#endif body = body->semantic(sc); -#if IN_LLVM - sc->enclosingScopeExit = oldScopeExit; -#endif sc = sc->push(); sc->tf = this; sc->sbreak = NULL; @@ -4937,9 +4905,6 @@ Statement *GotoStatement::semantic(Scope *sc) this->fd = sc->func; tf = sc->tf; os = sc->os; -#if IN_LLVM - enclosingScopeExit = sc->enclosingScopeExit; -#endif label = fd->searchLabel(ident); if (!label->statement && sc->fes) { @@ -5068,9 +5033,6 @@ Statement *LabelStatement::semantic(Scope *sc) ls->statement = this; tf = sc->tf; os = sc->os; -#if IN_LLVM - enclosingScopeExit = sc->enclosingScopeExit; -#endif sc = sc->push(); sc->scopesym = sc->enclosing->scopesym; sc->callSuper |= CSXlabel; diff --git a/dmd2/statement.h b/dmd2/statement.h index 179b3d1952..163a781399 100644 --- a/dmd2/statement.h +++ b/dmd2/statement.h @@ -458,9 +458,7 @@ public: bool isFinal; DefaultStatement *sdefault; -#if !IN_LLVM TryFinallyStatement *tf; -#endif GotoCaseStatements gotoCases; // array of unresolved GotoCaseStatement's CaseStatements *cases; // array of CaseStatement's int hasNoDefault; // !=0 if no default statement diff --git a/gen/functions.cpp b/gen/functions.cpp index 510c0358ef..f142bfe7b5 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -1196,7 +1196,7 @@ void DtoDefineFunction(FuncDeclaration* fd) } // output function body - Statement_toIR(fd->fbody, gIR); + codegenFunction(fd->fbody, gIR); irfunction->gen = 0; // TODO: clean up this mess diff --git a/gen/irstate.h b/gen/irstate.h index 85309da82b..a53c1cee9b 100644 --- a/gen/irstate.h +++ b/gen/irstate.h @@ -238,6 +238,7 @@ llvm::CallSite IRState::CreateCallOrInvoke(LLValue* Callee, const T &args, const } } +void codegenFunction(Statement *s, IRState *irs); void Statement_toIR(Statement *s, IRState *irs); #endif // LDC_GEN_IRSTATE_H diff --git a/gen/statements.cpp b/gen/statements.cpp index 660e0a1eb9..17b4356643 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -104,6 +104,225 @@ static LLValue* call_string_switch_runtime(llvm::Value* table, Expression* e) ////////////////////////////////////////////////////////////////////////////// +/* A visitor to walk entire tree of statements. + */ +class StatementVisitor : public Visitor +{ + void visitStmt(Statement *s) { s->accept(this); } +public: + void visit(ErrorStatement *s) { } + void visit(PeelStatement *s) + { + if (s->s) + visitStmt(s->s); + } + void visit(ExpStatement *s) { } + void visit(DtorExpStatement *s) { } + void visit(CompileStatement *s) { } + void visit(CompoundStatement *s) + { + if (s->statements && s->statements->dim) + { + for (size_t i = 0; i < s->statements->dim; i++) + { + if ((*s->statements)[i]) + visitStmt((*s->statements)[i]); + } + } + } + void visit(CompoundDeclarationStatement *s) { visit((CompoundStatement *)s); } + void visit(UnrolledLoopStatement *s) + { + if (s->statements && s->statements->dim) + { + for (size_t i = 0; i < s->statements->dim; i++) + { + if ((*s->statements)[i]) + visitStmt((*s->statements)[i]); + } + } + } + void visit(ScopeStatement *s) + { + if (s->statement) + visitStmt(s->statement); + } + void visit(WhileStatement *s) + { + if (s->body) + visitStmt(s->body); + } + void visit(DoStatement *s) + { + if (s->body) + visitStmt(s->body); + } + void visit(ForStatement *s) + { + if (s->init) + visitStmt(s->init); + if (s->body) + visitStmt(s->body); + } + void visit(ForeachStatement *s) + { + if (s->body) + visitStmt(s->body); + } + void visit(ForeachRangeStatement *s) + { + if (s->body) + visitStmt(s->body); + } + void visit(IfStatement *s) + { + if (s->ifbody) + visitStmt(s->ifbody); + if (s->elsebody) + visitStmt(s->elsebody); + } + void visit(ConditionalStatement *s) { } + void visit(PragmaStatement *s) { } + void visit(StaticAssertStatement *s) { } + void visit(SwitchStatement *s) + { + if (s->body) + visitStmt(s->body); + } + void visit(CaseStatement *s) + { + if (s->statement) + visitStmt(s->statement); + } + void visit(CaseRangeStatement *s) + { + if (s->statement) + visitStmt(s->statement); + } + void visit(DefaultStatement *s) + { + if (s->statement) + visitStmt(s->statement); + } + void visit(GotoDefaultStatement *s) { } + void visit(GotoCaseStatement *s) { } + void visit(SwitchErrorStatement *s) { } + void visit(ReturnStatement *s) { } + void visit(BreakStatement *s) { } + void visit(ContinueStatement *s) { } + void visit(SynchronizedStatement *s) + { + if (s->body) + visitStmt(s->body); + } + void visit(WithStatement *s) + { + if (s->body) + visitStmt(s->body); + } + void visit(TryCatchStatement *s) + { + if (s->body) + visitStmt(s->body); + if (s->catches && s->catches->dim) + { + for (size_t i = 0; i < s->catches->dim; i++) + { + Catch *c = (*s->catches)[i]; + if (c && c->handler) + visitStmt(c->handler); + } + } + } + void visit(TryFinallyStatement *s) + { + if (s->body) + visitStmt(s->body); + if (s->finalbody) + visitStmt(s->finalbody); + } + void visit(OnScopeStatement *s) { } + void visit(ThrowStatement *s) { } + void visit(DebugStatement *s) + { + if (s->statement) + visitStmt(s->statement); + } + void visit(GotoStatement *s) { } + void visit(LabelStatement *s) + { + if (s->statement) + visitStmt(s->statement); + } + void visit(AsmStatement *s) { } + void visit(ImportStatement *s) { } + void visit(AsmBlockStatement *s) { } +}; + +////////////////////////////////////////////////////////////////////////////// + +class FindEnclosingTryFinally : public StatementVisitor { + std::stack m_tryFinally; + std::stack m_switches; +public: + TryFinallyStatement *enclosingTryFinally() const + { + return m_tryFinally.empty() ? 0 : m_tryFinally.top(); + } + + SwitchStatement *enclosingSwitch() const + { + return m_switches.empty() ? 0 : m_switches.top(); + } + + void visit(SwitchStatement *s) + { + m_switches.push(s); + s->enclosingScopeExit = enclosingTryFinally(); + StatementVisitor::visit(s); + m_switches.pop(); + } + + void visit(CaseStatement *s) + { + s->enclosingScopeExit = enclosingTryFinally(); + if (s->enclosingScopeExit != enclosingSwitch()->enclosingScopeExit) + s->error("switch and case are in different try blocks"); + StatementVisitor::visit(s); + } + + void visit(DefaultStatement *s) + { + s->enclosingScopeExit = enclosingTryFinally(); + if (s->enclosingScopeExit != enclosingSwitch()->enclosingScopeExit) + s->error("switch and default case are in different try blocks"); + StatementVisitor::visit(s); + } + + void visit(TryFinallyStatement *s) + { + m_tryFinally.push(s); + s->body->accept(this); + m_tryFinally.pop(); + s->finalbody->accept(this); + } + + void visit(LabelStatement *s) + { + s->enclosingScopeExit = enclosingTryFinally(); + StatementVisitor::visit(s); + } + + void visit(GotoStatement *s) + { + s->enclosingScopeExit = enclosingTryFinally(); + StatementVisitor::visit(s); + } +}; + +////////////////////////////////////////////////////////////////////////////// + + class ToIRVisitor : public Visitor { IRState *irs; public: @@ -1573,6 +1792,13 @@ public: ////////////////////////////////////////////////////////////////////////////// +void codegenFunction(Statement *s, IRState *irs) +{ + FindEnclosingTryFinally v; + s->accept(&v); + Statement_toIR(s, irs); +} + void Statement_toIR(Statement *s, IRState *irs) { ToIRVisitor v(irs); diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index cbde5d8fe8..a7cd1422e7 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit cbde5d8fe8699e1781eb5236c0de0669b593b90a +Subproject commit a7cd1422e7019ee41279d3c48c3cf79b075378f9