/* st-reachable.cc: computes reachability of statements The code here follows the rules in JLS 14.20. The return value of reachability() is whether or not the given statement can complete normally. */ #include "AST.h" #include "ReachableContext.h" #include "code-util.h" #include "decls.h" #include "errors.h" bool StmtLblDecl::broken() const { return _broken; } void StmtLblDecl::broken (bool broke) { _broken = broke; } bool StmtLblDecl::continued() const { return _continued; } void StmtLblDecl::continued (bool cont) { _continued = cont; } static bool assertReachable(TreeNode *tn, TreeNode::ReachableContext &ctx) { if (!ctx.reachable && ctx.report) { tn->error() << "statement is not reachable" << endl; ctx.report = false; } return ctx.reachable; } // Make sure the given catch block's exception has not been caught yet. static bool checkDuplicatedCatch(CatchListNode *catches, int index) { CatchNode *cn = catches->child(index); for (int i = 0; i < index; i++) { if (isSubClass(cn->param()->dtype()->decl(), catches->child(i)->param()->dtype()->decl())) { cn->error() << "exception " << cn->param()->dtype()->decl()->fullName() << " has already been caught" << endl; return true; } } return false; } bool TreeNode::reachability(ReachableContext &ctx) { return ctx.reachable; } bool BlockNode::reachability(ReachableContext &ctx) { if (!assertReachable(this, ctx)) return false; if (stmts()->arity() == 0) return ctx.reachable; ReachableContext subCtx(ctx); subCtx.report = true; for (int i = 0; i < stmts()->arity(); i++) { subCtx.reachable &= stmts()->child(i)->reachability(subCtx); } // Propagate booleans upward. ctx.broken |= subCtx.broken; ctx.continued |= subCtx.continued; return subCtx.reachable; } /* Local class declarations do not exist anymore. */ bool VarDeclNode::reachability(ReachableContext &ctx) { return assertReachable(this, ctx); } bool EmptyStmtNode::reachability(ReachableContext &ctx) { return assertReachable(this, ctx); } bool LabeledStmtNode::reachability(ReachableContext &ctx) { if (!assertReachable(this, ctx)) return false; Environ newEnv(ctx.env); ReachableContext subCtx(ctx, &newEnv); newEnv.add(label()->decl()); label()->decl()->broken(false); label()->decl()->continued(false); return stmt()->reachability(subCtx) || label()->decl()->broken(); } bool ExpressionStmtNode::reachability(ReachableContext &ctx) { return assertReachable(this, ctx); } bool SwitchNode::reachability(ReachableContext &ctx) { if (!assertReachable(this, ctx)) return false; bool normal = false; bool defaultFound = false; ReachableContext subCtx(ctx); subCtx.broken = false; if (switchBlocks()->absent() || (switchBlocks()->arity() == 0)) return true; for (int i = 0; i < switchBlocks()->arity(); i++) { TreeNode *sb = switchBlocks()->child(i); normal = sb->reachability(subCtx); for (int j = 0; j < sb->cases()->arity(); j++) if (sb->cases()->child(j)->expr()->absent()) defaultFound = true; } // Propagate booleans upward. ctx.continued |= subCtx.continued; if (normal || subCtx.broken || !defaultFound) return true; return false; } bool SwitchBranchNode::reachability(ReachableContext &ctx) { if (!assertReachable(this, ctx)) return false; if (stmts()->absent() || (stmts()->arity() == 0)) return true; ReachableContext subCtx(ctx); subCtx.report = true; for (int i = 0; i < stmts()->arity(); i++) { subCtx.reachable &= stmts()->child(i)->reachability(subCtx); } // Propagate booleans upward. ctx.broken |= subCtx.broken; ctx.continued |= subCtx.continued; return subCtx.reachable; } bool WhileNode::reachability(ReachableContext &ctx) { if (!assertReachable(this, ctx)) return false; bool normal = false; ReachableContext subCtx(ctx); subCtx.broken = false; TreeNode *cond = test(); if (cond->constantType() != Common::BoolKind) normal = true; else if (!cond->literal().boolValue()) { normal = true; subCtx.reachable = false; } stmt()->reachability(subCtx); return normal || subCtx.broken; } bool DoNode::reachability(ReachableContext &ctx) { if (!assertReachable(this, ctx)) return false; ReachableContext subCtx(ctx); subCtx.broken = false; subCtx.continued = false; TreeNode *cond = test(); if ((cond->constantType() == Common::BoolKind) && cond->literal().boolValue()) { stmt()->reachability(subCtx); return subCtx.broken; } else if (stmt()->reachability(subCtx)) return true; else if (subCtx.continued || subCtx.broken) return true; else if (isLabeledStmtNode(parent()) && // hack parent()->label()->decl()->continued()) return true; else return false; } bool ForNode::reachability(ReachableContext &ctx) { if (!assertReachable(this, ctx)) return false; bool normal = false; ReachableContext subCtx(ctx); subCtx.broken = false; TreeNode *cond = test(); if (!cond->absent() && (cond->constantType() != Common::BoolKind)) normal = true; else if (!cond->absent() && !cond->literal().boolValue()) { normal = true; subCtx.reachable = false; } stmt()->reachability(subCtx); return normal || subCtx.broken; } bool BreakNode::reachability(ReachableContext &ctx) { if (!assertReachable(this, ctx)) return false; if (label()->absent()) ctx.broken = true; else label()->decl()->broken(true); return false; } bool ContinueNode::reachability(ReachableContext &ctx) { if (!assertReachable(this, ctx)) return false; if (label()->absent()) ctx.continued = true; else label()->decl()->continued(true); return false; } bool ReturnNode::reachability(ReachableContext &ctx) { assertReachable(this, ctx); return false; } bool ThrowNode::reachability(ReachableContext &ctx) { assertReachable(this, ctx); return false; } bool SynchronizedNode::reachability(ReachableContext &ctx) { if (!assertReachable(this, ctx)) return false; return stmt()->reachability(ctx); } bool TryStmtNode::reachability(ReachableContext &ctx) { if (!assertReachable(this, ctx)) return false; bool normal = block()->reachability(ctx); for (int i = 0; i < catches()->arity(); i++) { // For now, do not check that the corresponding exception in each catch // can be thrown in the try body. This is a bug (PR707). if (!checkDuplicatedCatch(catches(), i)) normal |= catches()->child(i)->reachability(ctx); } normal &= finally()->reachability(ctx); return normal; } bool TryNode::reachability(ReachableContext &ctx) { if (!assertReachable(this, ctx)) return false; return block()->reachability(ctx); } bool CatchNode::reachability(ReachableContext &ctx) { if (!assertReachable(this, ctx)) return false; return block()->reachability(ctx); } bool FinallyNode::reachability(ReachableContext &ctx) { if (!assertReachable(this, ctx)) return false; return block()->reachability(ctx); } bool IfStmtNode::reachability(ReachableContext &ctx) { if (!assertReachable(this, ctx)) return false; if (elsePart()->absent()) { thenPart()->reachability(ctx); return true; } else { return thenPart()->reachability(ctx) | elsePart()->reachability(ctx); } } // Due to lack of constant folding of domains, must assume that contents // of foreach are always reachable and that foreach can always complete // normally. bool ForEachStmtNode::reachability(ReachableContext &ctx) { if (!assertReachable(this, ctx)) return false; stmt()->reachability(ctx); return true; } bool CompileUnitNode::reachability(ReachableContext &ctx) { for (int i = 0; i < types()->arity(); i++) types()->child(i)->reachability(ctx); return true; } bool TypeDeclNode::reachability(ReachableContext &ctx) { for (int i = 0; i < members()->arity(); i++) members()->child(i)->reachability(ctx); return true; } bool TemplateDeclNode::reachability(ReachableContext &ctx) { return true; } bool FieldDeclNode::reachability(ReachableContext &ctx) { return true; } bool MethodSignatureNode::reachability(ReachableContext &ctx) { return true; } bool MethodDeclNode::reachability(ReachableContext &ctx) { ReachableContext subCtx(ctx, new Environ()); subCtx.reachable = true; if (!(decl()->modifiers() & (Abstract | Native)) && !body()->absent() && body()->reachability(subCtx) && (returnType()->kind() != VoidKind)) error() << "return required at the end of " << decl()->errorName() << endl; return true; } bool ConstructorDeclNode::reachability(ReachableContext &ctx) { ReachableContext subCtx(ctx, new Environ()); subCtx.reachable = true; body()->reachability(subCtx); return true; } bool InstanceInitNode::reachability(ReachableContext &ctx) { ReachableContext subCtx(ctx, new Environ()); subCtx.reachable = true; block()->reachability(subCtx); return true; } bool StaticInitNode::reachability(ReachableContext &ctx) { ReachableContext subCtx(ctx, new Environ()); subCtx.reachable = true; block()->reachability(subCtx); return true; }