diff --git a/clang/AST/StmtPrinter.cpp b/clang/AST/StmtPrinter.cpp index 1d6fbcc56b72..bdbbc12b1853 100644 --- a/clang/AST/StmtPrinter.cpp +++ b/clang/AST/StmtPrinter.cpp @@ -487,6 +487,15 @@ void StmtPrinter::VisitTypesCompatibleExpr(TypesCompatibleExpr *Node) { OS << Node->getArgType2().getAsString() << ")"; } +void StmtPrinter::VisitChooseExpr(ChooseExpr *Node) { + OS << "__builtin_choose_expr("; + PrintExpr(Node->getCond()); + OS << ","; + PrintExpr(Node->getLHS()); + OS << ","; + PrintExpr(Node->getRHS()); + OS << ")"; +} // C++ diff --git a/clang/Parse/ParseExpr.cpp b/clang/Parse/ParseExpr.cpp index b93234204464..75414f7b076a 100644 --- a/clang/Parse/ParseExpr.cpp +++ b/clang/Parse/ParseExpr.cpp @@ -805,19 +805,35 @@ Parser::ExprResult Parser::ParseBuiltinPrimaryExpression() { } } break; - case tok::kw___builtin_choose_expr: - Res = ParseAssignmentExpression(); - + case tok::kw___builtin_choose_expr: { + ExprResult Cond = ParseAssignmentExpression(); + if (Cond.isInvalid) { + SkipUntil(tok::r_paren); + return Cond; + } if (ExpectAndConsume(tok::comma, diag::err_expected_comma, "",tok::r_paren)) return ExprResult(true); - Res = ParseAssignmentExpression(); - + ExprResult Expr1 = ParseAssignmentExpression(); + if (Expr1.isInvalid) { + SkipUntil(tok::r_paren); + return Expr1; + } if (ExpectAndConsume(tok::comma, diag::err_expected_comma, "",tok::r_paren)) return ExprResult(true); - Res = ParseAssignmentExpression(); - break; + ExprResult Expr2 = ParseAssignmentExpression(); + if (Expr2.isInvalid) { + SkipUntil(tok::r_paren); + return Expr2; + } + if (Tok.getKind() != tok::r_paren) { + Diag(Tok, diag::err_expected_rparen); + return ExprResult(true); + } + return Actions.ParseChooseExpr(StartLoc, Cond.Val, Expr1.Val, Expr2.Val, + ConsumeParen()); + } case tok::kw___builtin_types_compatible_p: TypeTy *Ty1 = ParseTypeName(); diff --git a/clang/Sema/Sema.h b/clang/Sema/Sema.h index ff2e5d1fd049..b6d47971f0b5 100644 --- a/clang/Sema/Sema.h +++ b/clang/Sema/Sema.h @@ -285,6 +285,11 @@ public: virtual ExprResult ParseTypesCompatibleExpr(SourceLocation BuiltinLoc, TypeTy *arg1, TypeTy *arg2, SourceLocation RPLoc); + + // __builtin_choose_expr(constExpr, expr1, expr2) + virtual ExprResult ParseChooseExpr(SourceLocation BuiltinLoc, + ExprTy *cond, ExprTy *expr1, ExprTy *expr2, + SourceLocation RPLoc); /// ParseCXXCasts - Parse {dynamic,static,reinterpret,const}_cast's. virtual ExprResult ParseCXXCasts(SourceLocation OpLoc, tok::TokenKind Kind, diff --git a/clang/Sema/SemaExpr.cpp b/clang/Sema/SemaExpr.cpp index 2f072f12ab0b..7a4200632ce7 100644 --- a/clang/Sema/SemaExpr.cpp +++ b/clang/Sema/SemaExpr.cpp @@ -1584,3 +1584,25 @@ Sema::ExprResult Sema::ParseTypesCompatibleExpr(SourceLocation BuiltinLoc, return new TypesCompatibleExpr(Context.IntTy, BuiltinLoc, argT1, argT2, RPLoc); } +Sema::ExprResult Sema::ParseChooseExpr(SourceLocation BuiltinLoc, ExprTy *cond, + ExprTy *expr1, ExprTy *expr2, + SourceLocation RPLoc) { + Expr *CondExpr = static_cast(cond); + Expr *LHSExpr = static_cast(expr1); + Expr *RHSExpr = static_cast(expr2); + + assert((CondExpr && LHSExpr && RHSExpr) && "Missing type argument(s)"); + + // The conditional expression is required to be a constant expression. + llvm::APSInt condEval(32); + SourceLocation ExpLoc; + if (!CondExpr->isIntegerConstantExpr(condEval, Context, &ExpLoc)) + return Diag(ExpLoc, diag::err_typecheck_choose_expr_requires_constant, + CondExpr->getSourceRange()); + + // If the condition is > zero, then the AST type is the same as the LSHExpr. + QualType resType = condEval.getZExtValue() ? LHSExpr->getType() : + RHSExpr->getType(); + return new ChooseExpr(BuiltinLoc, CondExpr, LHSExpr, RHSExpr, resType, RPLoc); +} + diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index c9649e3875fc..e7d770a2247f 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -776,6 +776,35 @@ public: static bool classof(const TypesCompatibleExpr *) { return true; } }; +/// ChooseExpr - GNU builtin-in function __builtin_choose_expr. +/// This AST node is similar to the conditional operator (?:) in C, with +/// the following exceptions: +/// - the test expression much be a constant expression. +/// - the expression returned has it's type unaltered by promotion rules. +/// - does not evaluate the expression that was not chosen. +class ChooseExpr : public Expr { + Expr *Cond, *LHS, *RHS; // First, second, and third arguments. + SourceLocation BuiltinLoc, RParenLoc; +public: + ChooseExpr(SourceLocation BLoc, Expr *cond, Expr *lhs, Expr *rhs, QualType t, + SourceLocation RP) + : Expr(ChooseExprClass, t), + Cond(cond), LHS(lhs), RHS(rhs), BuiltinLoc(BLoc), RParenLoc(RP) {} + + Expr *getCond() const { return Cond; } + Expr *getLHS() const { return LHS; } + Expr *getRHS() const { return RHS; } + + virtual SourceRange getSourceRange() const { + return SourceRange(BuiltinLoc, RParenLoc); + } + virtual void visit(StmtVisitor &Visitor); + static bool classof(const Stmt *T) { + return T->getStmtClass() == ChooseExprClass; + } + static bool classof(const ChooseExpr *) { return true; } +}; + } // end namespace clang #endif diff --git a/clang/include/clang/AST/StmtNodes.def b/clang/include/clang/AST/StmtNodes.def index c610f143fca5..ac1ccacc9d6d 100644 --- a/clang/include/clang/AST/StmtNodes.def +++ b/clang/include/clang/AST/StmtNodes.def @@ -68,11 +68,12 @@ STMT(49, OCUVectorElementExpr , Expr) STMT(50, AddrLabelExpr , Expr) STMT(51, StmtExpr , Expr) STMT(52, TypesCompatibleExpr , Expr) +STMT(53, ChooseExpr , Expr) // C++ Expressions. -STMT(53, CXXCastExpr , Expr) -STMT(54, CXXBoolLiteralExpr , Expr) -LAST_EXPR(54) +STMT(54, CXXCastExpr , Expr) +STMT(55, CXXBoolLiteralExpr , Expr) +LAST_EXPR(55) #undef STMT #undef FIRST_STMT diff --git a/clang/include/clang/Basic/DiagnosticKinds.def b/clang/include/clang/Basic/DiagnosticKinds.def index acf82964145d..1ede4fd81548 100644 --- a/clang/include/clang/Basic/DiagnosticKinds.def +++ b/clang/include/clang/Basic/DiagnosticKinds.def @@ -652,7 +652,8 @@ DIAG(err_typecheck_cond_incompatible_operands, ERROR, "incompatible operand types ('%0' and '%1')") DIAG(ext_typecheck_cond_incompatible_pointers, WARNING, "pointer type mismatch ('%0' and '%1')") - +DIAG(err_typecheck_choose_expr_requires_constant, ERROR, + "'__builtin_choose_expr' requires a constant expression") DIAG(warn_unused_expr, WARNING, "expression result unused") diff --git a/clang/include/clang/Parse/Action.h b/clang/include/clang/Parse/Action.h index 2c3ec162e46a..088345cb16d2 100644 --- a/clang/include/clang/Parse/Action.h +++ b/clang/include/clang/Parse/Action.h @@ -381,6 +381,12 @@ public: SourceLocation RPLoc) { return 0; } + // __builtin_choose_expr(constExpr, expr1, expr2) + virtual ExprResult ParseChooseExpr(SourceLocation BuiltinLoc, + ExprTy *cond, ExprTy *expr1, ExprTy *expr2, + SourceLocation RPLoc) { + return 0; + } //===------------------------- C++ Expressions --------------------------===// diff --git a/clang/test/Parser/builtin_types_compatible.c b/clang/test/Parser/builtin_types_compatible.c index 5dbe0733c237..7b066c8e9ade 100644 --- a/clang/test/Parser/builtin_types_compatible.c +++ b/clang/test/Parser/builtin_types_compatible.c @@ -31,5 +31,14 @@ static void test() func_choose(a); func_choose(b); func_choose(d); + + int c; + struct xx { int a; } x, y; + + c = __builtin_choose_expr(a+3-7, b, x); // expected-error{{'__builtin_choose_expr' requires a constant expression}} + c = __builtin_choose_expr(0, b, x); // expected-error{{incompatible types assigning 'struct xx' to 'int'}} + c = __builtin_choose_expr(5+3-7, b, x); + y = __builtin_choose_expr(4+3-7, b, x); + }