/// \brief Issue an "unreachable code" diagnostic if the blocks contains or /// leads to another block that contains user code. /// /// Note, we rely on SILLocation information to determine if SILInstructions /// correspond to user code. static bool diagnoseUnreachableBlock(const SILBasicBlock &B, SILModule &M, const SILBasicBlockSet &Reachable, UnreachableUserCodeReportingState *State, const SILBasicBlock *TopLevelB, llvm::SmallPtrSetImpl<const SILBasicBlock*> &Visited){ if (Visited.count(&B)) return false; Visited.insert(&B); assert(State); for (auto I = B.begin(), E = B.end(); I != E; ++I) { SILLocation Loc = I->getLoc(); // If we've reached an implicit return, we have not found any user code and // can stop searching for it. if (Loc.is<ImplicitReturnLocation>() || Loc.isAutoGenerated()) return false; // Check if the instruction corresponds to user-written code, also make // sure we don't report an error twice for the same instruction. if (isUserCode(&*I) && !State->BlocksWithErrors.count(&B)) { // Emit the diagnostic. auto BrInfoIter = State->MetaMap.find(TopLevelB); assert(BrInfoIter != State->MetaMap.end()); auto BrInfo = BrInfoIter->second; switch (BrInfo.Kind) { case (UnreachableKind::FoldedBranch): // Emit the diagnostic on the unreachable block and emit the // note on the branch responsible for the unreachable code. diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::unreachable_code); diagnose(M.getASTContext(), BrInfo.Loc.getSourceLoc(), diag::unreachable_code_branch, BrInfo.CondIsAlwaysTrue); break; case (UnreachableKind::FoldedSwitchEnum): { // If we are warning about a switch condition being a constant, the main // emphasis should be on the condition (to ensure we have a single // message per switch). const SwitchStmt *SS = BrInfo.Loc.getAsASTNode<SwitchStmt>(); if (!SS) break; assert(SS); const Expr *SE = SS->getSubjectExpr(); diagnose(M.getASTContext(), SE->getLoc(), diag::switch_on_a_constant); diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::unreachable_code_note); break; } case (UnreachableKind::NoreturnCall): { // Specialcase when we are warning about unreachable code after a call // to a noreturn function. if (!BrInfo.Loc.isASTNode<ExplicitCastExpr>()) { assert(BrInfo.Loc.isASTNode<ApplyExpr>()); diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::unreachable_code); diagnose(M.getASTContext(), BrInfo.Loc.getSourceLoc(), diag::call_to_noreturn_note); } break; } } // Record that we've reported this unreachable block to avoid duplicates // in the future. State->BlocksWithErrors.insert(&B); return true; } } // This block could be empty if it's terminator has been folded. if (B.empty()) return false; // If we have not found user code in this block, inspect it's successors. // Check if at least one of the successors contains user code. for (auto I = B.succ_begin(), E = B.succ_end(); I != E; ++I) { SILBasicBlock *SB = *I; bool HasReachablePred = false; for (auto PI = SB->pred_begin(), PE = SB->pred_end(); PI != PE; ++PI) { if (Reachable.count(*PI)) HasReachablePred = true; } // If all of the predecessors of this successor are unreachable, check if // it contains user code. if (!HasReachablePred && diagnoseUnreachableBlock(*SB, M, Reachable, State, TopLevelB, Visited)) return true; } return false; }