summaryrefslogtreecommitdiff
path: root/lib/Sema/JumpDiagnostics.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Sema/JumpDiagnostics.cpp')
-rw-r--r--lib/Sema/JumpDiagnostics.cpp328
1 files changed, 191 insertions, 137 deletions
diff --git a/lib/Sema/JumpDiagnostics.cpp b/lib/Sema/JumpDiagnostics.cpp
index c394d24d5fdc..bdbe06c4969d 100644
--- a/lib/Sema/JumpDiagnostics.cpp
+++ b/lib/Sema/JumpDiagnostics.cpp
@@ -270,7 +270,8 @@ void JumpScopeChecker::BuildScopeInformation(VarDecl *D,
/// coherent VLA scope with a specified parent node. Walk through the
/// statements, adding any labels or gotos to LabelAndGotoScopes and recursively
/// walking the AST as needed.
-void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned &origParentScope) {
+void JumpScopeChecker::BuildScopeInformation(Stmt *S,
+ unsigned &origParentScope) {
// If this is a statement, rather than an expression, scopes within it don't
// propagate out into the enclosing scope. Otherwise we have to worry
// about block literals, which have the lifetime of their enclosing statement.
@@ -278,7 +279,7 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned &origParentScope)
unsigned &ParentScope = ((isa<Expr>(S) && !isa<StmtExpr>(S))
? origParentScope : independentParentScope);
- bool SkipFirstSubStmt = false;
+ unsigned StmtsToSkip = 0u;
// If we found a label, remember that it is in ParentScope scope.
switch (S->getStmtClass()) {
@@ -303,11 +304,15 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned &origParentScope)
break;
case Stmt::SwitchStmtClass:
- // Evaluate the condition variable before entering the scope of the switch
- // statement.
+ // Evaluate the C++17 init stmt and condition variable
+ // before entering the scope of the switch statement.
+ if (Stmt *Init = cast<SwitchStmt>(S)->getInit()) {
+ BuildScopeInformation(Init, ParentScope);
+ ++StmtsToSkip;
+ }
if (VarDecl *Var = cast<SwitchStmt>(S)->getConditionVariable()) {
BuildScopeInformation(Var, ParentScope);
- SkipFirstSubStmt = true;
+ ++StmtsToSkip;
}
// Fall through
@@ -318,199 +323,248 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned &origParentScope)
Jumps.push_back(S);
break;
+ case Stmt::IfStmtClass: {
+ IfStmt *IS = cast<IfStmt>(S);
+ if (!IS->isConstexpr())
+ break;
+
+ if (VarDecl *Var = IS->getConditionVariable())
+ BuildScopeInformation(Var, ParentScope);
+
+ // Cannot jump into the middle of the condition.
+ unsigned NewParentScope = Scopes.size();
+ Scopes.push_back(GotoScope(ParentScope,
+ diag::note_protected_by_constexpr_if, 0,
+ IS->getLocStart()));
+ BuildScopeInformation(IS->getCond(), NewParentScope);
+
+ // Jumps into either arm of an 'if constexpr' are not allowed.
+ NewParentScope = Scopes.size();
+ Scopes.push_back(GotoScope(ParentScope,
+ diag::note_protected_by_constexpr_if, 0,
+ IS->getLocStart()));
+ BuildScopeInformation(IS->getThen(), NewParentScope);
+ if (Stmt *Else = IS->getElse()) {
+ NewParentScope = Scopes.size();
+ Scopes.push_back(GotoScope(ParentScope,
+ diag::note_protected_by_constexpr_if, 0,
+ IS->getLocStart()));
+ BuildScopeInformation(Else, NewParentScope);
+ }
+ return;
+ }
+
case Stmt::CXXTryStmtClass: {
CXXTryStmt *TS = cast<CXXTryStmt>(S);
- unsigned newParentScope;
- Scopes.push_back(GotoScope(ParentScope,
- diag::note_protected_by_cxx_try,
- diag::note_exits_cxx_try,
- TS->getSourceRange().getBegin()));
- if (Stmt *TryBlock = TS->getTryBlock())
- BuildScopeInformation(TryBlock, (newParentScope = Scopes.size()-1));
+ {
+ unsigned NewParentScope = Scopes.size();
+ Scopes.push_back(GotoScope(ParentScope,
+ diag::note_protected_by_cxx_try,
+ diag::note_exits_cxx_try,
+ TS->getSourceRange().getBegin()));
+ if (Stmt *TryBlock = TS->getTryBlock())
+ BuildScopeInformation(TryBlock, NewParentScope);
+ }
// Jump from the catch into the try is not allowed either.
for (unsigned I = 0, E = TS->getNumHandlers(); I != E; ++I) {
CXXCatchStmt *CS = TS->getHandler(I);
+ unsigned NewParentScope = Scopes.size();
Scopes.push_back(GotoScope(ParentScope,
diag::note_protected_by_cxx_catch,
diag::note_exits_cxx_catch,
CS->getSourceRange().getBegin()));
- BuildScopeInformation(CS->getHandlerBlock(),
- (newParentScope = Scopes.size()-1));
+ BuildScopeInformation(CS->getHandlerBlock(), NewParentScope);
}
return;
}
case Stmt::SEHTryStmtClass: {
SEHTryStmt *TS = cast<SEHTryStmt>(S);
- unsigned newParentScope;
- Scopes.push_back(GotoScope(ParentScope,
- diag::note_protected_by_seh_try,
- diag::note_exits_seh_try,
- TS->getSourceRange().getBegin()));
- if (Stmt *TryBlock = TS->getTryBlock())
- BuildScopeInformation(TryBlock, (newParentScope = Scopes.size()-1));
+ {
+ unsigned NewParentScope = Scopes.size();
+ Scopes.push_back(GotoScope(ParentScope,
+ diag::note_protected_by_seh_try,
+ diag::note_exits_seh_try,
+ TS->getSourceRange().getBegin()));
+ if (Stmt *TryBlock = TS->getTryBlock())
+ BuildScopeInformation(TryBlock, NewParentScope);
+ }
// Jump from __except or __finally into the __try are not allowed either.
if (SEHExceptStmt *Except = TS->getExceptHandler()) {
+ unsigned NewParentScope = Scopes.size();
Scopes.push_back(GotoScope(ParentScope,
diag::note_protected_by_seh_except,
diag::note_exits_seh_except,
Except->getSourceRange().getBegin()));
- BuildScopeInformation(Except->getBlock(),
- (newParentScope = Scopes.size()-1));
+ BuildScopeInformation(Except->getBlock(), NewParentScope);
} else if (SEHFinallyStmt *Finally = TS->getFinallyHandler()) {
+ unsigned NewParentScope = Scopes.size();
Scopes.push_back(GotoScope(ParentScope,
diag::note_protected_by_seh_finally,
diag::note_exits_seh_finally,
Finally->getSourceRange().getBegin()));
- BuildScopeInformation(Finally->getBlock(),
- (newParentScope = Scopes.size()-1));
+ BuildScopeInformation(Finally->getBlock(), NewParentScope);
}
return;
}
- default:
- break;
- }
-
- for (Stmt *SubStmt : S->children()) {
- if (SkipFirstSubStmt) {
- SkipFirstSubStmt = false;
- continue;
- }
-
- if (!SubStmt) continue;
-
- // Cases, labels, and defaults aren't "scope parents". It's also
- // important to handle these iteratively instead of recursively in
- // order to avoid blowing out the stack.
- while (true) {
- Stmt *Next;
- if (CaseStmt *CS = dyn_cast<CaseStmt>(SubStmt))
- Next = CS->getSubStmt();
- else if (DefaultStmt *DS = dyn_cast<DefaultStmt>(SubStmt))
- Next = DS->getSubStmt();
- else if (LabelStmt *LS = dyn_cast<LabelStmt>(SubStmt))
- Next = LS->getSubStmt();
- else
- break;
-
- LabelAndGotoScopes[SubStmt] = ParentScope;
- SubStmt = Next;
- }
-
+ case Stmt::DeclStmtClass: {
// If this is a declstmt with a VLA definition, it defines a scope from here
// to the end of the containing context.
- if (DeclStmt *DS = dyn_cast<DeclStmt>(SubStmt)) {
- // The decl statement creates a scope if any of the decls in it are VLAs
- // or have the cleanup attribute.
- for (auto *I : DS->decls())
- BuildScopeInformation(I, ParentScope);
- continue;
- }
+ DeclStmt *DS = cast<DeclStmt>(S);
+ // The decl statement creates a scope if any of the decls in it are VLAs
+ // or have the cleanup attribute.
+ for (auto *I : DS->decls())
+ BuildScopeInformation(I, origParentScope);
+ return;
+ }
+
+ case Stmt::ObjCAtTryStmtClass: {
// Disallow jumps into any part of an @try statement by pushing a scope and
// walking all sub-stmts in that scope.
- if (ObjCAtTryStmt *AT = dyn_cast<ObjCAtTryStmt>(SubStmt)) {
- unsigned newParentScope;
- // Recursively walk the AST for the @try part.
+ ObjCAtTryStmt *AT = cast<ObjCAtTryStmt>(S);
+ // Recursively walk the AST for the @try part.
+ {
+ unsigned NewParentScope = Scopes.size();
Scopes.push_back(GotoScope(ParentScope,
diag::note_protected_by_objc_try,
diag::note_exits_objc_try,
AT->getAtTryLoc()));
if (Stmt *TryPart = AT->getTryBody())
- BuildScopeInformation(TryPart, (newParentScope = Scopes.size()-1));
-
- // Jump from the catch to the finally or try is not valid.
- for (unsigned I = 0, N = AT->getNumCatchStmts(); I != N; ++I) {
- ObjCAtCatchStmt *AC = AT->getCatchStmt(I);
- Scopes.push_back(GotoScope(ParentScope,
- diag::note_protected_by_objc_catch,
- diag::note_exits_objc_catch,
- AC->getAtCatchLoc()));
- // @catches are nested and it isn't
- BuildScopeInformation(AC->getCatchBody(),
- (newParentScope = Scopes.size()-1));
- }
+ BuildScopeInformation(TryPart, NewParentScope);
+ }
- // Jump from the finally to the try or catch is not valid.
- if (ObjCAtFinallyStmt *AF = AT->getFinallyStmt()) {
- Scopes.push_back(GotoScope(ParentScope,
- diag::note_protected_by_objc_finally,
- diag::note_exits_objc_finally,
- AF->getAtFinallyLoc()));
- BuildScopeInformation(AF, (newParentScope = Scopes.size()-1));
- }
+ // Jump from the catch to the finally or try is not valid.
+ for (unsigned I = 0, N = AT->getNumCatchStmts(); I != N; ++I) {
+ ObjCAtCatchStmt *AC = AT->getCatchStmt(I);
+ unsigned NewParentScope = Scopes.size();
+ Scopes.push_back(GotoScope(ParentScope,
+ diag::note_protected_by_objc_catch,
+ diag::note_exits_objc_catch,
+ AC->getAtCatchLoc()));
+ // @catches are nested and it isn't
+ BuildScopeInformation(AC->getCatchBody(), NewParentScope);
+ }
- continue;
+ // Jump from the finally to the try or catch is not valid.
+ if (ObjCAtFinallyStmt *AF = AT->getFinallyStmt()) {
+ unsigned NewParentScope = Scopes.size();
+ Scopes.push_back(GotoScope(ParentScope,
+ diag::note_protected_by_objc_finally,
+ diag::note_exits_objc_finally,
+ AF->getAtFinallyLoc()));
+ BuildScopeInformation(AF, NewParentScope);
}
- unsigned newParentScope;
+ return;
+ }
+
+ case Stmt::ObjCAtSynchronizedStmtClass: {
// Disallow jumps into the protected statement of an @synchronized, but
// allow jumps into the object expression it protects.
- if (ObjCAtSynchronizedStmt *AS =
- dyn_cast<ObjCAtSynchronizedStmt>(SubStmt)) {
- // Recursively walk the AST for the @synchronized object expr, it is
- // evaluated in the normal scope.
- BuildScopeInformation(AS->getSynchExpr(), ParentScope);
-
- // Recursively walk the AST for the @synchronized part, protected by a new
- // scope.
- Scopes.push_back(GotoScope(ParentScope,
- diag::note_protected_by_objc_synchronized,
- diag::note_exits_objc_synchronized,
- AS->getAtSynchronizedLoc()));
- BuildScopeInformation(AS->getSynchBody(),
- (newParentScope = Scopes.size()-1));
- continue;
- }
+ ObjCAtSynchronizedStmt *AS = cast<ObjCAtSynchronizedStmt>(S);
+ // Recursively walk the AST for the @synchronized object expr, it is
+ // evaluated in the normal scope.
+ BuildScopeInformation(AS->getSynchExpr(), ParentScope);
+
+ // Recursively walk the AST for the @synchronized part, protected by a new
+ // scope.
+ unsigned NewParentScope = Scopes.size();
+ Scopes.push_back(GotoScope(ParentScope,
+ diag::note_protected_by_objc_synchronized,
+ diag::note_exits_objc_synchronized,
+ AS->getAtSynchronizedLoc()));
+ BuildScopeInformation(AS->getSynchBody(), NewParentScope);
+ return;
+ }
+ case Stmt::ObjCAutoreleasePoolStmtClass: {
// Disallow jumps into the protected statement of an @autoreleasepool.
- if (ObjCAutoreleasePoolStmt *AS =
- dyn_cast<ObjCAutoreleasePoolStmt>(SubStmt)) {
- // Recursively walk the AST for the @autoreleasepool part, protected by a
- // new scope.
- Scopes.push_back(GotoScope(ParentScope,
- diag::note_protected_by_objc_autoreleasepool,
- diag::note_exits_objc_autoreleasepool,
- AS->getAtLoc()));
- BuildScopeInformation(AS->getSubStmt(),
- (newParentScope = Scopes.size() - 1));
- continue;
- }
+ ObjCAutoreleasePoolStmt *AS = cast<ObjCAutoreleasePoolStmt>(S);
+ // Recursively walk the AST for the @autoreleasepool part, protected by a
+ // new scope.
+ unsigned NewParentScope = Scopes.size();
+ Scopes.push_back(GotoScope(ParentScope,
+ diag::note_protected_by_objc_autoreleasepool,
+ diag::note_exits_objc_autoreleasepool,
+ AS->getAtLoc()));
+ BuildScopeInformation(AS->getSubStmt(), NewParentScope);
+ return;
+ }
+ case Stmt::ExprWithCleanupsClass: {
// Disallow jumps past full-expressions that use blocks with
// non-trivial cleanups of their captures. This is theoretically
// implementable but a lot of work which we haven't felt up to doing.
- if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(SubStmt)) {
- for (unsigned i = 0, e = EWC->getNumObjects(); i != e; ++i) {
- const BlockDecl *BDecl = EWC->getObject(i);
- for (const auto &CI : BDecl->captures()) {
- VarDecl *variable = CI.getVariable();
- BuildScopeInformation(variable, BDecl, ParentScope);
- }
+ ExprWithCleanups *EWC = cast<ExprWithCleanups>(S);
+ for (unsigned i = 0, e = EWC->getNumObjects(); i != e; ++i) {
+ const BlockDecl *BDecl = EWC->getObject(i);
+ for (const auto &CI : BDecl->captures()) {
+ VarDecl *variable = CI.getVariable();
+ BuildScopeInformation(variable, BDecl, origParentScope);
}
}
+ break;
+ }
+ case Stmt::MaterializeTemporaryExprClass: {
// Disallow jumps out of scopes containing temporaries lifetime-extended to
// automatic storage duration.
- if (MaterializeTemporaryExpr *MTE =
- dyn_cast<MaterializeTemporaryExpr>(SubStmt)) {
- if (MTE->getStorageDuration() == SD_Automatic) {
- SmallVector<const Expr *, 4> CommaLHS;
- SmallVector<SubobjectAdjustment, 4> Adjustments;
- const Expr *ExtendedObject =
- MTE->GetTemporaryExpr()->skipRValueSubobjectAdjustments(
- CommaLHS, Adjustments);
- if (ExtendedObject->getType().isDestructedType()) {
- Scopes.push_back(GotoScope(ParentScope, 0,
- diag::note_exits_temporary_dtor,
- ExtendedObject->getExprLoc()));
- ParentScope = Scopes.size()-1;
- }
+ MaterializeTemporaryExpr *MTE = cast<MaterializeTemporaryExpr>(S);
+ if (MTE->getStorageDuration() == SD_Automatic) {
+ SmallVector<const Expr *, 4> CommaLHS;
+ SmallVector<SubobjectAdjustment, 4> Adjustments;
+ const Expr *ExtendedObject =
+ MTE->GetTemporaryExpr()->skipRValueSubobjectAdjustments(
+ CommaLHS, Adjustments);
+ if (ExtendedObject->getType().isDestructedType()) {
+ Scopes.push_back(GotoScope(ParentScope, 0,
+ diag::note_exits_temporary_dtor,
+ ExtendedObject->getExprLoc()));
+ origParentScope = Scopes.size()-1;
}
}
+ break;
+ }
+
+ case Stmt::CaseStmtClass:
+ case Stmt::DefaultStmtClass:
+ case Stmt::LabelStmtClass:
+ LabelAndGotoScopes[S] = ParentScope;
+ break;
+
+ default:
+ break;
+ }
+
+ for (Stmt *SubStmt : S->children()) {
+ if (!SubStmt)
+ continue;
+ if (StmtsToSkip) {
+ --StmtsToSkip;
+ continue;
+ }
+
+ // Cases, labels, and defaults aren't "scope parents". It's also
+ // important to handle these iteratively instead of recursively in
+ // order to avoid blowing out the stack.
+ while (true) {
+ Stmt *Next;
+ if (CaseStmt *CS = dyn_cast<CaseStmt>(SubStmt))
+ Next = CS->getSubStmt();
+ else if (DefaultStmt *DS = dyn_cast<DefaultStmt>(SubStmt))
+ Next = DS->getSubStmt();
+ else if (LabelStmt *LS = dyn_cast<LabelStmt>(SubStmt))
+ Next = LS->getSubStmt();
+ else
+ break;
+
+ LabelAndGotoScopes[SubStmt] = ParentScope;
+ SubStmt = Next;
+ }
// Recursively walk the AST.
BuildScopeInformation(SubStmt, ParentScope);