forked from OSchip/llvm-project
For P0732R2, P1907R1: ensure that template parameter objects don't refer
to disallowed objects or have non-constant destruction.
This commit is contained in:
parent
aaa8b44d19
commit
7b3515880c
|
@ -711,13 +711,26 @@ public:
|
||||||
ArrayRef<const Expr*> Args,
|
ArrayRef<const Expr*> Args,
|
||||||
const Expr *This = nullptr) const;
|
const Expr *This = nullptr) const;
|
||||||
|
|
||||||
/// Indicates how the constant expression will be used.
|
enum class ConstantExprKind {
|
||||||
enum ConstExprUsage { EvaluateForCodeGen, EvaluateForMangling };
|
/// An integer constant expression (an array bound, enumerator, case value,
|
||||||
|
/// bit-field width, or similar) or similar.
|
||||||
|
Normal,
|
||||||
|
/// A non-class template argument. Such a value is only used for mangling,
|
||||||
|
/// not for code generation, so can refer to dllimported functions.
|
||||||
|
NonClassTemplateArgument,
|
||||||
|
/// A class template argument. Such a value is used for code generation.
|
||||||
|
ClassTemplateArgument,
|
||||||
|
/// An immediate invocation. The destruction of the end result of this
|
||||||
|
/// evaluation is not part of the evaluation, but all other temporaries
|
||||||
|
/// are destroyed.
|
||||||
|
ImmediateInvocation,
|
||||||
|
};
|
||||||
|
|
||||||
/// Evaluate an expression that is required to be a constant expression.
|
/// Evaluate an expression that is required to be a constant expression. Does
|
||||||
bool EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage,
|
/// not check the syntactic constraints for C and C++98 constant expressions.
|
||||||
const ASTContext &Ctx,
|
bool EvaluateAsConstantExpr(
|
||||||
bool InPlace = false) const;
|
EvalResult &Result, const ASTContext &Ctx,
|
||||||
|
ConstantExprKind Kind = ConstantExprKind::Normal) const;
|
||||||
|
|
||||||
/// If the current Expr is a pointer, this will try to statically
|
/// If the current Expr is a pointer, this will try to statically
|
||||||
/// determine the number of bytes available where the pointer is pointing.
|
/// determine the number of bytes available where the pointer is pointing.
|
||||||
|
@ -971,6 +984,8 @@ static_assert(llvm::PointerLikeTypeTraits<Expr *>::NumLowBitsAvailable <=
|
||||||
llvm::detail::ConstantLog2<alignof(Expr)>::value,
|
llvm::detail::ConstantLog2<alignof(Expr)>::value,
|
||||||
"PointerLikeTypeTraits<Expr*> assumes too much alignment.");
|
"PointerLikeTypeTraits<Expr*> assumes too much alignment.");
|
||||||
|
|
||||||
|
using ConstantExprKind = Expr::ConstantExprKind;
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Wrapper Expressions.
|
// Wrapper Expressions.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -1997,6 +2012,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
static StringRef getIdentKindName(IdentKind IK);
|
static StringRef getIdentKindName(IdentKind IK);
|
||||||
|
StringRef getIdentKindName() const {
|
||||||
|
return getIdentKindName(getIdentKind());
|
||||||
|
}
|
||||||
|
|
||||||
static std::string ComputeName(IdentKind IK, const Decl *CurrentDecl);
|
static std::string ComputeName(IdentKind IK, const Decl *CurrentDecl);
|
||||||
|
|
||||||
SourceLocation getBeginLoc() const { return getLocation(); }
|
SourceLocation getBeginLoc() const { return getLocation(); }
|
||||||
|
|
|
@ -234,6 +234,12 @@ def err_seh___finally_block : Error<
|
||||||
// Sema && AST
|
// Sema && AST
|
||||||
def note_invalid_subexpr_in_const_expr : Note<
|
def note_invalid_subexpr_in_const_expr : Note<
|
||||||
"subexpression not valid in a constant expression">;
|
"subexpression not valid in a constant expression">;
|
||||||
|
def note_constexpr_invalid_template_arg : Note<
|
||||||
|
"%select{pointer|reference}0 to %select{|subobject of }1"
|
||||||
|
"%select{type_info object|string literal|temporary object|"
|
||||||
|
"predefined '%3' variable}2 is not allowed in a template argument">;
|
||||||
|
def err_constexpr_invalid_template_arg : Error<
|
||||||
|
note_constexpr_invalid_template_arg.Text>;
|
||||||
|
|
||||||
// Sema && Frontend
|
// Sema && Frontend
|
||||||
let CategoryName = "Inline Assembly Issue" in {
|
let CategoryName = "Inline Assembly Issue" in {
|
||||||
|
|
|
@ -54,6 +54,7 @@ namespace llvm {
|
||||||
namespace clang {
|
namespace clang {
|
||||||
// Casting operators.
|
// Casting operators.
|
||||||
using llvm::isa;
|
using llvm::isa;
|
||||||
|
using llvm::isa_and_nonnull;
|
||||||
using llvm::cast;
|
using llvm::cast;
|
||||||
using llvm::dyn_cast;
|
using llvm::dyn_cast;
|
||||||
using llvm::dyn_cast_or_null;
|
using llvm::dyn_cast_or_null;
|
||||||
|
|
|
@ -142,6 +142,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned getDiagID() const { return DiagID; }
|
unsigned getDiagID() const { return DiagID; }
|
||||||
|
void setDiagID(unsigned ID) { DiagID = ID; }
|
||||||
|
|
||||||
void Emit(const DiagnosticBuilder &DB) const {
|
void Emit(const DiagnosticBuilder &DB) const {
|
||||||
if (!DiagStorage)
|
if (!DiagStorage)
|
||||||
|
|
|
@ -183,6 +183,37 @@ namespace {
|
||||||
return E && E->getType()->isPointerType() && tryUnwrapAllocSizeCall(E);
|
return E && E->getType()->isPointerType() && tryUnwrapAllocSizeCall(E);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determines whether the given kind of constant expression is only ever
|
||||||
|
/// used for name mangling. If so, it's permitted to reference things that we
|
||||||
|
/// can't generate code for (in particular, dllimported functions).
|
||||||
|
static bool isForManglingOnly(ConstantExprKind Kind) {
|
||||||
|
switch (Kind) {
|
||||||
|
case ConstantExprKind::Normal:
|
||||||
|
case ConstantExprKind::ClassTemplateArgument:
|
||||||
|
case ConstantExprKind::ImmediateInvocation:
|
||||||
|
// Note that non-type template arguments of class type are emitted as
|
||||||
|
// template parameter objects.
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case ConstantExprKind::NonClassTemplateArgument:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
llvm_unreachable("unknown ConstantExprKind");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isTemplateArgument(ConstantExprKind Kind) {
|
||||||
|
switch (Kind) {
|
||||||
|
case ConstantExprKind::Normal:
|
||||||
|
case ConstantExprKind::ImmediateInvocation:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case ConstantExprKind::ClassTemplateArgument:
|
||||||
|
case ConstantExprKind::NonClassTemplateArgument:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
llvm_unreachable("unknown ConstantExprKind");
|
||||||
|
}
|
||||||
|
|
||||||
/// The bound to claim that an array of unknown bound has.
|
/// The bound to claim that an array of unknown bound has.
|
||||||
/// The value in MostDerivedArraySize is undefined in this case. So, set it
|
/// The value in MostDerivedArraySize is undefined in this case. So, set it
|
||||||
/// to an arbitrary value that's likely to loudly break things if it's used.
|
/// to an arbitrary value that's likely to loudly break things if it's used.
|
||||||
|
@ -2114,7 +2145,7 @@ using CheckedTemporaries =
|
||||||
static bool CheckEvaluationResult(CheckEvaluationResultKind CERK,
|
static bool CheckEvaluationResult(CheckEvaluationResultKind CERK,
|
||||||
EvalInfo &Info, SourceLocation DiagLoc,
|
EvalInfo &Info, SourceLocation DiagLoc,
|
||||||
QualType Type, const APValue &Value,
|
QualType Type, const APValue &Value,
|
||||||
Expr::ConstExprUsage Usage,
|
ConstantExprKind Kind,
|
||||||
SourceLocation SubobjectLoc,
|
SourceLocation SubobjectLoc,
|
||||||
CheckedTemporaries &CheckedTemps);
|
CheckedTemporaries &CheckedTemps);
|
||||||
|
|
||||||
|
@ -2123,21 +2154,48 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK,
|
||||||
/// can fold this expression, whether or not it's a constant expression.
|
/// can fold this expression, whether or not it's a constant expression.
|
||||||
static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
|
static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
|
||||||
QualType Type, const LValue &LVal,
|
QualType Type, const LValue &LVal,
|
||||||
Expr::ConstExprUsage Usage,
|
ConstantExprKind Kind,
|
||||||
CheckedTemporaries &CheckedTemps) {
|
CheckedTemporaries &CheckedTemps) {
|
||||||
bool IsReferenceType = Type->isReferenceType();
|
bool IsReferenceType = Type->isReferenceType();
|
||||||
|
|
||||||
APValue::LValueBase Base = LVal.getLValueBase();
|
APValue::LValueBase Base = LVal.getLValueBase();
|
||||||
const SubobjectDesignator &Designator = LVal.getLValueDesignator();
|
const SubobjectDesignator &Designator = LVal.getLValueDesignator();
|
||||||
|
|
||||||
if (auto *VD = LVal.getLValueBase().dyn_cast<const ValueDecl *>()) {
|
const Expr *BaseE = Base.dyn_cast<const Expr *>();
|
||||||
if (auto *FD = dyn_cast<FunctionDecl>(VD)) {
|
const ValueDecl *BaseVD = Base.dyn_cast<const ValueDecl*>();
|
||||||
if (FD->isConsteval()) {
|
|
||||||
Info.FFDiag(Loc, diag::note_consteval_address_accessible)
|
// Additional restrictions apply in a template argument. We only enforce the
|
||||||
<< !Type->isAnyPointerType();
|
// C++20 restrictions here; additional syntactic and semantic restrictions
|
||||||
Info.Note(FD->getLocation(), diag::note_declared_at);
|
// are applied elsewhere.
|
||||||
return false;
|
if (isTemplateArgument(Kind)) {
|
||||||
}
|
int InvalidBaseKind = -1;
|
||||||
|
StringRef Ident;
|
||||||
|
if (Base.is<TypeInfoLValue>())
|
||||||
|
InvalidBaseKind = 0;
|
||||||
|
else if (isa_and_nonnull<StringLiteral>(BaseE))
|
||||||
|
InvalidBaseKind = 1;
|
||||||
|
else if (isa_and_nonnull<MaterializeTemporaryExpr>(BaseE) ||
|
||||||
|
isa_and_nonnull<LifetimeExtendedTemporaryDecl>(BaseVD))
|
||||||
|
InvalidBaseKind = 2;
|
||||||
|
else if (auto *PE = dyn_cast_or_null<PredefinedExpr>(BaseE)) {
|
||||||
|
InvalidBaseKind = 3;
|
||||||
|
Ident = PE->getIdentKindName();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InvalidBaseKind != -1) {
|
||||||
|
Info.FFDiag(Loc, diag::note_constexpr_invalid_template_arg)
|
||||||
|
<< IsReferenceType << !Designator.Entries.empty() << InvalidBaseKind
|
||||||
|
<< Ident;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto *FD = dyn_cast_or_null<FunctionDecl>(BaseVD)) {
|
||||||
|
if (FD->isConsteval()) {
|
||||||
|
Info.FFDiag(Loc, diag::note_consteval_address_accessible)
|
||||||
|
<< !Type->isAnyPointerType();
|
||||||
|
Info.Note(FD->getLocation(), diag::note_declared_at);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2181,19 +2239,20 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>()) {
|
if (BaseVD) {
|
||||||
if (const VarDecl *Var = dyn_cast<const VarDecl>(VD)) {
|
if (const VarDecl *Var = dyn_cast<const VarDecl>(BaseVD)) {
|
||||||
// Check if this is a thread-local variable.
|
// Check if this is a thread-local variable.
|
||||||
if (Var->getTLSKind())
|
if (Var->getTLSKind())
|
||||||
// FIXME: Diagnostic!
|
// FIXME: Diagnostic!
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// A dllimport variable never acts like a constant.
|
// A dllimport variable never acts like a constant, unless we're
|
||||||
if (Usage == Expr::EvaluateForCodeGen && Var->hasAttr<DLLImportAttr>())
|
// evaluating a value for use only in name mangling.
|
||||||
|
if (!isForManglingOnly(Kind) && Var->hasAttr<DLLImportAttr>())
|
||||||
// FIXME: Diagnostic!
|
// FIXME: Diagnostic!
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (const auto *FD = dyn_cast<const FunctionDecl>(VD)) {
|
if (const auto *FD = dyn_cast<const FunctionDecl>(BaseVD)) {
|
||||||
// __declspec(dllimport) must be handled very carefully:
|
// __declspec(dllimport) must be handled very carefully:
|
||||||
// We must never initialize an expression with the thunk in C++.
|
// We must never initialize an expression with the thunk in C++.
|
||||||
// Doing otherwise would allow the same id-expression to yield
|
// Doing otherwise would allow the same id-expression to yield
|
||||||
|
@ -2204,13 +2263,13 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
|
||||||
// The C language has no notion of ODR; furthermore, it has no notion of
|
// The C language has no notion of ODR; furthermore, it has no notion of
|
||||||
// dynamic initialization. This means that we are permitted to
|
// dynamic initialization. This means that we are permitted to
|
||||||
// perform initialization with the address of the thunk.
|
// perform initialization with the address of the thunk.
|
||||||
if (Info.getLangOpts().CPlusPlus && Usage == Expr::EvaluateForCodeGen &&
|
if (Info.getLangOpts().CPlusPlus && !isForManglingOnly(Kind) &&
|
||||||
FD->hasAttr<DLLImportAttr>())
|
FD->hasAttr<DLLImportAttr>())
|
||||||
// FIXME: Diagnostic!
|
// FIXME: Diagnostic!
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (const auto *MTE = dyn_cast_or_null<MaterializeTemporaryExpr>(
|
} else if (const auto *MTE =
|
||||||
Base.dyn_cast<const Expr *>())) {
|
dyn_cast_or_null<MaterializeTemporaryExpr>(BaseE)) {
|
||||||
if (CheckedTemps.insert(MTE).second) {
|
if (CheckedTemps.insert(MTE).second) {
|
||||||
QualType TempType = getType(Base);
|
QualType TempType = getType(Base);
|
||||||
if (TempType.isDestructedType()) {
|
if (TempType.isDestructedType()) {
|
||||||
|
@ -2224,7 +2283,7 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
|
||||||
assert(V && "evasluation result refers to uninitialised temporary");
|
assert(V && "evasluation result refers to uninitialised temporary");
|
||||||
if (!CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression,
|
if (!CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression,
|
||||||
Info, MTE->getExprLoc(), TempType, *V,
|
Info, MTE->getExprLoc(), TempType, *V,
|
||||||
Usage, SourceLocation(), CheckedTemps))
|
Kind, SourceLocation(), CheckedTemps))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2243,9 +2302,8 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
|
||||||
|
|
||||||
// Does this refer one past the end of some object?
|
// Does this refer one past the end of some object?
|
||||||
if (!Designator.Invalid && Designator.isOnePastTheEnd()) {
|
if (!Designator.Invalid && Designator.isOnePastTheEnd()) {
|
||||||
const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>();
|
|
||||||
Info.FFDiag(Loc, diag::note_constexpr_past_end, 1)
|
Info.FFDiag(Loc, diag::note_constexpr_past_end, 1)
|
||||||
<< !Designator.Entries.empty() << !!VD << VD;
|
<< !Designator.Entries.empty() << !!BaseVD << BaseVD;
|
||||||
NoteLValueLocation(Info, Base);
|
NoteLValueLocation(Info, Base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2258,7 +2316,7 @@ static bool CheckMemberPointerConstantExpression(EvalInfo &Info,
|
||||||
SourceLocation Loc,
|
SourceLocation Loc,
|
||||||
QualType Type,
|
QualType Type,
|
||||||
const APValue &Value,
|
const APValue &Value,
|
||||||
Expr::ConstExprUsage Usage) {
|
ConstantExprKind Kind) {
|
||||||
const ValueDecl *Member = Value.getMemberPointerDecl();
|
const ValueDecl *Member = Value.getMemberPointerDecl();
|
||||||
const auto *FD = dyn_cast_or_null<CXXMethodDecl>(Member);
|
const auto *FD = dyn_cast_or_null<CXXMethodDecl>(Member);
|
||||||
if (!FD)
|
if (!FD)
|
||||||
|
@ -2268,7 +2326,7 @@ static bool CheckMemberPointerConstantExpression(EvalInfo &Info,
|
||||||
Info.Note(FD->getLocation(), diag::note_declared_at);
|
Info.Note(FD->getLocation(), diag::note_declared_at);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return Usage == Expr::EvaluateForMangling || FD->isVirtual() ||
|
return isForManglingOnly(Kind) || FD->isVirtual() ||
|
||||||
!FD->hasAttr<DLLImportAttr>();
|
!FD->hasAttr<DLLImportAttr>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2307,7 +2365,7 @@ static bool CheckLiteralType(EvalInfo &Info, const Expr *E,
|
||||||
static bool CheckEvaluationResult(CheckEvaluationResultKind CERK,
|
static bool CheckEvaluationResult(CheckEvaluationResultKind CERK,
|
||||||
EvalInfo &Info, SourceLocation DiagLoc,
|
EvalInfo &Info, SourceLocation DiagLoc,
|
||||||
QualType Type, const APValue &Value,
|
QualType Type, const APValue &Value,
|
||||||
Expr::ConstExprUsage Usage,
|
ConstantExprKind Kind,
|
||||||
SourceLocation SubobjectLoc,
|
SourceLocation SubobjectLoc,
|
||||||
CheckedTemporaries &CheckedTemps) {
|
CheckedTemporaries &CheckedTemps) {
|
||||||
if (!Value.hasValue()) {
|
if (!Value.hasValue()) {
|
||||||
|
@ -2330,20 +2388,20 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK,
|
||||||
QualType EltTy = Type->castAsArrayTypeUnsafe()->getElementType();
|
QualType EltTy = Type->castAsArrayTypeUnsafe()->getElementType();
|
||||||
for (unsigned I = 0, N = Value.getArrayInitializedElts(); I != N; ++I) {
|
for (unsigned I = 0, N = Value.getArrayInitializedElts(); I != N; ++I) {
|
||||||
if (!CheckEvaluationResult(CERK, Info, DiagLoc, EltTy,
|
if (!CheckEvaluationResult(CERK, Info, DiagLoc, EltTy,
|
||||||
Value.getArrayInitializedElt(I), Usage,
|
Value.getArrayInitializedElt(I), Kind,
|
||||||
SubobjectLoc, CheckedTemps))
|
SubobjectLoc, CheckedTemps))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!Value.hasArrayFiller())
|
if (!Value.hasArrayFiller())
|
||||||
return true;
|
return true;
|
||||||
return CheckEvaluationResult(CERK, Info, DiagLoc, EltTy,
|
return CheckEvaluationResult(CERK, Info, DiagLoc, EltTy,
|
||||||
Value.getArrayFiller(), Usage, SubobjectLoc,
|
Value.getArrayFiller(), Kind, SubobjectLoc,
|
||||||
CheckedTemps);
|
CheckedTemps);
|
||||||
}
|
}
|
||||||
if (Value.isUnion() && Value.getUnionField()) {
|
if (Value.isUnion() && Value.getUnionField()) {
|
||||||
return CheckEvaluationResult(
|
return CheckEvaluationResult(
|
||||||
CERK, Info, DiagLoc, Value.getUnionField()->getType(),
|
CERK, Info, DiagLoc, Value.getUnionField()->getType(),
|
||||||
Value.getUnionValue(), Usage, Value.getUnionField()->getLocation(),
|
Value.getUnionValue(), Kind, Value.getUnionField()->getLocation(),
|
||||||
CheckedTemps);
|
CheckedTemps);
|
||||||
}
|
}
|
||||||
if (Value.isStruct()) {
|
if (Value.isStruct()) {
|
||||||
|
@ -2352,7 +2410,7 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK,
|
||||||
unsigned BaseIndex = 0;
|
unsigned BaseIndex = 0;
|
||||||
for (const CXXBaseSpecifier &BS : CD->bases()) {
|
for (const CXXBaseSpecifier &BS : CD->bases()) {
|
||||||
if (!CheckEvaluationResult(CERK, Info, DiagLoc, BS.getType(),
|
if (!CheckEvaluationResult(CERK, Info, DiagLoc, BS.getType(),
|
||||||
Value.getStructBase(BaseIndex), Usage,
|
Value.getStructBase(BaseIndex), Kind,
|
||||||
BS.getBeginLoc(), CheckedTemps))
|
BS.getBeginLoc(), CheckedTemps))
|
||||||
return false;
|
return false;
|
||||||
++BaseIndex;
|
++BaseIndex;
|
||||||
|
@ -2364,7 +2422,7 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK,
|
||||||
|
|
||||||
if (!CheckEvaluationResult(CERK, Info, DiagLoc, I->getType(),
|
if (!CheckEvaluationResult(CERK, Info, DiagLoc, I->getType(),
|
||||||
Value.getStructField(I->getFieldIndex()),
|
Value.getStructField(I->getFieldIndex()),
|
||||||
Usage, I->getLocation(), CheckedTemps))
|
Kind, I->getLocation(), CheckedTemps))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2373,13 +2431,13 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK,
|
||||||
CERK == CheckEvaluationResultKind::ConstantExpression) {
|
CERK == CheckEvaluationResultKind::ConstantExpression) {
|
||||||
LValue LVal;
|
LValue LVal;
|
||||||
LVal.setFrom(Info.Ctx, Value);
|
LVal.setFrom(Info.Ctx, Value);
|
||||||
return CheckLValueConstantExpression(Info, DiagLoc, Type, LVal, Usage,
|
return CheckLValueConstantExpression(Info, DiagLoc, Type, LVal, Kind,
|
||||||
CheckedTemps);
|
CheckedTemps);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Value.isMemberPointer() &&
|
if (Value.isMemberPointer() &&
|
||||||
CERK == CheckEvaluationResultKind::ConstantExpression)
|
CERK == CheckEvaluationResultKind::ConstantExpression)
|
||||||
return CheckMemberPointerConstantExpression(Info, DiagLoc, Type, Value, Usage);
|
return CheckMemberPointerConstantExpression(Info, DiagLoc, Type, Value, Kind);
|
||||||
|
|
||||||
// Everything else is fine.
|
// Everything else is fine.
|
||||||
return true;
|
return true;
|
||||||
|
@ -2388,17 +2446,16 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK,
|
||||||
/// Check that this core constant expression value is a valid value for a
|
/// Check that this core constant expression value is a valid value for a
|
||||||
/// constant expression. If not, report an appropriate diagnostic. Does not
|
/// constant expression. If not, report an appropriate diagnostic. Does not
|
||||||
/// check that the expression is of literal type.
|
/// check that the expression is of literal type.
|
||||||
static bool
|
static bool CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc,
|
||||||
CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type,
|
QualType Type, const APValue &Value,
|
||||||
const APValue &Value,
|
ConstantExprKind Kind) {
|
||||||
Expr::ConstExprUsage Usage = Expr::EvaluateForCodeGen) {
|
|
||||||
// Nothing to check for a constant expression of type 'cv void'.
|
// Nothing to check for a constant expression of type 'cv void'.
|
||||||
if (Type->isVoidType())
|
if (Type->isVoidType())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
CheckedTemporaries CheckedTemps;
|
CheckedTemporaries CheckedTemps;
|
||||||
return CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression,
|
return CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression,
|
||||||
Info, DiagLoc, Type, Value, Usage,
|
Info, DiagLoc, Type, Value, Kind,
|
||||||
SourceLocation(), CheckedTemps);
|
SourceLocation(), CheckedTemps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2409,7 +2466,7 @@ static bool CheckFullyInitialized(EvalInfo &Info, SourceLocation DiagLoc,
|
||||||
CheckedTemporaries CheckedTemps;
|
CheckedTemporaries CheckedTemps;
|
||||||
return CheckEvaluationResult(
|
return CheckEvaluationResult(
|
||||||
CheckEvaluationResultKind::FullyInitialized, Info, DiagLoc, Type, Value,
|
CheckEvaluationResultKind::FullyInitialized, Info, DiagLoc, Type, Value,
|
||||||
Expr::EvaluateForCodeGen, SourceLocation(), CheckedTemps);
|
ConstantExprKind::Normal, SourceLocation(), CheckedTemps);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enforce C++2a [expr.const]/4.17, which disallows new-expressions unless
|
/// Enforce C++2a [expr.const]/4.17, which disallows new-expressions unless
|
||||||
|
@ -3212,6 +3269,13 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we're currently evaluating the initializer of this declaration, use that
|
||||||
|
// in-flight value.
|
||||||
|
if (Info.EvaluatingDecl == Base) {
|
||||||
|
Result = Info.EvaluatingDeclValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (isa<ParmVarDecl>(VD)) {
|
if (isa<ParmVarDecl>(VD)) {
|
||||||
// Assume parameters of a potential constant expression are usable in
|
// Assume parameters of a potential constant expression are usable in
|
||||||
// constant expressions.
|
// constant expressions.
|
||||||
|
@ -3261,14 +3325,6 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're currently evaluating the initializer of this declaration, use that
|
|
||||||
// in-flight value.
|
|
||||||
if (declaresSameEntity(Info.EvaluatingDecl.dyn_cast<const ValueDecl *>(),
|
|
||||||
VD)) {
|
|
||||||
Result = Info.EvaluatingDeclValue;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that we can fold the initializer. In C++, we will have already done
|
// Check that we can fold the initializer. In C++, we will have already done
|
||||||
// this in the cases where it matters for conformance.
|
// this in the cases where it matters for conformance.
|
||||||
if (!VD->evaluateValue()) {
|
if (!VD->evaluateValue()) {
|
||||||
|
@ -3470,26 +3526,20 @@ static bool lifetimeStartedInEvaluation(EvalInfo &Info,
|
||||||
if (Base.getCallIndex())
|
if (Base.getCallIndex())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
auto *Evaluating = Info.EvaluatingDecl.dyn_cast<const ValueDecl*>();
|
|
||||||
if (!Evaluating)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
auto *BaseD = Base.dyn_cast<const ValueDecl*>();
|
|
||||||
|
|
||||||
switch (Info.IsEvaluatingDecl) {
|
switch (Info.IsEvaluatingDecl) {
|
||||||
case EvalInfo::EvaluatingDeclKind::None:
|
case EvalInfo::EvaluatingDeclKind::None:
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case EvalInfo::EvaluatingDeclKind::Ctor:
|
case EvalInfo::EvaluatingDeclKind::Ctor:
|
||||||
// The variable whose initializer we're evaluating.
|
// The variable whose initializer we're evaluating.
|
||||||
if (BaseD)
|
if (Info.EvaluatingDecl == Base)
|
||||||
return declaresSameEntity(Evaluating, BaseD);
|
return true;
|
||||||
|
|
||||||
// A temporary lifetime-extended by the variable whose initializer we're
|
// A temporary lifetime-extended by the variable whose initializer we're
|
||||||
// evaluating.
|
// evaluating.
|
||||||
if (auto *BaseE = Base.dyn_cast<const Expr *>())
|
if (auto *BaseE = Base.dyn_cast<const Expr *>())
|
||||||
if (auto *BaseMTE = dyn_cast<MaterializeTemporaryExpr>(BaseE))
|
if (auto *BaseMTE = dyn_cast<MaterializeTemporaryExpr>(BaseE))
|
||||||
return declaresSameEntity(BaseMTE->getExtendingDecl(), Evaluating);
|
return Info.EvaluatingDecl == BaseMTE->getExtendingDecl();
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case EvalInfo::EvaluatingDeclKind::Dtor:
|
case EvalInfo::EvaluatingDeclKind::Dtor:
|
||||||
|
@ -3497,16 +3547,13 @@ static bool lifetimeStartedInEvaluation(EvalInfo &Info,
|
||||||
// [during constant destruction] the lifetime of a and its non-mutable
|
// [during constant destruction] the lifetime of a and its non-mutable
|
||||||
// subobjects (but not its mutable subobjects) [are] considered to start
|
// subobjects (but not its mutable subobjects) [are] considered to start
|
||||||
// within e.
|
// within e.
|
||||||
//
|
if (MutableSubobject || Base != Info.EvaluatingDecl)
|
||||||
|
return false;
|
||||||
// FIXME: We can meaningfully extend this to cover non-const objects, but
|
// FIXME: We can meaningfully extend this to cover non-const objects, but
|
||||||
// we will need special handling: we should be able to access only
|
// we will need special handling: we should be able to access only
|
||||||
// subobjects of such objects that are themselves declared const.
|
// subobjects of such objects that are themselves declared const.
|
||||||
if (!BaseD ||
|
QualType T = getType(Base);
|
||||||
!(BaseD->getType().isConstQualified() ||
|
return T.isConstQualified() || T->isReferenceType();
|
||||||
BaseD->getType()->isReferenceType()) ||
|
|
||||||
MutableSubobject)
|
|
||||||
return false;
|
|
||||||
return declaresSameEntity(Evaluating, BaseD);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm_unreachable("unknown evaluating decl kind");
|
llvm_unreachable("unknown evaluating decl kind");
|
||||||
|
@ -3958,12 +4005,10 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
|
||||||
APValue *BaseVal = nullptr;
|
APValue *BaseVal = nullptr;
|
||||||
QualType BaseType = getType(LVal.Base);
|
QualType BaseType = getType(LVal.Base);
|
||||||
|
|
||||||
if (const ConstantExpr *CE =
|
if (Info.getLangOpts().CPlusPlus14 && LVal.Base == Info.EvaluatingDecl &&
|
||||||
dyn_cast_or_null<ConstantExpr>(LVal.Base.dyn_cast<const Expr *>())) {
|
lifetimeStartedInEvaluation(Info, LVal.Base)) {
|
||||||
/// Nested immediate invocation have been previously removed so if we found
|
// This is the object whose initializer we're evaluating, so its lifetime
|
||||||
/// a ConstantExpr it can only be the EvaluatingDecl.
|
// started in the current evaluation.
|
||||||
assert(CE->isImmediateInvocation() && CE == Info.EvaluatingDecl);
|
|
||||||
(void)CE;
|
|
||||||
BaseVal = Info.EvaluatingDeclValue;
|
BaseVal = Info.EvaluatingDeclValue;
|
||||||
} else if (const ValueDecl *D = LVal.Base.dyn_cast<const ValueDecl *>()) {
|
} else if (const ValueDecl *D = LVal.Base.dyn_cast<const ValueDecl *>()) {
|
||||||
// Allow reading from a GUID declaration.
|
// Allow reading from a GUID declaration.
|
||||||
|
@ -12736,7 +12781,8 @@ bool RecordExprEvaluator::VisitBinCmp(const BinaryOperator *E) {
|
||||||
LV.set(VD);
|
LV.set(VD);
|
||||||
if (!handleLValueToRValueConversion(Info, E, E->getType(), LV, Result))
|
if (!handleLValueToRValueConversion(Info, E, E->getType(), LV, Result))
|
||||||
return false;
|
return false;
|
||||||
return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result);
|
return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result,
|
||||||
|
ConstantExprKind::Normal);
|
||||||
};
|
};
|
||||||
return EvaluateComparisonBinaryOperator(Info, E, OnSuccess, [&]() {
|
return EvaluateComparisonBinaryOperator(Info, E, OnSuccess, [&]() {
|
||||||
return ExprEvaluatorBaseTy::VisitBinCmp(E);
|
return ExprEvaluatorBaseTy::VisitBinCmp(E);
|
||||||
|
@ -14506,7 +14552,8 @@ static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check this core constant expression is a constant expression.
|
// Check this core constant expression is a constant expression.
|
||||||
return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result) &&
|
return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result,
|
||||||
|
ConstantExprKind::Normal) &&
|
||||||
CheckMemoryLeaks(Info);
|
CheckMemoryLeaks(Info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14661,15 +14708,36 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx,
|
||||||
Result.HasSideEffects ||
|
Result.HasSideEffects ||
|
||||||
!CheckLValueConstantExpression(Info, getExprLoc(),
|
!CheckLValueConstantExpression(Info, getExprLoc(),
|
||||||
Ctx.getLValueReferenceType(getType()), LV,
|
Ctx.getLValueReferenceType(getType()), LV,
|
||||||
Expr::EvaluateForCodeGen, CheckedTemps))
|
ConstantExprKind::Normal, CheckedTemps))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
LV.moveInto(Result.Val);
|
LV.moveInto(Result.Val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Expr::EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage,
|
static bool EvaluateDestruction(const ASTContext &Ctx, APValue::LValueBase Base,
|
||||||
const ASTContext &Ctx, bool InPlace) const {
|
APValue DestroyedValue, QualType Type,
|
||||||
|
SourceLocation Loc, Expr::EvalStatus &EStatus) {
|
||||||
|
EvalInfo Info(Ctx, EStatus, EvalInfo::EM_ConstantExpression);
|
||||||
|
Info.setEvaluatingDecl(Base, DestroyedValue,
|
||||||
|
EvalInfo::EvaluatingDeclKind::Dtor);
|
||||||
|
Info.InConstantContext = true;
|
||||||
|
|
||||||
|
LValue LVal;
|
||||||
|
LVal.set(Base);
|
||||||
|
|
||||||
|
if (!HandleDestruction(Info, Loc, Base, DestroyedValue, Type) ||
|
||||||
|
EStatus.HasSideEffects)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!Info.discardCleanups())
|
||||||
|
llvm_unreachable("Unhandled cleanup; missing full expression marker?");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx,
|
||||||
|
ConstantExprKind Kind) const {
|
||||||
assert(!isValueDependent() &&
|
assert(!isValueDependent() &&
|
||||||
"Expression evaluator can't be called on a dependent expression.");
|
"Expression evaluator can't be called on a dependent expression.");
|
||||||
|
|
||||||
|
@ -14677,22 +14745,44 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage,
|
||||||
EvalInfo Info(Ctx, Result, EM);
|
EvalInfo Info(Ctx, Result, EM);
|
||||||
Info.InConstantContext = true;
|
Info.InConstantContext = true;
|
||||||
|
|
||||||
if (InPlace) {
|
// The type of the object we're initializing is 'const T' for a class NTTP.
|
||||||
Info.setEvaluatingDecl(this, Result.Val);
|
QualType T = getType();
|
||||||
LValue LVal;
|
if (Kind == ConstantExprKind::ClassTemplateArgument)
|
||||||
LVal.set(this);
|
T.addConst();
|
||||||
if (!::EvaluateInPlace(Result.Val, Info, LVal, this) ||
|
|
||||||
Result.HasSideEffects)
|
// If we're evaluating a prvalue, fake up a MaterializeTemporaryExpr to
|
||||||
return false;
|
// represent the result of the evaluation. CheckConstantExpression ensures
|
||||||
} else if (!::Evaluate(Result.Val, Info, this) || Result.HasSideEffects)
|
// this doesn't escape.
|
||||||
|
MaterializeTemporaryExpr BaseMTE(T, const_cast<Expr*>(this), true);
|
||||||
|
APValue::LValueBase Base(&BaseMTE);
|
||||||
|
|
||||||
|
Info.setEvaluatingDecl(Base, Result.Val);
|
||||||
|
LValue LVal;
|
||||||
|
LVal.set(Base);
|
||||||
|
|
||||||
|
if (!::EvaluateInPlace(Result.Val, Info, LVal, this) || Result.HasSideEffects)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!Info.discardCleanups())
|
if (!Info.discardCleanups())
|
||||||
llvm_unreachable("Unhandled cleanup; missing full expression marker?");
|
llvm_unreachable("Unhandled cleanup; missing full expression marker?");
|
||||||
|
|
||||||
return CheckConstantExpression(Info, getExprLoc(), getStorageType(Ctx, this),
|
if (!CheckConstantExpression(Info, getExprLoc(), getStorageType(Ctx, this),
|
||||||
Result.Val, Usage) &&
|
Result.Val, Kind))
|
||||||
CheckMemoryLeaks(Info);
|
return false;
|
||||||
|
if (!CheckMemoryLeaks(Info))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If this is a class template argument, it's required to have constant
|
||||||
|
// destruction too.
|
||||||
|
if (Kind == ConstantExprKind::ClassTemplateArgument &&
|
||||||
|
(!EvaluateDestruction(Ctx, Base, Result.Val, T, getBeginLoc(), Result) ||
|
||||||
|
Result.HasSideEffects)) {
|
||||||
|
// FIXME: Prefix a note to indicate that the problem is lack of constant
|
||||||
|
// destruction.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
|
bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
|
||||||
|
@ -14741,7 +14831,8 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
|
||||||
if (!Info.discardCleanups())
|
if (!Info.discardCleanups())
|
||||||
llvm_unreachable("Unhandled cleanup; missing full expression marker?");
|
llvm_unreachable("Unhandled cleanup; missing full expression marker?");
|
||||||
}
|
}
|
||||||
return CheckConstantExpression(Info, DeclLoc, DeclTy, Value) &&
|
return CheckConstantExpression(Info, DeclLoc, DeclTy, Value,
|
||||||
|
ConstantExprKind::Normal) &&
|
||||||
CheckMemoryLeaks(Info);
|
CheckMemoryLeaks(Info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14759,24 +14850,11 @@ bool VarDecl::evaluateDestruction(
|
||||||
else if (!getDefaultInitValue(getType(), DestroyedValue))
|
else if (!getDefaultInitValue(getType(), DestroyedValue))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
EvalInfo Info(getASTContext(), EStatus, EvalInfo::EM_ConstantExpression);
|
if (!EvaluateDestruction(getASTContext(), this, std::move(DestroyedValue),
|
||||||
Info.setEvaluatingDecl(this, DestroyedValue,
|
getType(), getLocation(), EStatus) ||
|
||||||
EvalInfo::EvaluatingDeclKind::Dtor);
|
|
||||||
Info.InConstantContext = true;
|
|
||||||
|
|
||||||
SourceLocation DeclLoc = getLocation();
|
|
||||||
QualType DeclTy = getType();
|
|
||||||
|
|
||||||
LValue LVal;
|
|
||||||
LVal.set(this);
|
|
||||||
|
|
||||||
if (!HandleDestruction(Info, DeclLoc, LVal.Base, DestroyedValue, DeclTy) ||
|
|
||||||
EStatus.HasSideEffects)
|
EStatus.HasSideEffects)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!Info.discardCleanups())
|
|
||||||
llvm_unreachable("Unhandled cleanup; missing full expression marker?");
|
|
||||||
|
|
||||||
ensureEvaluatedStmt()->HasConstantDestruction = true;
|
ensureEvaluatedStmt()->HasConstantDestruction = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1814,8 +1814,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
|
||||||
SmallVector<PartialDiagnosticAt, 8> Notes;
|
SmallVector<PartialDiagnosticAt, 8> Notes;
|
||||||
Expr::EvalResult Eval;
|
Expr::EvalResult Eval;
|
||||||
Eval.Diag = &Notes;
|
Eval.Diag = &Notes;
|
||||||
if ((!ProbArg->EvaluateAsConstantExpr(Eval, Expr::EvaluateForCodeGen,
|
if ((!ProbArg->EvaluateAsConstantExpr(Eval, Context)) ||
|
||||||
Context)) ||
|
|
||||||
!Eval.Val.isFloat()) {
|
!Eval.Val.isFloat()) {
|
||||||
Diag(ProbArg->getBeginLoc(), diag::err_probability_not_constant_float)
|
Diag(ProbArg->getBeginLoc(), diag::err_probability_not_constant_float)
|
||||||
<< ProbArg->getSourceRange();
|
<< ProbArg->getSourceRange();
|
||||||
|
@ -3295,8 +3294,7 @@ bool Sema::CheckAMDGCNBuiltinFunctionCall(unsigned BuiltinID,
|
||||||
ArgExpr = Arg.get();
|
ArgExpr = Arg.get();
|
||||||
Expr::EvalResult ArgResult1;
|
Expr::EvalResult ArgResult1;
|
||||||
// Check that sync scope is a constant literal
|
// Check that sync scope is a constant literal
|
||||||
if (!ArgExpr->EvaluateAsConstantExpr(ArgResult1, Expr::EvaluateForCodeGen,
|
if (!ArgExpr->EvaluateAsConstantExpr(ArgResult1, Context))
|
||||||
Context))
|
|
||||||
return Diag(ArgExpr->getExprLoc(), diag::err_expr_not_string_literal)
|
return Diag(ArgExpr->getExprLoc(), diag::err_expr_not_string_literal)
|
||||||
<< ArgExpr->getType();
|
<< ArgExpr->getType();
|
||||||
|
|
||||||
|
|
|
@ -16224,8 +16224,8 @@ static void EvaluateAndDiagnoseImmediateInvocation(
|
||||||
Expr::EvalResult Eval;
|
Expr::EvalResult Eval;
|
||||||
Eval.Diag = &Notes;
|
Eval.Diag = &Notes;
|
||||||
ConstantExpr *CE = Candidate.getPointer();
|
ConstantExpr *CE = Candidate.getPointer();
|
||||||
bool Result = CE->EvaluateAsConstantExpr(Eval, Expr::EvaluateForCodeGen,
|
bool Result = CE->EvaluateAsConstantExpr(
|
||||||
SemaRef.getASTContext(), true);
|
Eval, SemaRef.getASTContext(), ConstantExprKind::ImmediateInvocation);
|
||||||
if (!Result || !Notes.empty()) {
|
if (!Result || !Notes.empty()) {
|
||||||
Expr *InnerExpr = CE->getSubExpr()->IgnoreImplicit();
|
Expr *InnerExpr = CE->getSubExpr()->IgnoreImplicit();
|
||||||
if (auto *FunctionalCast = dyn_cast<CXXFunctionalCastExpr>(InnerExpr))
|
if (auto *FunctionalCast = dyn_cast<CXXFunctionalCastExpr>(InnerExpr))
|
||||||
|
|
|
@ -5703,11 +5703,16 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From,
|
||||||
SmallVector<PartialDiagnosticAt, 8> Notes;
|
SmallVector<PartialDiagnosticAt, 8> Notes;
|
||||||
Expr::EvalResult Eval;
|
Expr::EvalResult Eval;
|
||||||
Eval.Diag = &Notes;
|
Eval.Diag = &Notes;
|
||||||
Expr::ConstExprUsage Usage = CCE == Sema::CCEK_TemplateArg
|
|
||||||
? Expr::EvaluateForMangling
|
|
||||||
: Expr::EvaluateForCodeGen;
|
|
||||||
|
|
||||||
if (!Result.get()->EvaluateAsConstantExpr(Eval, Usage, S.Context) ||
|
ConstantExprKind Kind;
|
||||||
|
if (CCE == Sema::CCEK_TemplateArg && T->isRecordType())
|
||||||
|
Kind = ConstantExprKind::ClassTemplateArgument;
|
||||||
|
else if (CCE == Sema::CCEK_TemplateArg)
|
||||||
|
Kind = ConstantExprKind::NonClassTemplateArgument;
|
||||||
|
else
|
||||||
|
Kind = ConstantExprKind::Normal;
|
||||||
|
|
||||||
|
if (!Result.get()->EvaluateAsConstantExpr(Eval, S.Context, Kind) ||
|
||||||
(RequireInt && !Eval.Val.isInt())) {
|
(RequireInt && !Eval.Val.isInt())) {
|
||||||
// The expression can't be folded, so we can't keep it at this position in
|
// The expression can't be folded, so we can't keep it at this position in
|
||||||
// the AST.
|
// the AST.
|
||||||
|
@ -5726,9 +5731,14 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From,
|
||||||
|
|
||||||
// It's not a constant expression. Produce an appropriate diagnostic.
|
// It's not a constant expression. Produce an appropriate diagnostic.
|
||||||
if (Notes.size() == 1 &&
|
if (Notes.size() == 1 &&
|
||||||
Notes[0].second.getDiagID() == diag::note_invalid_subexpr_in_const_expr)
|
Notes[0].second.getDiagID() == diag::note_invalid_subexpr_in_const_expr) {
|
||||||
S.Diag(Notes[0].first, diag::err_expr_not_cce) << CCE;
|
S.Diag(Notes[0].first, diag::err_expr_not_cce) << CCE;
|
||||||
else {
|
} else if (!Notes.empty() && Notes[0].second.getDiagID() ==
|
||||||
|
diag::note_constexpr_invalid_template_arg) {
|
||||||
|
Notes[0].second.setDiagID(diag::err_constexpr_invalid_template_arg);
|
||||||
|
for (unsigned I = 0; I < Notes.size(); ++I)
|
||||||
|
S.Diag(Notes[I].first, Notes[I].second);
|
||||||
|
} else {
|
||||||
S.Diag(From->getBeginLoc(), diag::err_expr_not_cce)
|
S.Diag(From->getBeginLoc(), diag::err_expr_not_cce)
|
||||||
<< CCE << From->getSourceRange();
|
<< CCE << From->getSourceRange();
|
||||||
for (unsigned I = 0; I < Notes.size(); ++I)
|
for (unsigned I = 0; I < Notes.size(); ++I)
|
||||||
|
|
|
@ -4,10 +4,25 @@
|
||||||
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||||
|
|
||||||
namespace dr100 { // dr100: yes
|
namespace dr100 { // dr100: yes
|
||||||
template<const char *> struct A {}; // expected-note 0-1{{declared here}}
|
template<const char (*)[4]> struct A {}; // expected-note 0-1{{declared here}}
|
||||||
template<const char (&)[4]> struct B {}; // expected-note 0-1{{declared here}}
|
template<const char (&)[4]> struct B {}; // expected-note 0-1{{declared here}}
|
||||||
A<"foo"> a; // expected-error {{does not refer to any declaration}}
|
template<const char *> struct C {}; // expected-note 0-1{{declared here}}
|
||||||
B<"bar"> b; // expected-error {{does not refer to any declaration}}
|
template<const char &> struct D {}; // expected-note 0-1{{declared here}}
|
||||||
|
A<&"foo"> a; // #100a
|
||||||
|
B<"bar"> b; // #100b
|
||||||
|
C<"baz"> c; // #100c
|
||||||
|
D<*"quux"> d; // #100d
|
||||||
|
#if __cplusplus < 201703L
|
||||||
|
// expected-error@#100a {{does not refer to any declaration}}
|
||||||
|
// expected-error@#100b {{does not refer to any declaration}}
|
||||||
|
// expected-error@#100c {{does not refer to any declaration}}
|
||||||
|
// expected-error@#100d {{does not refer to any declaration}}
|
||||||
|
#else
|
||||||
|
// expected-error@#100a {{pointer to string literal is not allowed in a template argument}}
|
||||||
|
// expected-error@#100b {{reference to string literal is not allowed in a template argument}}
|
||||||
|
// expected-error@#100c {{pointer to subobject of string literal is not allowed in a template argument}}
|
||||||
|
// expected-error@#100d {{reference to subobject of string literal is not allowed in a template argument}}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace dr101 { // dr101: 3.5
|
namespace dr101 { // dr101: 3.5
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// RUN: %clang_cc1 -std=c++20 -verify %s
|
// RUN: %clang_cc1 -std=c++20 -fcxx-exceptions -verify %s
|
||||||
|
|
||||||
struct A { int n; };
|
struct A { int n; };
|
||||||
|
|
||||||
|
@ -26,3 +26,40 @@ template<A2 a2> struct C2 {
|
||||||
static constexpr const A2 &v = a2;
|
static constexpr const A2 &v = a2;
|
||||||
};
|
};
|
||||||
static_assert((void*)&C<A{}>::v != (void*)&C2<A2{}>::v);
|
static_assert((void*)&C<A{}>::v != (void*)&C2<A2{}>::v);
|
||||||
|
|
||||||
|
// A template parameter object shall have constant destruction.
|
||||||
|
namespace ConstDestruction {
|
||||||
|
struct D {
|
||||||
|
int n;
|
||||||
|
bool can_destroy;
|
||||||
|
|
||||||
|
constexpr ~D() {
|
||||||
|
if (!can_destroy)
|
||||||
|
throw "oh no"; // expected-note {{subexpression not valid}}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<D d>
|
||||||
|
void f() {} // expected-note 2{{invalid explicitly-specified argument}}
|
||||||
|
|
||||||
|
void g() {
|
||||||
|
f<D{0, true}>();
|
||||||
|
f<D{0, false}>(); // expected-error {{no matching function}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can SFINAE on constant destruction.
|
||||||
|
template<typename T> auto h(T t) -> decltype(f<T{1, false}>());
|
||||||
|
template<typename T> auto h(T t) -> decltype(f<T{1, true}>());
|
||||||
|
|
||||||
|
void i() {
|
||||||
|
h(D());
|
||||||
|
// Ensure we don't cache an invalid template argument after we've already
|
||||||
|
// seen it in a SFINAE context.
|
||||||
|
f<D{1, false}>(); // expected-error {{no matching function}}
|
||||||
|
f<D{1, true}>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<D d> struct Z {};
|
||||||
|
Z<D{2, true}> z1;
|
||||||
|
Z<D{2, false}> z2; // expected-error {{non-type template argument is not a constant expression}} expected-note-re {{in call to '{{.*}}->~D()'}}
|
||||||
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ template<typename T, typename U> constexpr bool is_same = false; // expected-not
|
||||||
template<typename T> constexpr bool is_same<T, T> = true;
|
template<typename T> constexpr bool is_same<T, T> = true;
|
||||||
|
|
||||||
namespace String {
|
namespace String {
|
||||||
A<const char*, "test"> a; // expected-error {{does not refer to any declaration}}
|
A<const char*, "test"> a; // expected-error {{pointer to subobject of string literal}}
|
||||||
A<const char (&)[5], "test"> b; // expected-error {{does not refer to any declaration}}
|
A<const char (&)[5], "test"> b; // expected-error {{reference to string literal}}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Array {
|
namespace Array {
|
||||||
|
@ -50,7 +50,7 @@ namespace Function {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Func() {
|
void Func() {
|
||||||
A<const char*, __func__> a; // expected-error {{does not refer to any declaration}}
|
A<const char*, __func__> a; // expected-error {{pointer to subobject of predefined '__func__' variable}}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace LabelAddrDiff {
|
namespace LabelAddrDiff {
|
||||||
|
@ -62,17 +62,17 @@ namespace LabelAddrDiff {
|
||||||
namespace Temp {
|
namespace Temp {
|
||||||
struct S { int n; };
|
struct S { int n; };
|
||||||
constexpr S &addr(S &&s) { return s; }
|
constexpr S &addr(S &&s) { return s; }
|
||||||
A<S &, addr({})> a; // expected-error {{constant}} expected-note 2{{temporary}}
|
A<S &, addr({})> a; // expected-error {{reference to temporary object}}
|
||||||
A<S *, &addr({})> b; // expected-error {{constant}} expected-note 2{{temporary}}
|
A<S *, &addr({})> b; // expected-error {{pointer to temporary object}}
|
||||||
A<int &, addr({}).n> c; // expected-error {{constant}} expected-note 2{{temporary}}
|
A<int &, addr({}).n> c; // expected-error {{reference to subobject of temporary object}}
|
||||||
A<int *, &addr({}).n> d; // expected-error {{constant}} expected-note 2{{temporary}}
|
A<int *, &addr({}).n> d; // expected-error {{pointer to subobject of temporary object}}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace std { struct type_info; }
|
namespace std { struct type_info; }
|
||||||
|
|
||||||
namespace RTTI {
|
namespace RTTI {
|
||||||
A<const std::type_info&, typeid(int)> a; // expected-error {{does not refer to any declaration}}
|
A<const std::type_info&, typeid(int)> a; // expected-error {{reference to type_info object}}
|
||||||
A<const std::type_info*, &typeid(int)> b; // expected-error {{does not refer to any declaration}}
|
A<const std::type_info*, &typeid(int)> b; // expected-error {{pointer to type_info object}}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace PtrMem {
|
namespace PtrMem {
|
||||||
|
@ -442,10 +442,8 @@ namespace PR42108 {
|
||||||
template <const S &> struct A {};
|
template <const S &> struct A {};
|
||||||
void f() {
|
void f() {
|
||||||
A<R{}>(); // expected-error {{would bind reference to a temporary}}
|
A<R{}>(); // expected-error {{would bind reference to a temporary}}
|
||||||
A<S{}>(); // expected-error {{non-type template argument is not a constant expression}} expected-note 2{{temporary}}
|
A<S{}>(); // expected-error {{reference to temporary object}}
|
||||||
// FIXME: We could diagnose this better if we treated this as not binding
|
A<T{}>(); // expected-error {{reference to temporary object}}
|
||||||
// directly. It's unclear whether that's the intent.
|
|
||||||
A<T{}>(); // expected-error {{non-type template argument is not a constant expression}} expected-note 2{{temporary}}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
using size_t = __SIZE_TYPE__;
|
using size_t = __SIZE_TYPE__;
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
struct type_info;
|
||||||
|
}
|
||||||
|
|
||||||
// floating-point arguments
|
// floating-point arguments
|
||||||
template<float> struct Float {};
|
template<float> struct Float {};
|
||||||
using F1 = Float<1.0f>; // FIXME expected-error {{sorry}}
|
using F1 = Float<1.0f>; // FIXME expected-error {{sorry}}
|
||||||
|
@ -220,3 +224,71 @@ namespace UnnamedBitfield {
|
||||||
// Once we support bit-casts involving bit-fields, this should be valid too.
|
// Once we support bit-casts involving bit-fields, this should be valid too.
|
||||||
using T = X<__builtin_bit_cast(A, 0)>; // expected-error {{constant}} expected-note {{not yet supported}}
|
using T = X<__builtin_bit_cast(A, 0)>; // expected-error {{constant}} expected-note {{not yet supported}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Temporary {
|
||||||
|
template<const int &> struct A {};
|
||||||
|
A<0> a0; // expected-error {{conversion from 'int' to 'const int &' in converted constant expression would bind reference to a temporary}}
|
||||||
|
|
||||||
|
A<(const int&)1> a1; // expected-error {{reference to temporary object is not allowed in a template argument}}
|
||||||
|
A<(int&&)2> a2; // expected-error {{reference to temporary object is not allowed in a template argument}}
|
||||||
|
|
||||||
|
// FIXME: There's really no good reason to reject these cases.
|
||||||
|
int &&r3 = 3;
|
||||||
|
const int &r4 = 4;
|
||||||
|
A<r3> a3; // expected-error {{reference to temporary object is not allowed in a template argument}}
|
||||||
|
A<r4> a4; // expected-error {{reference to temporary object is not allowed in a template argument}}
|
||||||
|
|
||||||
|
struct X { int a[5]; };
|
||||||
|
X &&x = X{};
|
||||||
|
A<x.a[3]> a5; // expected-error {{reference to subobject of temporary object}}
|
||||||
|
|
||||||
|
template<const int*> struct B {};
|
||||||
|
B<&(int&)(int&&)0> b0; // expected-error {{pointer to temporary object}}
|
||||||
|
B<&r3> b3; // expected-error {{pointer to temporary object}}
|
||||||
|
B<&x.a[3]> b5; // expected-error {{pointer to subobject of temporary object}}
|
||||||
|
|
||||||
|
struct C { const int *p[2]; };
|
||||||
|
template<C> struct D {};
|
||||||
|
D<C{nullptr, &r3}> d; // expected-error {{pointer to temporary object}}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace StringLiteral {
|
||||||
|
template<decltype(auto)> struct Y {};
|
||||||
|
Y<&"hello"> y1; // expected-error {{pointer to string literal}}
|
||||||
|
Y<"hello"> y2; // expected-error {{reference to string literal}}
|
||||||
|
Y<+"hello"> y3; // expected-error {{pointer to subobject of string literal}}
|
||||||
|
Y<"hello"[2]> y4; // expected-error {{reference to subobject of string literal}}
|
||||||
|
|
||||||
|
struct A { const char *p; };
|
||||||
|
struct B { const char &r; };
|
||||||
|
Y<A{"hello"}> y5; // expected-error {{pointer to subobject of string literal}}
|
||||||
|
Y<B{"hello"[2]}> y6; // expected-error {{reference to subobject of string literal}}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace TypeInfo {
|
||||||
|
template<decltype(auto)> struct Y {};
|
||||||
|
Y<&typeid(int)> y1; // expected-error {{pointer to type_info object}}
|
||||||
|
Y<typeid(int)> y2; // expected-error {{reference to type_info object}}
|
||||||
|
|
||||||
|
struct A { const std::type_info *p; };
|
||||||
|
struct B { const std::type_info &r; };
|
||||||
|
Y<A{&typeid(int)}> y3; // expected-error {{pointer to type_info object}}
|
||||||
|
Y<B{typeid(int)}> y4; // expected-error {{reference to type_info object}}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Predefined {
|
||||||
|
template<decltype(auto)> struct Y {};
|
||||||
|
|
||||||
|
struct A { const char *p; };
|
||||||
|
struct B { const char &r; };
|
||||||
|
void f() {
|
||||||
|
// decltype(__func__) is an array, which decays to a pointer parameter.
|
||||||
|
Y<__func__>(); // expected-error {{pointer to subobject of predefined '__func__' variable}}
|
||||||
|
Y<__PRETTY_FUNCTION__>(); // expected-error {{pointer to subobject}}
|
||||||
|
Y<(__func__)>(); // expected-error {{reference to predefined '__func__' variable}}
|
||||||
|
Y<&__func__>(); // expected-error {{pointer to predefined '__func__' variable}}
|
||||||
|
Y<*&__func__>(); // expected-error {{reference to predefined '__func__' variable}}
|
||||||
|
Y<A{__func__}>(); // expected-error {{pointer to subobject of predefined '__func__' variable}}
|
||||||
|
Y<B{__func__[0]}>(); // expected-error {{reference to subobject of predefined '__func__' variable}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue