diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index d8c0b624ef71..30d910c93022 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -289,6 +289,9 @@ class ASTContext : public RefCountedBase { /// Mapping from GUIDs to the corresponding MSGuidDecl. mutable llvm::FoldingSet MSGuidDecls; + /// Mapping from APValues to the corresponding TemplateParamObjects. + mutable llvm::FoldingSet TemplateParamObjectDecls; + /// A cache mapping a string value to a StringLiteral object with the same /// value. /// @@ -2868,6 +2871,11 @@ public: /// GUID value. MSGuidDecl *getMSGuidDecl(MSGuidDeclParts Parts) const; + /// Return the template parameter object of the given type with the given + /// value. + TemplateParamObjectDecl *getTemplateParamObjectDecl(QualType T, + const APValue &V) const; + /// Parses the target attributes passed in, and returns only the ones that are /// valid feature names. ParsedTargetAttr filterFunctionTargetAttrs(const TargetAttr *TD) const; diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 5f3525739091..7a175db8cb16 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -3226,7 +3226,7 @@ public: static bool classofKind(Kind K) { return K == VarTemplate; } }; -// \brief Declaration of a C++2a concept. +/// Declaration of a C++2a concept. class ConceptDecl : public TemplateDecl, public Mergeable { protected: Expr *ConstraintExpr; @@ -3255,6 +3255,9 @@ public: return isa(getTemplateParameters()->getParam(0)); } + ConceptDecl *getCanonicalDecl() override { return getFirstDecl(); } + const ConceptDecl *getCanonicalDecl() const { return getFirstDecl(); } + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == Concept; } @@ -3264,6 +3267,74 @@ public: friend class ASTDeclWriter; }; +/// A template parameter object. +/// +/// Template parameter objects represent values of class type used as template +/// arguments. There is one template parameter object for each such distinct +/// value used as a template argument across the program. +/// +/// \code +/// struct A { int x, y; }; +/// template struct S; +/// S s1; +/// S s2; // same type, argument is same TemplateParamObjectDecl. +/// \endcode +class TemplateParamObjectDecl : public ValueDecl, + public Mergeable, + public llvm::FoldingSetNode { +private: + /// The value of this template parameter object. + APValue Value; + + TemplateParamObjectDecl(DeclContext *DC, QualType T, const APValue &V) + : ValueDecl(TemplateParamObject, DC, SourceLocation(), DeclarationName(), + T), + Value(V) {} + + static TemplateParamObjectDecl *Create(const ASTContext &C, QualType T, + const APValue &V); + static TemplateParamObjectDecl *CreateDeserialized(ASTContext &C, + unsigned ID); + + /// Only ASTContext::getTemplateParamObjectDecl and deserialization + /// create these. + friend class ASTContext; + friend class ASTReader; + friend class ASTDeclReader; + +public: + /// Print this template parameter object in a human-readable format. + void printName(llvm::raw_ostream &OS) const override; + + /// Print this object as an equivalent expression. + void printAsExpr(llvm::raw_ostream &OS) const; + + /// Print this object as an initializer suitable for a variable of the + /// object's type. + void printAsInit(llvm::raw_ostream &OS) const; + + const APValue &getValue() const { return Value; } + + static void Profile(llvm::FoldingSetNodeID &ID, QualType T, + const APValue &V) { + ID.AddPointer(T.getCanonicalType().getAsOpaquePtr()); + V.profile(ID); + } + void Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, getType(), getValue()); + } + + TemplateParamObjectDecl *getCanonicalDecl() override { + return getFirstDecl(); + } + const TemplateParamObjectDecl *getCanonicalDecl() const { + return getFirstDecl(); + } + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == TemplateParamObject; } +}; + inline NamedDecl *getAsNamedDecl(TemplateParameter P) { if (auto *PD = P.dyn_cast()) return PD; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 6f07b92f2532..5e83cded0652 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1970,6 +1970,8 @@ DEF_TRAVERSE_DECL(MSPropertyDecl, { TRY_TO(TraverseDeclaratorHelper(D)); }) DEF_TRAVERSE_DECL(MSGuidDecl, {}) +DEF_TRAVERSE_DECL(TemplateParamObjectDecl, {}) + DEF_TRAVERSE_DECL(FieldDecl, { TRY_TO(TraverseDeclaratorHelper(D)); if (D->isBitField()) diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td index 866988ee3f01..4771a3549426 100644 --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -41,6 +41,7 @@ def Named : DeclNode; def OMPDeclareReduction : DeclNode, DeclContext; def OMPDeclareMapper : DeclNode, DeclContext; def MSGuid : DeclNode; + def TemplateParamObject : DeclNode; def Declarator : DeclNode; def Field : DeclNode; def ObjCIvar : DeclNode; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index ee942afd440a..641d3e73905e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1994,8 +1994,8 @@ def err_destructor_template : Error< // C++ initialization def err_init_conversion_failed : Error< - "cannot initialize %select{a variable|a parameter|return object|" - "statement expression result|an " + "cannot initialize %select{a variable|a parameter|template parameter|" + "return object|statement expression result|an " "exception object|a member subobject|an array element|a new value|a value|a " "base class|a constructor delegation|a vector element|a block element|a " "block element|a complex element|a lambda capture|a compound literal " @@ -2137,7 +2137,7 @@ def warn_unsequenced_mod_use : Warning< "unsequenced modification and access to %0">, InGroup; def select_initialized_entity_kind : TextSubstitution< - "%select{copying variable|copying parameter|" + "%select{copying variable|copying parameter|initializing template parameter|" "returning object|initializing statement expression result|" "throwing object|copying member subobject|copying array element|" "allocating object|copying temporary|initializing base subobject|" @@ -4492,6 +4492,10 @@ def note_not_structural_rvalue_ref_field : Note< def note_not_structural_subobject : Note< "%0 is not a structural type because it has a " "%select{non-static data member|base class}1 of non-structural type %2">; +def warn_cxx17_compat_template_nontype_parm_type : Warning< + "non-type template parameter of type %0 is incompatible with " + "C++ standards before C++20">, + DefaultIgnore, InGroup; def warn_cxx14_compat_template_nontype_parm_auto_type : Warning< "non-type template parameters declared with %0 are incompatible with C++ " "standards before C++17">, diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h index ca9e0a198cb9..6976e7c95c8b 100644 --- a/clang/include/clang/Sema/Initialization.h +++ b/clang/include/clang/Sema/Initialization.h @@ -55,6 +55,9 @@ public: /// The entity being initialized is a function parameter. EK_Parameter, + /// The entity being initialized is a non-type template parameter. + EK_TemplateParameter, + /// The entity being initialized is the result of a function call. EK_Result, @@ -175,7 +178,8 @@ private: }; union { - /// When Kind == EK_Variable, EK_Member or EK_Binding, the variable. + /// When Kind == EK_Variable, EK_Member, EK_Binding, or + /// EK_TemplateParameter, the variable, binding, or template parameter. VD Variable; /// When Kind == EK_RelatedResult, the ObjectiveC method where @@ -281,6 +285,17 @@ public: return Entity; } + /// Create the initialization entity for a template parameter. + static InitializedEntity + InitializeTemplateParameter(QualType T, NonTypeTemplateParmDecl *Param) { + InitializedEntity Entity; + Entity.Kind = EK_TemplateParameter; + Entity.Type = T; + Entity.Parent = nullptr; + Entity.Variable = {Param, false, false}; + return Entity; + } + /// Create the initialization entity for the result of a function. static InitializedEntity InitializeResult(SourceLocation ReturnLoc, QualType Type, bool NRVO) { @@ -441,6 +456,10 @@ public: getKind() == EK_Parameter_CF_Audited); } + bool isParamOrTemplateParamKind() const { + return isParameterKind() || getKind() == EK_TemplateParameter; + } + /// Determine whether this initialization consumes the /// parameter. bool isParameterConsumed() const { diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 7ced3d9a7bd4..18f115003f38 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3369,7 +3369,8 @@ public: ExprResult CheckConvertedConstantExpression(Expr *From, QualType T, llvm::APSInt &Value, CCEKind CCE); ExprResult CheckConvertedConstantExpression(Expr *From, QualType T, - APValue &Value, CCEKind CCE); + APValue &Value, CCEKind CCE, + NamedDecl *Dest = nullptr); /// Abstract base class used to perform a contextual implicit /// conversion from an expression to any type passing a filter. diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index c6f9f1d1a08f..a2ae032ed5b8 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1281,6 +1281,9 @@ public: /// A MSGuidDecl record. DECL_MS_GUID, + /// A TemplateParamObjectDecl record. + DECL_TEMPLATE_PARAM_OBJECT, + /// A VarDecl record. DECL_VAR, diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 32bb3f991d95..4f3566a66962 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -4870,9 +4870,16 @@ TemplateArgument ASTContext::getInjectedTemplateArg(NamedDecl *Param) { Arg = TemplateArgument(ArgType); } else if (auto *NTTP = dyn_cast(Param)) { + QualType T = + NTTP->getType().getNonPackExpansionType().getNonLValueExprType(*this); + // For class NTTPs, ensure we include the 'const' so the type matches that + // of a real template argument. + // FIXME: It would be more faithful to model this as something like an + // lvalue-to-rvalue conversion applied to a const-qualified lvalue. + if (T->isRecordType()) + T.addConst(); Expr *E = new (*this) DeclRefExpr( - *this, NTTP, /*enclosing*/ false, - NTTP->getType().getNonPackExpansionType().getNonLValueExprType(*this), + *this, NTTP, /*enclosing*/ false, T, Expr::getValueKindForType(NTTP->getType()), NTTP->getLocation()); if (NTTP->isParameterPack()) @@ -10962,6 +10969,27 @@ ASTContext::getMSGuidDecl(MSGuidDecl::Parts Parts) const { return New; } +TemplateParamObjectDecl * +ASTContext::getTemplateParamObjectDecl(QualType T, const APValue &V) const { + assert(T->isRecordType() && "template param object of unexpected type"); + + // C++ [temp.param]p8: + // [...] a static storage duration object of type 'const T' [...] + T.addConst(); + + llvm::FoldingSetNodeID ID; + TemplateParamObjectDecl::Profile(ID, T, V); + + void *InsertPos; + if (TemplateParamObjectDecl *Existing = + TemplateParamObjectDecls.FindNodeOrInsertPos(ID, InsertPos)) + return Existing; + + TemplateParamObjectDecl *New = TemplateParamObjectDecl::Create(*this, T, V); + TemplateParamObjectDecls.InsertNode(New, InsertPos); + return New; +} + bool ASTContext::AtomicUsesUnsupportedLibcall(const AtomicExpr *E) const { const llvm::Triple &T = getTargetInfo().getTriple(); if (!T.isOSDarwin()) diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp index 99ce46e83123..2bc731717b98 100644 --- a/clang/lib/AST/ASTDiagnostic.cpp +++ b/clang/lib/AST/ASTDiagnostic.cpp @@ -1834,7 +1834,14 @@ class TemplateDiff { if (VD) { if (AddressOf) OS << "&"; - OS << VD->getName(); + else if (auto *TPO = dyn_cast(VD)) { + // FIXME: Diffing the APValue would be neat. + // FIXME: Suppress this and use the full name of the declaration if the + // parameter is a pointer or reference. + TPO->printAsInit(OS); + return; + } + VD->printName(OS); return; } diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index f2502c327a11..0656efae5489 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -835,6 +835,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case ExternCContext: case Decomposition: case MSGuid: + case TemplateParamObject: case UsingDirective: case BuiltinTemplate: diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index d99a9c19c506..9918377070c3 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -1431,3 +1431,36 @@ void TypeConstraint::print(llvm::raw_ostream &OS, PrintingPolicy Policy) const { OS << ">"; } } + +TemplateParamObjectDecl *TemplateParamObjectDecl::Create(const ASTContext &C, + QualType T, + const APValue &V) { + DeclContext *DC = C.getTranslationUnitDecl(); + auto *TPOD = new (C, DC) TemplateParamObjectDecl(DC, T, V); + C.addDestruction(&TPOD->Value); + return TPOD; +} + +TemplateParamObjectDecl * +TemplateParamObjectDecl::CreateDeserialized(ASTContext &C, unsigned ID) { + auto *TPOD = new (C, ID) TemplateParamObjectDecl(nullptr, QualType(), APValue()); + C.addDestruction(&TPOD->Value); + return TPOD; +} + +void TemplateParamObjectDecl::printName(llvm::raw_ostream &OS) const { + OS << "