Skip to content

Commit b1e511b

Browse files
committed
Ignore trailing NullStmts in StmtExprs for GCC compatibility.
Ignore trailing NullStmts in compound expressions when determining the result type and value. This is to match the GCC behavior which ignores semicolons at the end of compound expressions. Patch by Dominic Ferreira. llvm-svn: 365498
1 parent d955573 commit b1e511b

File tree

9 files changed

+124
-44
lines changed

9 files changed

+124
-44
lines changed

clang/include/clang/AST/Stmt.h

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,11 +1349,6 @@ class CompoundStmt final : public Stmt,
13491349
return !body_empty() ? body_begin()[size() - 1] : nullptr;
13501350
}
13511351

1352-
void setLastStmt(Stmt *S) {
1353-
assert(!body_empty() && "setLastStmt");
1354-
body_begin()[size() - 1] = S;
1355-
}
1356-
13571352
using const_body_iterator = Stmt *const *;
13581353
using body_const_range = llvm::iterator_range<const_body_iterator>;
13591354

@@ -1396,6 +1391,26 @@ class CompoundStmt final : public Stmt,
13961391
return const_reverse_body_iterator(body_begin());
13971392
}
13981393

1394+
// Get the Stmt that StmtExpr would consider to be the result of this
1395+
// compound statement. This is used by StmtExpr to properly emulate the GCC
1396+
// compound expression extension, which ignores trailing NullStmts when
1397+
// getting the result of the expression.
1398+
// i.e. ({ 5;;; })
1399+
// ^^ ignored
1400+
// If we don't find something that isn't a NullStmt, just return the last
1401+
// Stmt.
1402+
Stmt *getStmtExprResult() {
1403+
for (auto *B : llvm::reverse(body())) {
1404+
if (!isa<NullStmt>(B))
1405+
return B;
1406+
}
1407+
return body_back();
1408+
}
1409+
1410+
const Stmt *getStmtExprResult() const {
1411+
return const_cast<CompoundStmt *>(this)->getStmtExprResult();
1412+
}
1413+
13991414
SourceLocation getBeginLoc() const { return CompoundStmtBits.LBraceLoc; }
14001415
SourceLocation getEndLoc() const { return RBraceLoc; }
14011416

clang/lib/CodeGen/CGStmt.cpp

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -381,44 +381,48 @@ CodeGenFunction::EmitCompoundStmtWithoutScope(const CompoundStmt &S,
381381
bool GetLast,
382382
AggValueSlot AggSlot) {
383383

384-
for (CompoundStmt::const_body_iterator I = S.body_begin(),
385-
E = S.body_end()-GetLast; I != E; ++I)
386-
EmitStmt(*I);
384+
const Stmt *ExprResult = S.getStmtExprResult();
385+
assert((!GetLast || (GetLast && ExprResult)) &&
386+
"If GetLast is true then the CompoundStmt must have a StmtExprResult");
387387

388388
Address RetAlloca = Address::invalid();
389-
if (GetLast) {
390-
// We have to special case labels here. They are statements, but when put
391-
// at the end of a statement expression, they yield the value of their
392-
// subexpression. Handle this by walking through all labels we encounter,
393-
// emitting them before we evaluate the subexpr.
394-
// Similar issues arise for attributed statements.
395-
const Stmt *LastStmt = S.body_back();
396-
while (!isa<Expr>(LastStmt)) {
397-
if (const auto *LS = dyn_cast<LabelStmt>(LastStmt)) {
398-
EmitLabel(LS->getDecl());
399-
LastStmt = LS->getSubStmt();
400-
} else if (const auto *AS = dyn_cast<AttributedStmt>(LastStmt)) {
401-
// FIXME: Update this if we ever have attributes that affect the
402-
// semantics of an expression.
403-
LastStmt = AS->getSubStmt();
404-
} else {
405-
llvm_unreachable("unknown value statement");
389+
390+
for (auto *CurStmt : S.body()) {
391+
if (GetLast && ExprResult == CurStmt) {
392+
// We have to special case labels here. They are statements, but when put
393+
// at the end of a statement expression, they yield the value of their
394+
// subexpression. Handle this by walking through all labels we encounter,
395+
// emitting them before we evaluate the subexpr.
396+
// Similar issues arise for attributed statements.
397+
while (!isa<Expr>(ExprResult)) {
398+
if (const auto *LS = dyn_cast<LabelStmt>(ExprResult)) {
399+
EmitLabel(LS->getDecl());
400+
ExprResult = LS->getSubStmt();
401+
} else if (const auto *AS = dyn_cast<AttributedStmt>(ExprResult)) {
402+
// FIXME: Update this if we ever have attributes that affect the
403+
// semantics of an expression.
404+
ExprResult = AS->getSubStmt();
405+
} else {
406+
llvm_unreachable("unknown value statement");
407+
}
406408
}
407-
}
408409

409-
EnsureInsertPoint();
410+
EnsureInsertPoint();
410411

411-
const Expr *E = cast<Expr>(LastStmt);
412-
QualType ExprTy = E->getType();
413-
if (hasAggregateEvaluationKind(ExprTy)) {
414-
EmitAggExpr(E, AggSlot);
412+
const Expr *E = cast<Expr>(ExprResult);
413+
QualType ExprTy = E->getType();
414+
if (hasAggregateEvaluationKind(ExprTy)) {
415+
EmitAggExpr(E, AggSlot);
416+
} else {
417+
// We can't return an RValue here because there might be cleanups at
418+
// the end of the StmtExpr. Because of that, we have to emit the result
419+
// here into a temporary alloca.
420+
RetAlloca = CreateMemTemp(ExprTy);
421+
EmitAnyExprToMem(E, RetAlloca, Qualifiers(),
422+
/*IsInit*/ false);
423+
}
415424
} else {
416-
// We can't return an RValue here because there might be cleanups at
417-
// the end of the StmtExpr. Because of that, we have to emit the result
418-
// here into a temporary alloca.
419-
RetAlloca = CreateMemTemp(ExprTy);
420-
EmitAnyExprToMem(E, RetAlloca, Qualifiers(),
421-
/*IsInit*/ false);
425+
EmitStmt(CurStmt);
422426
}
423427
}
424428

clang/lib/Parse/ParseStmt.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -972,10 +972,16 @@ bool Parser::ConsumeNullStmt(StmtVector &Stmts) {
972972
StmtResult Parser::handleExprStmt(ExprResult E, ParsedStmtContext StmtCtx) {
973973
bool IsStmtExprResult = false;
974974
if ((StmtCtx & ParsedStmtContext::InStmtExpr) != ParsedStmtContext()) {
975-
// Look ahead to see if the next two tokens close the statement expression;
976-
// if so, this expression statement is the last statement in a
977-
// statment expression.
978-
IsStmtExprResult = Tok.is(tok::r_brace) && NextToken().is(tok::r_paren);
975+
// For GCC compatibility we skip past NullStmts.
976+
unsigned LookAhead = 0;
977+
while (GetLookAheadToken(LookAhead).is(tok::semi)) {
978+
++LookAhead;
979+
}
980+
// Then look to see if the next two tokens close the statement expression;
981+
// if so, this expression statement is the last statement in a statment
982+
// expression.
983+
IsStmtExprResult = GetLookAheadToken(LookAhead).is(tok::r_brace) &&
984+
GetLookAheadToken(LookAhead + 1).is(tok::r_paren);
979985
}
980986

981987
if (IsStmtExprResult)

clang/lib/Sema/SemaExpr.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13432,7 +13432,9 @@ Sema::ActOnStmtExpr(SourceLocation LPLoc, Stmt *SubStmt,
1343213432
QualType Ty = Context.VoidTy;
1343313433
bool StmtExprMayBindToTemp = false;
1343413434
if (!Compound->body_empty()) {
13435-
if (const auto *LastStmt = dyn_cast<ValueStmt>(Compound->body_back())) {
13435+
// For GCC compatibility we get the last Stmt excluding trailing NullStmts.
13436+
if (const auto *LastStmt =
13437+
dyn_cast<ValueStmt>(Compound->getStmtExprResult())) {
1343613438
if (const Expr *Value = LastStmt->getExprStmt()) {
1343713439
StmtExprMayBindToTemp = true;
1343813440
Ty = Value->getType();

clang/lib/Sema/TreeTransform.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6612,13 +6612,13 @@ TreeTransform<Derived>::TransformCompoundStmt(CompoundStmt *S,
66126612
bool IsStmtExpr) {
66136613
Sema::CompoundScopeRAII CompoundScope(getSema());
66146614

6615+
const Stmt *ExprResult = S->getStmtExprResult();
66156616
bool SubStmtInvalid = false;
66166617
bool SubStmtChanged = false;
66176618
SmallVector<Stmt*, 8> Statements;
66186619
for (auto *B : S->body()) {
66196620
StmtResult Result = getDerived().TransformStmt(
6620-
B,
6621-
IsStmtExpr && B == S->body_back() ? SDK_StmtExprResult : SDK_Discarded);
6621+
B, IsStmtExpr && B == ExprResult ? SDK_StmtExprResult : SDK_Discarded);
66226622

66236623
if (Result.isInvalid()) {
66246624
// Immediately fail if this was a DeclStmt, since it's very

clang/test/AST/ast-dump-stmt.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,4 +372,14 @@ void TestMiscStmts(void) {
372372
// CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} <col:13> 'int' 10
373373
// CHECK-NEXT: ImplicitCastExpr
374374
// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:17> 'int' lvalue Var 0x{{[^ ]*}} 'a' 'int'
375+
({int a = 10; a;;; });
376+
// CHECK-NEXT: StmtExpr 0x{{[^ ]*}} <line:[[@LINE-1]]:3, col:23> 'int'
377+
// CHECK-NEXT: CompoundStmt
378+
// CHECK-NEXT: DeclStmt
379+
// CHECK-NEXT: VarDecl 0x{{[^ ]*}} <col:5, col:13> col:9 used a 'int' cinit
380+
// CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} <col:13> 'int' 10
381+
// CHECK-NEXT: ImplicitCastExpr
382+
// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:17> 'int' lvalue Var 0x{{[^ ]*}} 'a' 'int'
383+
// CHECK-NEXT: NullStmt
384+
// CHECK-NEXT: NullStmt
375385
}

clang/test/CodeGen/exprs.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,13 @@ void f18() {
196196
}
197197
// CHECK-LABEL: define void @f18()
198198
// CHECK: call i32 @returns_int()
199+
200+
// Ensure the right stmt is returned
201+
int f19() {
202+
return ({ 3;;4;; });
203+
}
204+
// CHECK-LABEL: define i32 @f19()
205+
// CHECK: [[T:%.*]] = alloca i32
206+
// CHECK: store i32 4, i32* [[T]]
207+
// CHECK: [[L:%.*]] = load i32, i32* [[T]]
208+
// CHECK: ret i32 [[L]]

clang/test/Sema/statements.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,21 @@ void test_pr22849() {
119119
SIZE = sizeof(({unsigned long __ptr; __ptr;}))
120120
};
121121
}
122+
123+
// GCC ignores empty statements at the end of compound expressions where the
124+
// result type is concerned.
125+
void test13() {
126+
int a;
127+
a = ({ 1; });
128+
a = ({1;; });
129+
a = ({int x = 1; (void)x; }); // expected-error {{assigning to 'int' from incompatible type 'void'}}
130+
a = ({int x = 1; (void)x;; }); // expected-error {{assigning to 'int' from incompatible type 'void'}}
131+
}
132+
133+
void test14() { return ({}); }
134+
void test15() {
135+
return ({;;;; });
136+
}
137+
void test16() {
138+
return ({test:;; });
139+
}

clang/test/SemaCXX/statements.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,18 @@ void test5() {
3737

3838
struct MMX_t {};
3939
void test6() { __asm__("" : "=m"(*(MMX_t *)0)); }
40+
41+
template <typename T>
42+
T test7(T v) {
43+
return ({ // expected-warning{{use of GNU statement expression extension}}
44+
T a = v;
45+
a;
46+
;
47+
;
48+
});
49+
}
50+
51+
void test8() {
52+
int a = test7(1);
53+
double b = test7(2.0);
54+
}

0 commit comments

Comments
 (0)