[Clang] Diagnose jumps into statement expressions
authorCorentin Jabot <corentinjabot@gmail.com>
Fri, 7 Jul 2023 08:58:13 +0000 (10:58 +0200)
committerCorentin Jabot <corentinjabot@gmail.com>
Fri, 21 Jul 2023 13:08:51 +0000 (15:08 +0200)
Such jumps are not allowed by GCC and allowing them
can lead to situations where we jumps into unevaluated
statements.

Fixes #63682

Reviewed By: aaron.ballman, #clang-language-wg

Differential Revision: https://reviews.llvm.org/D154696

clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Sema/JumpDiagnostics.cpp
clang/lib/Sema/SemaExpr.cpp
clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p2.cpp
clang/test/Sema/asm-goto.cpp
clang/test/Sema/scope-check.c
clang/test/SemaCXX/constant-expression-cxx14.cpp
clang/test/SemaObjC/scope-check.m

index 45381e7..f412e79 100644 (file)
@@ -660,6 +660,9 @@ Bug Fixes in This Version
 - Fixed false positive error diagnostic observed from mixing ``asm goto`` with
   ``__attribute__((cleanup()))`` variables falsely warning that jumps to
   non-targets would skip cleanup.
+- Correcly diagnose jumps into statement expressions.
+  This ensures the behavior of Clang is consistent with GCC.
+  (`#63682 <https://github.com/llvm/llvm-project/issues/63682>`_)
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -949,7 +952,7 @@ AST Matchers
 
 - The ``hasBody`` matcher now matches coroutine body nodes in
   ``CoroutineBodyStmts``.
-  
+
 - Add ``arrayInitIndexExpr`` and ``arrayInitLoopExpr`` matchers.
 
 clang-format
index 99917f1..b87414d 100644 (file)
@@ -6219,6 +6219,8 @@ def note_enters_block_captures_non_trivial_c_struct : Note<
   "to destroy">;
 def note_enters_compound_literal_scope : Note<
   "jump enters lifetime of a compound literal that is non-trivial to destruct">;
+def note_enters_statement_expression : Note<
+  "jump enters a statement expression">;
 
 def note_exits_cleanup : Note<
   "jump exits scope of variable with __attribute__((cleanup))">;
index 5a6df56..45ff36d 100644 (file)
@@ -466,6 +466,21 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S,
     return;
   }
 
+  case Stmt::StmtExprClass: {
+    // [GNU]
+    // Jumping into a statement expression with goto or using
+    // a switch statement outside the statement expression with
+    // a case or default label inside the statement expression is not permitted.
+    // Jumping out of a statement expression is permitted.
+    StmtExpr *SE = cast<StmtExpr>(S);
+    unsigned NewParentScope = Scopes.size();
+    Scopes.push_back(GotoScope(ParentScope,
+                               diag::note_enters_statement_expression,
+                               /*OutDiag=*/0, SE->getBeginLoc()));
+    BuildScopeInformation(SE->getSubStmt(), NewParentScope);
+    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.
index 2a7ae73..240f152 100644 (file)
@@ -16509,6 +16509,8 @@ ExprResult Sema::ActOnAddrLabel(SourceLocation OpLoc, SourceLocation LabLoc,
 
 void Sema::ActOnStartStmtExpr() {
   PushExpressionEvaluationContext(ExprEvalContexts.back().Context);
+  // Make sure we diagnose jumping into a statement expression.
+  setFunctionHasBranchProtectedScope();
 }
 
 void Sema::ActOnStmtExprError() {
index 0c357db..55af13b 100644 (file)
@@ -153,7 +153,8 @@ a:  if constexpr(sizeof(n) == 4) // expected-error {{redefinition}} expected-not
 
   void evil_things() {
     goto evil_label; // expected-error {{cannot jump}}
-    if constexpr (true || ({evil_label: false;})) {} // expected-note {{constexpr if}}
+    if constexpr (true || ({evil_label: false;})) {} // expected-note {{constexpr if}} \
+                                                     // expected-note {{jump enters a statement expression}}
 
     if constexpr (true) // expected-note {{constexpr if}}
       goto surprise; // expected-error {{cannot jump}}
index 52b9c37..4c624d2 100644 (file)
@@ -50,8 +50,9 @@ int test3(int n)
   // expected-error@+2 {{cannot jump from this asm goto statement to one of its possible targets}}
   // expected-error@+1 {{cannot jump from this asm goto statement to one of its possible targets}}
   asm volatile goto("testl %0, %0; jne %l1;" :: "r"(n)::label_true, loop);
-  // expected-note@+2 {{jump bypasses initialization of variable length array}}
-  // expected-note@+1 {{possible target of asm goto statement}}
+  // expected-note@+3 {{jump bypasses initialization of variable length array}}
+  // expected-note@+2 {{possible target of asm goto statement}}
+  // expected-note@+1 {{jump enters a statement expression}}
   return ({int a[n];label_true: 2;});
   // expected-note@+1 {{jump bypasses initialization of variable length array}}
   int b[n];
@@ -69,3 +70,17 @@ l0:;
   asm goto(""::::l1);
 l1:;
 }
+
+void statement_expressions() {
+  ({
+    __label__ label;
+    asm goto("" : : : : label);
+    label:;
+  });
+
+  ({
+    __label__ label;
+    asm goto("" : : : : label);
+    label:;
+  });
+}
index cc088ab..014cedb 100644 (file)
@@ -65,7 +65,8 @@ int test8(int x) {
 
   // Statement expressions.
   goto L3;   // expected-error {{cannot jump from this goto statement to its label}}
-  int Y = ({  int a[x];   // expected-note {{jump bypasses initialization of variable length array}}  
+  int Y = ({  int a[x];   // expected-note {{jump bypasses initialization of variable length array}} \
+                          // expected-note {{jump enters a statement expression}}
            L3: 4; });
   
   goto L4; // expected-error {{cannot jump from this goto statement to its label}}
@@ -107,25 +108,25 @@ int test8(int x) {
            4; })];
   L10:; // bad
   }
-  
+
   {
     // FIXME: Crashes goto checker.
     //goto L11;// ok
     //int A[({   L11: 4; })];
   }
-  
+
   {
     goto L12;
-    
+
     int y = 4;   // fixme-warn: skips initializer.
   L12:
     ;
   }
-  
+
   // Statement expressions 2.
   goto L1;     // expected-error {{cannot jump from this goto statement to its label}}
-  return x == ({
-                 int a[x];   // expected-note {{jump bypasses initialization of variable length array}}  
+  return x == ({             // expected-note {{jump enters a statement expression}}
+                 int a[x];   // expected-note {{jump bypasses initialization of variable length array}}
                L1:
                  42; });
 }
@@ -232,3 +233,27 @@ void test15(int n, void *pc) {
 
 // rdar://9024687
 int test16(int [sizeof &&z]); // expected-error {{use of address-of-label extension outside of a function body}}
+
+void GH63682() {
+  {
+    goto L; // expected-error {{cannot jump from this goto statement to its label}}
+    (void)sizeof (int){({ L:; 1; })}; // expected-note {{jump enters a statement expression}}
+  }
+  {
+    goto M; // expected-error {{cannot jump from this goto statement to its label}}
+    (void)({ M:; 1; }); // expected-note {{jump enters a statement expression}}
+  }
+  {
+    (void)({ goto N; 1; });  // ok
+    N: ;
+  }
+  {
+    (void)sizeof (int){({ goto O; 1; })}; // ok (not evaluated)
+    O: ;
+  }
+  {
+    (void)sizeof(({goto P;}), 0); // expected-error {{cannot jump from this goto statement to its label}}
+    return;
+    (void)({P:1;});  // expected-note {{jump enters a statement expression}}
+  }
+}
index 52a7cce..cf242aa 100644 (file)
@@ -831,8 +831,9 @@ namespace StmtExpr {
     case 0:
       return 0;
 
-      ({
-        case 1: // expected-note {{not supported}}
+      ({  // expected-note {{jump enters a statement expression}}
+        case 1:// expected-error {{cannot jump from switch statement to this case label}} \
+               // expected-note  {{not supported}}
           return 1;
       });
     }
index 563dca0..8f3fd30 100644 (file)
@@ -15,7 +15,7 @@ L2: ;
   } @finally {// expected-note {{jump bypasses initialization of @finally block}}
 L3: ;
   }
-  
+
   @try {
     goto L4; // expected-error{{cannot jump}}
     goto L5; // expected-error{{cannot jump}}
@@ -27,8 +27,8 @@ L3: ;
   } @finally { // expected-note {{jump bypasses initialization of @finally block}}
   L4: ;
   }
-  
+
+
   @try { // expected-note 2 {{jump bypasses initialization of @try block}}
   L7: ;
   } @catch (C *c) {
@@ -36,21 +36,18 @@ L3: ;
   } @finally {
     goto L7; // expected-error{{cannot jump}}
   }
-  
+
   goto L8;  // expected-error{{cannot jump}}
-  @try { 
+  @try {
   } @catch (A *c) {
   } @catch (B *c) {
   } @catch (C *c) { // expected-note {{jump bypasses initialization of @catch block}}
   L8: ;
   }
-  
-  // rdar://6810106
   id X;
   goto L9;    // expected-error{{cannot jump}}
-  goto L10;   // ok
-  @synchronized    // expected-note {{jump bypasses initialization of @synchronized block}}
-  ( ({ L10: ; X; })) {
+  @synchronized (X)  // expected-note {{jump bypasses initialization of @synchronized block}}
+  {
   L9:
     ;
   }
@@ -90,7 +87,7 @@ void test3(void) {
     goto L0;     // expected-error {{cannot jump}}
     typedef int A[n];  // expected-note {{jump bypasses initialization of VLA typedef}}
   L0:
-    
+
     goto L1;      // expected-error {{cannot jump}}
     A b, c[10];        // expected-note 2 {{jump bypasses initialization of variable length array}}
   L1: