forked from OSchip/llvm-project
Use is.constant intrinsic for __builtin_constant_p
Summary: A __builtin_constant_p may end up with a constant after inlining. Use the is.constant intrinsic if it's a variable that's in a context where it may resolve to a constant, e.g., an argument to a function after inlining. Reviewers: rsmith, shafik Subscribers: jfb, kristina, cfe-commits, nickdesaulniers, jyknight Differential Revision: https://reviews.llvm.org/D54355 llvm-svn: 347294
This commit is contained in:
parent
cabb36d38d
commit
107b0e9881
|
@ -901,10 +901,15 @@ public:
|
|||
|
||||
/// ConstantExpr - An expression that occurs in a constant context.
|
||||
class ConstantExpr : public FullExpr {
|
||||
public:
|
||||
ConstantExpr(Expr *subexpr)
|
||||
: FullExpr(ConstantExprClass, subexpr) {}
|
||||
|
||||
public:
|
||||
static ConstantExpr *Create(const ASTContext &Context, Expr *E) {
|
||||
assert(!isa<ConstantExpr>(E));
|
||||
return new (Context) ConstantExpr(E);
|
||||
}
|
||||
|
||||
/// Build an empty constant expression wrapper.
|
||||
explicit ConstantExpr(EmptyShell Empty)
|
||||
: FullExpr(ConstantExprClass, Empty) {}
|
||||
|
@ -3091,8 +3096,8 @@ inline Expr *Expr::IgnoreImpCasts() {
|
|||
while (true)
|
||||
if (ImplicitCastExpr *ice = dyn_cast<ImplicitCastExpr>(e))
|
||||
e = ice->getSubExpr();
|
||||
else if (ConstantExpr *ce = dyn_cast<ConstantExpr>(e))
|
||||
e = ce->getSubExpr();
|
||||
else if (FullExpr *fe = dyn_cast<FullExpr>(e))
|
||||
e = fe->getSubExpr();
|
||||
else
|
||||
break;
|
||||
return e;
|
||||
|
|
|
@ -6375,7 +6375,7 @@ ExpectedStmt ASTNodeImporter::VisitConstantExpr(ConstantExpr *E) {
|
|||
Expr *ToSubExpr;
|
||||
std::tie(ToSubExpr) = *Imp;
|
||||
|
||||
return new (Importer.getToContext()) ConstantExpr(ToSubExpr);
|
||||
return ConstantExpr::Create(Importer.getToContext(), ToSubExpr);
|
||||
}
|
||||
|
||||
ExpectedStmt ASTNodeImporter::VisitParenExpr(ParenExpr *E) {
|
||||
|
|
|
@ -2594,8 +2594,8 @@ Expr *Expr::IgnoreParenCasts() {
|
|||
E = NTTP->getReplacement();
|
||||
continue;
|
||||
}
|
||||
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(E)) {
|
||||
E = CE->getSubExpr();
|
||||
if (FullExpr *FE = dyn_cast<FullExpr>(E)) {
|
||||
E = FE->getSubExpr();
|
||||
continue;
|
||||
}
|
||||
return E;
|
||||
|
@ -2619,8 +2619,8 @@ Expr *Expr::IgnoreCasts() {
|
|||
E = NTTP->getReplacement();
|
||||
continue;
|
||||
}
|
||||
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(E)) {
|
||||
E = CE->getSubExpr();
|
||||
if (FullExpr *FE = dyn_cast<FullExpr>(E)) {
|
||||
E = FE->getSubExpr();
|
||||
continue;
|
||||
}
|
||||
return E;
|
||||
|
@ -2648,8 +2648,8 @@ Expr *Expr::IgnoreParenLValueCasts() {
|
|||
= dyn_cast<SubstNonTypeTemplateParmExpr>(E)) {
|
||||
E = NTTP->getReplacement();
|
||||
continue;
|
||||
} else if (ConstantExpr *CE = dyn_cast<ConstantExpr>(E)) {
|
||||
E = CE->getSubExpr();
|
||||
} else if (FullExpr *FE = dyn_cast<FullExpr>(E)) {
|
||||
E = FE->getSubExpr();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
@ -2920,6 +2920,12 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef,
|
|||
|
||||
break;
|
||||
}
|
||||
case ConstantExprClass: {
|
||||
// FIXME: We should be able to return "true" here, but it can lead to extra
|
||||
// error messages. E.g. in Sema/array-init.c.
|
||||
const Expr *Exp = cast<ConstantExpr>(this)->getSubExpr();
|
||||
return Exp->isConstantInitializer(Ctx, false, Culprit);
|
||||
}
|
||||
case CompoundLiteralExprClass: {
|
||||
// This handles gcc's extension that allows global initializers like
|
||||
// "struct x {int x;} x = (struct x) {};".
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "clang/AST/TypeLoc.h"
|
||||
#include "clang/Basic/Builtins.h"
|
||||
#include "clang/Basic/TargetInfo.h"
|
||||
#include "llvm/Support/SaveAndRestore.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
|
@ -721,6 +722,10 @@ namespace {
|
|||
/// Whether or not we're currently speculatively evaluating.
|
||||
bool IsSpeculativelyEvaluating;
|
||||
|
||||
/// Whether or not we're in a context where the front end requires a
|
||||
/// constant value.
|
||||
bool InConstantContext;
|
||||
|
||||
enum EvaluationMode {
|
||||
/// Evaluate as a constant expression. Stop if we find that the expression
|
||||
/// is not a constant expression.
|
||||
|
@ -782,7 +787,7 @@ namespace {
|
|||
EvaluatingDecl((const ValueDecl *)nullptr),
|
||||
EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false),
|
||||
HasFoldFailureDiagnostic(false), IsSpeculativelyEvaluating(false),
|
||||
EvalMode(Mode) {}
|
||||
InConstantContext(false), EvalMode(Mode) {}
|
||||
|
||||
void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {
|
||||
EvaluatingDecl = Base;
|
||||
|
@ -7348,6 +7353,8 @@ public:
|
|||
// Visitor Methods
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
bool VisitConstantExpr(const ConstantExpr *E);
|
||||
|
||||
bool VisitIntegerLiteral(const IntegerLiteral *E) {
|
||||
return Success(E->getValue(), E);
|
||||
}
|
||||
|
@ -8088,6 +8095,11 @@ static bool tryEvaluateBuiltinObjectSize(const Expr *E, unsigned Type,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool IntExprEvaluator::VisitConstantExpr(const ConstantExpr *E) {
|
||||
llvm::SaveAndRestore<bool> InConstantContext(Info.InConstantContext, true);
|
||||
return ExprEvaluatorBaseTy::VisitConstantExpr(E);
|
||||
}
|
||||
|
||||
bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) {
|
||||
if (unsigned BuiltinOp = E->getBuiltinCallee())
|
||||
return VisitBuiltinCallExpr(E, BuiltinOp);
|
||||
|
@ -8175,8 +8187,20 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
|
|||
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);
|
||||
auto ArgTy = Arg->IgnoreImplicit()->getType();
|
||||
if (!Info.InConstantContext && !Arg->HasSideEffects(Info.Ctx) &&
|
||||
!ArgTy->isAggregateType() && !ArgTy->isPointerType()) {
|
||||
// We can delay calculation of __builtin_constant_p until after
|
||||
// inlining. 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:
|
||||
|
@ -10746,6 +10770,14 @@ static bool FastEvaluateAsRValue(const Expr *Exp, Expr::EvalResult &Result,
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool EvaluateAsRValue(const Expr *E, Expr::EvalResult &Result,
|
||||
const ASTContext &Ctx, EvalInfo &Info) {
|
||||
bool IsConst;
|
||||
if (FastEvaluateAsRValue(E, Result, Ctx, IsConst))
|
||||
return IsConst;
|
||||
|
||||
return EvaluateAsRValue(Info, E, Result.Val);
|
||||
}
|
||||
|
||||
/// EvaluateAsRValue - Return true if this is a constant which we can fold using
|
||||
/// any crazy technique (that has nothing to do with language standards) that
|
||||
|
@ -10753,12 +10785,8 @@ static bool FastEvaluateAsRValue(const Expr *Exp, Expr::EvalResult &Result,
|
|||
/// in Result. If this expression is a glvalue, an lvalue-to-rvalue conversion
|
||||
/// will be applied to the result.
|
||||
bool Expr::EvaluateAsRValue(EvalResult &Result, const ASTContext &Ctx) const {
|
||||
bool IsConst;
|
||||
if (FastEvaluateAsRValue(this, Result, Ctx, IsConst))
|
||||
return IsConst;
|
||||
|
||||
EvalInfo Info(Ctx, Result, EvalInfo::EM_IgnoreSideEffects);
|
||||
return ::EvaluateAsRValue(Info, this, Result.Val);
|
||||
return ::EvaluateAsRValue(this, Result, Ctx, Info);
|
||||
}
|
||||
|
||||
bool Expr::EvaluateAsBooleanCondition(bool &Result,
|
||||
|
@ -10878,35 +10906,40 @@ bool Expr::isEvaluatable(const ASTContext &Ctx, SideEffectsKind SEK) const {
|
|||
|
||||
APSInt Expr::EvaluateKnownConstInt(const ASTContext &Ctx,
|
||||
SmallVectorImpl<PartialDiagnosticAt> *Diag) const {
|
||||
EvalResult EvalResult;
|
||||
EvalResult.Diag = Diag;
|
||||
bool Result = EvaluateAsRValue(EvalResult, Ctx);
|
||||
EvalResult EVResult;
|
||||
EVResult.Diag = Diag;
|
||||
EvalInfo Info(Ctx, EVResult, EvalInfo::EM_IgnoreSideEffects);
|
||||
Info.InConstantContext = true;
|
||||
|
||||
bool Result = ::EvaluateAsRValue(this, EVResult, Ctx, Info);
|
||||
(void)Result;
|
||||
assert(Result && "Could not evaluate expression");
|
||||
assert(EvalResult.Val.isInt() && "Expression did not evaluate to integer");
|
||||
assert(EVResult.Val.isInt() && "Expression did not evaluate to integer");
|
||||
|
||||
return EvalResult.Val.getInt();
|
||||
return EVResult.Val.getInt();
|
||||
}
|
||||
|
||||
APSInt Expr::EvaluateKnownConstIntCheckOverflow(
|
||||
const ASTContext &Ctx, SmallVectorImpl<PartialDiagnosticAt> *Diag) const {
|
||||
EvalResult EvalResult;
|
||||
EvalResult.Diag = Diag;
|
||||
EvalInfo Info(Ctx, EvalResult, EvalInfo::EM_EvaluateForOverflow);
|
||||
bool Result = ::EvaluateAsRValue(Info, this, EvalResult.Val);
|
||||
EvalResult EVResult;
|
||||
EVResult.Diag = Diag;
|
||||
EvalInfo Info(Ctx, EVResult, EvalInfo::EM_EvaluateForOverflow);
|
||||
Info.InConstantContext = true;
|
||||
|
||||
bool Result = ::EvaluateAsRValue(Info, this, EVResult.Val);
|
||||
(void)Result;
|
||||
assert(Result && "Could not evaluate expression");
|
||||
assert(EvalResult.Val.isInt() && "Expression did not evaluate to integer");
|
||||
assert(EVResult.Val.isInt() && "Expression did not evaluate to integer");
|
||||
|
||||
return EvalResult.Val.getInt();
|
||||
return EVResult.Val.getInt();
|
||||
}
|
||||
|
||||
void Expr::EvaluateForOverflow(const ASTContext &Ctx) const {
|
||||
bool IsConst;
|
||||
EvalResult EvalResult;
|
||||
if (!FastEvaluateAsRValue(this, EvalResult, Ctx, IsConst)) {
|
||||
EvalInfo Info(Ctx, EvalResult, EvalInfo::EM_EvaluateForOverflow);
|
||||
(void)::EvaluateAsRValue(Info, this, EvalResult.Val);
|
||||
EvalResult EVResult;
|
||||
if (!FastEvaluateAsRValue(this, EVResult, Ctx, IsConst)) {
|
||||
EvalInfo Info(Ctx, EVResult, EvalInfo::EM_EvaluateForOverflow);
|
||||
(void)::EvaluateAsRValue(Info, this, EVResult.Val);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10959,7 +10992,11 @@ static ICEDiag Worst(ICEDiag A, ICEDiag B) { return A.Kind >= B.Kind ? A : B; }
|
|||
|
||||
static ICEDiag CheckEvalInICE(const Expr* E, const ASTContext &Ctx) {
|
||||
Expr::EvalResult EVResult;
|
||||
if (!E->EvaluateAsRValue(EVResult, Ctx) || EVResult.HasSideEffects ||
|
||||
Expr::EvalStatus Status;
|
||||
EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression);
|
||||
|
||||
Info.InConstantContext = true;
|
||||
if (!::EvaluateAsRValue(E, EVResult, Ctx, Info) || EVResult.HasSideEffects ||
|
||||
!EVResult.Val.isInt())
|
||||
return ICEDiag(IK_NotICE, E->getBeginLoc());
|
||||
|
||||
|
|
|
@ -1926,6 +1926,26 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
|
|||
case Builtin::BI__builtin_rotateright64:
|
||||
return emitRotate(E, true);
|
||||
|
||||
case Builtin::BI__builtin_constant_p: {
|
||||
llvm::Type *ResultType = ConvertType(E->getType());
|
||||
if (CGM.getCodeGenOpts().OptimizationLevel == 0)
|
||||
// At -O0, we don't perform inlining, so we don't need to delay the
|
||||
// processing.
|
||||
return RValue::get(ConstantInt::get(ResultType, 0));
|
||||
if (auto *DRE = dyn_cast<DeclRefExpr>(E->getArg(0)->IgnoreImplicit())) {
|
||||
auto DREType = DRE->getType();
|
||||
if (DREType->isAggregateType() || DREType->isFunctionType())
|
||||
return RValue::get(ConstantInt::get(ResultType, 0));
|
||||
}
|
||||
Value *ArgValue = EmitScalarExpr(E->getArg(0));
|
||||
llvm::Type *ArgType = ArgValue->getType();
|
||||
|
||||
Value *F = CGM.getIntrinsic(Intrinsic::is_constant, ArgType);
|
||||
Value *Result = Builder.CreateCall(F, ArgValue);
|
||||
if (Result->getType() != ResultType)
|
||||
Result = Builder.CreateIntCast(Result, ResultType, /*isSigned*/true);
|
||||
return RValue::get(Result);
|
||||
}
|
||||
case Builtin::BI__builtin_object_size: {
|
||||
unsigned Type =
|
||||
E->getArg(1)->EvaluateKnownConstInt(getContext()).getZExtValue();
|
||||
|
|
|
@ -13861,6 +13861,8 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
|
|||
ExprResult Converted = PerformContextuallyConvertToBool(AssertExpr);
|
||||
if (Converted.isInvalid())
|
||||
Failed = true;
|
||||
else
|
||||
Converted = ConstantExpr::Create(Context, Converted.get());
|
||||
|
||||
llvm::APSInt Cond;
|
||||
if (!Failed && VerifyIntegerConstantExpression(Converted.get(), &Cond,
|
||||
|
|
|
@ -5795,6 +5795,8 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
|
|||
? VK_RValue
|
||||
: VK_LValue;
|
||||
|
||||
if (isFileScope)
|
||||
LiteralExpr = ConstantExpr::Create(Context, LiteralExpr);
|
||||
Expr *E = new (Context) CompoundLiteralExpr(LParenLoc, TInfo, literalType,
|
||||
VK, LiteralExpr, isFileScope);
|
||||
if (isFileScope) {
|
||||
|
@ -5803,7 +5805,6 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
|
|||
!literalType->isDependentType()) // C99 6.5.2.5p3
|
||||
if (CheckForConstantInitializer(LiteralExpr, literalType))
|
||||
return ExprError();
|
||||
E = new (Context) ConstantExpr(E);
|
||||
} else if (literalType.getAddressSpace() != LangAS::opencl_private &&
|
||||
literalType.getAddressSpace() != LangAS::Default) {
|
||||
// Embedded-C extensions to C99 6.5.2.5:
|
||||
|
@ -14167,12 +14168,15 @@ Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result,
|
|||
return ExprError();
|
||||
}
|
||||
|
||||
if (!isa<ConstantExpr>(E))
|
||||
E = ConstantExpr::Create(Context, E);
|
||||
|
||||
// Circumvent ICE checking in C++11 to avoid evaluating the expression twice
|
||||
// in the non-ICE case.
|
||||
if (!getLangOpts().CPlusPlus11 && E->isIntegerConstantExpr(Context)) {
|
||||
if (Result)
|
||||
*Result = E->EvaluateKnownConstIntCheckOverflow(Context);
|
||||
return new (Context) ConstantExpr(E);
|
||||
return E;
|
||||
}
|
||||
|
||||
Expr::EvalResult EvalResult;
|
||||
|
@ -14190,7 +14194,7 @@ Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result,
|
|||
if (Folded && getLangOpts().CPlusPlus11 && Notes.empty()) {
|
||||
if (Result)
|
||||
*Result = EvalResult.Val.getInt();
|
||||
return new (Context) ConstantExpr(E);
|
||||
return E;
|
||||
}
|
||||
|
||||
// If our only note is the usual "invalid subexpression" note, just point
|
||||
|
@ -14218,7 +14222,7 @@ Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result,
|
|||
|
||||
if (Result)
|
||||
*Result = EvalResult.Val.getInt();
|
||||
return new (Context) ConstantExpr(E);
|
||||
return E;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -5469,7 +5469,7 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From,
|
|||
|
||||
if (Notes.empty()) {
|
||||
// It's a constant expression.
|
||||
return new (S.Context) ConstantExpr(Result.get());
|
||||
return ConstantExpr::Create(S.Context, Result.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -178,6 +178,8 @@ getDeducedParameterFromExpr(TemplateDeductionInfo &Info, Expr *E) {
|
|||
while (true) {
|
||||
if (ImplicitCastExpr *IC = dyn_cast<ImplicitCastExpr>(E))
|
||||
E = IC->getSubExpr();
|
||||
else if (ConstantExpr *CE = dyn_cast<ConstantExpr>(E))
|
||||
E = CE->getSubExpr();
|
||||
else if (SubstNonTypeTemplateParmExpr *Subst =
|
||||
dyn_cast<SubstNonTypeTemplateParmExpr>(E))
|
||||
E = Subst->getReplacement();
|
||||
|
@ -5225,6 +5227,8 @@ MarkUsedTemplateParameters(ASTContext &Ctx,
|
|||
while (true) {
|
||||
if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E))
|
||||
E = ICE->getSubExpr();
|
||||
else if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(E))
|
||||
E = CE->getSubExpr();
|
||||
else if (const SubstNonTypeTemplateParmExpr *Subst =
|
||||
dyn_cast<SubstNonTypeTemplateParmExpr>(E))
|
||||
E = Subst->getReplacement();
|
||||
|
|
|
@ -2233,10 +2233,6 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM,
|
|||
T = Context.getConstantArrayType(T, ConstVal, ASM, Quals);
|
||||
}
|
||||
|
||||
if (ArraySize && !CurContext->isFunctionOrMethod())
|
||||
// A file-scoped array must have a constant array size.
|
||||
ArraySize = new (Context) ConstantExpr(ArraySize);
|
||||
|
||||
// OpenCL v1.2 s6.9.d: variable length arrays are not supported.
|
||||
if (getLangOpts().OpenCL && T->isVariableArrayType()) {
|
||||
Diag(Loc, diag::err_opencl_vla);
|
||||
|
|
|
@ -1283,9 +1283,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
|||
break;
|
||||
|
||||
case Expr::ConstantExprClass:
|
||||
// Handled due to it being a wrapper class.
|
||||
break;
|
||||
|
||||
case Stmt::ExprWithCleanupsClass:
|
||||
// Handled due to fully linearised CFG.
|
||||
break;
|
||||
|
|
|
@ -70,14 +70,14 @@ void test_constant_p() {
|
|||
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(i + 42) == 0); // expected-warning {{UNKNOWN}}
|
||||
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}}
|
||||
}
|
||||
|
|
|
@ -122,6 +122,14 @@ int test16() {
|
|||
__builtin_constant_p(1, 2); // expected-error {{too many arguments}}
|
||||
}
|
||||
|
||||
// __builtin_constant_p cannot resolve non-constants 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};
|
||||
|
@ -161,6 +169,7 @@ void test17() {
|
|||
F(&test17_d);
|
||||
F((struct Aggregate){0, 1});
|
||||
F((IntVector){0, 1, 2, 3});
|
||||
F(test17);
|
||||
|
||||
// Ensure that a technique used in glibc is handled correctly.
|
||||
#define OPT(...) (__builtin_constant_p(__VA_ARGS__) && strlen(__VA_ARGS__) < 4)
|
||||
|
|
|
@ -36,8 +36,8 @@ namespace brace_initializers {
|
|||
|
||||
POD p = (POD){1, 2};
|
||||
// CHECK-NOT: CXXBindTemporaryExpr {{.*}} 'brace_initializers::POD'
|
||||
// CHECK: ConstantExpr {{.*}} 'brace_initializers::POD'
|
||||
// CHECK-NEXT: CompoundLiteralExpr {{.*}} 'brace_initializers::POD'
|
||||
// CHECK: CompoundLiteralExpr {{.*}} 'brace_initializers::POD'
|
||||
// CHECK-NEXT: ConstantExpr {{.*}} 'brace_initializers::POD'
|
||||
// CHECK-NEXT: InitListExpr {{.*}} 'brace_initializers::POD'
|
||||
// CHECK-NEXT: IntegerLiteral {{.*}} 1{{$}}
|
||||
// CHECK-NEXT: IntegerLiteral {{.*}} 2{{$}}
|
||||
|
|
Loading…
Reference in New Issue