domdom updated this revision to Diff 184001.
domdom added a comment.

Thanks for your comments, @aaron.ballman. I've addressed the comments and added 
a test case as suggested.

This revealed an issue with the code-gen side of things, so I fixed that and 
added another test case for that as well.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D57086/new/

https://reviews.llvm.org/D57086

Files:
  clang/include/clang/AST/Stmt.h
  clang/lib/CodeGen/CGStmt.cpp
  clang/lib/Parse/ParseStmt.cpp
  clang/lib/Sema/SemaExpr.cpp
  clang/test/AST/ast-dump-stmt.c
  clang/test/CodeGen/exprs.c
  clang/test/Sema/statements.c

Index: clang/test/Sema/statements.c
===================================================================
--- clang/test/Sema/statements.c
+++ clang/test/Sema/statements.c
@@ -119,3 +119,17 @@
     SIZE = sizeof(({unsigned long __ptr; __ptr;}))
   };
 }
+
+// GCC ignores empty statements at the end of compound expressions where the
+// result type is concerned.
+void test13() {
+  int a;
+  a = ({1;});
+  a = ({1;;});
+  a = ({int x = 1; (void)x;}); // expected-error {{assigning to 'int' from incompatible type 'void'}}
+  a = ({int x = 1; (void)x;;}); // expected-error {{assigning to 'int' from incompatible type 'void'}}
+}
+
+void test14() { return ({}); }
+void test15() { return ({;;;;}); }
+void test16() { return ({test:;;}); }
Index: clang/test/CodeGen/exprs.c
===================================================================
--- clang/test/CodeGen/exprs.c
+++ clang/test/CodeGen/exprs.c
@@ -196,3 +196,13 @@
 }
 // CHECK-LABEL: define void @f18()
 // CHECK: call i32 @returns_int()
+
+// Ensure the right stmt is returned
+int f19() {
+  return ({ 3;;4;; });
+}
+// CHECK-LABEL: define i32 @f19()
+// CHECK: [[T:%.*]] = alloca i32
+// CHECK: store i32 4, i32* [[T]]
+// CHECK: [[L:%.*]] = load i32, i32* [[T]]
+// CHECK: ret i32 [[L]]
Index: clang/test/AST/ast-dump-stmt.c
===================================================================
--- clang/test/AST/ast-dump-stmt.c
+++ clang/test/AST/ast-dump-stmt.c
@@ -372,4 +372,14 @@
   // CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} <col:13> 'int' 10
   // CHECK-NEXT: ImplicitCastExpr
   // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:17> 'int' lvalue Var 0x{{[^ ]*}} 'a' 'int'
+  ({int a = 10; a;;;});
+  // CHECK-NEXT: StmtExpr 0x{{[^ ]*}} <line:[[@LINE-1]]:3, col:22> 'int'
+  // CHECK-NEXT: CompoundStmt
+  // CHECK-NEXT: DeclStmt
+  // CHECK-NEXT: VarDecl 0x{{[^ ]*}} <col:5, col:13> col:9 used a 'int' cinit
+  // CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} <col:13> 'int' 10
+  // CHECK-NEXT: ImplicitCastExpr
+  // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:17> 'int' lvalue Var 0x{{[^ ]*}} 'a' 'int'
+  // CHECK-NEXT: NullStmt
+  // CHECK-NEXT: NullStmt
 }
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -13320,11 +13320,12 @@
   // More semantic analysis is needed.
 
   // If there are sub-stmts in the compound stmt, take the type of the last one
-  // as the type of the stmtexpr.
+  // as the type of the stmtexpr. For GCC compatibility this excludes trailing
+  // NullStmts.
   QualType Ty = Context.VoidTy;
   bool StmtExprMayBindToTemp = false;
   if (!Compound->body_empty()) {
-    Stmt *LastStmt = Compound->body_back();
+    Stmt *LastStmt = Compound->getStmtExprResult();
     LabelStmt *LastLabelStmt = nullptr;
     // If LastStmt is a label, skip down through into the body.
     while (LabelStmt *Label = dyn_cast<LabelStmt>(LastStmt)) {
@@ -13360,7 +13361,7 @@
           return ExprError();
         if (LastExpr.get() != nullptr) {
           if (!LastLabelStmt)
-            Compound->setLastStmt(LastExpr.get());
+            Compound->setStmtExpr(LastExpr.get());
           else
             LastLabelStmt->setSubStmt(LastExpr.get());
           StmtExprMayBindToTemp = true;
Index: clang/lib/Parse/ParseStmt.cpp
===================================================================
--- clang/lib/Parse/ParseStmt.cpp
+++ clang/lib/Parse/ParseStmt.cpp
@@ -959,10 +959,16 @@
 
 bool Parser::isExprValueDiscarded() {
   if (Actions.isCurCompoundStmtAStmtExpr()) {
-    // Look to see if the next two tokens close the statement expression;
-    // if so, this expression statement is the last statement in a
-    // statment expression.
-    return Tok.isNot(tok::r_brace) || NextToken().isNot(tok::r_paren);
+    // For GCC compatibility we skip past NullStmts.
+    unsigned LookAhead = 0;
+    while (GetLookAheadToken(LookAhead).is(tok::semi)) {
+      ++LookAhead;
+    }
+    // Then look to see if the next two tokens close the statement expression;
+    // if so, this expression statement is the last statement in a statment
+    // expression.
+    return GetLookAheadToken(LookAhead).isNot(tok::r_brace) ||
+           GetLookAheadToken(LookAhead + 1).isNot(tok::r_paren);
   }
   return true;
 }
Index: clang/lib/CodeGen/CGStmt.cpp
===================================================================
--- clang/lib/CodeGen/CGStmt.cpp
+++ clang/lib/CodeGen/CGStmt.cpp
@@ -382,36 +382,38 @@
                                               bool GetLast,
                                               AggValueSlot AggSlot) {
 
-  for (CompoundStmt::const_body_iterator I = S.body_begin(),
-       E = S.body_end()-GetLast; I != E; ++I)
-    EmitStmt(*I);
-
+  Stmt *ExprResult = GetLast ? S.getStmtExprResult() : nullptr;
   Address RetAlloca = Address::invalid();
-  if (GetLast) {
-    // We have to special case labels here.  They are statements, but when put
-    // at the end of a statement expression, they yield the value of their
-    // subexpression.  Handle this by walking through all labels we encounter,
-    // emitting them before we evaluate the subexpr.
-    const Stmt *LastStmt = S.body_back();
-    while (const LabelStmt *LS = dyn_cast<LabelStmt>(LastStmt)) {
-      EmitLabel(LS->getDecl());
-      LastStmt = LS->getSubStmt();
-    }
 
-    EnsureInsertPoint();
+  for (CompoundStmt::const_body_iterator I = S.body_begin(),
+      E = S.body_end(); I != E; ++I) {
+    if (GetLast && ExprResult == *I) {
+      // We have to special case labels here.  They are statements, but when put
+      // at the end of a statement expression, they yield the value of their
+      // subexpression.  Handle this by walking through all labels we encounter,
+      // emitting them before we evaluate the subexpr.
+      const Stmt *LastStmt = ExprResult;
+      while (const LabelStmt *LS = dyn_cast<LabelStmt>(LastStmt)) {
+        EmitLabel(LS->getDecl());
+        LastStmt = LS->getSubStmt();
+      }
+
+      EnsureInsertPoint();
 
-    QualType ExprTy = cast<Expr>(LastStmt)->getType();
-    if (hasAggregateEvaluationKind(ExprTy)) {
-      EmitAggExpr(cast<Expr>(LastStmt), AggSlot);
+      QualType ExprTy = cast<Expr>(LastStmt)->getType();
+      if (hasAggregateEvaluationKind(ExprTy)) {
+        EmitAggExpr(cast<Expr>(LastStmt), AggSlot);
+      } else {
+        // We can't return an RValue here because there might be cleanups at
+        // the end of the StmtExpr.  Because of that, we have to emit the result
+        // here into a temporary alloca.
+        RetAlloca = CreateMemTemp(ExprTy);
+        EmitAnyExprToMem(cast<Expr>(LastStmt), RetAlloca, Qualifiers(),
+            /*IsInit*/false);
+      }
     } else {
-      // We can't return an RValue here because there might be cleanups at
-      // the end of the StmtExpr.  Because of that, we have to emit the result
-      // here into a temporary alloca.
-      RetAlloca = CreateMemTemp(ExprTy);
-      EmitAnyExprToMem(cast<Expr>(LastStmt), RetAlloca, Qualifiers(),
-                       /*IsInit*/false);
+      EmitStmt(*I);
     }
-
   }
 
   return RetAlloca;
Index: clang/include/clang/AST/Stmt.h
===================================================================
--- clang/include/clang/AST/Stmt.h
+++ clang/include/clang/AST/Stmt.h
@@ -1250,6 +1250,21 @@
 
   void setStmts(ArrayRef<Stmt *> Stmts);
 
+  // GCC ignores empty statements at the end of compound expressions.
+  // i.e. ({ 5;;; })
+  //           ^^ ignored
+  // This gets the index of the last Stmt before the trailing NullStmts. If
+  // this compound expression contains nothing but NullStmts, then we return
+  // the index of the last one.
+  unsigned getLastNonNullStmt() const {
+    assert(!body_empty() && "getLastNonNullStmt");
+    for (unsigned I = size(), E = 0; I != E; I--) {
+      if (!isa<NullStmt>(body_begin()[I - 1]))
+        return I - 1;
+    }
+    return size() - 1;
+  }
+
 public:
   static CompoundStmt *Create(const ASTContext &C, ArrayRef<Stmt *> Stmts,
                               SourceLocation LB, SourceLocation RB);
@@ -1279,9 +1294,20 @@
     return !body_empty() ? body_begin()[size() - 1] : nullptr;
   }
 
-  void setLastStmt(Stmt *S) {
-    assert(!body_empty() && "setLastStmt");
-    body_begin()[size() - 1] = S;
+  // Replace the Stmt that would be the result of this compound expression with
+  // another Stmt.
+  void setStmtExpr(Stmt *S) {
+    assert(!body_empty() && "setStmtExpr");
+    unsigned ExprResult = getLastNonNullStmt();
+    assert(!isa<NullStmt>(body_begin()[ExprResult])
+        && "setStmtExpr but there is no non-NullStmt");
+    body_begin()[ExprResult] = S;
+  }
+
+  // Get the Stmt representing the result of this compound expression.
+  Stmt *getStmtExprResult() const {
+    unsigned ExprResult = getLastNonNullStmt();
+    return body_begin()[ExprResult];
   }
 
   using const_body_iterator = Stmt *const *;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to