diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2019-06-11 17:50:32 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2019-06-11 17:50:32 +0000 |
commit | 715f7a1bd058c64a39cc4773114dfb46ae8cc8a3 (patch) | |
tree | d9f4369bedcbe50c9dc03ee5498544702d86616e /clang | |
parent | ef2d6d99c0d35e46dce7fff6b999a3a1fc08aecc (diff) | |
download | bcm5719-llvm-715f7a1bd058c64a39cc4773114dfb46ae8cc8a3.tar.gz bcm5719-llvm-715f7a1bd058c64a39cc4773114dfb46ae8cc8a3.zip |
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
Diffstat (limited to 'clang')
28 files changed, 484 insertions, 168 deletions
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index f295eca44e8..37b6181c4e3 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -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; diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 438b10cc964..682e39fbf08 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -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 { diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index d3b3bc27643..1e8ca63952a 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -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; diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h index cc0c1c82dff..be793117850 100644 --- a/clang/include/clang/Basic/Specifiers.h +++ b/clang/include/clang/Basic/Specifiers.h @@ -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 { diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index a065be308ee..8b9abd3a73b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -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 { diff --git a/clang/include/clang/Sema/SemaInternal.h b/clang/include/clang/Sema/SemaInternal.h index dfeca60349e..dfb34daa14d 100644 --- a/clang/include/clang/Sema/SemaInternal.h +++ b/clang/include/clang/Sema/SemaInternal.h @@ -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. diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index e0a9da5d2ad..c4ae3b80b96 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -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; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 8b77e01f4e8..1b744c82a09 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -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. diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index ee9d853f9f8..b772518fc70 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -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, diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp index 991cf096142..1290847e1aa 100644 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -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) { diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index c15713b50db..3b8c8f22c37 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -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( diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 61f9de9a292..db18ac49c6e 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -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() && diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 5b576555146..46b1af5e1cf 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -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(); diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 1a8948b94dc..077bb0a61ac 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -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()) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 2159a20c2c1..653bf879828 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -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. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 6bd7b4e0711..10837f68943 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -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. diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 2b714a36e2a..5cc3fb616df 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -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; } +/// 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. + // + // 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). + // Rebuild a subexpression. + auto Rebuild = [&](Expr *Sub) { + return rebuildPotentialResultsAsNonOdrUsed(S, Sub, NOUR); + }; -// 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) { + // 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; - 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()) + // 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; - return IsVariableAConstantExpression(Var, Context); -} + }; + // Mark that this expression does not constitute an odr-use. + auto MarkNotOdrUsed = [&] { + S.MaybeODRUseExprs.erase(E); + if (LambdaScopeInfo *LSI = S.getCurLambda()) + LSI->markVariableExprAsNonODRUsed(E); + }; -void Sema::UpdateMarkingForLValueToRValue(Expr *E) { - // 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()); + // 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; - // 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 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, ... - for (VarDecl *VD : Vars) { - if (VD && IsVariableNonDependentAndAConstantExpression(VD, Context)) - LSI->markVariableExprAsNonODRUsed(SansParensExpr); + // [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()); - } }; } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 2f7e4a0f15c..2e5d1c7c38f 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -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 diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 6c5847ddfcd..d0ff0994ed8 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -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. diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index b89bb044b6d..a5ed9364509 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -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: diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 5c0f2dff56e..958957a70b3 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -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)); diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index d52a4a85b32..2b707520a8e 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -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(); } diff --git a/clang/test/AST/ast-dump-color.cpp b/clang/test/AST/ast-dump-color.cpp index 8cf52049251..fe67c537752 100644 --- a/clang/test/AST/ast-dump-color.cpp +++ b/clang/test/AST/ast-dump-color.cpp @@ -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]] diff --git a/clang/test/AST/ast-dump-expr-json.c b/clang/test/AST/ast-dump-expr-json.c index 77183be2f91..5dd86f38f5b 100644 --- a/clang/test/AST/ast-dump-expr-json.c +++ b/clang/test/AST/ast-dump-expr-json.c @@ -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: }, diff --git a/clang/test/AST/ast-dump-expr-json.cpp b/clang/test/AST/ast-dump-expr-json.cpp index d56d79d8c16..04c467d81c6 100644 --- a/clang/test/AST/ast-dump-expr-json.cpp +++ b/clang/test/AST/ast-dump-expr-json.cpp @@ -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: } diff --git a/clang/test/AST/ast-dump-stmt-json.c b/clang/test/AST/ast-dump-stmt-json.c index b43efd58fec..b93363ecb2b 100644 --- a/clang/test/AST/ast-dump-stmt-json.c +++ b/clang/test/AST/ast-dump-stmt-json.c @@ -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: }, diff --git a/clang/test/AST/ast-dump-stmt-json.cpp b/clang/test/AST/ast-dump-stmt-json.cpp index 883ac59409e..b9428618c30 100644 --- a/clang/test/AST/ast-dump-stmt-json.cpp +++ b/clang/test/AST/ast-dump-stmt-json.cpp @@ -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: } diff --git a/clang/test/PCH/cxx_exprs.cpp b/clang/test/PCH/cxx_exprs.cpp index 928a21125db..dd99b04a7d2 100644 --- a/clang/test/PCH/cxx_exprs.cpp +++ b/clang/test/PCH/cxx_exprs.cpp @@ -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; |