Author: Richard Smith Date: 2020-10-21T13:21:41-07:00 New Revision: ba4768c966581658465f7366df9b0811f468a2d7
URL: https://github.com/llvm/llvm-project/commit/ba4768c966581658465f7366df9b0811f468a2d7 DIFF: https://github.com/llvm/llvm-project/commit/ba4768c966581658465f7366df9b0811f468a2d7.diff LOG: [c++20] For P0732R2 / P1907R1: Basic frontend support for class types as non-type template parameters. Create a unique TemplateParamObjectDecl instance for each such value, representing the globally unique template parameter object to which the template parameter refers. No IR generation support yet; that will follow in a separate patch. Added: clang/test/CXX/temp/temp.param/p8-cxx20.cpp clang/test/PCH/cxx20-template-args.cpp Modified: clang/include/clang/AST/ASTContext.h clang/include/clang/AST/DeclTemplate.h clang/include/clang/AST/RecursiveASTVisitor.h clang/include/clang/Basic/DeclNodes.td clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Sema/Initialization.h clang/include/clang/Sema/Sema.h clang/include/clang/Serialization/ASTBitCodes.h clang/lib/AST/ASTContext.cpp clang/lib/AST/ASTDiagnostic.cpp clang/lib/AST/DeclBase.cpp clang/lib/AST/DeclTemplate.cpp clang/lib/AST/ExprClassification.cpp clang/lib/AST/ExprConstant.cpp clang/lib/AST/ItaniumMangle.cpp clang/lib/AST/MicrosoftMangle.cpp clang/lib/AST/StmtPrinter.cpp clang/lib/AST/TemplateBase.cpp clang/lib/CodeGen/CGDecl.cpp clang/lib/CodeGen/CGExpr.cpp clang/lib/CodeGen/CGExprConstant.cpp clang/lib/CodeGen/CodeGenModule.cpp clang/lib/CodeGen/CodeGenModule.h clang/lib/Sema/SemaExpr.cpp clang/lib/Sema/SemaInit.cpp clang/lib/Sema/SemaOverload.cpp clang/lib/Sema/SemaTemplate.cpp clang/lib/Sema/SemaTemplateDeduction.cpp clang/lib/Sema/SemaTemplateInstantiateDecl.cpp clang/lib/Serialization/ASTCommon.cpp clang/lib/Serialization/ASTReaderDecl.cpp clang/lib/Serialization/ASTWriterDecl.cpp clang/test/CXX/drs/dr3xx.cpp clang/test/SemaCXX/cxx17-compat.cpp clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp clang/tools/libclang/CIndex.cpp Removed: ################################################################################ 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<ASTContext> { /// Mapping from GUIDs to the corresponding MSGuidDecl. mutable llvm::FoldingSet<MSGuidDecl> MSGuidDecls; + /// Mapping from APValues to the corresponding TemplateParamObjects. + mutable llvm::FoldingSet<TemplateParamObjectDecl> TemplateParamObjectDecls; + /// A cache mapping a string value to a StringLiteral object with the same /// value. /// @@ -2868,6 +2871,11 @@ class ASTContext : public RefCountedBase<ASTContext> { /// 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 @@ class VarTemplateDecl : public RedeclarableTemplateDecl { 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<ConceptDecl> { protected: Expr *ConstraintExpr; @@ -3255,6 +3255,9 @@ class ConceptDecl : public TemplateDecl, public Mergeable<ConceptDecl> { return isa<TemplateTypeParmDecl>(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 @@ class ConceptDecl : public TemplateDecl, public Mergeable<ConceptDecl> { 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<A> struct S; +/// S<A{1, 2}> s1; +/// S<A{1, 2}> s2; // same type, argument is same TemplateParamObjectDecl. +/// \endcode +class TemplateParamObjectDecl : public ValueDecl, + public Mergeable<TemplateParamObjectDecl>, + 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<TemplateTypeParmDecl *>()) 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<Decl, "named declarations", 1>; def OMPDeclareReduction : DeclNode<Value>, DeclContext; def OMPDeclareMapper : DeclNode<Value>, DeclContext; def MSGuid : DeclNode<Value>; + def TemplateParamObject : DeclNode<Value>; def Declarator : DeclNode<Value, "declarators", 1>; def Field : DeclNode<Declarator, "non-static data members">; def ObjCIvar : DeclNode<Field>; 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<Unsequenced>; 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<CXXPre20Compat>; 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 @@ class alignas(8) InitializedEntity { /// 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 @@ class alignas(8) InitializedEntity { }; 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 @@ class alignas(8) InitializedEntity { 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 @@ class alignas(8) InitializedEntity { 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 @@ class Sema final { 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 @@ class TypeIdx { /// 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<NonTypeTemplateParmDecl>(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<TemplateParamObjectDecl>(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 << "<template param "; + printAsExpr(OS); + OS << ">"; +} + +void TemplateParamObjectDecl::printAsExpr(llvm::raw_ostream &OS) const { + const ASTContext &Ctx = getASTContext(); + getType().getUnqualifiedType().print(OS, Ctx.getPrintingPolicy()); + printAsInit(OS); +} + +void TemplateParamObjectDecl::printAsInit(llvm::raw_ostream &OS) const { + const ASTContext &Ctx = getASTContext(); + getValue().printPretty(OS, Ctx, getType()); +} diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 31aa734ffedb..0286c730ce4e 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -453,12 +453,14 @@ static Cl::Kinds ClassifyDecl(ASTContext &Ctx, const Decl *D) { bool islvalue; if (const auto *NTTParm = dyn_cast<NonTypeTemplateParmDecl>(D)) - islvalue = NTTParm->getType()->isReferenceType(); + islvalue = NTTParm->getType()->isReferenceType() || + NTTParm->getType()->isRecordType(); else islvalue = isa<VarDecl>(D) || isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D) || isa<BindingDecl>(D) || isa<MSGuidDecl>(D) || + isa<TemplateParamObjectDecl>(D) || (Ctx.getLangOpts().CPlusPlus && (isa<FunctionDecl>(D) || isa<MSPropertyDecl>(D) || isa<FunctionTemplateDecl>(D))); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index b2ddca7f7ce0..7a933e3eaafb 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -1978,6 +1978,8 @@ static bool IsGlobalLValue(APValue::LValueBase B) { // ... the address of an object with static storage duration, if (const VarDecl *VD = dyn_cast<VarDecl>(D)) return VD->hasGlobalStorage(); + if (isa<TemplateParamObjectDecl>(D)) + return true; // ... the address of a function, // ... the address of a GUID [MS extension], return isa<FunctionDecl>(D) || isa<MSGuidDecl>(D); @@ -3980,6 +3982,16 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, return CompleteObject(LVal.Base, &V, GD->getType()); } + // Allow reading from template parameter objects. + if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(D)) { + if (isModification(AK)) { + Info.FFDiag(E, diag::note_constexpr_modify_global); + return CompleteObject(); + } + return CompleteObject(LVal.Base, const_cast<APValue *>(&TPO->getValue()), + TPO->getType()); + } + // In C++98, const, non-volatile integers initialized with ICEs are ICEs. // In C++11, constexpr, non-volatile variables initialized with constant // expressions are constant expressions too. Inside constexpr functions, @@ -8026,14 +8038,13 @@ static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info, } bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) { - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(E->getDecl())) - return Success(FD); - if (const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl())) + const NamedDecl *D = E->getDecl(); + if (isa<FunctionDecl, MSGuidDecl, TemplateParamObjectDecl>(D)) + return Success(cast<ValueDecl>(D)); + if (const VarDecl *VD = dyn_cast<VarDecl>(D)) return VisitVarDecl(E, VD); - if (const BindingDecl *BD = dyn_cast<BindingDecl>(E->getDecl())) + if (const BindingDecl *BD = dyn_cast<BindingDecl>(D)) return Visit(BD->getBinding()); - if (const MSGuidDecl *GD = dyn_cast<MSGuidDecl>(E->getDecl())) - return Success(GD); return Error(E); } diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 26fc7f8ec816..2417da392876 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -660,7 +660,12 @@ void CXXNameMangler::mangle(GlobalDecl GD) { mangleName(GuidD); else if (const BindingDecl *BD = dyn_cast<BindingDecl>(GD.getDecl())) mangleName(BD); - else + else if (isa<TemplateParamObjectDecl>(GD.getDecl())) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle template parameter objects yet"); + Diags.Report(SourceLocation(), DiagID); + } else llvm_unreachable("unexpected kind of global decl"); } diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index f6e6fefe98d2..227f5279d63d 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -503,7 +503,12 @@ void MicrosoftCXXNameMangler::mangle(const NamedDecl *D, StringRef Prefix) { // MSVC appears to mangle GUIDs as if they were variables of type // 'const struct __s_GUID'. Out << "3U__s_GUID@@B"; - else + else if (isa<TemplateParamObjectDecl>(D)) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle template parameter objects yet"); + Diags.Report(SourceLocation(), DiagID); + } else llvm_unreachable("Tried to mangle unexpected NamedDecl!"); } diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 1390876dc0df..55a721194ccf 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -970,6 +970,10 @@ void StmtPrinter::VisitDeclRefExpr(DeclRefExpr *Node) { OCED->getInit()->IgnoreImpCasts()->printPretty(OS, nullptr, Policy); return; } + if (const auto *TPOD = dyn_cast<TemplateParamObjectDecl>(Node->getDecl())) { + TPOD->printAsExpr(OS); + return; + } if (NestedNameSpecifier *Qualifier = Node->getQualifier()) Qualifier->print(OS, Policy); if (Node->hasTemplateKeyword()) diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp index 891c9bd0fd7a..22af2545779d 100644 --- a/clang/lib/AST/TemplateBase.cpp +++ b/clang/lib/AST/TemplateBase.cpp @@ -352,6 +352,13 @@ void TemplateArgument::print(const PrintingPolicy &Policy, case Declaration: { NamedDecl *ND = getAsDecl(); + if (getParamTypeForDecl()->isRecordType()) { + if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(ND)) { + // FIXME: Include the type if it's not obvious from the context. + TPO->printAsInit(Out); + break; + } + } if (!getParamTypeForDecl()->isReferenceType()) Out << '&'; ND->printQualifiedName(Out); diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 7daf91ff73e3..96922e415423 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -117,6 +117,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::Label: // __label__ x; case Decl::Import: case Decl::MSGuid: // __declspec(uuid("...")) + case Decl::TemplateParamObject: case Decl::OMPThreadPrivate: case Decl::OMPAllocate: case Decl::OMPCapturedExpr: diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index ed2c8e6a71f1..28be81c919b2 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -2827,6 +2827,10 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) { return MakeAddrLValue(CGM.GetAddrOfMSGuidDecl(GD), T, AlignmentSource::Decl); + if (const auto *TPO = dyn_cast<TemplateParamObjectDecl>(ND)) + return MakeAddrLValue(CGM.GetAddrOfTemplateParamObject(TPO), T, + AlignmentSource::Decl); + llvm_unreachable("Unhandled DeclRefExpr"); } diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 011d39cfa294..ca1d3a937fa8 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1905,6 +1905,9 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) { if (auto *GD = dyn_cast<MSGuidDecl>(D)) return CGM.GetAddrOfMSGuidDecl(GD); + if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(D)) + return CGM.GetAddrOfTemplateParamObject(TPO); + return nullptr; } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 93b49ec981e8..8b53ec9ab4a9 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2542,6 +2542,12 @@ ConstantAddress CodeGenModule::GetAddrOfMSGuidDecl(const MSGuidDecl *GD) { return ConstantAddress(Addr, Alignment); } +ConstantAddress CodeGenModule::GetAddrOfTemplateParamObject( + const TemplateParamObjectDecl *TPO) { + ErrorUnsupported(TPO, "template parameter object"); + return ConstantAddress::invalid(); +} + ConstantAddress CodeGenModule::GetWeakRefReference(const ValueDecl *VD) { const AliasAttr *AA = VD->getAttr<AliasAttr>(); assert(AA && "No alias?"); diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 088ed2830fb8..fc732dbea521 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -859,6 +859,10 @@ class CodeGenModule : public CodeGenTypeCache { /// Get the address of a GUID. ConstantAddress GetAddrOfMSGuidDecl(const MSGuidDecl *GD); + /// Get the address of a template parameter object. + ConstantAddress + GetAddrOfTemplateParamObject(const TemplateParamObjectDecl *TPO); + /// Get the address of the thunk for the given global decl. llvm::Constant *GetAddrOfThunk(StringRef Name, llvm::Type *FnTy, GlobalDecl GD); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 7b4e9edbfbf3..e844ad34240d 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -3226,6 +3226,17 @@ ExprResult Sema::BuildDeclarationNameExpr( break; } + // [expr.prim.id.unqual]p2: + // If the entity is a template parameter object for a template + // parameter of type T, the type of the expression is const T. + // [...] The expression is an lvalue if the entity is a [...] template + // parameter object. + if (type->isRecordType()) { + type = type.getUnqualifiedType().withConst(); + valueKind = VK_LValue; + break; + } + // For non-references, we need to strip qualifiers just in case // the template parameter was declared as 'const int' or whatever. valueKind = VK_RValue; @@ -3325,8 +3336,9 @@ ExprResult Sema::BuildDeclarationNameExpr( case Decl::MSProperty: case Decl::MSGuid: - // FIXME: Should MSGuidDecl be subject to capture in OpenMP, - // or duplicated between host and device? + case Decl::TemplateParamObject: + // FIXME: Should MSGuidDecl and template parameter objects be subject to + // capture in OpenMP, or duplicated between host and device? valueKind = VK_LValue; break; diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 485cb85f517f..419b25d278f5 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -1123,6 +1123,7 @@ static void warnBracedScalarInit(Sema &S, const InitializedEntity &Entity, case InitializedEntity::EK_ArrayElement: case InitializedEntity::EK_Parameter: case InitializedEntity::EK_Parameter_CF_Audited: + case InitializedEntity::EK_TemplateParameter: case InitializedEntity::EK_Result: // Extra braces here are suspicious. DiagID = diag::warn_braces_around_init; @@ -3283,6 +3284,7 @@ DeclarationName InitializedEntity::getName() const { case EK_Variable: case EK_Member: case EK_Binding: + case EK_TemplateParameter: return Variable.VariableOrMember->getDeclName(); case EK_LambdaCapture: @@ -3313,6 +3315,7 @@ ValueDecl *InitializedEntity::getDecl() const { case EK_Variable: case EK_Member: case EK_Binding: + case EK_TemplateParameter: return Variable.VariableOrMember; case EK_Parameter: @@ -3350,6 +3353,7 @@ bool InitializedEntity::allowsNRVO() const { case EK_Variable: case EK_Parameter: case EK_Parameter_CF_Audited: + case EK_TemplateParameter: case EK_Member: case EK_Binding: case EK_New: @@ -3381,6 +3385,7 @@ unsigned InitializedEntity::dumpImpl(raw_ostream &OS) const { case EK_Parameter: OS << "Parameter"; break; case EK_Parameter_CF_Audited: OS << "CF audited function Parameter"; break; + case EK_TemplateParameter: OS << "TemplateParameter"; break; case EK_Result: OS << "Result"; break; case EK_StmtExprResult: OS << "StmtExprResult"; break; case EK_Exception: OS << "Exception"; break; @@ -6001,6 +6006,11 @@ getAssignmentAction(const InitializedEntity &Entity, bool Diagnose = false) { // FIXME: Can we tell apart casting vs. converting? return Sema::AA_Casting; + case InitializedEntity::EK_TemplateParameter: + // This is really initialization, but refer to it as conversion for + // consistency with CheckConvertedConstantExpression. + return Sema::AA_Converting; + case InitializedEntity::EK_Member: case InitializedEntity::EK_Binding: case InitializedEntity::EK_ArrayElement: @@ -6035,6 +6045,7 @@ static bool shouldBindAsTemporary(const InitializedEntity &Entity) { case InitializedEntity::EK_LambdaToBlockConversionBlockElement: case InitializedEntity::EK_LambdaCapture: case InitializedEntity::EK_CompoundLiteralInit: + case InitializedEntity::EK_TemplateParameter: return false; case InitializedEntity::EK_Parameter: @@ -6069,6 +6080,7 @@ static bool shouldDestroyEntity(const InitializedEntity &Entity) { case InitializedEntity::EK_Variable: case InitializedEntity::EK_Parameter: case InitializedEntity::EK_Parameter_CF_Audited: + case InitializedEntity::EK_TemplateParameter: case InitializedEntity::EK_Temporary: case InitializedEntity::EK_ArrayElement: case InitializedEntity::EK_Exception: @@ -6102,6 +6114,7 @@ static SourceLocation getInitializationLoc(const InitializedEntity &Entity, case InitializedEntity::EK_Member: case InitializedEntity::EK_Parameter: case InitializedEntity::EK_Parameter_CF_Audited: + case InitializedEntity::EK_TemplateParameter: case InitializedEntity::EK_Temporary: case InitializedEntity::EK_New: case InitializedEntity::EK_Base: @@ -6345,7 +6358,7 @@ static void CheckCXX98CompatAccessibleCopy(Sema &S, void InitializationSequence::PrintInitLocationNote(Sema &S, const InitializedEntity &Entity) { - if (Entity.isParameterKind() && Entity.getDecl()) { + if (Entity.isParamOrTemplateParamKind() && Entity.getDecl()) { if (Entity.getDecl()->getLocation().isInvalid()) return; @@ -6611,6 +6624,10 @@ static LifetimeResult getEntityLifetime( // the call. return {nullptr, LK_FullExpression}; + case InitializedEntity::EK_TemplateParameter: + // FIXME: This will always be ill-formed; should we eagerly diagnose it here? + return {nullptr, LK_FullExpression}; + case InitializedEntity::EK_Result: // -- The lifetime of a temporary bound to the returned value in a // function return statement is not extended; the temporary is @@ -7882,7 +7899,7 @@ ExprResult InitializationSequence::Perform(Sema &S, if (S.getLangOpts().CPlusPlus11 && Entity.getType()->isReferenceType() && Args.size() == 1 && isa<InitListExpr>(Args[0]) && - !Entity.isParameterKind()) { + !Entity.isParamOrTemplateParamKind()) { // Produce a C++98 compatibility warning if we are initializing a reference // from an initializer list. For parameters, we produce a better warning // elsewhere. diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index d2eabd0c0512..7da2a8c2d84f 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -5571,7 +5571,8 @@ static bool CheckConvertedConstantConversions(Sema &S, static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From, QualType T, APValue &Value, Sema::CCEKind CCE, - bool RequireInt) { + bool RequireInt, + NamedDecl *Dest) { assert(S.getLangOpts().CPlusPlus11 && "converted constant expression outside C++11"); @@ -5601,9 +5602,10 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From, SCS = &ICS.Standard; break; case ImplicitConversionSequence::UserDefinedConversion: - // We are converting to a non-class type, so the Before sequence - // must be trivial. - SCS = &ICS.UserDefined.After; + if (T->isRecordType()) + SCS = &ICS.UserDefined.Before; + else + SCS = &ICS.UserDefined.After; break; case ImplicitConversionSequence::AmbiguousConversion: case ImplicitConversionSequence::BadConversion: @@ -5630,8 +5632,20 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From, << From->getType() << From->getSourceRange() << T; } - ExprResult Result = - S.PerformImplicitConversion(From, T, ICS, Sema::AA_Converting); + // Usually we can simply apply the ImplicitConversionSequence we formed + // earlier, but that's not guaranteed to work when initializing an object of + // class type. + ExprResult Result; + if (T->isRecordType()) { + assert(CCE == Sema::CCEK_TemplateArg && + "unexpected class type converted constant expr"); + Result = S.PerformCopyInitialization( + InitializedEntity::InitializeTemplateParameter( + T, cast<NonTypeTemplateParmDecl>(Dest)), + SourceLocation(), From); + } else { + Result = S.PerformImplicitConversion(From, T, ICS, Sema::AA_Converting); + } if (Result.isInvalid()) return Result; @@ -5724,8 +5738,10 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From, } ExprResult Sema::CheckConvertedConstantExpression(Expr *From, QualType T, - APValue &Value, CCEKind CCE) { - return ::CheckConvertedConstantExpression(*this, From, T, Value, CCE, false); + APValue &Value, CCEKind CCE, + NamedDecl *Dest) { + return ::CheckConvertedConstantExpression(*this, From, T, Value, CCE, false, + Dest); } ExprResult Sema::CheckConvertedConstantExpression(Expr *From, QualType T, @@ -5734,7 +5750,8 @@ ExprResult Sema::CheckConvertedConstantExpression(Expr *From, QualType T, assert(T->isIntegralOrEnumerationType() && "unexpected converted const type"); APValue V; - auto R = ::CheckConvertedConstantExpression(*this, From, T, V, CCE, true); + auto R = ::CheckConvertedConstantExpression(*this, From, T, V, CCE, true, + /*Dest=*/nullptr); if (!R.isInvalid() && !R.get()->isValueDependent()) Value = V.getInt(); return R; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 660964c8f92b..4017e65118d9 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1441,6 +1441,7 @@ QualType Sema::CheckNonTypeTemplateParameterType(QualType T, return QualType(); } + Diag(Loc, diag::warn_cxx17_compat_template_nontype_parm_type) << T; return T.getUnqualifiedType(); } @@ -6857,9 +6858,12 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, assert(!ParamType.hasQualifiers() && "non-type template parameter type cannot be qualified"); + // FIXME: When Param is a reference, should we check that Arg is an lvalue? if (CTAK == CTAK_Deduced && - !Context.hasSameType(ParamType.getNonLValueExprType(Context), - Arg->getType())) { + (ParamType->isReferenceType() + ? !Context.hasSameType(ParamType.getNonReferenceType(), + Arg->getType()) + : !Context.hasSameUnqualifiedType(ParamType, Arg->getType()))) { // FIXME: If either type is dependent, we skip the check. This isn't // correct, since during deduction we're supposed to have replaced each // template parameter with some unique (non-dependent) placeholder. @@ -6915,12 +6919,38 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, *this, Sema::ExpressionEvaluationContext::ConstantEvaluated); if (getLangOpts().CPlusPlus17) { + QualType CanonParamType = Context.getCanonicalType(ParamType); + + // Avoid making a copy when initializing a template parameter of class type + // from a template parameter object of the same type. This is going beyond + // the standard, but is required for soundness: in + // template<A a> struct X { X *p; X<a> *q; }; + // ... we need p and q to have the same type. + // + // Similarly, don't inject a call to a copy constructor when initializing + // from a template parameter of the same type. + Expr *InnerArg = Arg; + while (auto *SNTTP = dyn_cast<SubstNonTypeTemplateParmExpr>(InnerArg)) + InnerArg = SNTTP->getReplacement(); + if (ParamType->isRecordType() && isa<DeclRefExpr>(InnerArg) && + Context.hasSameUnqualifiedType(ParamType, InnerArg->getType())) { + NamedDecl *ND = cast<DeclRefExpr>(InnerArg)->getDecl(); + if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(ND)) { + Converted = TemplateArgument(TPO, CanonParamType); + return Arg; + } + if (isa<NonTypeTemplateParmDecl>(ND)) { + Converted = TemplateArgument(Arg); + return Arg; + } + } + // C++17 [temp.arg.nontype]p1: // A template-argument for a non-type template parameter shall be // a converted constant expression of the type of the template-parameter. APValue Value; ExprResult ArgResult = CheckConvertedConstantExpression( - Arg, ParamType, Value, CCEK_TemplateArg); + Arg, ParamType, Value, CCEK_TemplateArg, Param); if (ArgResult.isInvalid()) return ExprError(); @@ -6931,8 +6961,6 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, return ArgResult; } - QualType CanonParamType = Context.getCanonicalType(ParamType); - // Convert the APValue to a TemplateArgument. switch (Value.getKind()) { case APValue::None: @@ -7002,6 +7030,13 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, : TemplateArgument(CanonParamType, /*isNullPtr*/true); break; } + case APValue::Struct: + case APValue::Union: + // Get or create the corresponding template parameter object. + Converted = TemplateArgument( + Context.getTemplateParamObjectDecl(CanonParamType, Value), + CanonParamType); + break; case APValue::AddrLabelDiff: return Diag(StartLoc, diag::err_non_type_template_arg_addr_label_ diff ); case APValue::FixedPoint: @@ -7010,8 +7045,6 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, case APValue::ComplexFloat: case APValue::Vector: case APValue::Array: - case APValue::Struct: - case APValue::Union: return Diag(StartLoc, diag::err_non_type_template_arg_unsupported) << ParamType; } @@ -7505,6 +7538,11 @@ Sema::BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg, RefExpr = CreateBuiltinUnaryOp(Loc, UO_AddrOf, RefExpr.get()); if (RefExpr.isInvalid()) return ExprError(); + } else if (ParamType->isRecordType()) { + assert(isa<TemplateParamObjectDecl>(VD) && + "arg for class template param not a template parameter object"); + // No conversions apply in this case. + return RefExpr; } else { assert(ParamType->isReferenceType() && "unexpected type for decl template argument"); diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index b266f360a138..1c20ca619138 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -171,30 +171,41 @@ static void MarkUsedTemplateParameters(ASTContext &Ctx, QualType T, /// If the given expression is of a form that permits the deduction /// of a non-type template parameter, return the declaration of that /// non-type template parameter. -static NonTypeTemplateParmDecl * -getDeducedParameterFromExpr(TemplateDeductionInfo &Info, Expr *E) { +static const NonTypeTemplateParmDecl * +getDeducedParameterFromExpr(const Expr *E, unsigned Depth) { // If we are within an alias template, the expression may have undergone // any number of parameter substitutions already. while (true) { - if (ImplicitCastExpr *IC = dyn_cast<ImplicitCastExpr>(E)) + if (const auto *IC = dyn_cast<ImplicitCastExpr>(E)) E = IC->getSubExpr(); - else if (ConstantExpr *CE = dyn_cast<ConstantExpr>(E)) + else if (const auto *CE = dyn_cast<ConstantExpr>(E)) E = CE->getSubExpr(); - else if (SubstNonTypeTemplateParmExpr *Subst = - dyn_cast<SubstNonTypeTemplateParmExpr>(E)) + else if (const auto *Subst = dyn_cast<SubstNonTypeTemplateParmExpr>(E)) E = Subst->getReplacement(); - else + else if (const auto *CCE = dyn_cast<CXXConstructExpr>(E)) { + // Look through implicit copy construction from an lvalue of the same type. + if (CCE->getParenOrBraceRange().isValid()) + break; + // Note, there could be default arguments. + assert(CCE->getNumArgs() >= 1 && "implicit construct expr should have 1 arg"); + E = CCE->getArg(0); + } else break; } - if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) - if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(DRE->getDecl())) - if (NTTP->getDepth() == Info.getDeducedDepth()) + if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) + if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(DRE->getDecl())) + if (NTTP->getDepth() == Depth) return NTTP; return nullptr; } +static const NonTypeTemplateParmDecl * +getDeducedParameterFromExpr(TemplateDeductionInfo &Info, Expr *E) { + return getDeducedParameterFromExpr(E, Info.getDeducedDepth()); +} + /// Determine whether two declaration pointers refer to the same /// declaration. static bool isSameDeclaration(Decl *X, Decl *Y) { @@ -374,7 +385,7 @@ checkDeducedTemplateArguments(ASTContext &Context, /// deduction is funneled through here. static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument( Sema &S, TemplateParameterList *TemplateParams, - NonTypeTemplateParmDecl *NTTP, const DeducedTemplateArgument &NewDeduced, + const NonTypeTemplateParmDecl *NTTP, const DeducedTemplateArgument &NewDeduced, QualType ValueType, TemplateDeductionInfo &Info, SmallVectorImpl<DeducedTemplateArgument> &Deduced) { assert(NTTP->getDepth() == Info.getDeducedDepth() && @@ -383,7 +394,7 @@ static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument( DeducedTemplateArgument Result = checkDeducedTemplateArguments( S.Context, Deduced[NTTP->getIndex()], NewDeduced); if (Result.isNull()) { - Info.Param = NTTP; + Info.Param = const_cast<NonTypeTemplateParmDecl*>(NTTP); Info.FirstArg = Deduced[NTTP->getIndex()]; Info.SecondArg = NewDeduced; return Sema::TDK_Inconsistent; @@ -410,10 +421,16 @@ static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument( // type from an argument (of non-reference type) should be performed. // For now, we just remove reference types from both sides and let // the final check for matching types sort out the mess. + ValueType = ValueType.getNonReferenceType(); + if (ParamType->isReferenceType()) + ParamType = ParamType.getNonReferenceType(); + else + // Top-level cv-qualifiers are irrelevant for a non-reference type. + ValueType = ValueType.getUnqualifiedType(); + return DeduceTemplateArgumentsByTypeMatch( - S, TemplateParams, ParamType.getNonReferenceType(), - ValueType.getNonReferenceType(), Info, Deduced, TDF_SkipNonDependent, - /*PartialOrdering=*/false, + S, TemplateParams, ParamType, ValueType, Info, Deduced, + TDF_SkipNonDependent, /*PartialOrdering=*/false, /*ArrayBound=*/NewDeduced.wasDeducedFromArrayBound()); } @@ -421,7 +438,7 @@ static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument( /// from the given integral constant. static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument( Sema &S, TemplateParameterList *TemplateParams, - NonTypeTemplateParmDecl *NTTP, const llvm::APSInt &Value, + const NonTypeTemplateParmDecl *NTTP, const llvm::APSInt &Value, QualType ValueType, bool DeducedFromArrayBound, TemplateDeductionInfo &Info, SmallVectorImpl<DeducedTemplateArgument> &Deduced) { return DeduceNonTypeTemplateArgument( @@ -435,7 +452,7 @@ static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument( /// from the given null pointer template argument type. static Sema::TemplateDeductionResult DeduceNullPtrTemplateArgument( Sema &S, TemplateParameterList *TemplateParams, - NonTypeTemplateParmDecl *NTTP, QualType NullPtrType, + const NonTypeTemplateParmDecl *NTTP, QualType NullPtrType, TemplateDeductionInfo &Info, SmallVectorImpl<DeducedTemplateArgument> &Deduced) { Expr *Value = @@ -454,7 +471,7 @@ static Sema::TemplateDeductionResult DeduceNullPtrTemplateArgument( /// \returns true if deduction succeeded, false otherwise. static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument( Sema &S, TemplateParameterList *TemplateParams, - NonTypeTemplateParmDecl *NTTP, Expr *Value, TemplateDeductionInfo &Info, + const NonTypeTemplateParmDecl *NTTP, Expr *Value, TemplateDeductionInfo &Info, SmallVectorImpl<DeducedTemplateArgument> &Deduced) { return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, DeducedTemplateArgument(Value), @@ -467,7 +484,7 @@ static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument( /// \returns true if deduction succeeded, false otherwise. static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument( Sema &S, TemplateParameterList *TemplateParams, - NonTypeTemplateParmDecl *NTTP, ValueDecl *D, QualType T, + const NonTypeTemplateParmDecl *NTTP, ValueDecl *D, QualType T, TemplateDeductionInfo &Info, SmallVectorImpl<DeducedTemplateArgument> &Deduced) { D = D ? cast<ValueDecl>(D->getCanonicalDecl()) : nullptr; @@ -1757,7 +1774,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, return Result; // Determine the array bound is something we can deduce. - NonTypeTemplateParmDecl *NTTP + const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(Info, DependentArrayParm->getSizeExpr()); if (!NTTP) return Sema::TDK_Success; @@ -1826,7 +1843,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, // deducing through the noexcept-specifier if it's part of the canonical // type. libstdc++ relies on this. Expr *NoexceptExpr = FunctionProtoParam->getNoexceptExpr(); - if (NonTypeTemplateParmDecl *NTTP = + if (const NonTypeTemplateParmDecl *NTTP = NoexceptExpr ? getDeducedParameterFromExpr(Info, NoexceptExpr) : nullptr) { assert(NTTP->getDepth() == Info.getDeducedDepth() && @@ -2015,7 +2032,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, return Result; // Perform deduction on the vector size, if we can. - NonTypeTemplateParmDecl *NTTP = + const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(Info, VectorParam->getSizeExpr()); if (!NTTP) return Sema::TDK_Success; @@ -2039,7 +2056,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, return Result; // Perform deduction on the vector size, if we can. - NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr( + const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr( Info, VectorParam->getSizeExpr()); if (!NTTP) return Sema::TDK_Success; @@ -2068,8 +2085,8 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, return Result; // Perform deduction on the vector size, if we can. - NonTypeTemplateParmDecl *NTTP - = getDeducedParameterFromExpr(Info, VectorParam->getSizeExpr()); + const NonTypeTemplateParmDecl *NTTP = + getDeducedParameterFromExpr(Info, VectorParam->getSizeExpr()); if (!NTTP) return Sema::TDK_Success; @@ -2094,8 +2111,8 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, return Result; // Perform deduction on the vector size, if we can. - NonTypeTemplateParmDecl *NTTP - = getDeducedParameterFromExpr(Info, VectorParam->getSizeExpr()); + const NonTypeTemplateParmDecl *NTTP = + getDeducedParameterFromExpr(Info, VectorParam->getSizeExpr()); if (!NTTP) return Sema::TDK_Success; @@ -2171,7 +2188,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, return Sema::TDK_NonDeducedMismatch; } - NonTypeTemplateParmDecl *NTTP = + const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(Info, ParamExpr); if (!NTTP) return Sema::TDK_Success; @@ -2218,7 +2235,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, return Result; // Perform deduction on the address space, if we can. - NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr( + const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr( Info, AddressSpaceParam->getAddrSpaceExpr()); if (!NTTP) return Sema::TDK_Success; @@ -2241,7 +2258,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, return Result; // Perform deduction on the address space, if we can. - NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr( + const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr( Info, AddressSpaceParam->getAddrSpaceExpr()); if (!NTTP) return Sema::TDK_Success; @@ -2260,7 +2277,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, if (IntParam->isUnsigned() != IntArg->isUnsigned()) return Sema::TDK_NonDeducedMismatch; - NonTypeTemplateParmDecl *NTTP = + const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(Info, IntParam->getNumBitsExpr()); if (!NTTP) return Sema::TDK_Success; @@ -2377,8 +2394,8 @@ DeduceTemplateArguments(Sema &S, return Sema::TDK_NonDeducedMismatch; case TemplateArgument::Expression: - if (NonTypeTemplateParmDecl *NTTP - = getDeducedParameterFromExpr(Info, Param.getAsExpr())) { + if (const NonTypeTemplateParmDecl *NTTP = + getDeducedParameterFromExpr(Info, Param.getAsExpr())) { if (Arg.getKind() == TemplateArgument::Integral) return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, Arg.getAsIntegral(), @@ -3982,7 +3999,7 @@ static Sema::TemplateDeductionResult DeduceFromInitializerList( // from the length of the initializer list. if (auto *DependentArrTy = dyn_cast_or_null<DependentSizedArrayType>(ArrTy)) { // Determine the array bound is something we can deduce. - if (NonTypeTemplateParmDecl *NTTP = + if (const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(Info, DependentArrTy->getSizeExpr())) { // We can perform template argument deduction for the given non-type // template parameter. @@ -5728,26 +5745,7 @@ MarkUsedTemplateParameters(ASTContext &Ctx, if (const PackExpansionExpr *Expansion = dyn_cast<PackExpansionExpr>(E)) E = Expansion->getPattern(); - // Skip through any implicit casts we added while type-checking, and any - // substitutions performed by template alias expansion. - while (true) { - if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) - E = ICE->getSubExpr(); - else if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(E)) - E = CE->getSubExpr(); - else if (const SubstNonTypeTemplateParmExpr *Subst = - dyn_cast<SubstNonTypeTemplateParmExpr>(E)) - E = Subst->getReplacement(); - else - break; - } - - const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E); - if (!DRE) - return; - - const NonTypeTemplateParmDecl *NTTP - = dyn_cast<NonTypeTemplateParmDecl>(DRE->getDecl()); + const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(E, Depth); if (!NTTP) return; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 525f3031573a..51f74d0b1d99 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -737,6 +737,11 @@ Decl *TemplateDeclInstantiator::VisitMSGuidDecl(MSGuidDecl *D) { llvm_unreachable("GUID declaration cannot be instantiated"); } +Decl *TemplateDeclInstantiator::VisitTemplateParamObjectDecl( + TemplateParamObjectDecl *D) { + llvm_unreachable("template parameter objects cannot be instantiated"); +} + Decl * TemplateDeclInstantiator::VisitLabelDecl(LabelDecl *D) { LabelDecl *Inst = LabelDecl::Create(SemaRef.Context, Owner, D->getLocation(), diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index bf583b02f96b..54c7f051459f 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -378,6 +378,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::Field: case Decl::MSProperty: case Decl::MSGuid: + case Decl::TemplateParamObject: case Decl::ObjCIvar: case Decl::ObjCAtDefsField: case Decl::NonTypeTemplateParm: diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 4d50e4af31f7..fd3b981150a0 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -369,6 +369,7 @@ namespace clang { void VisitFieldDecl(FieldDecl *FD); void VisitMSPropertyDecl(MSPropertyDecl *FD); void VisitMSGuidDecl(MSGuidDecl *D); + void VisitTemplateParamObjectDecl(TemplateParamObjectDecl *D); void VisitIndirectFieldDecl(IndirectFieldDecl *FD); RedeclarableResult VisitVarDeclImpl(VarDecl *D); void VisitVarDecl(VarDecl *VD) { VisitVarDeclImpl(VD); } @@ -1377,6 +1378,17 @@ void ASTDeclReader::VisitMSGuidDecl(MSGuidDecl *D) { Reader.getContext().setPrimaryMergedDecl(D, Existing->getCanonicalDecl()); } +void ASTDeclReader::VisitTemplateParamObjectDecl(TemplateParamObjectDecl *D) { + VisitValueDecl(D); + D->Value = Record.readAPValue(); + + // Add this template parameter object to the AST context's lookup structure, + // and merge if needed. + if (TemplateParamObjectDecl *Existing = + Reader.getContext().TemplateParamObjectDecls.GetOrInsertNode(D)) + Reader.getContext().setPrimaryMergedDecl(D, Existing->getCanonicalDecl()); +} + void ASTDeclReader::VisitIndirectFieldDecl(IndirectFieldDecl *FD) { VisitValueDecl(FD); @@ -2409,8 +2421,10 @@ void ASTDeclReader::VisitLifetimeExtendedTemporaryDecl( VisitDecl(D); D->ExtendingDecl = readDeclAs<ValueDecl>(); D->ExprWithTemporary = Record.readStmt(); - if (Record.readInt()) + if (Record.readInt()) { D->Value = new (D->getASTContext()) APValue(Record.readAPValue()); + D->getASTContext().addDestruction(D->Value); + } D->ManglingNumber = Record.readInt(); mergeMergeable(D); } @@ -3983,6 +3997,9 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { case DECL_MS_GUID: D = MSGuidDecl::CreateDeserialized(Context, ID); break; + case DECL_TEMPLATE_PARAM_OBJECT: + D = TemplateParamObjectDecl::CreateDeserialized(Context, ID); + break; case DECL_CAPTURED: D = CapturedDecl::CreateDeserialized(Context, ID, Record.readInt()); break; diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 5b0a6ffb9566..df7304457419 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -96,6 +96,7 @@ namespace clang { void VisitFieldDecl(FieldDecl *D); void VisitMSPropertyDecl(MSPropertyDecl *D); void VisitMSGuidDecl(MSGuidDecl *D); + void VisitTemplateParamObjectDecl(TemplateParamObjectDecl *D); void VisitIndirectFieldDecl(IndirectFieldDecl *D); void VisitVarDecl(VarDecl *D); void VisitImplicitParamDecl(ImplicitParamDecl *D); @@ -965,6 +966,12 @@ void ASTDeclWriter::VisitMSGuidDecl(MSGuidDecl *D) { Code = serialization::DECL_MS_GUID; } +void ASTDeclWriter::VisitTemplateParamObjectDecl(TemplateParamObjectDecl *D) { + VisitValueDecl(D); + Record.AddAPValue(D->getValue()); + Code = serialization::DECL_TEMPLATE_PARAM_OBJECT; +} + void ASTDeclWriter::VisitIndirectFieldDecl(IndirectFieldDecl *D) { VisitValueDecl(D); Record.push_back(D->getChainingSize()); diff --git a/clang/test/CXX/drs/dr3xx.cpp b/clang/test/CXX/drs/dr3xx.cpp index be3169ceab76..cec9acf63a9e 100644 --- a/clang/test/CXX/drs/dr3xx.cpp +++ b/clang/test/CXX/drs/dr3xx.cpp @@ -906,8 +906,8 @@ namespace dr367 { // dr367: yes namespace dr368 { // dr368: yes template<typename T, T> struct S {}; // expected-note {{here}} template<typename T> int f(S<T, T()> *); // expected-error {{function type}} - //template<typename T> int g(S<T, (T())> *); // FIXME: crashes clang - template<typename T> int g(S<T, true ? T() : T()> *); // expected-note {{cannot have type 'dr368::X'}} + template<typename T> int g(S<T, (T())> *); // expected-note {{type 'dr368::X'}} + template<typename T> int g(S<T, true ? T() : T()> *); // expected-note {{type 'dr368::X'}} struct X {}; int n = g<X>(0); // expected-error {{no matching}} } diff --git a/clang/test/CXX/temp/temp.param/p8-cxx20.cpp b/clang/test/CXX/temp/temp.param/p8-cxx20.cpp new file mode 100644 index 000000000000..7a31846756c3 --- /dev/null +++ b/clang/test/CXX/temp/temp.param/p8-cxx20.cpp @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -std=c++20 -verify %s + +struct A { int n; }; + +template<A a> struct B { + static constexpr A &v = a; // expected-error {{binding reference of type 'A' to value of type 'const A' drops 'const' qualifier}} +}; + +template<A a> struct C { + static constexpr const A &v = a; +}; + +// All such template parameters in the program of the same type with the same +// value denote the same template parameter object. +template<A a, typename T> void check() { + static_assert(&a == &T::v); // expected-error {{failed}} +} + +using T = C<A{1}>; +template void check<A{1}, T>(); +template void check<A{2}, T>(); // expected-note {{instantiation of}} + +// Different types with the same value are unequal. +struct A2 { int n; }; +template<A2 a2> struct C2 { + static constexpr const A2 &v = a2; +}; +static_assert((void*)&C<A{}>::v != (void*)&C2<A2{}>::v); diff --git a/clang/test/PCH/cxx20-template-args.cpp b/clang/test/PCH/cxx20-template-args.cpp new file mode 100644 index 000000000000..f95ec52dca27 --- /dev/null +++ b/clang/test/PCH/cxx20-template-args.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -std=c++20 -include %s %s -o %t + +// RUN: %clang_cc1 -std=c++20 -emit-pch %s -o %t +// RUN: %clang_cc1 -std=c++20 -include-pch %t -verify %s + +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +struct A { int n; }; + +template<A a> constexpr const A &get = a; + +constexpr const A &v = get<A{}>; + +#else /*included pch*/ + +template<A a> constexpr const A &get2 = a; + +constexpr const A &v2 = get2<A{}>; + +static_assert(&v == &v2); + +#endif // HEADER diff --git a/clang/test/SemaCXX/cxx17-compat.cpp b/clang/test/SemaCXX/cxx17-compat.cpp index b65ed3ea340b..41a465ac613e 100644 --- a/clang/test/SemaCXX/cxx17-compat.cpp +++ b/clang/test/SemaCXX/cxx17-compat.cpp @@ -121,3 +121,13 @@ struct DefaultedComparisons { // expected-warning@-10 {{defaulted comparison operators are incompatible with C++ standards before C++20}} #endif }; + +namespace NTTP { + struct A {}; + template<A> struct Class {}; +#if __cplusplus <= 201703L + // expected-error@-2 {{non-type template parameter cannot have type 'NTTP::A' before C++20}} +#else + // expected-warning@-4 {{non-type template parameter of type 'NTTP::A' is incompatible with C++ standards before C++20}} +#endif +} diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp index aa9e71ff1f6e..d0ace9b2b2b5 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp @@ -1,11 +1,14 @@ // RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s +using size_t = __SIZE_TYPE__; + // floating-point arguments template<float> struct Float {}; using F1 = Float<1.0f>; // FIXME expected-error {{sorry}} using F1 = Float<2.0f / 2>; // FIXME expected-error {{sorry}} struct S { int n[3]; } s; // expected-note 1+{{here}} +union U { int a, b; } u; int n; // expected-note 1+{{here}} // pointers to subobjects @@ -31,8 +34,13 @@ using IP3 = IntRef<*(s.n + 3)>; // expected-error {{not a constant expression}} // classes template<S> struct Struct {}; -using S123 = Struct<S{1, 2, 3}>; // FIXME: expected-error {{sorry}} -using S123 = Struct<S{1, 2, 3}>; // FIXME: expected-error {{sorry}} +using S123 = Struct<S{1, 2, 3}>; +using S123 = Struct<S{1, 2, 3}>; // expected-note {{previous}} +using S123 = Struct<S{1, 2, 4}>; // expected-error {{ diff erent types}} +template<U> struct Union {}; +using U1 = Union<U{1}>; +using U1 = Union<U{.a = 1}>; // expected-note {{previous}} +using U1 = Union<U{.b = 1}>; // expected-error {{ diff erent types}} // miscellaneous scalar types template<_Complex int> struct ComplexInt {}; @@ -42,3 +50,137 @@ using CI = ComplexInt<1 + 3i>; // FIXME: expected-error {{sorry}} template<_Complex float> struct ComplexFloat {}; using CF = ComplexFloat<1.0f + 3.0fi>; // FIXME: expected-error {{sorry}} using CF = ComplexFloat<1.0f + 3.0fi>; // FIXME: expected-error {{sorry}} + +namespace ClassNTTP { + struct A { // expected-note 2{{candidate}} + int x, y; + }; + template<A a> constexpr int f() { return a.y; } + static_assert(f<A{1,2}>() == 2); + + template<A a> int id; + constexpr A a = {1, 2}; + static_assert(&id<A{1,2}> == &id<a>); + static_assert(&id<A{1,3}> != &id<a>); + + int k = id<1>; // expected-error {{no viable conversion from 'int' to 'ClassNTTP::A'}} + + struct B { + constexpr B() {} + constexpr B(int) = delete; // expected-note {{here}} + }; + template<B> struct Q {}; // expected-note {{passing argument to parameter here}} + Q<1> q; // expected-error {{conversion function from 'int' to 'ClassNTTP::B' invokes a deleted function}} + + struct C { + constexpr C() {} + C(const C&) = delete; // expected-note {{here}} + }; + template<C> struct R {}; // expected-note {{passing argument to parameter here}} + constexpr C c; + R<c> r; // expected-error {{call to deleted constructor}} +} + +namespace ConvertedConstant { + struct A { + constexpr A(float) {} + }; + template <A> struct X {}; + void f(X<1.0f>) {} // OK, user-defined conversion + void f(X<2>) {} // expected-error {{conversion from 'int' to 'ConvertedConstant::A' is not allowed in a converted constant expression}} +} + +namespace CopyCounting { + // Make sure we don't use the copy constructor when transferring the "same" + // template parameter object around. + struct A { int n; constexpr A(int n = 0) : n(n) {} constexpr A(const A &a) : n(a.n+1) {} }; + template<A a> struct X {}; + template<A a> constexpr int f(X<a> x) { return a.n; } + + static_assert(f(X<A{}>()) == 0); + + template<A a> struct Y { void f(); }; + template<A a> void g(Y<a> y) { y.Y<a>::f(); } + void h() { constexpr A a; g<a>(Y<a>{}); } + + template<A a> struct Z { + constexpr int f() { + constexpr A v = a; // this is {a.n+1} + return Z<v>().f() + 1; // this is Z<{a.n+2}> + } + }; + template<> struct Z<A{20}> { + constexpr int f() { + return 32; + } + }; + static_assert(Z<A{}>().f() == 42); +} + +namespace StableAddress { + template<size_t N> struct str { + char arr[N]; + }; + // FIXME: Deduction guide not needed with P1816R0. + template<size_t N> str(const char (&)[N]) -> str<N>; + + // FIXME: Explicit size not needed. + template<str<15> s> constexpr int sum() { + int n = 0; + for (char c : s.arr) + n += c; + return n; + } + static_assert(sum<str{"$hello $world."}>() == 1234); +} + +namespace TemplateSpecializations { + struct A { int arr[10]; }; + template<A> struct X; // expected-note {{here}} + + using T = X<A{1, 2, 3}>; + using T = X<A{1, 2, 3, 0}>; + using T = X<A{1, 2, 3, 0, 0}>; + using T = X<A{1, 2, 3, 0, 0, 0}>; + + template<> struct X<A{1, 2, 3, 4}> {}; + X<A{1, 2, 3, 4, 0}> x; + + template<auto V, auto W> constexpr bool Same = false; + template<auto V> constexpr bool Same<V, V> = true; + static_assert(Same<A{}, A{0, 0}>); + static_assert(Same<A{1}, A{1, 0}>); + static_assert(!Same<A{1}, A{1, 1}>); + + // We can't directly specialize on member values... + template<int N> // expected-note {{parameter 'N'}} + struct X<A{N, N}> {}; // expected-error {{cannot be deduced}} + + // ... but we can fake it up. + // template<int N> struct X<A{N, N}> + template <A V> requires Same<V, A{V.arr[0], V.arr[0]}> + struct X<V> { + static constexpr bool match = true; + }; + static_assert(X<A{1, 1}>::match); + static_assert(X<A{2, 2}>::match); + static_assert(X<A{1, 2}>::match); // expected-error {{undefined}} + + template<int, A> struct Y; // expected-note {{here}} + template<int N> struct Y<N, A{N, N, N}> {}; + Y<1, A{1, 1, 1, 0}> y1; + Y<1, A{1, 1, 1, 1}> y2; // expected-error {{undefined}} + + template<A, A> struct Z; // expected-note {{here}} + template<A V> struct Z<V, V> {}; + Z<A{1, 2}, A{1, 2, 0}> z1; + Z<A{1, 2}, A{1, 3}> z2; // expected-error {{undefined}} + + template struct Z<A{1}, A{1, 0}>; +} + +namespace Diags { + struct A { int n, m; }; + template<A a> struct X { static_assert(a.n == a.m); }; // expected-error {{static_assert failed due to requirement 'Diags::A{1, 2}.n == Diags::A{1, 2}.m'}} + template struct X<A{1, 2}>; // expected-note {{in instantiation of template class 'Diags::X<{1, 2}>' requested here}} +} diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index a64f90f8f74b..3baff9d04b86 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -6376,6 +6376,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::Binding: case Decl::MSProperty: case Decl::MSGuid: + case Decl::TemplateParamObject: case Decl::IndirectField: case Decl::ObjCIvar: case Decl::ObjCAtDefsField: _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits