Path: blob/main/contrib/llvm-project/clang/lib/Sema/JumpDiagnostics.cpp
35233 views
//===--- JumpDiagnostics.cpp - Protected scope jump analysis ------*- C++ -*-=//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements the JumpScopeChecker class, which is used to diagnose9// jumps that enter a protected scope in an invalid way.10//11//===----------------------------------------------------------------------===//1213#include "clang/AST/DeclCXX.h"14#include "clang/AST/Expr.h"15#include "clang/AST/ExprCXX.h"16#include "clang/AST/StmtCXX.h"17#include "clang/AST/StmtObjC.h"18#include "clang/AST/StmtOpenACC.h"19#include "clang/AST/StmtOpenMP.h"20#include "clang/Basic/SourceLocation.h"21#include "clang/Sema/SemaInternal.h"22#include "llvm/ADT/BitVector.h"23using namespace clang;2425namespace {2627/// JumpScopeChecker - This object is used by Sema to diagnose invalid jumps28/// into VLA and other protected scopes. For example, this rejects:29/// goto L;30/// int a[n];31/// L:32///33/// We also detect jumps out of protected scopes when it's not possible to do34/// cleanups properly. Indirect jumps and ASM jumps can't do cleanups because35/// the target is unknown. Return statements with \c [[clang::musttail]] cannot36/// handle any cleanups due to the nature of a tail call.37class JumpScopeChecker {38Sema &S;3940/// Permissive - True when recovering from errors, in which case precautions41/// are taken to handle incomplete scope information.42const bool Permissive;4344/// GotoScope - This is a record that we use to keep track of all of the45/// scopes that are introduced by VLAs and other things that scope jumps like46/// gotos. This scope tree has nothing to do with the source scope tree,47/// because you can have multiple VLA scopes per compound statement, and most48/// compound statements don't introduce any scopes.49struct GotoScope {50/// ParentScope - The index in ScopeMap of the parent scope. This is 0 for51/// the parent scope is the function body.52unsigned ParentScope;5354/// InDiag - The note to emit if there is a jump into this scope.55unsigned InDiag;5657/// OutDiag - The note to emit if there is an indirect jump out58/// of this scope. Direct jumps always clean up their current scope59/// in an orderly way.60unsigned OutDiag;6162/// Loc - Location to emit the diagnostic.63SourceLocation Loc;6465GotoScope(unsigned parentScope, unsigned InDiag, unsigned OutDiag,66SourceLocation L)67: ParentScope(parentScope), InDiag(InDiag), OutDiag(OutDiag), Loc(L) {}68};6970SmallVector<GotoScope, 48> Scopes;71llvm::DenseMap<Stmt*, unsigned> LabelAndGotoScopes;72SmallVector<Stmt*, 16> Jumps;7374SmallVector<Stmt*, 4> IndirectJumps;75SmallVector<LabelDecl *, 4> IndirectJumpTargets;76SmallVector<AttributedStmt *, 4> MustTailStmts;7778public:79JumpScopeChecker(Stmt *Body, Sema &S);80private:81void BuildScopeInformation(Decl *D, unsigned &ParentScope);82void BuildScopeInformation(VarDecl *D, const BlockDecl *BDecl,83unsigned &ParentScope);84void BuildScopeInformation(CompoundLiteralExpr *CLE, unsigned &ParentScope);85void BuildScopeInformation(Stmt *S, unsigned &origParentScope);8687void VerifyJumps();88void VerifyIndirectJumps();89void VerifyMustTailStmts();90void NoteJumpIntoScopes(ArrayRef<unsigned> ToScopes);91void DiagnoseIndirectOrAsmJump(Stmt *IG, unsigned IGScope, LabelDecl *Target,92unsigned TargetScope);93void CheckJump(Stmt *From, Stmt *To, SourceLocation DiagLoc,94unsigned JumpDiag, unsigned JumpDiagWarning,95unsigned JumpDiagCXX98Compat);96void CheckGotoStmt(GotoStmt *GS);97const Attr *GetMustTailAttr(AttributedStmt *AS);9899unsigned GetDeepestCommonScope(unsigned A, unsigned B);100};101} // end anonymous namespace102103#define CHECK_PERMISSIVE(x) (assert(Permissive || !(x)), (Permissive && (x)))104105JumpScopeChecker::JumpScopeChecker(Stmt *Body, Sema &s)106: S(s), Permissive(s.hasAnyUnrecoverableErrorsInThisFunction()) {107// Add a scope entry for function scope.108Scopes.push_back(GotoScope(~0U, ~0U, ~0U, SourceLocation()));109110// Build information for the top level compound statement, so that we have a111// defined scope record for every "goto" and label.112unsigned BodyParentScope = 0;113BuildScopeInformation(Body, BodyParentScope);114115// Check that all jumps we saw are kosher.116VerifyJumps();117VerifyIndirectJumps();118VerifyMustTailStmts();119}120121/// GetDeepestCommonScope - Finds the innermost scope enclosing the122/// two scopes.123unsigned JumpScopeChecker::GetDeepestCommonScope(unsigned A, unsigned B) {124while (A != B) {125// Inner scopes are created after outer scopes and therefore have126// higher indices.127if (A < B) {128assert(Scopes[B].ParentScope < B);129B = Scopes[B].ParentScope;130} else {131assert(Scopes[A].ParentScope < A);132A = Scopes[A].ParentScope;133}134}135return A;136}137138typedef std::pair<unsigned,unsigned> ScopePair;139140/// GetDiagForGotoScopeDecl - If this decl induces a new goto scope, return a141/// diagnostic that should be emitted if control goes over it. If not, return 0.142static ScopePair GetDiagForGotoScopeDecl(Sema &S, const Decl *D) {143if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {144unsigned InDiag = 0;145unsigned OutDiag = 0;146147if (VD->getType()->isVariablyModifiedType())148InDiag = diag::note_protected_by_vla;149150if (VD->hasAttr<BlocksAttr>())151return ScopePair(diag::note_protected_by___block,152diag::note_exits___block);153154if (VD->hasAttr<CleanupAttr>())155return ScopePair(diag::note_protected_by_cleanup,156diag::note_exits_cleanup);157158if (VD->hasLocalStorage()) {159switch (VD->getType().isDestructedType()) {160case QualType::DK_objc_strong_lifetime:161return ScopePair(diag::note_protected_by_objc_strong_init,162diag::note_exits_objc_strong);163164case QualType::DK_objc_weak_lifetime:165return ScopePair(diag::note_protected_by_objc_weak_init,166diag::note_exits_objc_weak);167168case QualType::DK_nontrivial_c_struct:169return ScopePair(diag::note_protected_by_non_trivial_c_struct_init,170diag::note_exits_dtor);171172case QualType::DK_cxx_destructor:173OutDiag = diag::note_exits_dtor;174break;175176case QualType::DK_none:177break;178}179}180181const Expr *Init = VD->getInit();182if (S.Context.getLangOpts().CPlusPlus && VD->hasLocalStorage() && Init &&183!Init->containsErrors()) {184// C++11 [stmt.dcl]p3:185// A program that jumps from a point where a variable with automatic186// storage duration is not in scope to a point where it is in scope187// is ill-formed unless the variable has scalar type, class type with188// a trivial default constructor and a trivial destructor, a189// cv-qualified version of one of these types, or an array of one of190// the preceding types and is declared without an initializer.191192// C++03 [stmt.dcl.p3:193// A program that jumps from a point where a local variable194// with automatic storage duration is not in scope to a point195// where it is in scope is ill-formed unless the variable has196// POD type and is declared without an initializer.197198InDiag = diag::note_protected_by_variable_init;199200// For a variable of (array of) class type declared without an201// initializer, we will have call-style initialization and the initializer202// will be the CXXConstructExpr with no intervening nodes.203if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(Init)) {204const CXXConstructorDecl *Ctor = CCE->getConstructor();205if (Ctor->isTrivial() && Ctor->isDefaultConstructor() &&206VD->getInitStyle() == VarDecl::CallInit) {207if (OutDiag)208InDiag = diag::note_protected_by_variable_nontriv_destructor;209else if (!Ctor->getParent()->isPOD())210InDiag = diag::note_protected_by_variable_non_pod;211else212InDiag = 0;213}214}215}216217return ScopePair(InDiag, OutDiag);218}219220if (const TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D)) {221if (TD->getUnderlyingType()->isVariablyModifiedType())222return ScopePair(isa<TypedefDecl>(TD)223? diag::note_protected_by_vla_typedef224: diag::note_protected_by_vla_type_alias,2250);226}227228return ScopePair(0U, 0U);229}230231/// Build scope information for a declaration that is part of a DeclStmt.232void JumpScopeChecker::BuildScopeInformation(Decl *D, unsigned &ParentScope) {233// If this decl causes a new scope, push and switch to it.234std::pair<unsigned,unsigned> Diags = GetDiagForGotoScopeDecl(S, D);235if (Diags.first || Diags.second) {236Scopes.push_back(GotoScope(ParentScope, Diags.first, Diags.second,237D->getLocation()));238ParentScope = Scopes.size()-1;239}240241// If the decl has an initializer, walk it with the potentially new242// scope we just installed.243if (VarDecl *VD = dyn_cast<VarDecl>(D))244if (Expr *Init = VD->getInit())245BuildScopeInformation(Init, ParentScope);246}247248/// Build scope information for a captured block literal variables.249void JumpScopeChecker::BuildScopeInformation(VarDecl *D,250const BlockDecl *BDecl,251unsigned &ParentScope) {252// exclude captured __block variables; there's no destructor253// associated with the block literal for them.254if (D->hasAttr<BlocksAttr>())255return;256QualType T = D->getType();257QualType::DestructionKind destructKind = T.isDestructedType();258if (destructKind != QualType::DK_none) {259std::pair<unsigned,unsigned> Diags;260switch (destructKind) {261case QualType::DK_cxx_destructor:262Diags = ScopePair(diag::note_enters_block_captures_cxx_obj,263diag::note_exits_block_captures_cxx_obj);264break;265case QualType::DK_objc_strong_lifetime:266Diags = ScopePair(diag::note_enters_block_captures_strong,267diag::note_exits_block_captures_strong);268break;269case QualType::DK_objc_weak_lifetime:270Diags = ScopePair(diag::note_enters_block_captures_weak,271diag::note_exits_block_captures_weak);272break;273case QualType::DK_nontrivial_c_struct:274Diags = ScopePair(diag::note_enters_block_captures_non_trivial_c_struct,275diag::note_exits_block_captures_non_trivial_c_struct);276break;277case QualType::DK_none:278llvm_unreachable("non-lifetime captured variable");279}280SourceLocation Loc = D->getLocation();281if (Loc.isInvalid())282Loc = BDecl->getLocation();283Scopes.push_back(GotoScope(ParentScope,284Diags.first, Diags.second, Loc));285ParentScope = Scopes.size()-1;286}287}288289/// Build scope information for compound literals of C struct types that are290/// non-trivial to destruct.291void JumpScopeChecker::BuildScopeInformation(CompoundLiteralExpr *CLE,292unsigned &ParentScope) {293unsigned InDiag = diag::note_enters_compound_literal_scope;294unsigned OutDiag = diag::note_exits_compound_literal_scope;295Scopes.push_back(GotoScope(ParentScope, InDiag, OutDiag, CLE->getExprLoc()));296ParentScope = Scopes.size() - 1;297}298299/// BuildScopeInformation - The statements from CI to CE are known to form a300/// coherent VLA scope with a specified parent node. Walk through the301/// statements, adding any labels or gotos to LabelAndGotoScopes and recursively302/// walking the AST as needed.303void JumpScopeChecker::BuildScopeInformation(Stmt *S,304unsigned &origParentScope) {305// If this is a statement, rather than an expression, scopes within it don't306// propagate out into the enclosing scope. Otherwise we have to worry307// about block literals, which have the lifetime of their enclosing statement.308unsigned independentParentScope = origParentScope;309unsigned &ParentScope = ((isa<Expr>(S) && !isa<StmtExpr>(S))310? origParentScope : independentParentScope);311312unsigned StmtsToSkip = 0u;313314// If we found a label, remember that it is in ParentScope scope.315switch (S->getStmtClass()) {316case Stmt::AddrLabelExprClass:317IndirectJumpTargets.push_back(cast<AddrLabelExpr>(S)->getLabel());318break;319320case Stmt::ObjCForCollectionStmtClass: {321auto *CS = cast<ObjCForCollectionStmt>(S);322unsigned Diag = diag::note_protected_by_objc_fast_enumeration;323unsigned NewParentScope = Scopes.size();324Scopes.push_back(GotoScope(ParentScope, Diag, 0, S->getBeginLoc()));325BuildScopeInformation(CS->getBody(), NewParentScope);326return;327}328329case Stmt::IndirectGotoStmtClass:330// "goto *&&lbl;" is a special case which we treat as equivalent331// to a normal goto. In addition, we don't calculate scope in the332// operand (to avoid recording the address-of-label use), which333// works only because of the restricted set of expressions which334// we detect as constant targets.335if (cast<IndirectGotoStmt>(S)->getConstantTarget())336goto RecordJumpScope;337338LabelAndGotoScopes[S] = ParentScope;339IndirectJumps.push_back(S);340break;341342case Stmt::SwitchStmtClass:343// Evaluate the C++17 init stmt and condition variable344// before entering the scope of the switch statement.345if (Stmt *Init = cast<SwitchStmt>(S)->getInit()) {346BuildScopeInformation(Init, ParentScope);347++StmtsToSkip;348}349if (VarDecl *Var = cast<SwitchStmt>(S)->getConditionVariable()) {350BuildScopeInformation(Var, ParentScope);351++StmtsToSkip;352}353goto RecordJumpScope;354355case Stmt::GCCAsmStmtClass:356if (!cast<GCCAsmStmt>(S)->isAsmGoto())357break;358[[fallthrough]];359360case Stmt::GotoStmtClass:361RecordJumpScope:362// Remember both what scope a goto is in as well as the fact that we have363// it. This makes the second scan not have to walk the AST again.364LabelAndGotoScopes[S] = ParentScope;365Jumps.push_back(S);366break;367368case Stmt::IfStmtClass: {369IfStmt *IS = cast<IfStmt>(S);370if (!(IS->isConstexpr() || IS->isConsteval() ||371IS->isObjCAvailabilityCheck()))372break;373374unsigned Diag = diag::note_protected_by_if_available;375if (IS->isConstexpr())376Diag = diag::note_protected_by_constexpr_if;377else if (IS->isConsteval())378Diag = diag::note_protected_by_consteval_if;379380if (VarDecl *Var = IS->getConditionVariable())381BuildScopeInformation(Var, ParentScope);382383// Cannot jump into the middle of the condition.384unsigned NewParentScope = Scopes.size();385Scopes.push_back(GotoScope(ParentScope, Diag, 0, IS->getBeginLoc()));386387if (!IS->isConsteval())388BuildScopeInformation(IS->getCond(), NewParentScope);389390// Jumps into either arm of an 'if constexpr' are not allowed.391NewParentScope = Scopes.size();392Scopes.push_back(GotoScope(ParentScope, Diag, 0, IS->getBeginLoc()));393BuildScopeInformation(IS->getThen(), NewParentScope);394if (Stmt *Else = IS->getElse()) {395NewParentScope = Scopes.size();396Scopes.push_back(GotoScope(ParentScope, Diag, 0, IS->getBeginLoc()));397BuildScopeInformation(Else, NewParentScope);398}399return;400}401402case Stmt::CXXTryStmtClass: {403CXXTryStmt *TS = cast<CXXTryStmt>(S);404{405unsigned NewParentScope = Scopes.size();406Scopes.push_back(GotoScope(ParentScope,407diag::note_protected_by_cxx_try,408diag::note_exits_cxx_try,409TS->getSourceRange().getBegin()));410if (Stmt *TryBlock = TS->getTryBlock())411BuildScopeInformation(TryBlock, NewParentScope);412}413414// Jump from the catch into the try is not allowed either.415for (unsigned I = 0, E = TS->getNumHandlers(); I != E; ++I) {416CXXCatchStmt *CS = TS->getHandler(I);417unsigned NewParentScope = Scopes.size();418Scopes.push_back(GotoScope(ParentScope,419diag::note_protected_by_cxx_catch,420diag::note_exits_cxx_catch,421CS->getSourceRange().getBegin()));422BuildScopeInformation(CS->getHandlerBlock(), NewParentScope);423}424return;425}426427case Stmt::SEHTryStmtClass: {428SEHTryStmt *TS = cast<SEHTryStmt>(S);429{430unsigned NewParentScope = Scopes.size();431Scopes.push_back(GotoScope(ParentScope,432diag::note_protected_by_seh_try,433diag::note_exits_seh_try,434TS->getSourceRange().getBegin()));435if (Stmt *TryBlock = TS->getTryBlock())436BuildScopeInformation(TryBlock, NewParentScope);437}438439// Jump from __except or __finally into the __try are not allowed either.440if (SEHExceptStmt *Except = TS->getExceptHandler()) {441unsigned NewParentScope = Scopes.size();442Scopes.push_back(GotoScope(ParentScope,443diag::note_protected_by_seh_except,444diag::note_exits_seh_except,445Except->getSourceRange().getBegin()));446BuildScopeInformation(Except->getBlock(), NewParentScope);447} else if (SEHFinallyStmt *Finally = TS->getFinallyHandler()) {448unsigned NewParentScope = Scopes.size();449Scopes.push_back(GotoScope(ParentScope,450diag::note_protected_by_seh_finally,451diag::note_exits_seh_finally,452Finally->getSourceRange().getBegin()));453BuildScopeInformation(Finally->getBlock(), NewParentScope);454}455456return;457}458459case Stmt::DeclStmtClass: {460// If this is a declstmt with a VLA definition, it defines a scope from here461// to the end of the containing context.462DeclStmt *DS = cast<DeclStmt>(S);463// The decl statement creates a scope if any of the decls in it are VLAs464// or have the cleanup attribute.465for (auto *I : DS->decls())466BuildScopeInformation(I, origParentScope);467return;468}469470case Stmt::StmtExprClass: {471// [GNU]472// Jumping into a statement expression with goto or using473// a switch statement outside the statement expression with474// a case or default label inside the statement expression is not permitted.475// Jumping out of a statement expression is permitted.476StmtExpr *SE = cast<StmtExpr>(S);477unsigned NewParentScope = Scopes.size();478Scopes.push_back(GotoScope(ParentScope,479diag::note_enters_statement_expression,480/*OutDiag=*/0, SE->getBeginLoc()));481BuildScopeInformation(SE->getSubStmt(), NewParentScope);482return;483}484485case Stmt::ObjCAtTryStmtClass: {486// Disallow jumps into any part of an @try statement by pushing a scope and487// walking all sub-stmts in that scope.488ObjCAtTryStmt *AT = cast<ObjCAtTryStmt>(S);489// Recursively walk the AST for the @try part.490{491unsigned NewParentScope = Scopes.size();492Scopes.push_back(GotoScope(ParentScope,493diag::note_protected_by_objc_try,494diag::note_exits_objc_try,495AT->getAtTryLoc()));496if (Stmt *TryPart = AT->getTryBody())497BuildScopeInformation(TryPart, NewParentScope);498}499500// Jump from the catch to the finally or try is not valid.501for (ObjCAtCatchStmt *AC : AT->catch_stmts()) {502unsigned NewParentScope = Scopes.size();503Scopes.push_back(GotoScope(ParentScope,504diag::note_protected_by_objc_catch,505diag::note_exits_objc_catch,506AC->getAtCatchLoc()));507// @catches are nested and it isn't508BuildScopeInformation(AC->getCatchBody(), NewParentScope);509}510511// Jump from the finally to the try or catch is not valid.512if (ObjCAtFinallyStmt *AF = AT->getFinallyStmt()) {513unsigned NewParentScope = Scopes.size();514Scopes.push_back(GotoScope(ParentScope,515diag::note_protected_by_objc_finally,516diag::note_exits_objc_finally,517AF->getAtFinallyLoc()));518BuildScopeInformation(AF, NewParentScope);519}520521return;522}523524case Stmt::ObjCAtSynchronizedStmtClass: {525// Disallow jumps into the protected statement of an @synchronized, but526// allow jumps into the object expression it protects.527ObjCAtSynchronizedStmt *AS = cast<ObjCAtSynchronizedStmt>(S);528// Recursively walk the AST for the @synchronized object expr, it is529// evaluated in the normal scope.530BuildScopeInformation(AS->getSynchExpr(), ParentScope);531532// Recursively walk the AST for the @synchronized part, protected by a new533// scope.534unsigned NewParentScope = Scopes.size();535Scopes.push_back(GotoScope(ParentScope,536diag::note_protected_by_objc_synchronized,537diag::note_exits_objc_synchronized,538AS->getAtSynchronizedLoc()));539BuildScopeInformation(AS->getSynchBody(), NewParentScope);540return;541}542543case Stmt::ObjCAutoreleasePoolStmtClass: {544// Disallow jumps into the protected statement of an @autoreleasepool.545ObjCAutoreleasePoolStmt *AS = cast<ObjCAutoreleasePoolStmt>(S);546// Recursively walk the AST for the @autoreleasepool part, protected by a547// new scope.548unsigned NewParentScope = Scopes.size();549Scopes.push_back(GotoScope(ParentScope,550diag::note_protected_by_objc_autoreleasepool,551diag::note_exits_objc_autoreleasepool,552AS->getAtLoc()));553BuildScopeInformation(AS->getSubStmt(), NewParentScope);554return;555}556557case Stmt::ExprWithCleanupsClass: {558// Disallow jumps past full-expressions that use blocks with559// non-trivial cleanups of their captures. This is theoretically560// implementable but a lot of work which we haven't felt up to doing.561ExprWithCleanups *EWC = cast<ExprWithCleanups>(S);562for (unsigned i = 0, e = EWC->getNumObjects(); i != e; ++i) {563if (auto *BDecl = EWC->getObject(i).dyn_cast<BlockDecl *>())564for (const auto &CI : BDecl->captures()) {565VarDecl *variable = CI.getVariable();566BuildScopeInformation(variable, BDecl, origParentScope);567}568else if (auto *CLE = EWC->getObject(i).dyn_cast<CompoundLiteralExpr *>())569BuildScopeInformation(CLE, origParentScope);570else571llvm_unreachable("unexpected cleanup object type");572}573break;574}575576case Stmt::MaterializeTemporaryExprClass: {577// Disallow jumps out of scopes containing temporaries lifetime-extended to578// automatic storage duration.579MaterializeTemporaryExpr *MTE = cast<MaterializeTemporaryExpr>(S);580if (MTE->getStorageDuration() == SD_Automatic) {581const Expr *ExtendedObject =582MTE->getSubExpr()->skipRValueSubobjectAdjustments();583if (ExtendedObject->getType().isDestructedType()) {584Scopes.push_back(GotoScope(ParentScope, 0,585diag::note_exits_temporary_dtor,586ExtendedObject->getExprLoc()));587origParentScope = Scopes.size()-1;588}589}590break;591}592593case Stmt::CaseStmtClass:594case Stmt::DefaultStmtClass:595case Stmt::LabelStmtClass:596LabelAndGotoScopes[S] = ParentScope;597break;598599case Stmt::AttributedStmtClass: {600AttributedStmt *AS = cast<AttributedStmt>(S);601if (GetMustTailAttr(AS)) {602LabelAndGotoScopes[AS] = ParentScope;603MustTailStmts.push_back(AS);604}605break;606}607608case Stmt::OpenACCComputeConstructClass: {609unsigned NewParentScope = Scopes.size();610OpenACCComputeConstruct *CC = cast<OpenACCComputeConstruct>(S);611Scopes.push_back(GotoScope(612ParentScope, diag::note_acc_branch_into_compute_construct,613diag::note_acc_branch_out_of_compute_construct, CC->getBeginLoc()));614BuildScopeInformation(CC->getStructuredBlock(), NewParentScope);615return;616}617618default:619if (auto *ED = dyn_cast<OMPExecutableDirective>(S)) {620if (!ED->isStandaloneDirective()) {621unsigned NewParentScope = Scopes.size();622Scopes.emplace_back(ParentScope,623diag::note_omp_protected_structured_block,624diag::note_omp_exits_structured_block,625ED->getStructuredBlock()->getBeginLoc());626BuildScopeInformation(ED->getStructuredBlock(), NewParentScope);627return;628}629}630break;631}632633for (Stmt *SubStmt : S->children()) {634if (!SubStmt)635continue;636if (StmtsToSkip) {637--StmtsToSkip;638continue;639}640641// Cases, labels, and defaults aren't "scope parents". It's also642// important to handle these iteratively instead of recursively in643// order to avoid blowing out the stack.644while (true) {645Stmt *Next;646if (SwitchCase *SC = dyn_cast<SwitchCase>(SubStmt))647Next = SC->getSubStmt();648else if (LabelStmt *LS = dyn_cast<LabelStmt>(SubStmt))649Next = LS->getSubStmt();650else651break;652653LabelAndGotoScopes[SubStmt] = ParentScope;654SubStmt = Next;655}656657// Recursively walk the AST.658BuildScopeInformation(SubStmt, ParentScope);659}660}661662/// VerifyJumps - Verify each element of the Jumps array to see if they are663/// valid, emitting diagnostics if not.664void JumpScopeChecker::VerifyJumps() {665while (!Jumps.empty()) {666Stmt *Jump = Jumps.pop_back_val();667668// With a goto,669if (GotoStmt *GS = dyn_cast<GotoStmt>(Jump)) {670// The label may not have a statement if it's coming from inline MS ASM.671if (GS->getLabel()->getStmt()) {672CheckJump(GS, GS->getLabel()->getStmt(), GS->getGotoLoc(),673diag::err_goto_into_protected_scope,674diag::ext_goto_into_protected_scope,675diag::warn_cxx98_compat_goto_into_protected_scope);676}677CheckGotoStmt(GS);678continue;679}680681// If an asm goto jumps to a different scope, things like destructors or682// initializers might not be run which may be suprising to users. Perhaps683// this behavior can be changed in the future, but today Clang will not684// generate such code. Produce a diagnostic instead. See also the685// discussion here: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110728.686if (auto *G = dyn_cast<GCCAsmStmt>(Jump)) {687for (AddrLabelExpr *L : G->labels()) {688LabelDecl *LD = L->getLabel();689unsigned JumpScope = LabelAndGotoScopes[G];690unsigned TargetScope = LabelAndGotoScopes[LD->getStmt()];691if (JumpScope != TargetScope)692DiagnoseIndirectOrAsmJump(G, JumpScope, LD, TargetScope);693}694continue;695}696697// We only get indirect gotos here when they have a constant target.698if (IndirectGotoStmt *IGS = dyn_cast<IndirectGotoStmt>(Jump)) {699LabelDecl *Target = IGS->getConstantTarget();700CheckJump(IGS, Target->getStmt(), IGS->getGotoLoc(),701diag::err_goto_into_protected_scope,702diag::ext_goto_into_protected_scope,703diag::warn_cxx98_compat_goto_into_protected_scope);704continue;705}706707SwitchStmt *SS = cast<SwitchStmt>(Jump);708for (SwitchCase *SC = SS->getSwitchCaseList(); SC;709SC = SC->getNextSwitchCase()) {710if (CHECK_PERMISSIVE(!LabelAndGotoScopes.count(SC)))711continue;712SourceLocation Loc;713if (CaseStmt *CS = dyn_cast<CaseStmt>(SC))714Loc = CS->getBeginLoc();715else if (DefaultStmt *DS = dyn_cast<DefaultStmt>(SC))716Loc = DS->getBeginLoc();717else718Loc = SC->getBeginLoc();719CheckJump(SS, SC, Loc, diag::err_switch_into_protected_scope, 0,720diag::warn_cxx98_compat_switch_into_protected_scope);721}722}723}724725/// VerifyIndirectJumps - Verify whether any possible indirect goto jump might726/// cross a protection boundary. Unlike direct jumps, indirect goto jumps727/// count cleanups as protection boundaries: since there's no way to know where728/// the jump is going, we can't implicitly run the right cleanups the way we729/// can with direct jumps. Thus, an indirect/asm jump is "trivial" if it730/// bypasses no initializations and no teardowns. More formally, an731/// indirect/asm jump from A to B is trivial if the path out from A to DCA(A,B)732/// is trivial and the path in from DCA(A,B) to B is trivial, where DCA(A,B) is733/// the deepest common ancestor of A and B. Jump-triviality is transitive but734/// asymmetric.735///736/// A path in is trivial if none of the entered scopes have an InDiag.737/// A path out is trivial is none of the exited scopes have an OutDiag.738///739/// Under these definitions, this function checks that the indirect740/// jump between A and B is trivial for every indirect goto statement A741/// and every label B whose address was taken in the function.742void JumpScopeChecker::VerifyIndirectJumps() {743if (IndirectJumps.empty())744return;745// If there aren't any address-of-label expressions in this function,746// complain about the first indirect goto.747if (IndirectJumpTargets.empty()) {748S.Diag(IndirectJumps[0]->getBeginLoc(),749diag::err_indirect_goto_without_addrlabel);750return;751}752// Collect a single representative of every scope containing an indirect753// goto. For most code bases, this substantially cuts down on the number of754// jump sites we'll have to consider later.755using JumpScope = std::pair<unsigned, Stmt *>;756SmallVector<JumpScope, 32> JumpScopes;757{758llvm::DenseMap<unsigned, Stmt*> JumpScopesMap;759for (Stmt *IG : IndirectJumps) {760if (CHECK_PERMISSIVE(!LabelAndGotoScopes.count(IG)))761continue;762unsigned IGScope = LabelAndGotoScopes[IG];763if (!JumpScopesMap.contains(IGScope))764JumpScopesMap[IGScope] = IG;765}766JumpScopes.reserve(JumpScopesMap.size());767for (auto &Pair : JumpScopesMap)768JumpScopes.emplace_back(Pair);769}770771// Collect a single representative of every scope containing a772// label whose address was taken somewhere in the function.773// For most code bases, there will be only one such scope.774llvm::DenseMap<unsigned, LabelDecl*> TargetScopes;775for (LabelDecl *TheLabel : IndirectJumpTargets) {776if (CHECK_PERMISSIVE(!LabelAndGotoScopes.count(TheLabel->getStmt())))777continue;778unsigned LabelScope = LabelAndGotoScopes[TheLabel->getStmt()];779if (!TargetScopes.contains(LabelScope))780TargetScopes[LabelScope] = TheLabel;781}782783// For each target scope, make sure it's trivially reachable from784// every scope containing a jump site.785//786// A path between scopes always consists of exitting zero or more787// scopes, then entering zero or more scopes. We build a set of788// of scopes S from which the target scope can be trivially789// entered, then verify that every jump scope can be trivially790// exitted to reach a scope in S.791llvm::BitVector Reachable(Scopes.size(), false);792for (auto [TargetScope, TargetLabel] : TargetScopes) {793Reachable.reset();794795// Mark all the enclosing scopes from which you can safely jump796// into the target scope. 'Min' will end up being the index of797// the shallowest such scope.798unsigned Min = TargetScope;799while (true) {800Reachable.set(Min);801802// Don't go beyond the outermost scope.803if (Min == 0) break;804805// Stop if we can't trivially enter the current scope.806if (Scopes[Min].InDiag) break;807808Min = Scopes[Min].ParentScope;809}810811// Walk through all the jump sites, checking that they can trivially812// reach this label scope.813for (auto [JumpScope, JumpStmt] : JumpScopes) {814unsigned Scope = JumpScope;815// Walk out the "scope chain" for this scope, looking for a scope816// we've marked reachable. For well-formed code this amortizes817// to O(JumpScopes.size() / Scopes.size()): we only iterate818// when we see something unmarked, and in well-formed code we819// mark everything we iterate past.820bool IsReachable = false;821while (true) {822if (Reachable.test(Scope)) {823// If we find something reachable, mark all the scopes we just824// walked through as reachable.825for (unsigned S = JumpScope; S != Scope; S = Scopes[S].ParentScope)826Reachable.set(S);827IsReachable = true;828break;829}830831// Don't walk out if we've reached the top-level scope or we've832// gotten shallower than the shallowest reachable scope.833if (Scope == 0 || Scope < Min) break;834835// Don't walk out through an out-diagnostic.836if (Scopes[Scope].OutDiag) break;837838Scope = Scopes[Scope].ParentScope;839}840841// Only diagnose if we didn't find something.842if (IsReachable) continue;843844DiagnoseIndirectOrAsmJump(JumpStmt, JumpScope, TargetLabel, TargetScope);845}846}847}848849/// Return true if a particular error+note combination must be downgraded to a850/// warning in Microsoft mode.851static bool IsMicrosoftJumpWarning(unsigned JumpDiag, unsigned InDiagNote) {852return (JumpDiag == diag::err_goto_into_protected_scope &&853(InDiagNote == diag::note_protected_by_variable_init ||854InDiagNote == diag::note_protected_by_variable_nontriv_destructor));855}856857/// Return true if a particular note should be downgraded to a compatibility858/// warning in C++11 mode.859static bool IsCXX98CompatWarning(Sema &S, unsigned InDiagNote) {860return S.getLangOpts().CPlusPlus11 &&861InDiagNote == diag::note_protected_by_variable_non_pod;862}863864/// Produce primary diagnostic for an indirect jump statement.865static void DiagnoseIndirectOrAsmJumpStmt(Sema &S, Stmt *Jump,866LabelDecl *Target, bool &Diagnosed) {867if (Diagnosed)868return;869bool IsAsmGoto = isa<GCCAsmStmt>(Jump);870S.Diag(Jump->getBeginLoc(), diag::err_indirect_goto_in_protected_scope)871<< IsAsmGoto;872S.Diag(Target->getStmt()->getIdentLoc(), diag::note_indirect_goto_target)873<< IsAsmGoto;874Diagnosed = true;875}876877/// Produce note diagnostics for a jump into a protected scope.878void JumpScopeChecker::NoteJumpIntoScopes(ArrayRef<unsigned> ToScopes) {879if (CHECK_PERMISSIVE(ToScopes.empty()))880return;881for (unsigned I = 0, E = ToScopes.size(); I != E; ++I)882if (Scopes[ToScopes[I]].InDiag)883S.Diag(Scopes[ToScopes[I]].Loc, Scopes[ToScopes[I]].InDiag);884}885886/// Diagnose an indirect jump which is known to cross scopes.887void JumpScopeChecker::DiagnoseIndirectOrAsmJump(Stmt *Jump, unsigned JumpScope,888LabelDecl *Target,889unsigned TargetScope) {890if (CHECK_PERMISSIVE(JumpScope == TargetScope))891return;892893unsigned Common = GetDeepestCommonScope(JumpScope, TargetScope);894bool Diagnosed = false;895896// Walk out the scope chain until we reach the common ancestor.897for (unsigned I = JumpScope; I != Common; I = Scopes[I].ParentScope)898if (Scopes[I].OutDiag) {899DiagnoseIndirectOrAsmJumpStmt(S, Jump, Target, Diagnosed);900S.Diag(Scopes[I].Loc, Scopes[I].OutDiag);901}902903SmallVector<unsigned, 10> ToScopesCXX98Compat;904905// Now walk into the scopes containing the label whose address was taken.906for (unsigned I = TargetScope; I != Common; I = Scopes[I].ParentScope)907if (IsCXX98CompatWarning(S, Scopes[I].InDiag))908ToScopesCXX98Compat.push_back(I);909else if (Scopes[I].InDiag) {910DiagnoseIndirectOrAsmJumpStmt(S, Jump, Target, Diagnosed);911S.Diag(Scopes[I].Loc, Scopes[I].InDiag);912}913914// Diagnose this jump if it would be ill-formed in C++98.915if (!Diagnosed && !ToScopesCXX98Compat.empty()) {916bool IsAsmGoto = isa<GCCAsmStmt>(Jump);917S.Diag(Jump->getBeginLoc(),918diag::warn_cxx98_compat_indirect_goto_in_protected_scope)919<< IsAsmGoto;920S.Diag(Target->getStmt()->getIdentLoc(), diag::note_indirect_goto_target)921<< IsAsmGoto;922NoteJumpIntoScopes(ToScopesCXX98Compat);923}924}925926/// CheckJump - Validate that the specified jump statement is valid: that it is927/// jumping within or out of its current scope, not into a deeper one.928void JumpScopeChecker::CheckJump(Stmt *From, Stmt *To, SourceLocation DiagLoc,929unsigned JumpDiagError, unsigned JumpDiagWarning,930unsigned JumpDiagCXX98Compat) {931if (CHECK_PERMISSIVE(!LabelAndGotoScopes.count(From)))932return;933if (CHECK_PERMISSIVE(!LabelAndGotoScopes.count(To)))934return;935936unsigned FromScope = LabelAndGotoScopes[From];937unsigned ToScope = LabelAndGotoScopes[To];938939// Common case: exactly the same scope, which is fine.940if (FromScope == ToScope) return;941942// Warn on gotos out of __finally blocks.943if (isa<GotoStmt>(From) || isa<IndirectGotoStmt>(From)) {944// If FromScope > ToScope, FromScope is more nested and the jump goes to a945// less nested scope. Check if it crosses a __finally along the way.946for (unsigned I = FromScope; I > ToScope; I = Scopes[I].ParentScope) {947if (Scopes[I].InDiag == diag::note_protected_by_seh_finally) {948S.Diag(From->getBeginLoc(), diag::warn_jump_out_of_seh_finally);949break;950} else if (Scopes[I].InDiag ==951diag::note_omp_protected_structured_block) {952S.Diag(From->getBeginLoc(), diag::err_goto_into_protected_scope);953S.Diag(To->getBeginLoc(), diag::note_omp_exits_structured_block);954break;955} else if (Scopes[I].InDiag ==956diag::note_acc_branch_into_compute_construct) {957S.Diag(From->getBeginLoc(), diag::err_goto_into_protected_scope);958S.Diag(Scopes[I].Loc, diag::note_acc_branch_out_of_compute_construct);959return;960}961}962}963964unsigned CommonScope = GetDeepestCommonScope(FromScope, ToScope);965966// It's okay to jump out from a nested scope.967if (CommonScope == ToScope) return;968969// Pull out (and reverse) any scopes we might need to diagnose skipping.970SmallVector<unsigned, 10> ToScopesCXX98Compat;971SmallVector<unsigned, 10> ToScopesError;972SmallVector<unsigned, 10> ToScopesWarning;973for (unsigned I = ToScope; I != CommonScope; I = Scopes[I].ParentScope) {974if (S.getLangOpts().MSVCCompat && JumpDiagWarning != 0 &&975IsMicrosoftJumpWarning(JumpDiagError, Scopes[I].InDiag))976ToScopesWarning.push_back(I);977else if (IsCXX98CompatWarning(S, Scopes[I].InDiag))978ToScopesCXX98Compat.push_back(I);979else if (Scopes[I].InDiag)980ToScopesError.push_back(I);981}982983// Handle warnings.984if (!ToScopesWarning.empty()) {985S.Diag(DiagLoc, JumpDiagWarning);986NoteJumpIntoScopes(ToScopesWarning);987assert(isa<LabelStmt>(To));988LabelStmt *Label = cast<LabelStmt>(To);989Label->setSideEntry(true);990}991992// Handle errors.993if (!ToScopesError.empty()) {994S.Diag(DiagLoc, JumpDiagError);995NoteJumpIntoScopes(ToScopesError);996}997998// Handle -Wc++98-compat warnings if the jump is well-formed.999if (ToScopesError.empty() && !ToScopesCXX98Compat.empty()) {1000S.Diag(DiagLoc, JumpDiagCXX98Compat);1001NoteJumpIntoScopes(ToScopesCXX98Compat);1002}1003}10041005void JumpScopeChecker::CheckGotoStmt(GotoStmt *GS) {1006if (GS->getLabel()->isMSAsmLabel()) {1007S.Diag(GS->getGotoLoc(), diag::err_goto_ms_asm_label)1008<< GS->getLabel()->getIdentifier();1009S.Diag(GS->getLabel()->getLocation(), diag::note_goto_ms_asm_label)1010<< GS->getLabel()->getIdentifier();1011}1012}10131014void JumpScopeChecker::VerifyMustTailStmts() {1015for (AttributedStmt *AS : MustTailStmts) {1016for (unsigned I = LabelAndGotoScopes[AS]; I; I = Scopes[I].ParentScope) {1017if (Scopes[I].OutDiag) {1018S.Diag(AS->getBeginLoc(), diag::err_musttail_scope);1019S.Diag(Scopes[I].Loc, Scopes[I].OutDiag);1020}1021}1022}1023}10241025const Attr *JumpScopeChecker::GetMustTailAttr(AttributedStmt *AS) {1026ArrayRef<const Attr *> Attrs = AS->getAttrs();1027const auto *Iter =1028llvm::find_if(Attrs, [](const Attr *A) { return isa<MustTailAttr>(A); });1029return Iter != Attrs.end() ? *Iter : nullptr;1030}10311032void Sema::DiagnoseInvalidJumps(Stmt *Body) {1033(void)JumpScopeChecker(Body, *this);1034}103510361037