Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -2413,6 +2413,11 @@ /// evaluate side-effects within its arguments. bool isUnevaluatedBuiltinCall(const ASTContext &Ctx) const; + // Used by __builtin_constant_p() to indicate that its evaluation can be + // delayed until after inlining. + void setCanDelayEvaluation(bool V) { CallExprBits.CanDelayEvaluation = V; } + bool getCanDelayEvaluation() const { return CallExprBits.CanDelayEvaluation; } + /// getCallReturnType - Get the return type of the call expr. This is not /// always the type of the expr itself, if the return type is a reference /// type. Index: include/clang/AST/Stmt.h =================================================================== --- include/clang/AST/Stmt.h +++ include/clang/AST/Stmt.h @@ -213,6 +213,7 @@ unsigned : NumExprBits; unsigned NumPreArgs : 1; + unsigned CanDelayEvaluation : 1; }; class ExprWithCleanupsBitfields { Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -4100,6 +4100,7 @@ void MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T); void MarkDeclarationsReferencedInExpr(Expr *E, bool SkipLocalVariables = false); + void MaybeMarkBuiltinConstantPCannotDelayEvaluation(Expr *E); /// Try to recover by turning the given expression into a /// call. Returns true if recovery was attempted or an error was Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -1213,6 +1213,7 @@ } CallExprBits.NumPreArgs = NumPreArgs; + CallExprBits.CanDelayEvaluation = true; RParenLoc = rparenloc; } Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -8165,8 +8165,19 @@ return Success(Val.countLeadingZeros(), E); } - case Builtin::BI__builtin_constant_p: - return Success(EvaluateBuiltinConstantP(Info.Ctx, E->getArg(0)), E); + case Builtin::BI__builtin_constant_p: { + auto Arg = E->getArg(0); + if (EvaluateBuiltinConstantP(Info.Ctx, Arg)) + return Success(true, E); + Arg = Arg->IgnoreParenCasts(); + if (isa(Arg->IgnoreParenCasts()) && + !Arg->HasSideEffects(Info.Ctx) && E->getCanDelayEvaluation()) { + // Note: This diagnostic won't be shown to the user. + Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr); + return false; + } + return Success(false, E); + } case Builtin::BI__builtin_ctz: case Builtin::BI__builtin_ctzl: Index: lib/CodeGen/CGBuiltin.cpp =================================================================== --- lib/CodeGen/CGBuiltin.cpp +++ lib/CodeGen/CGBuiltin.cpp @@ -1768,6 +1768,12 @@ case Builtin::BI__builtin_rotateright64: return emitRotate(E, true); + case Builtin::BI__builtin_constant_p: + if (CGM.getCodeGenOpts().OptimizationLevel == 0 || + hasAggregateEvaluationKind(E->getArg(0)->getType())) + return RValue::get(ConstantInt::get(Int32Ty, 0)); + return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::is_constant)); + case Builtin::BI__builtin_object_size: { unsigned Type = E->getArg(1)->EvaluateKnownConstInt(getContext()).getZExtValue(); Index: lib/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp +++ lib/Parse/ParseDecl.cpp @@ -4516,6 +4516,9 @@ AssignedVal = ParseConstantExpression(); if (AssignedVal.isInvalid()) SkipUntil(tok::comma, tok::r_brace, StopBeforeMatch); + else + Actions.MaybeMarkBuiltinConstantPCannotDelayEvaluation( + AssignedVal.get()); } // Install the enumerator constant into EnumDecl. @@ -6642,6 +6645,8 @@ T.consumeClose(); MaybeParseCXX11Attributes(DS.getAttributes()); + if (NumElements.get() && !Actions.CurContext->isFunctionOrMethod()) + Actions.MaybeMarkBuiltinConstantPCannotDelayEvaluation(NumElements.get()); // Remember that we parsed a array type, and remember its features. D.AddTypeInfo( Index: lib/Parse/ParseInit.cpp =================================================================== --- lib/Parse/ParseInit.cpp +++ lib/Parse/ParseInit.cpp @@ -401,7 +401,9 @@ if (!getLangOpts().CPlusPlus) Diag(LBraceLoc, diag::ext_gnu_empty_initializer); // Match the '}'. - return Actions.ActOnInitList(LBraceLoc, None, ConsumeBrace()); + auto Init = Actions.ActOnInitList(LBraceLoc, None, ConsumeBrace()); + Actions.MaybeMarkBuiltinConstantPCannotDelayEvaluation(Init.get()); + return Init; } // Enter an appropriate expression evaluation context for an initializer list. @@ -469,9 +471,12 @@ bool closed = !T.consumeClose(); - if (InitExprsOk && closed) - return Actions.ActOnInitList(LBraceLoc, InitExprs, - T.getCloseLocation()); + if (InitExprsOk && closed) { + auto Init = Actions.ActOnInitList(LBraceLoc, InitExprs, + T.getCloseLocation()); + Actions.MaybeMarkBuiltinConstantPCannotDelayEvaluation(Init.get()); + return Init; + } return ExprError(); // an error occurred. } Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -15607,6 +15607,49 @@ EvaluatedExprMarker(*this, SkipLocalVariables).Visit(E); } +namespace { + // Helper class that marks all calls to __builtin_constant_p that cannot be + // evaluated after inlining. + // + // GCC honors __builtin_constant_p() after inlining. Clang performs inlining + // too late to do that in the front-end. Therefore, clang converts bcp calls + // that can't be evaluated to a constant to an intrinsic so that the + // middle-end can determine whether or not it's constant after inlining. But + // this won't work for initializer lists and other constructs. For those, we + // need to be conservative in our analysis. So if we cannot determine that a + // value is constant before LLVM IR generation, then we resolve the bcp call + // to "false". + struct BuiltinConstantPCannotDelayEvaluation : + public EvaluatedExprVisitor { + typedef EvaluatedExprVisitor + Inherited; + + BuiltinConstantPCannotDelayEvaluation(Sema &S) + : Inherited(S.Context) { } + + void VisitCallExpr(CallExpr *E) { + auto DC = E->getDirectCallee(); + if (!DC || DC->getBuiltinID() != Builtin::BI__builtin_constant_p) + return; + E->setCanDelayEvaluation(false); + } + + void VisitInitListExpr(InitListExpr *E) { + for (auto Elem : *E) + Visit(Elem); + } + }; +} + +// Mark all calls to __builtin_constant_p that cannot have their evaluation +// delayed. I.e., we won't generate an llvm.is.constant() intrinsic if the front +// end is unable to ensure the argument is constant. E.g., when a +// __builtin_constant_p() is used in an InitListExpr or subscript of a global +// VarDecl. +void Sema::MaybeMarkBuiltinConstantPCannotDelayEvaluation(Expr *E) { + BuiltinConstantPCannotDelayEvaluation(*this).Visit(E); +} + /// Emit a diagnostic that describes an effect on the run-time behavior /// of the program being compiled. /// Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -679,6 +679,7 @@ void ASTStmtReader::VisitCallExpr(CallExpr *E) { VisitExpr(E); E->setNumArgs(Record.getContext(), Record.readInt()); + E->setCanDelayEvaluation(Record.readInt()); E->setRParenLoc(ReadSourceLocation()); E->setCallee(Record.readSubExpr()); for (unsigned I = 0, N = E->getNumArgs(); I != N; ++I) Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -590,6 +590,7 @@ void ASTStmtWriter::VisitCallExpr(CallExpr *E) { VisitExpr(E); Record.push_back(E->getNumArgs()); + Record.push_back(E->getCanDelayEvaluation()); Record.AddSourceLocation(E->getRParenLoc()); Record.AddStmt(E->getCallee()); for (CallExpr::arg_iterator Arg = E->arg_begin(), ArgEnd = E->arg_end(); Index: test/Analysis/builtin-functions.cpp =================================================================== --- test/Analysis/builtin-functions.cpp +++ test/Analysis/builtin-functions.cpp @@ -70,14 +70,14 @@ const int j = 2; constexpr int k = 3; clang_analyzer_eval(__builtin_constant_p(42) == 1); // expected-warning {{TRUE}} - clang_analyzer_eval(__builtin_constant_p(i) == 0); // expected-warning {{TRUE}} + clang_analyzer_eval(__builtin_constant_p(i) == 0); // expected-warning {{UNKNOWN}} clang_analyzer_eval(__builtin_constant_p(j) == 1); // expected-warning {{TRUE}} clang_analyzer_eval(__builtin_constant_p(k) == 1); // expected-warning {{TRUE}} clang_analyzer_eval(__builtin_constant_p(i + 42) == 0); // expected-warning {{TRUE}} clang_analyzer_eval(__builtin_constant_p(j + 42) == 1); // expected-warning {{TRUE}} clang_analyzer_eval(__builtin_constant_p(k + 42) == 1); // expected-warning {{TRUE}} clang_analyzer_eval(__builtin_constant_p(" ") == 1); // expected-warning {{TRUE}} - clang_analyzer_eval(__builtin_constant_p(test_constant_p) == 0); // expected-warning {{TRUE}} + clang_analyzer_eval(__builtin_constant_p(test_constant_p) == 0); // expected-warning {{UNKNOWN}} clang_analyzer_eval(__builtin_constant_p(k - 3) == 0); // expected-warning {{FALSE}} clang_analyzer_eval(__builtin_constant_p(k - 3) == 1); // expected-warning {{TRUE}} } Index: test/Sema/builtins.c =================================================================== --- test/Sema/builtins.c +++ test/Sema/builtins.c @@ -122,6 +122,14 @@ __builtin_constant_p(1, 2); // expected-error {{too many arguments}} } +// __builtin_constant_p cannot resolve non-constants such as a file scoped array. +int expr; +char y[__builtin_constant_p(expr) ? -1 : 1]; // no warning, the builtin is false. + +// no warning, the builtin is false. +struct foo { int a; }; +struct foo x = (struct foo) { __builtin_constant_p(42) ? 37 : 927 }; + const int test17_n = 0; const char test17_c[] = {1, 2, 3, 0}; const char test17_d[] = {1, 2, 3, 4}; @@ -168,14 +176,17 @@ // a builtin. ASSERT(OPT("abc")); ASSERT(!OPT("abcd")); + // In these cases, the strlen is non-constant, but the __builtin_constant_p - // is 0: the array size is not an ICE but is foldable. - ASSERT(!OPT(test17_c)); // expected-warning {{folded}} + // is 0: the array size is not an ICE. + ASSERT(!OPT(test17_c)); // no warning expected + ASSERT(!OPT((char*)test17_c)); // no warning expected + ASSERT(!OPT(test17_d)); // no warning expected + ASSERT(!OPT((char*)test17_d)); // no warning expected + + // These are foldable. ASSERT(!OPT(&test17_c[0])); // expected-warning {{folded}} - ASSERT(!OPT((char*)test17_c)); // expected-warning {{folded}} - ASSERT(!OPT(test17_d)); // expected-warning {{folded}} ASSERT(!OPT(&test17_d[0])); // expected-warning {{folded}} - ASSERT(!OPT((char*)test17_d)); // expected-warning {{folded}} #undef OPT #undef T @@ -287,3 +298,22 @@ memcpy(buf, src, 11); // expected-warning{{'memcpy' will always overflow; destination buffer has size 10, but size argument is 11}} my_memcpy(buf, src, 11); // expected-warning{{'__builtin___memcpy_chk' will always overflow; destination buffer has size 10, but size argument is 11}} } + +size_t test24(int a) { + char x[__builtin_constant_p(a) ? -1 : 1]; // no warning expected + return strlen(x); +} + +__attribute__((always_inline)) +size_t test25(int a) { + char x[__builtin_constant_p(a) ? 1 : -1]; // no warning expected + return strlen(x); +} + +size_t test26() { + return test25(2); +} + +void f(int n) { + enum E { a = __builtin_constant_p(n) }; // ok, a == 0, not an error because a's value is non-constant +}