diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -745,8 +745,11 @@ def : Property<"type", QualType> { let Read = [{ node.getAsType() }]; } + def : Property<"isDefaulted", Bool> { + let Read = [{ node.getIsDefaulted() }]; + } def : Creator<[{ - return TemplateArgument(type); + return TemplateArgument(type, /* isNullPtr */ false, isDefaulted); }]>; } let Class = PropertyTypeCase in { @@ -756,16 +759,22 @@ def : Property<"parameterType", QualType> { let Read = [{ node.getParamTypeForDecl() }]; } + def : Property<"isDefaulted", Bool> { + let Read = [{ node.getIsDefaulted() }]; + } def : Creator<[{ - return TemplateArgument(declaration, parameterType); + return TemplateArgument(declaration, parameterType, isDefaulted); }]>; } let Class = PropertyTypeCase in { def : Property<"type", QualType> { let Read = [{ node.getNullPtrType() }]; } + def : Property<"isDefaulted", Bool> { + let Read = [{ node.getIsDefaulted() }]; + } def : Creator<[{ - return TemplateArgument(type, /*nullptr*/ true); + return TemplateArgument(type, /*nullptr*/ true, isDefaulted); }]>; } let Class = PropertyTypeCase in { @@ -775,16 +784,22 @@ def : Property<"type", QualType> { let Read = [{ node.getIntegralType() }]; } + def : Property<"isDefaulted", Bool> { + let Read = [{ node.getIsDefaulted() }]; + } def : Creator<[{ - return TemplateArgument(ctx, value, type); + return TemplateArgument(ctx, value, type, isDefaulted); }]>; } let Class = PropertyTypeCase in { def : Property<"name", TemplateName> { let Read = [{ node.getAsTemplateOrTemplatePattern() }]; } + def : Property<"isDefaulted", Bool> { + let Read = [{ node.getIsDefaulted() }]; + } def : Creator<[{ - return TemplateArgument(name); + return TemplateArgument(name, isDefaulted); }]>; } let Class = PropertyTypeCase in { @@ -798,19 +813,25 @@ [](unsigned i) { return uint32_t(i); }) }]; } + def : Property<"isDefaulted", Bool> { + let Read = [{ node.getIsDefaulted() }]; + } def : Creator<[{ auto numExpansionsUnsigned = llvm::transformOptional( numExpansions, [](uint32_t i) { return unsigned(i); }); - return TemplateArgument(name, numExpansionsUnsigned); + return TemplateArgument(name, numExpansionsUnsigned, isDefaulted); }]>; } let Class = PropertyTypeCase in { def : Property<"expression", ExprRef> { let Read = [{ node.getAsExpr() }]; } + def : Property<"isDefaulted", Bool> { + let Read = [{ node.getIsDefaulted() }]; + } def : Creator<[{ - return TemplateArgument(expression); + return TemplateArgument(expression, isDefaulted); }]>; } let Class = PropertyTypeCase in { diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h --- a/clang/include/clang/AST/TemplateBase.h +++ b/clang/include/clang/AST/TemplateBase.h @@ -103,12 +103,14 @@ /// The kind of template argument we're storing. struct DA { - unsigned Kind; + unsigned Kind : 31; + unsigned IsDefaulted : 1; void *QT; ValueDecl *D; }; struct I { - unsigned Kind; + unsigned Kind : 31; + unsigned IsDefaulted : 1; // We store a decomposed APSInt with the data allocated by ASTContext if // BitWidth > 64. The memory may be shared between multiple // TemplateArgument instances. @@ -124,17 +126,20 @@ void *Type; }; struct A { - unsigned Kind; + unsigned Kind : 31; + unsigned IsDefaulted : 1; unsigned NumArgs; const TemplateArgument *Args; }; struct TA { - unsigned Kind; + unsigned Kind : 31; + unsigned IsDefaulted : 1; unsigned NumExpansions; void *Name; }; struct TV { - unsigned Kind; + unsigned Kind : 31; + unsigned IsDefaulted : 1; uintptr_t V; }; union { @@ -147,27 +152,31 @@ public: /// Construct an empty, invalid template argument. - constexpr TemplateArgument() : TypeOrValue({Null, 0}) {} + constexpr TemplateArgument() : TypeOrValue({Null, 0, /* IsDefaulted */ 0}) {} /// Construct a template type argument. - TemplateArgument(QualType T, bool isNullPtr = false) { + TemplateArgument(QualType T, bool isNullPtr = false, + bool IsDefaulted = false) { TypeOrValue.Kind = isNullPtr ? NullPtr : Type; + TypeOrValue.IsDefaulted = IsDefaulted; TypeOrValue.V = reinterpret_cast(T.getAsOpaquePtr()); } /// Construct a template argument that refers to a /// declaration, which is either an external declaration or a /// template declaration. - TemplateArgument(ValueDecl *D, QualType QT) { + TemplateArgument(ValueDecl *D, QualType QT, bool IsDefaulted = false) { assert(D && "Expected decl"); DeclArg.Kind = Declaration; + DeclArg.IsDefaulted = IsDefaulted; DeclArg.QT = QT.getAsOpaquePtr(); DeclArg.D = D; } /// Construct an integral constant template argument. The memory to /// store the value is allocated with Ctx. - TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value, QualType Type); + TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value, QualType Type, + bool IsDefaulted = false); /// Construct an integral constant template argument with the same /// value as Other but a different type. @@ -184,8 +193,12 @@ /// is taken. /// /// \param Name The template name. - TemplateArgument(TemplateName Name) { + /// + /// \param IsDefaulted If 'true', implies that this TemplateArgument + /// corresponds to a default template parameter + TemplateArgument(TemplateName Name, bool IsDefaulted = false) { TemplateArg.Kind = Template; + TemplateArg.IsDefaulted = IsDefaulted; TemplateArg.Name = Name.getAsVoidPointer(); TemplateArg.NumExpansions = 0; } @@ -201,8 +214,13 @@ /// /// \param NumExpansions The number of expansions that will be generated by /// instantiating - TemplateArgument(TemplateName Name, std::optional NumExpansions) { + /// + /// \param IsDefaulted If 'true', implies that this TemplateArgument + /// corresponds to a default template parameter + TemplateArgument(TemplateName Name, std::optional NumExpansions, + bool IsDefaulted = false) { TemplateArg.Kind = TemplateExpansion; + TemplateArg.IsDefaulted = IsDefaulted; TemplateArg.Name = Name.getAsVoidPointer(); if (NumExpansions) TemplateArg.NumExpansions = *NumExpansions + 1; @@ -215,8 +233,9 @@ /// This form of template argument only occurs in template argument /// lists used for dependent types and for expression; it will not /// occur in a non-dependent, canonical template argument list. - TemplateArgument(Expr *E) { + TemplateArgument(Expr *E, bool IsDefaulted = false) { TypeOrValue.Kind = Expression; + TypeOrValue.IsDefaulted = IsDefaulted; TypeOrValue.V = reinterpret_cast(E); } @@ -226,12 +245,11 @@ /// outlives the TemplateArgument itself. explicit TemplateArgument(ArrayRef Args) { this->Args.Kind = Pack; + this->Args.IsDefaulted = false; this->Args.Args = Args.data(); this->Args.NumArgs = Args.size(); } - TemplateArgument(TemplateName, bool) = delete; - static TemplateArgument getEmptyPack() { return TemplateArgument(std::nullopt); } @@ -334,6 +352,14 @@ Integer.Type = T.getAsOpaquePtr(); } + /// Set to 'true' if this TemplateArgument corresponds to a + /// default template parameter. + void setIsDefaulted(bool v) { TypeOrValue.IsDefaulted = v; } + + /// If returns 'true', this TemplateArgument corresponds to a + /// default template parameter. + bool getIsDefaulted() const { return (bool)TypeOrValue.IsDefaulted; } + /// If this is a non-type template argument, get its type. Otherwise, /// returns a null QualType. QualType getNonTypeTemplateArgumentType() const; diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -6739,26 +6739,29 @@ case TemplateArgument::Declaration: { auto *D = cast(Arg.getAsDecl()->getCanonicalDecl()); - return TemplateArgument(D, getCanonicalType(Arg.getParamTypeForDecl())); + return TemplateArgument(D, getCanonicalType(Arg.getParamTypeForDecl()), + Arg.getIsDefaulted()); } case TemplateArgument::NullPtr: return TemplateArgument(getCanonicalType(Arg.getNullPtrType()), - /*isNullPtr*/true); + /*isNullPtr*/ true, Arg.getIsDefaulted()); case TemplateArgument::Template: - return TemplateArgument(getCanonicalTemplateName(Arg.getAsTemplate())); + return TemplateArgument(getCanonicalTemplateName(Arg.getAsTemplate()), + Arg.getIsDefaulted()); case TemplateArgument::TemplateExpansion: - return TemplateArgument(getCanonicalTemplateName( - Arg.getAsTemplateOrTemplatePattern()), - Arg.getNumTemplateExpansions()); + return TemplateArgument( + getCanonicalTemplateName(Arg.getAsTemplateOrTemplatePattern()), + Arg.getNumTemplateExpansions(), Arg.getIsDefaulted()); case TemplateArgument::Integral: return TemplateArgument(Arg, getCanonicalType(Arg.getIntegralType())); case TemplateArgument::Type: - return TemplateArgument(getCanonicalType(Arg.getAsType())); + return TemplateArgument(getCanonicalType(Arg.getAsType()), + /*isNullPtr*/ false, Arg.getIsDefaulted()); case TemplateArgument::Pack: { bool AnyNonCanonArgs = false; diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -903,22 +903,42 @@ FD, Template, TSK, TemplateArgs, ArgsAsWritten, POI, MSInfo); } +/// Creates a copy of the 'TemplateArgument's in 'Args'. +/// If a 'TemplateArgument' corresponds to a default template +/// parameter this function will mark it as such. +static TemplateArgumentList * +CreateCopyWithDefaultedArgs(ASTContext &Ctx, + TemplateParameterList const *Params, + ArrayRef Args) { + int i = 0; + auto MutArgs = Args.copy(Ctx); + for (auto &Arg : MutArgs) { + if (clang::isSubstitutedDefaultArgument(Ctx, Arg, Params->getParam(i), Args, + Params->getDepth())) { + Arg.setIsDefaulted(true); + } + ++i; + } + + return TemplateArgumentList::CreateCopy(Ctx, MutArgs); +} + //===----------------------------------------------------------------------===// // ClassTemplateSpecializationDecl Implementation //===----------------------------------------------------------------------===// -ClassTemplateSpecializationDecl:: -ClassTemplateSpecializationDecl(ASTContext &Context, Kind DK, TagKind TK, - DeclContext *DC, SourceLocation StartLoc, - SourceLocation IdLoc, - ClassTemplateDecl *SpecializedTemplate, - ArrayRef Args, - ClassTemplateSpecializationDecl *PrevDecl) +ClassTemplateSpecializationDecl::ClassTemplateSpecializationDecl( + ASTContext &Context, Kind DK, TagKind TK, DeclContext *DC, + SourceLocation StartLoc, SourceLocation IdLoc, + ClassTemplateDecl *SpecializedTemplate, ArrayRef Args, + ClassTemplateSpecializationDecl *PrevDecl) : CXXRecordDecl(DK, TK, Context, DC, StartLoc, IdLoc, SpecializedTemplate->getIdentifier(), PrevDecl), - SpecializedTemplate(SpecializedTemplate), - TemplateArgs(TemplateArgumentList::CreateCopy(Context, Args)), - SpecializationKind(TSK_Undeclared) { + SpecializedTemplate(SpecializedTemplate), + SpecializationKind(TSK_Undeclared) { + + TemplateArgs = CreateCopyWithDefaultedArgs( + Context, SpecializedTemplate->getTemplateParameters(), Args); } ClassTemplateSpecializationDecl::ClassTemplateSpecializationDecl(ASTContext &C, diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp --- a/clang/lib/AST/TemplateBase.cpp +++ b/clang/lib/AST/TemplateBase.cpp @@ -161,8 +161,9 @@ //===----------------------------------------------------------------------===// TemplateArgument::TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value, - QualType Type) { + QualType Type, bool IsDefaulted) { Integer.Kind = Integral; + Integer.IsDefaulted = IsDefaulted; // Copy the APSInt value into our decomposed form. Integer.BitWidth = Value.getBitWidth(); Integer.IsUnsigned = Value.isUnsigned(); diff --git a/clang/unittests/AST/DeclTest.cpp b/clang/unittests/AST/DeclTest.cpp --- a/clang/unittests/AST/DeclTest.cpp +++ b/clang/unittests/AST/DeclTest.cpp @@ -490,3 +490,34 @@ ASSERT_TRUE(AlignedArrayDelete->getOwningModule()); EXPECT_TRUE(AlignedArrayDelete->getOwningModule()->isGlobalModule()); } + +TEST(Decl, TemplateArgumentDefaulted) { + llvm::Annotations Code(R"cpp( + template + struct Alloc {}; + + template > + struct Foo { + }; + + Foo> X; + )cpp"); + + auto AST = + tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{"-std=c++20"}); + ASTContext &Ctx = AST->getASTContext(); + + auto const *CTSD = selectFirst( + "id", + match(classTemplateSpecializationDecl(hasName("Foo")).bind("id"), Ctx)); + ASSERT_NE(CTSD, nullptr); + auto const &ArgList = CTSD->getTemplateArgs(); + + EXPECT_FALSE(ArgList.get(0).getIsDefaulted()); + EXPECT_FALSE(ArgList.get(1).getIsDefaulted()); + EXPECT_TRUE(ArgList.get(2).getIsDefaulted()); + EXPECT_TRUE(ArgList.get(3).getIsDefaulted()); +}