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