diff options
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;  | 

