For DR712: store on a DeclRefExpr whether it constitutes an odr-use.

Begin restructuring to support the forms of non-odr-use reference
permitted by DR712.

llvm-svn: 363086
This commit is contained in:
Richard Smith 2019-06-11 17:50:32 +00:00
parent ef2d6d99c0
commit 715f7a1bd0
28 changed files with 485 additions and 169 deletions

View File

@ -1226,10 +1226,15 @@ public:
void setInit(Expr *I);
/// Determine whether this variable's value can be used in a
/// Determine whether this variable's value might be usable in a
/// constant expression, according to the relevant language standard.
/// This only checks properties of the declaration, and does not check
/// whether the initializer is in fact a constant expression.
bool mightBeUsableInConstantExpressions(ASTContext &C) const;
/// Determine whether this variable's value can be used in a
/// constant expression, according to the relevant language standard,
/// including checking whether it was initialized by a constant expression.
bool isUsableInConstantExpressions(ASTContext &C) const;
EvaluatedStmt *ensureEvaluatedStmt() const;

View File

@ -1115,7 +1115,7 @@ class DeclRefExpr final
bool RefersToEnlosingVariableOrCapture,
const DeclarationNameInfo &NameInfo, NamedDecl *FoundD,
const TemplateArgumentListInfo *TemplateArgs, QualType T,
ExprValueKind VK);
ExprValueKind VK, NonOdrUseReason NOUR);
/// Construct an empty declaration reference expression.
explicit DeclRefExpr(EmptyShell Empty) : Expr(DeclRefExprClass, Empty) {}
@ -1128,14 +1128,16 @@ public:
DeclRefExpr(const ASTContext &Ctx, ValueDecl *D,
bool RefersToEnclosingVariableOrCapture, QualType T,
ExprValueKind VK, SourceLocation L,
const DeclarationNameLoc &LocInfo = DeclarationNameLoc());
const DeclarationNameLoc &LocInfo = DeclarationNameLoc(),
NonOdrUseReason NOUR = NOUR_None);
static DeclRefExpr *
Create(const ASTContext &Context, NestedNameSpecifierLoc QualifierLoc,
SourceLocation TemplateKWLoc, ValueDecl *D,
bool RefersToEnclosingVariableOrCapture, SourceLocation NameLoc,
QualType T, ExprValueKind VK, NamedDecl *FoundD = nullptr,
const TemplateArgumentListInfo *TemplateArgs = nullptr);
const TemplateArgumentListInfo *TemplateArgs = nullptr,
NonOdrUseReason NOUR = NOUR_None);
static DeclRefExpr *
Create(const ASTContext &Context, NestedNameSpecifierLoc QualifierLoc,
@ -1143,7 +1145,8 @@ public:
bool RefersToEnclosingVariableOrCapture,
const DeclarationNameInfo &NameInfo, QualType T, ExprValueKind VK,
NamedDecl *FoundD = nullptr,
const TemplateArgumentListInfo *TemplateArgs = nullptr);
const TemplateArgumentListInfo *TemplateArgs = nullptr,
NonOdrUseReason NOUR = NOUR_None);
/// Construct an empty declaration reference expression.
static DeclRefExpr *CreateEmpty(const ASTContext &Context, bool HasQualifier,
@ -1274,6 +1277,11 @@ public:
DeclRefExprBits.HadMultipleCandidates = V;
}
/// Is this expression a non-odr-use reference, and if so, why?
NonOdrUseReason isNonOdrUse() const {
return static_cast<NonOdrUseReason>(DeclRefExprBits.NonOdrUseReason);
}
/// Does this DeclRefExpr refer to an enclosing local or a captured
/// variable?
bool refersToEnclosingVariableOrCapture() const {

View File

@ -351,6 +351,7 @@ protected:
unsigned HasFoundDecl : 1;
unsigned HadMultipleCandidates : 1;
unsigned RefersToEnclosingVariableOrCapture : 1;
unsigned NonOdrUseReason : 2;
/// The location of the declaration name itself.
SourceLocation Loc;

View File

@ -148,6 +148,20 @@ namespace clang {
OK_ObjCSubscript
};
/// The reason why a DeclRefExpr does not constitute an odr-use.
enum NonOdrUseReason {
/// This is an odr-use.
NOUR_None = 0,
/// This name appears in an unevaluated operand.
NOUR_Unevaluated,
/// This name appears as a potential result of an lvalue-to-rvalue
/// conversion that is a constant expression.
NOUR_Constant,
/// This name appears as a potential result of a discarded value
/// expression.
NOUR_Discarded,
};
/// Describes the kind of template specialization that a
/// particular template specialization declaration represents.
enum TemplateSpecializationKind {

View File

@ -4185,7 +4185,7 @@ public:
void MarkCaptureUsedInEnclosingContext(VarDecl *Capture, SourceLocation Loc,
unsigned CapturingScopeIndex);
void UpdateMarkingForLValueToRValue(Expr *E);
ExprResult CheckLValueToRValueConversionOperand(Expr *E);
void CleanupVarDeclMarking();
enum TryCaptureKind {

View File

@ -38,15 +38,6 @@ FTIHasNonVoidParameters(const DeclaratorChunk::FunctionTypeInfo &FTI) {
return FTI.NumParams && !FTIHasSingleVoidParameter(FTI);
}
// This requires the variable to be non-dependent and the initializer
// to not be value dependent.
inline bool IsVariableAConstantExpression(VarDecl *Var, ASTContext &Context) {
const VarDecl *DefVD = nullptr;
return !isa<ParmVarDecl>(Var) &&
Var->isUsableInConstantExpressions(Context) &&
Var->getAnyInitializer(DefVD) && DefVD->checkInitIsICE();
}
// Helper function to check whether D's attributes match current CUDA mode.
// Decls with mismatched attributes and related diagnostics may have to be
// ignored during this CUDA compilation pass.

View File

@ -6189,7 +6189,7 @@ ExpectedStmt ASTNodeImporter::VisitDeclRefExpr(DeclRefExpr *E) {
auto *ToE = DeclRefExpr::Create(
Importer.getToContext(), ToQualifierLoc, ToTemplateKeywordLoc, ToDecl,
E->refersToEnclosingVariableOrCapture(), ToLocation, ToType,
E->getValueKind(), ToFoundD, ToResInfo);
E->getValueKind(), ToFoundD, ToResInfo, E->isNonOdrUse());
if (E->hadMultipleCandidates())
ToE->setHadMultipleCandidates(true);
return ToE;

View File

@ -2245,12 +2245,16 @@ void VarDecl::setInit(Expr *I) {
Init = I;
}
bool VarDecl::isUsableInConstantExpressions(ASTContext &C) const {
bool VarDecl::mightBeUsableInConstantExpressions(ASTContext &C) const {
const LangOptions &Lang = C.getLangOpts();
if (!Lang.CPlusPlus)
return false;
// Function parameters are never usable in constant expressions.
if (isa<ParmVarDecl>(this))
return false;
// In C++11, any variable of reference type can be used in a constant
// expression if it is initialized by a constant expression.
if (Lang.CPlusPlus11 && getType()->isReferenceType())
@ -2272,6 +2276,22 @@ bool VarDecl::isUsableInConstantExpressions(ASTContext &C) const {
return Lang.CPlusPlus11 && isConstexpr();
}
bool VarDecl::isUsableInConstantExpressions(ASTContext &Context) const {
// C++2a [expr.const]p3:
// A variable is usable in constant expressions after its initializing
// declaration is encountered...
const VarDecl *DefVD = nullptr;
const Expr *Init = getAnyInitializer(DefVD);
if (!Init || Init->isValueDependent())
return false;
// ... if it is a constexpr variable, or it is of reference type or of
// const-qualified integral or enumeration type, ...
if (!DefVD->mightBeUsableInConstantExpressions(Context))
return false;
// ... and its initializer is a constant initializer.
return DefVD->checkInitIsICE();
}
/// Convert the initializer for this declaration to the elaborated EvaluatedStmt
/// form, which contains extra information on the evaluated value of the
/// initializer.

View File

@ -344,7 +344,8 @@ void DeclRefExpr::computeDependence(const ASTContext &Ctx) {
DeclRefExpr::DeclRefExpr(const ASTContext &Ctx, ValueDecl *D,
bool RefersToEnclosingVariableOrCapture, QualType T,
ExprValueKind VK, SourceLocation L,
const DeclarationNameLoc &LocInfo)
const DeclarationNameLoc &LocInfo,
NonOdrUseReason NOUR)
: Expr(DeclRefExprClass, T, VK, OK_Ordinary, false, false, false, false),
D(D), DNLoc(LocInfo) {
DeclRefExprBits.HasQualifier = false;
@ -353,6 +354,7 @@ DeclRefExpr::DeclRefExpr(const ASTContext &Ctx, ValueDecl *D,
DeclRefExprBits.HadMultipleCandidates = false;
DeclRefExprBits.RefersToEnclosingVariableOrCapture =
RefersToEnclosingVariableOrCapture;
DeclRefExprBits.NonOdrUseReason = NOUR;
DeclRefExprBits.Loc = L;
computeDependence(Ctx);
}
@ -363,7 +365,7 @@ DeclRefExpr::DeclRefExpr(const ASTContext &Ctx,
bool RefersToEnclosingVariableOrCapture,
const DeclarationNameInfo &NameInfo, NamedDecl *FoundD,
const TemplateArgumentListInfo *TemplateArgs,
QualType T, ExprValueKind VK)
QualType T, ExprValueKind VK, NonOdrUseReason NOUR)
: Expr(DeclRefExprClass, T, VK, OK_Ordinary, false, false, false, false),
D(D), DNLoc(NameInfo.getInfo()) {
DeclRefExprBits.Loc = NameInfo.getLoc();
@ -384,6 +386,7 @@ DeclRefExpr::DeclRefExpr(const ASTContext &Ctx,
= (TemplateArgs || TemplateKWLoc.isValid()) ? 1 : 0;
DeclRefExprBits.RefersToEnclosingVariableOrCapture =
RefersToEnclosingVariableOrCapture;
DeclRefExprBits.NonOdrUseReason = NOUR;
if (TemplateArgs) {
bool Dependent = false;
bool InstantiationDependent = false;
@ -405,30 +408,27 @@ DeclRefExpr::DeclRefExpr(const ASTContext &Ctx,
DeclRefExpr *DeclRefExpr::Create(const ASTContext &Context,
NestedNameSpecifierLoc QualifierLoc,
SourceLocation TemplateKWLoc,
ValueDecl *D,
SourceLocation TemplateKWLoc, ValueDecl *D,
bool RefersToEnclosingVariableOrCapture,
SourceLocation NameLoc,
QualType T,
ExprValueKind VK,
NamedDecl *FoundD,
const TemplateArgumentListInfo *TemplateArgs) {
SourceLocation NameLoc, QualType T,
ExprValueKind VK, NamedDecl *FoundD,
const TemplateArgumentListInfo *TemplateArgs,
NonOdrUseReason NOUR) {
return Create(Context, QualifierLoc, TemplateKWLoc, D,
RefersToEnclosingVariableOrCapture,
DeclarationNameInfo(D->getDeclName(), NameLoc),
T, VK, FoundD, TemplateArgs);
T, VK, FoundD, TemplateArgs, NOUR);
}
DeclRefExpr *DeclRefExpr::Create(const ASTContext &Context,
NestedNameSpecifierLoc QualifierLoc,
SourceLocation TemplateKWLoc,
ValueDecl *D,
SourceLocation TemplateKWLoc, ValueDecl *D,
bool RefersToEnclosingVariableOrCapture,
const DeclarationNameInfo &NameInfo,
QualType T,
ExprValueKind VK,
QualType T, ExprValueKind VK,
NamedDecl *FoundD,
const TemplateArgumentListInfo *TemplateArgs) {
const TemplateArgumentListInfo *TemplateArgs,
NonOdrUseReason NOUR) {
// Filter out cases where the found Decl is the same as the value refenenced.
if (D == FoundD)
FoundD = nullptr;
@ -443,8 +443,8 @@ DeclRefExpr *DeclRefExpr::Create(const ASTContext &Context,
void *Mem = Context.Allocate(Size, alignof(DeclRefExpr));
return new (Mem) DeclRefExpr(Context, QualifierLoc, TemplateKWLoc, D,
RefersToEnclosingVariableOrCapture,
NameInfo, FoundD, TemplateArgs, T, VK);
RefersToEnclosingVariableOrCapture, NameInfo,
FoundD, TemplateArgs, T, VK, NOUR);
}
DeclRefExpr *DeclRefExpr::CreateEmpty(const ASTContext &Context,

View File

@ -805,6 +805,12 @@ void JSONNodeDumper::VisitDeclRefExpr(const DeclRefExpr *DRE) {
if (DRE->getDecl() != DRE->getFoundDecl())
JOS.attribute("foundReferencedDecl",
createBareDeclRef(DRE->getFoundDecl()));
switch (DRE->isNonOdrUse()) {
case NOUR_None: break;
case NOUR_Unevaluated: JOS.attribute("nonOdrUseReason", "unevaluated"); break;
case NOUR_Constant: JOS.attribute("nonOdrUseReason", "constant"); break;
case NOUR_Discarded: JOS.attribute("nonOdrUseReason", "discarded"); break;
}
}
void JSONNodeDumper::VisitPredefinedExpr(const PredefinedExpr *PE) {

View File

@ -715,6 +715,12 @@ void TextNodeDumper::VisitDeclRefExpr(const DeclRefExpr *Node) {
dumpBareDeclRef(Node->getFoundDecl());
OS << ")";
}
switch (Node->isNonOdrUse()) {
case NOUR_None: break;
case NOUR_Unevaluated: OS << " non_odr_use_unevaluated"; break;
case NOUR_Constant: OS << " non_odr_use_constant"; break;
case NOUR_Discarded: OS << " non_odr_use_discarded"; break;
}
}
void TextNodeDumper::VisitUnresolvedLookupExpr(

View File

@ -1783,8 +1783,8 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
}
llvm::Constant *constant = nullptr;
if (emission.IsConstantAggregate || D.isConstexpr() ||
D.isUsableInConstantExpressions(getContext())) {
if (emission.IsConstantAggregate ||
D.mightBeUsableInConstantExpressions(getContext())) {
assert(!capturedByInit && "constant init contains a capturing block?");
constant = ConstantEmitter(*this).tryEmitAbstractForInitializer(D);
if (constant && !constant->isZeroValue() &&

View File

@ -1398,7 +1398,7 @@ static bool isConstantEmittableObjectType(QualType type) {
/// Can we constant-emit a load of a reference to a variable of the
/// given type? This is different from predicates like
/// Decl::isUsableInConstantExpressions because we do want it to apply
/// Decl::mightBeUsableInConstantExpressions because we do want it to apply
/// in situations that don't necessarily satisfy the language's rules
/// for this (e.g. C++'s ODR-use rules). For example, we want to able
/// to do this with const float variables even if those variables
@ -1492,11 +1492,17 @@ CodeGenFunction::tryEmitAsConstant(DeclRefExpr *refExpr) {
static DeclRefExpr *tryToConvertMemberExprToDeclRefExpr(CodeGenFunction &CGF,
const MemberExpr *ME) {
if (auto *VD = dyn_cast<VarDecl>(ME->getMemberDecl())) {
// FIXME: Copy this from the MemberExpr once we store it there.
NonOdrUseReason NOUR = NOUR_None;
if (VD->getType()->isReferenceType() &&
VD->isUsableInConstantExpressions(CGF.getContext()))
NOUR = NOUR_Constant;
// Try to emit static variable member expressions as DREs.
return DeclRefExpr::Create(
CGF.getContext(), NestedNameSpecifierLoc(), SourceLocation(), VD,
/*RefersToEnclosingVariableOrCapture=*/false, ME->getExprLoc(),
ME->getType(), ME->getValueKind());
ME->getType(), ME->getValueKind(), nullptr, nullptr, NOUR);
}
return nullptr;
}
@ -2462,12 +2468,11 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
// A DeclRefExpr for a reference initialized by a constant expression can
// appear without being odr-used. Directly emit the constant initializer.
const Expr *Init = VD->getAnyInitializer(VD);
VD->getAnyInitializer(VD);
const auto *BD = dyn_cast_or_null<BlockDecl>(CurCodeDecl);
if (Init && !isa<ParmVarDecl>(VD) && VD->getType()->isReferenceType() &&
VD->isUsableInConstantExpressions(getContext()) &&
VD->checkInitIsICE() &&
if (E->isNonOdrUse() == NOUR_Constant && VD->getType()->isReferenceType() &&
// Do not emit if it is private OpenMP variable.
// FIXME: This should be handled in odr-use marking, not here.
!(E->refersToEnclosingVariableOrCapture() &&
((CapturedStmtInfo &&
(LocalDeclMap.count(VD->getCanonicalDecl()) ||
@ -2489,6 +2494,8 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
return MakeAddrLValue(Address(Val, Alignment), T, AlignmentSource::Decl);
}
// FIXME: Handle other kinds of non-odr-use DeclRefExprs.
// Check for captured variables.
if (E->refersToEnclosingVariableOrCapture()) {
VD = VD->getCanonicalDecl();

View File

@ -587,7 +587,7 @@ static bool ShouldRemoveFromUnused(Sema *SemaRef, const DeclaratorDecl *D) {
// warn even if the variable isn't odr-used. (isReferenced doesn't
// precisely reflect that, but it's a decent approximation.)
if (VD->isReferenced() &&
VD->isUsableInConstantExpressions(SemaRef->Context))
VD->mightBeUsableInConstantExpressions(SemaRef->Context))
return true;
if (VarTemplateDecl *Template = VD->getDescribedVarTemplate())

View File

@ -5231,15 +5231,10 @@ Sema::SemaBuiltinAtomicOverloaded(ExprResult TheCallResult) {
}
// Create a new DeclRefExpr to refer to the new decl.
DeclRefExpr* NewDRE = DeclRefExpr::Create(
Context,
DRE->getQualifierLoc(),
SourceLocation(),
NewBuiltinDecl,
/*enclosing*/ false,
DRE->getLocation(),
Context.BuiltinFnTy,
DRE->getValueKind());
DeclRefExpr *NewDRE = DeclRefExpr::Create(
Context, DRE->getQualifierLoc(), SourceLocation(), NewBuiltinDecl,
/*enclosing*/ false, DRE->getLocation(), Context.BuiltinFnTy,
DRE->getValueKind(), nullptr, nullptr, DRE->isNonOdrUse());
// Set the callee in the CallExpr.
// FIXME: This loses syntactic information.

View File

@ -11994,7 +11994,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
for (unsigned I = 0, N = Notes.size(); I != N; ++I)
Diag(Notes[I].first, Notes[I].second);
}
} else if (var->isUsableInConstantExpressions(Context)) {
} else if (var->mightBeUsableInConstantExpressions(Context)) {
// Check whether the initializer of a const variable of integral or
// enumeration type is an ICE now, since we can't tell whether it was
// initialized by a constant expression if we check later.

View File

@ -625,15 +625,18 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) {
Context.getTargetInfo().getCXXABI().isMicrosoft())
(void)isCompleteType(E->getExprLoc(), T);
UpdateMarkingForLValueToRValue(E);
ExprResult Res = CheckLValueToRValueConversionOperand(E);
if (Res.isInvalid())
return Res;
E = Res.get();
// Loading a __weak object implicitly retains the value, so we need a cleanup to
// balance that.
if (E->getType().getObjCLifetime() == Qualifiers::OCL_Weak)
Cleanup.setExprNeedsCleanups(true);
ExprResult Res = ImplicitCastExpr::Create(Context, T, CK_LValueToRValue, E,
nullptr, VK_RValue);
Res = ImplicitCastExpr::Create(Context, T, CK_LValueToRValue, E, nullptr,
VK_RValue);
// C11 6.3.2.1p2:
// ... if the lvalue has atomic type, the value has the non-atomic version
@ -1794,9 +1797,19 @@ Sema::BuildDeclRefExpr(ValueDecl *D, QualType Ty, ExprValueKind VK,
isa<VarDecl>(D) &&
NeedToCaptureVariable(cast<VarDecl>(D), NameInfo.getLoc());
NonOdrUseReason NOUR;
if (isUnevaluatedContext())
NOUR = NOUR_Unevaluated;
else if (isa<VarDecl>(D) && D->getType()->isReferenceType() &&
!(getLangOpts().OpenMP && isOpenMPCapturedDecl(D)) &&
cast<VarDecl>(D)->isUsableInConstantExpressions(Context))
NOUR = NOUR_Constant;
else
NOUR = NOUR_None;
DeclRefExpr *E = DeclRefExpr::Create(Context, NNS, TemplateKWLoc, D,
RefersToCapturedVariable, NameInfo, Ty,
VK, FoundD, TemplateArgs);
VK, FoundD, TemplateArgs, NOUR);
MarkDeclRefReferenced(E);
if (getLangOpts().ObjCWeak && isa<VarDecl>(D) &&
@ -5626,7 +5639,8 @@ ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,
NDecl = FDecl;
Fn = DeclRefExpr::Create(
Context, FDecl->getQualifierLoc(), SourceLocation(), FDecl, false,
SourceLocation(), FDecl->getType(), Fn->getValueKind(), FDecl);
SourceLocation(), FDecl->getType(), Fn->getValueKind(), FDecl,
nullptr, DRE->isNonOdrUse());
}
}
} else if (isa<MemberExpr>(NakedFn))
@ -15779,59 +15793,258 @@ QualType Sema::getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc) {
return DeclRefType;
}
// If either the type of the variable or the initializer is dependent,
// return false. Otherwise, determine whether the variable is a constant
// expression. Use this if you need to know if a variable that might or
// might not be dependent is truly a constant expression.
static inline bool IsVariableNonDependentAndAConstantExpression(VarDecl *Var,
ASTContext &Context) {
if (Var->getType()->isDependentType())
return false;
const VarDecl *DefVD = nullptr;
Var->getAnyInitializer(DefVD);
if (!DefVD)
return false;
EvaluatedStmt *Eval = DefVD->ensureEvaluatedStmt();
Expr *Init = cast<Expr>(Eval->Value);
if (Init->isValueDependent())
return false;
return IsVariableAConstantExpression(Var, Context);
}
void Sema::UpdateMarkingForLValueToRValue(Expr *E) {
/// Walk the set of potential results of an expression and mark them all as
/// non-odr-uses if they satisfy the side-conditions of the NonOdrUseReason.
///
/// \return A new expression if we found any potential results, ExprEmpty() if
/// not, and ExprError() if we diagnosed an error.
static ExprResult rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E,
NonOdrUseReason NOUR) {
// Per C++11 [basic.def.odr], a variable is odr-used "unless it is
// an object that satisfies the requirements for appearing in a
// constant expression (5.19) and the lvalue-to-rvalue conversion (4.1)
// is immediately applied." This function handles the lvalue-to-rvalue
// conversion part.
MaybeODRUseExprs.erase(E->IgnoreParens());
//
// If we encounter a node that claims to be an odr-use but shouldn't be, we
// transform it into the relevant kind of non-odr-use node and rebuild the
// tree of nodes leading to it.
//
// This is a mini-TreeTransform that only transforms a restricted subset of
// nodes (and only certain operands of them).
// If we are in a lambda, check if this DeclRefExpr or MemberExpr refers
// to a variable that is a constant expression, and if so, identify it as
// a reference to a variable that does not involve an odr-use of that
// variable.
if (LambdaScopeInfo *LSI = getCurLambda()) {
Expr *SansParensExpr = E->IgnoreParens();
VarDecl *Var;
ArrayRef<VarDecl *> Vars(&Var, &Var + 1);
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(SansParensExpr))
Var = dyn_cast<VarDecl>(DRE->getFoundDecl());
else if (MemberExpr *ME = dyn_cast<MemberExpr>(SansParensExpr))
Var = dyn_cast<VarDecl>(ME->getMemberDecl());
else if (auto *FPPE = dyn_cast<FunctionParmPackExpr>(SansParensExpr))
Vars = llvm::makeArrayRef(FPPE->begin(), FPPE->end());
else
Vars = None;
// Rebuild a subexpression.
auto Rebuild = [&](Expr *Sub) {
return rebuildPotentialResultsAsNonOdrUsed(S, Sub, NOUR);
};
for (VarDecl *VD : Vars) {
if (VD && IsVariableNonDependentAndAConstantExpression(VD, Context))
LSI->markVariableExprAsNonODRUsed(SansParensExpr);
// Check whether a potential result satisfies the requirements of NOUR.
auto IsPotentialResultOdrUsed = [&](NamedDecl *D) {
// Any entity other than a VarDecl is always odr-used whenever it's named
// in a potentially-evaluated expression.
auto *VD = dyn_cast<VarDecl>(D);
if (!VD)
return true;
// C++2a [basic.def.odr]p4:
// A variable x whose name appears as a potentially-evalauted expression
// e is odr-used by e unless
// -- x is a reference that is usable in constant expressions, or
// -- x is a variable of non-reference type that is usable in constant
// expressions and has no mutable subobjects, and e is an element of
// the set of potential results of an expression of
// non-volatile-qualified non-class type to which the lvalue-to-rvalue
// conversion is applied, or
// -- x is a variable of non-reference type, and e is an element of the
// set of potential results of a discarded-value expression to which
// the lvalue-to-rvalue conversion is not applied
//
// We check the first bullet and the "potentially-evaluated" condition in
// BuildDeclRefExpr. We check the type requirements in the second bullet
// in CheckLValueToRValueConversionOperand below.
switch (NOUR) {
case NOUR_None:
case NOUR_Unevaluated:
llvm_unreachable("unexpected non-odr-use-reason");
case NOUR_Constant:
// Constant references were handled when they were built.
if (VD->getType()->isReferenceType())
return true;
if (auto *RD = VD->getType()->getAsCXXRecordDecl())
if (RD->hasMutableFields())
return true;
if (!VD->isUsableInConstantExpressions(S.Context))
return true;
break;
case NOUR_Discarded:
if (VD->getType()->isReferenceType())
return true;
break;
}
return false;
};
// Mark that this expression does not constitute an odr-use.
auto MarkNotOdrUsed = [&] {
S.MaybeODRUseExprs.erase(E);
if (LambdaScopeInfo *LSI = S.getCurLambda())
LSI->markVariableExprAsNonODRUsed(E);
};
// C++2a [basic.def.odr]p2:
// The set of potential results of an expression e is defined as follows:
switch (E->getStmtClass()) {
// -- If e is an id-expression, ...
case Expr::DeclRefExprClass: {
auto *DRE = cast<DeclRefExpr>(E);
if (DRE->isNonOdrUse() || IsPotentialResultOdrUsed(DRE->getDecl()))
break;
// Rebuild as a non-odr-use DeclRefExpr.
MarkNotOdrUsed();
TemplateArgumentListInfo TemplateArgStorage, *TemplateArgs = nullptr;
if (DRE->hasExplicitTemplateArgs()) {
DRE->copyTemplateArgumentsInto(TemplateArgStorage);
TemplateArgs = &TemplateArgStorage;
}
return DeclRefExpr::Create(
S.Context, DRE->getQualifierLoc(), DRE->getTemplateKeywordLoc(),
DRE->getDecl(), DRE->refersToEnclosingVariableOrCapture(),
DRE->getNameInfo(), DRE->getType(), DRE->getValueKind(),
DRE->getFoundDecl(), TemplateArgs, NOUR);
}
case Expr::FunctionParmPackExprClass: {
auto *FPPE = cast<FunctionParmPackExpr>(E);
// If any of the declarations in the pack is odr-used, then the expression
// as a whole constitutes an odr-use.
for (VarDecl *D : *FPPE)
if (IsPotentialResultOdrUsed(D))
return ExprEmpty();
// FIXME: Rebuild as a non-odr-use FunctionParmPackExpr? In practice,
// nothing cares about whether we marked this as an odr-use, but it might
// be useful for non-compiler tools.
MarkNotOdrUsed();
break;
}
// FIXME: Implement these.
// -- If e is a subscripting operation with an array operand...
// -- If e is a class member access expression [...] naming a non-static
// data member...
// -- If e is a class member access expression naming a static data member,
// ...
case Expr::MemberExprClass: {
auto *ME = cast<MemberExpr>(E);
if (ME->getMemberDecl()->isCXXInstanceMember())
// FIXME: Recurse to the left-hand side.
break;
// FIXME: Track whether a MemberExpr constitutes an odr-use; bail out here
// if we've already marked it.
if (IsPotentialResultOdrUsed(ME->getMemberDecl()))
break;
// FIXME: Rebuild as a non-odr-use MemberExpr.
MarkNotOdrUsed();
return ExprEmpty();
}
// FIXME: Implement this.
// -- If e is a pointer-to-member expression of the form e1 .* e2 ...
// -- If e has the form (e1)...
case Expr::ParenExprClass: {
auto *PE = dyn_cast<ParenExpr>(E);
ExprResult Sub = Rebuild(PE->getSubExpr());
if (!Sub.isUsable())
return Sub;
return S.ActOnParenExpr(PE->getLParen(), PE->getRParen(), Sub.get());
}
// FIXME: Implement these.
// -- If e is a glvalue conditional expression, ...
// -- If e is a comma expression, ...
// [Clang extension]
// -- If e has the form __extension__ e1...
case Expr::UnaryOperatorClass: {
auto *UO = cast<UnaryOperator>(E);
if (UO->getOpcode() != UO_Extension)
break;
ExprResult Sub = Rebuild(UO->getSubExpr());
if (!Sub.isUsable())
return Sub;
return S.BuildUnaryOp(nullptr, UO->getOperatorLoc(), UO_Extension,
Sub.get());
}
// [Clang extension]
// -- If e has the form _Generic(...), the set of potential results is the
// union of the sets of potential results of the associated expressions.
case Expr::GenericSelectionExprClass: {
auto *GSE = dyn_cast<GenericSelectionExpr>(E);
SmallVector<Expr *, 4> AssocExprs;
bool AnyChanged = false;
for (Expr *OrigAssocExpr : GSE->getAssocExprs()) {
ExprResult AssocExpr = Rebuild(OrigAssocExpr);
if (AssocExpr.isInvalid())
return ExprError();
if (AssocExpr.isUsable()) {
AssocExprs.push_back(AssocExpr.get());
AnyChanged = true;
} else {
AssocExprs.push_back(OrigAssocExpr);
}
}
return AnyChanged ? S.CreateGenericSelectionExpr(
GSE->getGenericLoc(), GSE->getDefaultLoc(),
GSE->getRParenLoc(), GSE->getControllingExpr(),
GSE->getAssocTypeSourceInfos(), AssocExprs)
: ExprEmpty();
}
// [Clang extension]
// -- If e has the form __builtin_choose_expr(...), the set of potential
// results is the union of the sets of potential results of the
// second and third subexpressions.
case Expr::ChooseExprClass: {
auto *CE = dyn_cast<ChooseExpr>(E);
ExprResult LHS = Rebuild(CE->getLHS());
if (LHS.isInvalid())
return ExprError();
ExprResult RHS = Rebuild(CE->getLHS());
if (RHS.isInvalid())
return ExprError();
if (!LHS.get() && !RHS.get())
return ExprEmpty();
if (!LHS.isUsable())
LHS = CE->getLHS();
if (!RHS.isUsable())
RHS = CE->getRHS();
return S.ActOnChooseExpr(CE->getBuiltinLoc(), CE->getCond(), LHS.get(),
RHS.get(), CE->getRParenLoc());
}
// Step through non-syntactic nodes.
case Expr::ConstantExprClass: {
auto *CE = dyn_cast<ConstantExpr>(E);
ExprResult Sub = Rebuild(CE->getSubExpr());
if (!Sub.isUsable())
return Sub;
return ConstantExpr::Create(S.Context, Sub.get());
}
default:
break;
}
// Can't traverse through this node. Nothing to do.
return ExprEmpty();
}
ExprResult Sema::CheckLValueToRValueConversionOperand(Expr *E) {
// C++2a [basic.def.odr]p4:
// [...] an expression of non-volatile-qualified non-class type to which
// the lvalue-to-rvalue conversion is applied [...]
if (E->getType().isVolatileQualified() || E->getType()->getAs<RecordType>())
return E;
ExprResult Result =
rebuildPotentialResultsAsNonOdrUsed(*this, E, NOUR_Constant);
if (Result.isInvalid())
return ExprError();
return Result.get() ? Result : E;
}
ExprResult Sema::ActOnConstantExpression(ExprResult Res) {
@ -15844,8 +16057,7 @@ ExprResult Sema::ActOnConstantExpression(ExprResult Res) {
// deciding whether it is an odr-use, just assume we will apply the
// lvalue-to-rvalue conversion. In the one case where this doesn't happen
// (a non-type template argument), we have special handling anyway.
UpdateMarkingForLValueToRValue(Res.get());
return Res;
return CheckLValueToRValueConversionOperand(Res.get());
}
void Sema::CleanupVarDeclMarking() {
@ -15889,7 +16101,7 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
OdrUseContext OdrUse = isOdrUseContext(SemaRef);
bool UsableInConstantExpr =
Var->isUsableInConstantExpressions(SemaRef.Context);
Var->mightBeUsableInConstantExpressions(SemaRef.Context);
// C++20 [expr.const]p12:
// A variable [...] is needed for constant evaluation if it is [...] a
@ -15964,7 +16176,7 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
}
}
// C++20 [basic.def.odr]p4:
// C++2a [basic.def.odr]p4:
// A variable x whose name appears as a potentially-evaluated expression e
// is odr-used by e unless
// -- x is a reference that is usable in constant expressions
@ -15978,11 +16190,14 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
// lvalue-to-rvalue conversion is not applied [FIXME]
//
// We check the first part of the second bullet here, and
// Sema::UpdateMarkingForLValueToRValue deals with the second part.
// Sema::CheckLValueToRValueConversionOperand deals with the second part.
// FIXME: To get the third bullet right, we need to delay this even for
// variables that are not usable in constant expressions.
DeclRefExpr *DRE = dyn_cast_or_null<DeclRefExpr>(E);
switch (OdrUse) {
case OdrUseContext::None:
assert((!DRE || DRE->isNonOdrUse() == NOUR_Unevaluated) &&
"missing non-odr-use marking for unevaluated operand");
break;
case OdrUseContext::FormallyOdrUsed:
@ -15991,19 +16206,21 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
break;
case OdrUseContext::Used:
if (E && IsVariableAConstantExpression(Var, SemaRef.Context)) {
// A reference initialized by a constant expression can never be
// odr-used, so simply ignore it.
if (!Var->getType()->isReferenceType() ||
(SemaRef.LangOpts.OpenMP && SemaRef.isOpenMPCapturedDecl(Var)))
SemaRef.MaybeODRUseExprs.insert(E);
} else {
MarkVarDeclODRUsed(Var, Loc, SemaRef,
/*MaxFunctionScopeIndex ptr*/ nullptr);
}
// If we already know this isn't an odr-use, there's nothing more to do.
if (DRE && DRE->isNonOdrUse())
break;
// If we might later find that this expression isn't actually an odr-use,
// delay the marking.
if (E && Var->isUsableInConstantExpressions(SemaRef.Context))
SemaRef.MaybeODRUseExprs.insert(E);
else
MarkVarDeclODRUsed(Var, Loc, SemaRef);
break;
case OdrUseContext::Dependent:
// If we already know this isn't an odr-use, there's nothing more to do.
if (DRE && DRE->isNonOdrUse())
break;
// If this is a dependent context, we don't need to mark variables as
// odr-used, but we may still need to track them for lambda capture.
// FIXME: Do we also need to do this inside dependent typeid expressions
@ -16028,7 +16245,7 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
// FIXME: We can simplify this a lot after implementing P0588R1.
assert(E && "Capture variable should be used in an expression.");
if (!Var->getType()->isReferenceType() ||
!IsVariableNonDependentAndAConstantExpression(Var, SemaRef.Context))
!Var->isUsableInConstantExpressions(SemaRef.Context))
LSI->addPotentialCapture(E->IgnoreParens());
}
}
@ -16241,13 +16458,6 @@ namespace {
void VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) {
Visit(E->getExpr());
}
void VisitImplicitCastExpr(ImplicitCastExpr *E) {
Inherited::VisitImplicitCastExpr(E);
if (E->getCastKind() == CK_LValueToRValue)
S.UpdateMarkingForLValueToRValue(E->getSubExpr());
}
};
}

View File

@ -7395,7 +7395,7 @@ static inline bool VariableCanNeverBeAConstantExpression(VarDecl *Var,
return false;
}
return !IsVariableAConstantExpression(Var, Context);
return !Var->isUsableInConstantExpressions(Context);
}
/// Check if the current lambda has any potential captures

View File

@ -285,7 +285,7 @@ static void instantiateOMPDeclareSimdDeclAttr(
SmallVector<Expr *, 4> Uniforms, Aligneds, Alignments, Linears, Steps;
SmallVector<unsigned, 4> LinModifiers;
auto &&Subst = [&](Expr *E) -> ExprResult {
auto SubstExpr = [&](Expr *E) -> ExprResult {
if (auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
if (auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl())) {
Sema::ContextRAII SavedContext(S, FD);
@ -300,6 +300,17 @@ static void instantiateOMPDeclareSimdDeclAttr(
return S.SubstExpr(E, TemplateArgs);
};
// Substitute a single OpenMP clause, which is a potentially-evaluated
// full-expression.
auto Subst = [&](Expr *E) -> ExprResult {
EnterExpressionEvaluationContext Evaluated(
S, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
ExprResult Res = SubstExpr(E);
if (Res.isInvalid())
return Res;
return S.ActOnFinishFullExpr(Res.get(), false);
};
ExprResult Simdlen;
if (auto *E = Attr.getSimdlen())
Simdlen = Subst(E);
@ -4714,8 +4725,12 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
// of reference types, [...] explicit instantiation declarations
// have the effect of suppressing the implicit instantiation of the entity
// to which they refer.
//
// FIXME: That's not exactly the same as "might be usable in constant
// expressions", which only allows constexpr variables and const integral
// types, not arbitrary const literal types.
if (TSK == TSK_ExplicitInstantiationDeclaration &&
!Var->isUsableInConstantExpressions(getASTContext()))
!Var->mightBeUsableInConstantExpressions(getASTContext()))
return;
// Make sure to pass the instantiated variable to the consumer at the end.

View File

@ -554,6 +554,7 @@ void ASTStmtReader::VisitDeclRefExpr(DeclRefExpr *E) {
E->DeclRefExprBits.HasTemplateKWAndArgsInfo = Record.readInt();
E->DeclRefExprBits.HadMultipleCandidates = Record.readInt();
E->DeclRefExprBits.RefersToEnclosingVariableOrCapture = Record.readInt();
E->DeclRefExprBits.NonOdrUseReason = Record.readInt();
unsigned NumTemplateArgs = 0;
if (E->hasTemplateKWAndArgsInfo())
NumTemplateArgs = Record.readInt();
@ -2524,7 +2525,7 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
/*HasFoundDecl=*/Record[ASTStmtReader::NumExprFields + 1],
/*HasTemplateKWAndArgsInfo=*/Record[ASTStmtReader::NumExprFields + 2],
/*NumTemplateArgs=*/Record[ASTStmtReader::NumExprFields + 2] ?
Record[ASTStmtReader::NumExprFields + 5] : 0);
Record[ASTStmtReader::NumExprFields + 6] : 0);
break;
case EXPR_INTEGER_LITERAL:

View File

@ -2212,8 +2212,8 @@ void ASTWriter::WriteDeclAbbrevs() {
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //GetDeclFound
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //ExplicitTemplateArgs
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //HadMultipleCandidates
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed,
1)); // RefersToEnclosingVariableOrCapture
Abv->Add(BitCodeAbbrevOp(0)); // RefersToEnclosingVariableOrCapture
Abv->Add(BitCodeAbbrevOp(0)); // NonOdrUseReason
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // DeclRef
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Location
DeclRefExprAbbrev = Stream.EmitAbbrev(std::move(Abv));

View File

@ -456,6 +456,7 @@ void ASTStmtWriter::VisitDeclRefExpr(DeclRefExpr *E) {
Record.push_back(E->hasTemplateKWAndArgsInfo());
Record.push_back(E->hadMultipleCandidates());
Record.push_back(E->refersToEnclosingVariableOrCapture());
Record.push_back(E->isNonOdrUse());
if (E->hasTemplateKWAndArgsInfo()) {
unsigned NumTemplateArgs = E->getNumTemplateArgs();
@ -466,7 +467,8 @@ void ASTStmtWriter::VisitDeclRefExpr(DeclRefExpr *E) {
if ((!E->hasTemplateKWAndArgsInfo()) && (!E->hasQualifier()) &&
(E->getDecl() == E->getFoundDecl()) &&
nk == DeclarationName::Identifier) {
nk == DeclarationName::Identifier &&
!E->refersToEnclosingVariableOrCapture() && !E->isNonOdrUse()) {
AbbrevToUse = Writer.getDeclRefExprAbbrev();
}

View File

@ -86,7 +86,7 @@ struct Invalid {
//CHECK: {{^}}[[Blue]]| `-[[RESET]][[MAGENTA]]CXXConstructExpr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:8[[RESET]]> [[Green]]'class Mutex':'Mutex'[[RESET]][[Cyan]][[RESET]][[Cyan]][[RESET]] [[Green]]'void () noexcept'[[RESET]]{{$}}
//CHECK: {{^}}[[Blue]]|-[[RESET]][[GREEN]]VarDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:26:1[[RESET]], [[Yellow]]col:5[[RESET]]> [[Yellow]]col:5[[RESET]][[CYAN]] TestExpr[[RESET]] [[Green]]'int'[[RESET]]
//CHECK: {{^}}[[Blue]]| `-[[RESET]][[BLUE]]GuardedByAttr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:29[[RESET]], [[Yellow]]col:43[[RESET]]>{{$}}
//CHECK: {{^}}[[Blue]]| `-[[RESET]][[MAGENTA]]DeclRefExpr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:40[[RESET]]> [[Green]]'class Mutex':'Mutex'[[RESET]][[Cyan]] lvalue[[RESET]][[Cyan]][[RESET]] [[GREEN]]Var[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]][[CYAN]] 'mu1'[[RESET]] [[Green]]'class Mutex':'Mutex'[[RESET]]{{$}}
//CHECK: {{^}}[[Blue]]| `-[[RESET]][[MAGENTA]]DeclRefExpr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:40[[RESET]]> [[Green]]'class Mutex':'Mutex'[[RESET]][[Cyan]] lvalue[[RESET]][[Cyan]][[RESET]] [[GREEN]]Var[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]][[CYAN]] 'mu1'[[RESET]] [[Green]]'class Mutex':'Mutex'[[RESET]] non_odr_use_unevaluated{{$}}
//CHECK: {{^}}[[Blue]]|-[[RESET]][[GREEN]]CXXRecordDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:28:1[[RESET]], [[Yellow]]line:30:1[[RESET]]> [[Yellow]]line:28:8[[RESET]] struct[[CYAN]] Invalid[[RESET]] definition
//CHECK: {{^}}[[Blue]]| |-[[RESET]][[GREEN]]CXXRecordDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:1[[RESET]], [[Yellow]]col:8[[RESET]]> [[Yellow]]col:8[[RESET]] implicit referenced struct[[CYAN]] Invalid[[RESET]]
//CHECK: {{^}}[[Blue]]| |-[[RESET]][[GREEN]]CXXConstructorDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:29:3[[RESET]], [[Yellow]]col:42[[RESET]]> [[Yellow]]col:29[[RESET]] invalid[[CYAN]] Invalid[[RESET]] [[Green]]'void (int)'[[RESET]]

View File

@ -3877,7 +3877,8 @@ void PrimaryExpressions(int a) {
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "int"
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "nonOdrUseReason": "unevaluated"
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: },

View File

@ -1596,7 +1596,8 @@ void TestNonADLCall3() {
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "int *"
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "nonOdrUseReason": "unevaluated"
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: },
@ -1647,7 +1648,8 @@ void TestNonADLCall3() {
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "int *"
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "nonOdrUseReason": "unevaluated"
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: }

View File

@ -1342,7 +1342,8 @@ void TestMiscStmts(void) {
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "int"
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "nonOdrUseReason": "unevaluated"
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: },
@ -1456,7 +1457,8 @@ void TestMiscStmts(void) {
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "int"
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "nonOdrUseReason": "unevaluated"
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: },
@ -1596,7 +1598,8 @@ void TestMiscStmts(void) {
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "int"
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "nonOdrUseReason": "unevaluated"
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: },
@ -1736,7 +1739,8 @@ void TestMiscStmts(void) {
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "int"
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "nonOdrUseReason": "unevaluated"
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: },
@ -1951,7 +1955,8 @@ void TestMiscStmts(void) {
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "int"
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "nonOdrUseReason": "unevaluated"
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: },

View File

@ -70,7 +70,7 @@ void TestSwitch(int i) {
}
void TestIf(bool b) {
if (int i = 12; b)
if (const int i = 12; i)
;
if constexpr (sizeof(b) == 1)
@ -2719,7 +2719,7 @@ void TestIteration() {
// CHECK-NEXT: "line": 72
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "isUsed": true,
// CHECK-NEXT: "isReferenced": true,
// CHECK-NEXT: "name": "b",
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "bool"
@ -2768,7 +2768,7 @@ void TestIteration() {
// CHECK-NEXT: "line": 73
// CHECK-NEXT: },
// CHECK-NEXT: "end": {
// CHECK-NEXT: "col": 17,
// CHECK-NEXT: "col": 23,
// CHECK-NEXT: "file": "{{.*}}",
// CHECK-NEXT: "line": 73
// CHECK-NEXT: }
@ -2778,7 +2778,7 @@ void TestIteration() {
// CHECK-NEXT: "id": "0x{{.*}}",
// CHECK-NEXT: "kind": "VarDecl",
// CHECK-NEXT: "loc": {
// CHECK-NEXT: "col": 11,
// CHECK-NEXT: "col": 17,
// CHECK-NEXT: "file": "{{.*}}",
// CHECK-NEXT: "line": 73
// CHECK-NEXT: },
@ -2789,14 +2789,15 @@ void TestIteration() {
// CHECK-NEXT: "line": 73
// CHECK-NEXT: },
// CHECK-NEXT: "end": {
// CHECK-NEXT: "col": 15,
// CHECK-NEXT: "col": 21,
// CHECK-NEXT: "file": "{{.*}}",
// CHECK-NEXT: "line": 73
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "isReferenced": true,
// CHECK-NEXT: "name": "i",
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "int"
// CHECK-NEXT: "qualType": "const int"
// CHECK-NEXT: },
// CHECK-NEXT: "init": "c",
// CHECK-NEXT: "inner": [
@ -2805,12 +2806,12 @@ void TestIteration() {
// CHECK-NEXT: "kind": "IntegerLiteral",
// CHECK-NEXT: "range": {
// CHECK-NEXT: "begin": {
// CHECK-NEXT: "col": 15,
// CHECK-NEXT: "col": 21,
// CHECK-NEXT: "file": "{{.*}}",
// CHECK-NEXT: "line": 73
// CHECK-NEXT: },
// CHECK-NEXT: "end": {
// CHECK-NEXT: "col": 15,
// CHECK-NEXT: "col": 21,
// CHECK-NEXT: "file": "{{.*}}",
// CHECK-NEXT: "line": 73
// CHECK-NEXT: }
@ -2830,12 +2831,12 @@ void TestIteration() {
// CHECK-NEXT: "kind": "ImplicitCastExpr",
// CHECK-NEXT: "range": {
// CHECK-NEXT: "begin": {
// CHECK-NEXT: "col": 19,
// CHECK-NEXT: "col": 25,
// CHECK-NEXT: "file": "{{.*}}",
// CHECK-NEXT: "line": 73
// CHECK-NEXT: },
// CHECK-NEXT: "end": {
// CHECK-NEXT: "col": 19,
// CHECK-NEXT: "col": 25,
// CHECK-NEXT: "file": "{{.*}}",
// CHECK-NEXT: "line": 73
// CHECK-NEXT: }
@ -2844,35 +2845,59 @@ void TestIteration() {
// CHECK-NEXT: "qualType": "bool"
// CHECK-NEXT: },
// CHECK-NEXT: "valueCategory": "rvalue",
// CHECK-NEXT: "castKind": "LValueToRValue",
// CHECK-NEXT: "castKind": "IntegralToBoolean",
// CHECK-NEXT: "inner": [
// CHECK-NEXT: {
// CHECK-NEXT: "id": "0x{{.*}}",
// CHECK-NEXT: "kind": "DeclRefExpr",
// CHECK-NEXT: "kind": "ImplicitCastExpr",
// CHECK-NEXT: "range": {
// CHECK-NEXT: "begin": {
// CHECK-NEXT: "col": 19,
// CHECK-NEXT: "col": 25,
// CHECK-NEXT: "file": "{{.*}}",
// CHECK-NEXT: "line": 73
// CHECK-NEXT: },
// CHECK-NEXT: "end": {
// CHECK-NEXT: "col": 19,
// CHECK-NEXT: "col": 25,
// CHECK-NEXT: "file": "{{.*}}",
// CHECK-NEXT: "line": 73
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "bool"
// CHECK-NEXT: "qualType": "int"
// CHECK-NEXT: },
// CHECK-NEXT: "valueCategory": "lvalue",
// CHECK-NEXT: "referencedDecl": {
// CHECK-NEXT: "id": "0x{{.*}}",
// CHECK-NEXT: "kind": "ParmVarDecl",
// CHECK-NEXT: "name": "b",
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "bool"
// CHECK-NEXT: "valueCategory": "rvalue",
// CHECK-NEXT: "castKind": "LValueToRValue",
// CHECK-NEXT: "inner": [
// CHECK-NEXT: {
// CHECK-NEXT: "id": "0x{{.*}}",
// CHECK-NEXT: "kind": "DeclRefExpr",
// CHECK-NEXT: "range": {
// CHECK-NEXT: "begin": {
// CHECK-NEXT: "col": 25,
// CHECK-NEXT: "file": "{{.*}}",
// CHECK-NEXT: "line": 73
// CHECK-NEXT: },
// CHECK-NEXT: "end": {
// CHECK-NEXT: "col": 25,
// CHECK-NEXT: "file": "{{.*}}",
// CHECK-NEXT: "line": 73
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "const int"
// CHECK-NEXT: },
// CHECK-NEXT: "valueCategory": "lvalue",
// CHECK-NEXT: "referencedDecl": {
// CHECK-NEXT: "id": "0x{{.*}}",
// CHECK-NEXT: "kind": "VarDecl",
// CHECK-NEXT: "name": "i",
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "const int"
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "nonOdrUseReason": "constant"
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: },
@ -3019,7 +3044,8 @@ void TestIteration() {
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "bool"
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "nonOdrUseReason": "unevaluated"
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: }
@ -3217,7 +3243,8 @@ void TestIteration() {
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "bool"
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "nonOdrUseReason": "unevaluated"
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: }

View File

@ -29,7 +29,7 @@ dynamic_cast_result derived_ptr = d;
// CHECK-NEXT: ParenExpr {{.*}} <col:{{.*}}, col:{{.*}}> 'Derived *'{{$}}
// CHECK-NEXT: CXXDynamicCastExpr {{.*}} <col:{{.*}}, col:{{.*}}> 'Derived *' dynamic_cast<struct Derived *> <Dynamic>{{$}}
// CHECK-NEXT: ImplicitCastExpr {{.*}} <col:{{.*}}> 'Base *' <LValueToRValue> part_of_explicit_cast{{$}}
// CHECK-NEXT: DeclRefExpr {{.*}} <col:{{.*}}> 'Base *' lvalue Var {{.*}} 'base_ptr' 'Base *'{{$}}
// CHECK-NEXT: DeclRefExpr {{.*}} <col:{{.*}}> 'Base *' lvalue Var {{.*}} 'base_ptr' 'Base *' non_odr_use_unevaluated{{$}}
// CXXReinterpretCastExpr
reinterpret_cast_result void_ptr2 = &integer;
@ -46,7 +46,7 @@ const_cast_result char_ptr = &character;
// CHECK-NEXT: ParenExpr {{.*}} <col:{{.*}}, col:{{.*}}> 'char *'{{$}}
// CHECK-NEXT: CXXConstCastExpr {{.*}} <col:{{.*}}, col:{{.*}}> 'char *' const_cast<char *> <NoOp>{{$}}
// CHECK-NEXT: ImplicitCastExpr {{.*}} <col:{{.*}}> 'const char *' <LValueToRValue> part_of_explicit_cast{{$}}
// CHECK-NEXT: DeclRefExpr {{.*}} <col:{{.*}}> 'const char *' lvalue Var {{.*}} 'const_char_ptr_value' 'const char *'{{$}}
// CHECK-NEXT: DeclRefExpr {{.*}} <col:{{.*}}> 'const char *' lvalue Var {{.*}} 'const_char_ptr_value' 'const char *' non_odr_use_unevaluated{{$}}
// CXXFunctionalCastExpr
functional_cast_result *double_ptr = &floating;
@ -56,7 +56,7 @@ functional_cast_result *double_ptr = &floating;
// CHECK-NEXT: CXXFunctionalCastExpr {{.*}} <col:{{.*}}, col:{{.*}}> 'double' functional cast to double <NoOp>{{$}}
// CHECK-NEXT: ImplicitCastExpr {{.*}} <col:{{.*}}> 'double' <IntegralToFloating> part_of_explicit_cast{{$}}
// CHECK-NEXT: ImplicitCastExpr {{.*}} <col:{{.*}}> 'int' <LValueToRValue> part_of_explicit_cast{{$}}
// CHECK-NEXT: DeclRefExpr {{.*}} <col:{{.*}}> 'int' lvalue Var {{.*}} 'int_value' 'int'{{$}}
// CHECK-NEXT: DeclRefExpr {{.*}} <col:{{.*}}> 'int' lvalue Var {{.*}} 'int_value' 'int' non_odr_use_unevaluated{{$}}
// CXXBoolLiteralExpr
bool_literal_result *bool_ptr = &boolean;