llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Akash Manna (akash-manna-sky)

<details>
<summary>Changes</summary>

Fixes #<!-- -->215454

A declaration with no declarator, like `__typeof__(e);`, is accepted with just 
a warning, but it produces no Decl, so `ActOnDeclStmt` returns `StmtError()`. 
On its own that's harmless: the statement is simply dropped. Since cad09404cc80 
(#<!-- -->113760), though, `ParseCompoundStatementBody` returns `StmtError()` 
for a `({ ... })` whose last statement is invalid, and that turned the whole 
statement expression into an `ExprError` with no diagnostic behind it. 
Everything downstream assumes an `ExprError` was already reported. So `({ 
foo(); __typeof__(e); });` compiled with a warning and the call to `foo()` 
vanished from the IR, and in an `if` condition the parser's recovery produced a 
`RecoveryExpr` with zero errors emitted, which let CodeGen run and hit the 
`!isValueDependent()` assertion in `EvaluateAsInt`. Plain C, no templates 
involved.

The parser now appends a null statement in place of the dropped one instead of 
failing. The statement expression gets built with type `void`, which is what 
GCC does for this input, and the earlier statement can't be picked up as the 
value anymore, which is the thing #<!-- -->113760 was actually guarding 
against. Using it as an `if` condition now gives the normal "statement requires 
expression of scalar type" error, and side effects in the body are preserved. 
The one visible change to existing behaviour is that a statement expression 
whose last statement had a real error now has type `void`, so a use that needs 
a value gets a follow-on conversion error rather than being silently discarded. 

---
Full diff: https://github.com/llvm/llvm-project/pull/224682.diff


4 Files Affected:

- (modified) clang/docs/ReleaseNotes.md (+2) 
- (modified) clang/lib/Parse/ParseStmt.cpp (+11-9) 
- (added) clang/test/CodeGen/GH215454.c (+32) 
- (modified) clang/test/SemaCXX/gh113468.cpp (+1-1) 


``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index f4a34a37aff52..fe279a88cd942 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -547,6 +547,8 @@ features cannot lower the translation-unit ABI level;
 - Fixed a crash when an `asm` label names the register for a global variable 
of incomplete type. (#GH219746)
 - Fixed an ICE hat occurred when using `__imag int/float` as lvalue in 
assignment. (#GH119498)
 - Fixed an assertion failure in `-Wsign-compare` when a negated or 
complemented vector of unsigned integers was compared against a signed 
constant. (#GH203575)
+- Fixed a crash in code generation and silently dropped side effects when the 
last statement of a GNU statement
+  expression is a declaration that declares nothing, such as `({ f(); 
__typeof__(x); })`. (#GH215454)
 
 #### Bug Fixes to Compiler Builtins
 
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 14bea6a1a7948..15791273e71f7 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1185,7 +1185,7 @@ StmtResult Parser::ParseCompoundStatementBody(bool 
isStmtExpr) {
       ParsedStmtContext::Compound |
       (isStmtExpr ? ParsedStmtContext::InStmtExpr : ParsedStmtContext());
 
-  bool LastIsError = false;
+  bool LastIsInvalid = false;
   while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) &&
          Tok.isNot(tok::eof)) {
     if (Tok.is(tok::annot_pragma_unused)) {
@@ -1242,14 +1242,16 @@ StmtResult Parser::ParseCompoundStatementBody(bool 
isStmtExpr) {
 
     if (R.isUsable())
       Stmts.push_back(R.get());
-    LastIsError = R.isInvalid();
-  }
-  // StmtExpr needs to do copy initialization for last statement.
-  // If last statement is invalid, the last statement in `Stmts` will be
-  // incorrect. Then the whole compound statement should also be marked as
-  // invalid to prevent subsequent errors.
-  if (isStmtExpr && LastIsError && !Stmts.empty())
-    return StmtError();
+    LastIsInvalid = R.isInvalid();
+  }
+  // The last statement of a statement expression is its value and was already
+  // copy-initialized when parsed. If it was dropped, the statement now at the
+  // end must not become the value, so replace the dropped one with a null
+  // statement. Don't return StmtError here: an invalid statement does not
+  // imply an error was diagnosed (e.g. `__typeof__(x);` only warns), and an
+  // undiagnosed ExprError silently drops the statement expression.
+  if (isStmtExpr && LastIsInvalid)
+    Stmts.push_back(Actions.ActOnNullStmt(PrevTokLocation).get());
 
   // Warn the user that using option `-ffp-eval-method=source` on a
   // 32-bit target and feature `sse` disabled, or using
diff --git a/clang/test/CodeGen/GH215454.c b/clang/test/CodeGen/GH215454.c
new file mode 100644
index 0000000000000..aceba1d41474a
--- /dev/null
+++ b/clang/test/CodeGen/GH215454.c
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -std=gnu99 -verify -emit-llvm-only %s
+// RUN: %clang_cc1 -std=gnu99 -DCODEGEN -triple x86_64-unknown-linux-gnu 
-emit-llvm -o - %s | FileCheck %s
+
+// A declaration that declares nothing as the last statement of a statement
+// expression made the whole statement expression invalid without an error,
+// which dropped the call below or crashed CodeGen on a RecoveryExpr.
+
+void foo(void);
+
+// CHECK-LABEL: define{{.*}} void @keeps_side_effects(
+// CHECK: call void @foo()
+void keeps_side_effects(int e) {
+  ({ foo(); __typeof__(e); }); // expected-warning {{declaration does not 
declare anything}}
+}
+
+#ifndef CODEGEN
+void d2(int e) {
+  if (({ ; __typeof__(e); })) {} // expected-warning {{declaration does not 
declare anything}} \
+                                 // expected-error {{statement requires 
expression of scalar type ('void' invalid)}}
+}
+
+// Reproducer from the issue; the unclosed '({' makes recovery run to EOF.
+#define c(a, b)                                                                
\
+  {;__typeof__(b);}
+void d(int e) {if((c(, e););  // expected-warning {{'(' and '{' tokens 
introducing statement expression appear in different macro expansion contexts}} 
\
+                              // expected-note {{'{' token is here}} \
+                              // expected-warning {{declaration does not 
declare anything}} \
+                              // expected-error {{unexpected ';' before ')'}} \
+                              // expected-note {{to match this '{'}}
+}                             // expected-error {{expected expression}} \
+                              // expected-error@+2 {{expected '}'}}
+#endif
diff --git a/clang/test/SemaCXX/gh113468.cpp b/clang/test/SemaCXX/gh113468.cpp
index 94551986b0efa..f4252abd59c44 100644
--- a/clang/test/SemaCXX/gh113468.cpp
+++ b/clang/test/SemaCXX/gh113468.cpp
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
 
 constexpr int expr() {
-  if (({
+  if (({ // expected-error {{value of type 'void' is not contextually 
convertible to 'bool'}}
         int f;
         f = 0;
         if (f)

``````````

</details>


https://github.com/llvm/llvm-project/pull/224682
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to