diff --git a/core/clingutils/res/TClingUtils.h b/core/clingutils/res/TClingUtils.h index 593252673bf1d..02857d5519ca8 100644 --- a/core/clingutils/res/TClingUtils.h +++ b/core/clingutils/res/TClingUtils.h @@ -576,12 +576,14 @@ void GetCppName(std::string &output, const char *input); //______________________________________________________________________________ // Return the type with all parts fully qualified (most typedefs), // including template arguments, appended to name. -void GetFullyQualifiedTypeName(std::string &name, const clang::QualType &type, const cling::Interpreter &interpreter); +void GetFullyQualifiedTypeName(std::string &name, const clang::QualType &type, const cling::Interpreter &interpreter, + const clang::ClassTemplateSpecializationDecl *Spec = nullptr); //______________________________________________________________________________ // Return the type with all parts fully qualified (most typedefs), // including template arguments, appended to name, without using the interpreter -void GetFullyQualifiedTypeName(std::string &name, const clang::QualType &type, const clang::ASTContext &); +void GetFullyQualifiedTypeName(std::string &name, const clang::QualType &type, const clang::ASTContext &, + const clang::ClassTemplateSpecializationDecl *Spec = nullptr); //______________________________________________________________________________ // Return the type normalized for ROOT, diff --git a/core/clingutils/src/TClingUtils.cxx b/core/clingutils/src/TClingUtils.cxx index 2373f0cd429df..05090144023b7 100644 --- a/core/clingutils/src/TClingUtils.cxx +++ b/core/clingutils/src/TClingUtils.cxx @@ -1368,7 +1368,8 @@ bool ROOT::TMetaUtils::HasCustomConvStreamerMemberFunction(const AnnotatedRecord void ROOT::TMetaUtils::GetQualifiedName(std::string &qual_name, const clang::QualType &type, const clang::NamedDecl &forcontext) { - ROOT::TMetaUtils::GetFullyQualifiedTypeName(qual_name, type, forcontext.getASTContext()); + ROOT::TMetaUtils::GetFullyQualifiedTypeName(qual_name, type, forcontext.getASTContext(), + llvm::dyn_cast_or_null(&forcontext)); } //---- @@ -1483,6 +1484,7 @@ void ROOT::TMetaUtils::CreateNameTypeMap(const clang::CXXRecordDecl &cl, ROOT::M std::string typenameStr; const clang::ASTContext& astContext = cl.getASTContext(); + auto CTSD = llvm::dyn_cast(&cl); // Loop over the non static data member. for(clang::RecordDecl::field_iterator field_iter = cl.field_begin(), end = cl.field_end(); @@ -1507,7 +1509,7 @@ void ROOT::TMetaUtils::CreateNameTypeMap(const clang::CXXRecordDecl &cl, ROOT::M } } - GetFullyQualifiedTypeName(typenameStr, fieldType, astContext); + GetFullyQualifiedTypeName(typenameStr, fieldType, astContext, CTSD); nameType[field_iter->getName().str()] = ROOT::Internal::TSchemaType(typenameStr.c_str(),dims.str().c_str()); } @@ -3491,9 +3493,10 @@ llvm::StringRef ROOT::TMetaUtils::GetFileName(const clang::Decl& decl, void ROOT::TMetaUtils::GetFullyQualifiedTypeName(std::string &typenamestr, const clang::QualType &qtype, - const clang::ASTContext &astContext) + const clang::ASTContext &astContext, + const clang::ClassTemplateSpecializationDecl *Spec /*= nullptr*/) { - std::string fqname = cling::utils::TypeName::GetFullyQualifiedName(qtype, astContext); + std::string fqname = cling::utils::TypeName::GetFullyQualifiedName(qtype, astContext, Spec); TClassEdit::TSplitType splitname(fqname.c_str(), (TClassEdit::EModType)(TClassEdit::kLong64 | TClassEdit::kDropStd | TClassEdit::kDropStlDefault | TClassEdit::kKeepOuterConst)); splitname.ShortType(typenamestr,TClassEdit::kDropStd | TClassEdit::kDropStlDefault | TClassEdit::kKeepOuterConst); @@ -3503,7 +3506,8 @@ void ROOT::TMetaUtils::GetFullyQualifiedTypeName(std::string &typenamestr, void ROOT::TMetaUtils::GetFullyQualifiedTypeName(std::string &typenamestr, const clang::QualType &qtype, - const cling::Interpreter &interpreter) + const cling::Interpreter &interpreter, + const clang::ClassTemplateSpecializationDecl *Spec /*= nullptr*/) { // We need this because GetFullyQualifiedTypeName is triggering deserialization // This calling the same name function GetFullyQualifiedTypeName, but this should stay here because @@ -3512,7 +3516,8 @@ void ROOT::TMetaUtils::GetFullyQualifiedTypeName(std::string &typenamestr, GetFullyQualifiedTypeName(typenamestr, qtype, - interpreter.getCI()->getASTContext()); + interpreter.getCI()->getASTContext(), + Spec); } //////////////////////////////////////////////////////////////////////////////// diff --git a/core/dictgen/res/DictSelectionReader.h b/core/dictgen/res/DictSelectionReader.h index b19135badd4f2..c3f7ec7539e64 100644 --- a/core/dictgen/res/DictSelectionReader.h +++ b/core/dictgen/res/DictSelectionReader.h @@ -260,25 +260,16 @@ class DictSelectionReader : public clang::RecursiveASTVisitor fUnsplittableMembers {}; }; - inline bool - InSelectionNamespace(const clang::RecordDecl &, - const std::string &str = - ""); ///< Check if in the ROOT::Meta::Selection namespace - inline bool FirstPass(const clang::RecordDecl &); ///< First pass on the AST - inline bool SecondPass(const clang::RecordDecl &); ///< Second pass on the AST, using the information of the first one - inline void - ManageFields(const clang::RecordDecl &, - const std::string &, - ClassSelectionRule &, - bool); ///< Take care of the class fields - inline void - ManageBaseClasses(const clang::CXXRecordDecl &, const std::string &, bool &); ///< Take care of the class bases + bool InSelectionNamespace(const clang::RecordDecl &, + const std::string &str = ""); ///< Check if in the ROOT::Meta::Selection namespace + bool FirstPass(const clang::RecordDecl &); ///< First pass on the AST + bool SecondPass(const clang::RecordDecl &); ///< Second pass on the AST, using the information of the first one + void ManageFields(const clang::RecordDecl &, const std::string &, + ClassSelectionRule &, bool); ///< Take care of the class fields + void ManageBaseClasses(const clang::CXXRecordDecl &, const std::string &, bool &); ///< Take care of the class bases template - inline unsigned int ExtractTemplateArgValue( - const T &, - const std::string &); ///< Extract the value of the template parameter - inline const clang::TemplateArgumentList *GetTmplArgList( - const clang::CXXRecordDecl &); ///< Get the template arguments list if any + unsigned int ExtractTemplateArgValue(const T &, const std::string &); ///< Extract the value of the template parameter + const clang::TemplateArgumentList *GetTmplArgList(const clang::CXXRecordDecl &); ///< Get the template arguments list if any std::string PatternifyName(const std::string &className); ///< Transform instance name in pattern for selection void GetPointeeType(std::string &typeName); ///< Get name of the pointee type diff --git a/core/dictgen/src/DictSelectionReader.cxx b/core/dictgen/src/DictSelectionReader.cxx index e7c3128f02bc6..545e5ab79ae5a 100644 --- a/core/dictgen/src/DictSelectionReader.cxx +++ b/core/dictgen/src/DictSelectionReader.cxx @@ -384,12 +384,27 @@ bool DictSelectionReader::SecondPass(const clang::RecordDecl &recordDecl) bool DictSelectionReader::VisitRecordDecl(clang::RecordDecl *recordDecl) { + if (auto CXXRD = llvm::dyn_cast(recordDecl)) { + if (CXXRD->getDescribedClassTemplate()) { + // this is a member of a template; ignore: dictionaries can only + // select instances / specializations. + return true; + } + } + if (auto CXXRD = llvm::dyn_cast(recordDecl->getDeclContext())) { + if (CXXRD->getDescribedClassTemplate()) { + // this is a member of a template; ignore: dictionaries can only + // select instances / specializations. + return true; + } + } if (fIsFirstPass) return FirstPass(*recordDecl); else return SecondPass(*recordDecl); } + //////////////////////////////////////////////////////////////////////////////// /** diff --git a/core/metacling/src/TCling.cxx b/core/metacling/src/TCling.cxx index 986503d907196..1ce6a7e27c179 100644 --- a/core/metacling/src/TCling.cxx +++ b/core/metacling/src/TCling.cxx @@ -9125,10 +9125,7 @@ const char* TCling::MethodInfo_TypeName(MethodInfo_t* minfo) const std::string TCling::MethodInfo_TypeNormalizedName(MethodInfo_t* minfo) const { TClingMethodInfo* info = (TClingMethodInfo*) minfo; - if (info && info->IsValid()) - return info->Type()->NormalizedName(*fNormalizedCtxt); - else - return ""; + return info->NormalizedName(*fNormalizedCtxt); } //////////////////////////////////////////////////////////////////////////////// diff --git a/core/metacling/src/TClingDataMemberInfo.cxx b/core/metacling/src/TClingDataMemberInfo.cxx index 5363fc2c533f8..992326539cdf7 100644 --- a/core/metacling/src/TClingDataMemberInfo.cxx +++ b/core/metacling/src/TClingDataMemberInfo.cxx @@ -36,6 +36,7 @@ from the Clang C++ compiler, not CINT. #include "clang/AST/Attr.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/GlobalDecl.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" @@ -580,7 +581,8 @@ const char *TClingDataMemberInfo::TypeName() const // if (we_need_to_do_the_subst_because_the_class_is_a_template_instance_of_double32_t) vdType = ROOT::TMetaUtils::ReSubstTemplateArg(vdType, GetClassAsType() ); - ROOT::TMetaUtils::GetFullyQualifiedTypeName(buf, vdType, *fInterp); + auto CTSD = llvm::dyn_cast(vd->getDeclContext()); + ROOT::TMetaUtils::GetFullyQualifiedTypeName(buf, vdType, *fInterp, CTSD); return buf.c_str(); } diff --git a/core/metacling/src/TClingMethodArgInfo.cxx b/core/metacling/src/TClingMethodArgInfo.cxx index 1e33c62641bfa..28d8842491a4f 100644 --- a/core/metacling/src/TClingMethodArgInfo.cxx +++ b/core/metacling/src/TClingMethodArgInfo.cxx @@ -29,6 +29,7 @@ the Clang C++ compiler, not CINT. #include "cling/Interpreter/Interpreter.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/PrettyPrinter.h" @@ -148,6 +149,10 @@ const char *TClingMethodArgInfo::TypeName() const if (!IsValid()) { return 0; } - return Type()->Name(); + + const clang::ClassTemplateSpecializationDecl *spec = nullptr; + if (const auto FD = llvm::cast_or_null(TClingDeclInfo::GetDecl())) + spec = llvm::dyn_cast(FD->getDeclContext()); + return Type()->Name(spec); } diff --git a/core/metacling/src/TClingMethodInfo.cxx b/core/metacling/src/TClingMethodInfo.cxx index 8713f01c1bec9..fb6be70f3cae5 100644 --- a/core/metacling/src/TClingMethodInfo.cxx +++ b/core/metacling/src/TClingMethodInfo.cxx @@ -610,7 +610,9 @@ const char *TClingMethodInfo::GetPrototype() if (const clang::TypeDecl *td = llvm::dyn_cast(GetDecl()->getDeclContext())) { std::string name; clang::QualType qualType(td->getTypeForDecl(),0); - ROOT::TMetaUtils::GetFullyQualifiedTypeName(name,qualType,*fInterp); + const clang::ClassTemplateSpecializationDecl *spec + = llvm::dyn_cast(td); + ROOT::TMetaUtils::GetFullyQualifiedTypeName(name, qualType, *fInterp, spec); buf += name; buf += "::"; } else if (const clang::NamedDecl *nd = llvm::dyn_cast(FD->getDeclContext())) { @@ -655,9 +657,18 @@ const char *TClingMethodInfo::TypeName() const // FIXME: Cint does not check! return 0; } - return Type()->Name(); + + const clang::ClassTemplateSpecializationDecl *spec + = llvm::dyn_cast(GetDecl()->getDeclContext()); + return Type()->Name(spec); } +std::string TClingMethodInfo::NormalizedName(ROOT::TMetaUtils::TNormalizedCtxt &ctx) const +{ + return Type()->NormalizedName(ctx); +} + + const char *TClingMethodInfo::Title() { if (!IsValid()) { diff --git a/core/metacling/src/TClingMethodInfo.h b/core/metacling/src/TClingMethodInfo.h index e1b6e081ae339..1fd2c03b5778f 100644 --- a/core/metacling/src/TClingMethodInfo.h +++ b/core/metacling/src/TClingMethodInfo.h @@ -165,6 +165,7 @@ class TClingMethodInfo final : public TClingDeclInfo { const char *GetPrototype(); const char *Name() const override; const char *TypeName() const; + std::string NormalizedName(ROOT::TMetaUtils::TNormalizedCtxt &ctx) const; const char *Title(); }; diff --git a/core/metacling/src/TClingTypeInfo.cxx b/core/metacling/src/TClingTypeInfo.cxx index 3a3c124f330ab..afd6696e28939 100644 --- a/core/metacling/src/TClingTypeInfo.cxx +++ b/core/metacling/src/TClingTypeInfo.cxx @@ -33,6 +33,7 @@ but the type metadata comes from the Clang C++ compiler, not CINT. #include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/Type.h" #include "clang/AST/PrettyPrinter.h" #include "clang/Frontend/CompilerInstance.h" @@ -99,7 +100,7 @@ void TClingTypeInfo::Init(const char *name) //////////////////////////////////////////////////////////////////////////////// -const char *TClingTypeInfo::Name() const +const char *TClingTypeInfo::Name(const clang::ClassTemplateSpecializationDecl *spec) const { if (!IsValid()) { return ""; @@ -109,7 +110,7 @@ const char *TClingTypeInfo::Name() const buf.clear(); R__LOCKGUARD(gInterpreterMutex); - ROOT::TMetaUtils::GetFullyQualifiedTypeName(buf,fQualType,*fInterp); + ROOT::TMetaUtils::GetFullyQualifiedTypeName(buf,fQualType,*fInterp, spec); return buf.c_str(); // NOLINT } diff --git a/core/metacling/src/TClingTypeInfo.h b/core/metacling/src/TClingTypeInfo.h index 86fd73d12b58a..d9a49cc82d95c 100644 --- a/core/metacling/src/TClingTypeInfo.h +++ b/core/metacling/src/TClingTypeInfo.h @@ -30,6 +30,10 @@ #include "clang/AST/Type.h" +namespace clang { + class ClassTemplateSpecializationDecl; +} + namespace cling { class Interpreter; } @@ -63,7 +67,8 @@ class TClingTypeInfo final : public TClingDeclInfo { void Init(const char *name); // Set type by name. void Init(clang::QualType ty) { fQualType = ty; } bool IsValid() const override { return !fQualType.isNull(); } - const char *Name() const override; // Get name of type. + const char *Name() const override { return Name(nullptr); } + const char *Name(const clang::ClassTemplateSpecializationDecl *spec) const; // Get name of type. long Property() const; // Get properties of type. int RefType() const; // Get CINT reftype of type. int Size() const; // Get size in bytes of type. diff --git a/core/metacling/test/TClingMethodInfoTests.cxx b/core/metacling/test/TClingMethodInfoTests.cxx index 4f97f200fe8e8..c93e63d0cf97c 100644 --- a/core/metacling/test/TClingMethodInfoTests.cxx +++ b/core/metacling/test/TClingMethodInfoTests.cxx @@ -5,10 +5,13 @@ #include "TList.h" #include "TListOfFunctions.h" #include "TMethod.h" +#include "TMethodArg.h" #include "TROOT.h" #include "gtest/gtest.h" +#include + TEST(TClingMethodInfo, Prototype) { TClass *cl = TClass::GetClass("TObject"); @@ -551,3 +554,142 @@ namespace BUG6578 { std::cerr << " " << iCtorExpected.first << '\n'; } } + +// https://github.com/root-project/root/issues/7955 +TEST(TClingMethodInfo, NonDependentMemberTypes) +{ + gInterpreter->Declare(R"CODE( +struct AScope { + using TheType = int; +}; + + +template +struct Template1 { + using type1 = int; + using type2 = AScope; + void f0(type1); + void f1(type1*); + void g0(type2); + void g1(type2*); + void g2(type2::TheType); + void g3(type2::TheType*); +}; + +Template1 xf1; + +struct X1 { + void f1(Template1::type1) {} + void f2(Template1::type2) {} + void g1(Template1::type1) {} + void g2(Template1::type2) {} +}; + +struct Y1: Template1 { + void yf1(type1); + void yf2(type2); + void yf3(type2::TheType); +}; + +template using TAlias1 = Template1; +struct Z1 { + void f1(TAlias1::type1) {} + void f2(TAlias1::type2) {} + void g1(TAlias1::type1) {} + void g2(TAlias1::type2) {} +}; + +template +struct Template2 { + struct Inner { + using type = AScope; + }; + void f(typename Inner::type); +}; + +Template2 xf2; + +struct Y2 { + void g(Template2::Inner::type) {} + void h(Template2::Inner::type) {} +}; + +struct Z2: Template2 { + void one(Inner::type); +}; +)CODE"); + + + auto checkClass = [](const char *className, std::unordered_map funcPar0) { + TClass *cl = TClass::GetClass(className); + ASSERT_NE(cl, nullptr); + TListOfFunctions *methods = (TListOfFunctions *)cl->GetListOfMethods(); + for (TMethod *method : TRangeDynCast(methods)) { + auto args = method->GetListOfMethodArgs(); + if (args->GetSize() != 1) + continue; + auto arg0 = (TMethodArg*) args->At(0); + auto iExpected = funcPar0.find(std::string(method->GetName())); + if (iExpected == funcPar0.end()) + continue; + EXPECT_EQ(iExpected->second, arg0->GetFullTypeName()) + << " in function " << className << "::" << method->GetName(); + funcPar0.erase(iExpected); + } + EXPECT_EQ(funcPar0.size(), 0); + }; + checkClass("Template1", { + {"f0", "Template1::type1"}, + {"f1", "Template1::type1*"}, + {"g0", "Template1::type2"}, + {"g1", "Template1::type2*"}, + {"g2", "AScope::TheType"}, + {"g3", "AScope::TheType*"}}); + + // This one was giving "Template1::type1" before: + checkClass("Template1", { + {"f0", "Template1::type1"}, + {"f1", "Template1::type1*"}, + {"g0", "Template1::type2"}, + {"g1", "Template1::type2*"}, + {"g2", "AScope::TheType"}, + {"g3", "AScope::TheType*"}}); + + checkClass("TAlias1", { + {"f0", "Template1::type1"}, + {"f1", "Template1::type1*"}, + {"g0", "Template1::type2"}, + {"g1", "Template1::type2*"}, + {"g2", "AScope::TheType"}, + {"g3", "AScope::TheType*"}}); + + checkClass("X1", { + {"f1", "Template1::type1"}, + {"f2", "Template1::type2"}, + {"g1", "Template1::type1"}, + {"g2", "Template1::type2"}}); + + checkClass("Y1", { + {"yf1", "Template1::type1"}, + {"yf2", "Template1::type2"}, + {"yf3", "AScope::TheType"}}); + + checkClass("Z1", { + {"f1", "Template1::type1"}, + {"f2", "Template1::type2"}, + {"g1", "Template1::type1"}, + {"g2", "Template1::type2"}}); + + checkClass("Template2", { + {"f", "Template2::Inner::type"}}); + + checkClass("Template2", { + {"f", "Template2::Inner::type"}}); + + checkClass("Y2", { + {"g", "Template2::Inner::type"}, + {"h", "Template2::Inner::type"}}); + + checkClass("Z2", { + {"one", "Template2::Inner::type"}}); +} diff --git a/interpreter/cling/include/cling/Utils/AST.h b/interpreter/cling/include/cling/Utils/AST.h index 0e549e512e2c1..b1be6ec307421 100644 --- a/interpreter/cling/include/cling/Utils/AST.h +++ b/interpreter/cling/include/cling/Utils/AST.h @@ -16,6 +16,7 @@ namespace clang { class ASTContext; + class ClassTemplateSpecializationDecl; class Expr; class Decl; class DeclContext; @@ -300,7 +301,8 @@ namespace utils { /// returned. ///\param[in] Ctx - the ASTContext to be used. clang::QualType GetFullyQualifiedType(clang::QualType QT, - const clang::ASTContext& Ctx); + const clang::ASTContext& Ctx, + const clang::ClassTemplateSpecializationDecl* Spec = nullptr); ///\brief Get the fully qualified name for a type. This includes full /// qualification of all template parameters etc. @@ -309,7 +311,8 @@ namespace utils { /// returned. ///\param[in] Ctx - the ASTContext to be used. std::string GetFullyQualifiedName(clang::QualType QT, - const clang::ASTContext &Ctx); + const clang::ASTContext &Ctx, + const clang::ClassTemplateSpecializationDecl* Spec = nullptr); ///\brief Create a NestedNameSpecifier for Namesp and its enclosing /// scopes. @@ -331,7 +334,8 @@ namespace utils { /// qualified names. clang::NestedNameSpecifier* CreateNestedNameSpecifier(const clang::ASTContext& Ctx, - const clang::TagDecl *TD, bool FullyQualify); + const clang::TagDecl *TD, bool FullyQualify, + const clang::ClassTemplateSpecializationDecl* Spec = nullptr); ///\brief Create a NestedNameSpecifier for TypedefDecl and its enclosing /// scopes. @@ -344,7 +348,8 @@ namespace utils { clang::NestedNameSpecifier* CreateNestedNameSpecifier(const clang::ASTContext& Ctx, const clang::TypedefNameDecl *TD, - bool FullyQualify); + bool FullyQualify, + const clang::ClassTemplateSpecializationDecl* Spec = nullptr); } // end namespace TypeName } // end namespace utils diff --git a/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.cpp b/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.cpp index 810cb4aa9572b..04f9498476bff 100644 --- a/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.cpp +++ b/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.cpp @@ -111,7 +111,9 @@ namespace cling { } void ForwardDeclPrinter::Visit(clang::QualType QT) { - QT = utils::TypeName::GetFullyQualifiedType(QT, m_Ctx); + QT = utils::TypeName::GetFullyQualifiedType(QT, m_Ctx, + m_SpecContextStack.empty() ? nullptr + : m_SpecContextStack.top()); Visit(QT.getTypePtr()); } @@ -936,7 +938,9 @@ namespace cling { } if (!ArgQT.isNull()) { QualType ArgFQQT - = utils::TypeName::GetFullyQualifiedType(ArgQT, m_Ctx); + = utils::TypeName::GetFullyQualifiedType(ArgQT, m_Ctx, + m_SpecContextStack.empty() ? nullptr + : m_SpecContextStack.top()); Visit(ArgFQQT); if (m_SkipFlag) { skipDecl(0, "type template param default failed"); @@ -1092,6 +1096,19 @@ namespace cling { // return; // } + struct PopStackRAII{ + std::stack &m_Stk; + PopStackRAII(std::stack &stk, + ClassTemplateSpecializationDecl* D): + m_Stk(stk) + { + m_Stk.push(D); + } + ~PopStackRAII() { + m_Stk.pop(); + } + } popStackRAII(m_SpecContextStack, D); + const TemplateArgumentList& iargs = D->getTemplateInstantiationArgs(); for (const TemplateArgument& TA: iargs.asArray()) { VisitTemplateArgument(TA); diff --git a/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.h b/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.h index a4b0c68c932ff..14caafc0d505a 100644 --- a/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.h +++ b/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.h @@ -107,6 +107,7 @@ namespace cling { llvm::DenseMap m_Visited; // fwd decl success std::stack m_StreamStack; + std::stack m_SpecContextStack; std::set m_BuiltinNames; IgnoreFilesFunc_t m_IgnoreFile; // Call back to ignore some top level files. diff --git a/interpreter/cling/lib/Utils/AST.cpp b/interpreter/cling/lib/Utils/AST.cpp index f5ae42adb0a26..0fbe62201fba6 100644 --- a/interpreter/cling/lib/Utils/AST.cpp +++ b/interpreter/cling/lib/Utils/AST.cpp @@ -48,7 +48,8 @@ namespace utils { QualType QT, const Transform::Config& TypeConfig, bool fullyQualifyType, - bool fullyQualifyTmpltArg); + bool fullyQualifyTmpltArg, + const ClassTemplateSpecializationDecl* Spec); static NestedNameSpecifier* GetPartiallyDesugaredNNS(const ASTContext& Ctx, @@ -58,11 +59,12 @@ namespace utils { static NestedNameSpecifier* CreateNestedNameSpecifierForScopeOf(const ASTContext& Ctx, const Decl *decl, - bool FullyQualified); + bool FullyQualified, + const ClassTemplateSpecializationDecl* Spec); static NestedNameSpecifier* GetFullyQualifiedNameSpecifier(const ASTContext& Ctx, - NestedNameSpecifier* scope); + NestedNameSpecifier& scope); bool Analyze::IsWrapper(const FunctionDecl* ND) { if (!ND) @@ -213,7 +215,8 @@ namespace utils { } static bool - GetFullyQualifiedTemplateName(const ASTContext& Ctx, TemplateName &tname) { + GetFullyQualifiedTemplateName(const ASTContext& Ctx, TemplateName &tname, + const ClassTemplateSpecializationDecl* Spec) { bool changed = false; NestedNameSpecifier *NNS = 0; @@ -223,7 +226,7 @@ namespace utils { if (qtname && !qtname->hasTemplateKeyword()) { NNS = qtname->getQualifier(); - NestedNameSpecifier *qNNS = GetFullyQualifiedNameSpecifier(Ctx,NNS); + NestedNameSpecifier *qNNS = GetFullyQualifiedNameSpecifier(Ctx, *NNS); if (qNNS != NNS) { changed = true; NNS = qNNS; @@ -231,7 +234,7 @@ namespace utils { NNS = 0; } } else { - NNS = CreateNestedNameSpecifierForScopeOf(Ctx, argtdecl, true); + NNS = CreateNestedNameSpecifierForScopeOf(Ctx, argtdecl, true, Spec); } if (NNS) { tname = Ctx.getQualifiedTemplateName(NNS, @@ -244,6 +247,7 @@ namespace utils { static bool GetFullyQualifiedTemplateArgument(const ASTContext& Ctx, + const ClassTemplateSpecializationDecl* Spec, TemplateArgument &arg) { bool changed = false; @@ -253,14 +257,14 @@ namespace utils { if (arg.getKind() == TemplateArgument::Template) { TemplateName tname = arg.getAsTemplate(); - changed = GetFullyQualifiedTemplateName(Ctx, tname); + changed = GetFullyQualifiedTemplateName(Ctx, tname, Spec); if (changed) { arg = TemplateArgument(tname); } } else if (arg.getKind() == TemplateArgument::Type) { QualType SubTy = arg.getAsType(); // Check if the type needs more desugaring and recurse. - QualType QTFQ = TypeName::GetFullyQualifiedType(SubTy, Ctx); + QualType QTFQ = TypeName::GetFullyQualifiedType(SubTy, Ctx, Spec); if (QTFQ != SubTy) { arg = TemplateArgument(QTFQ); changed = true; @@ -269,7 +273,7 @@ namespace utils { SmallVector desArgs; for (auto I = arg.pack_begin(), E = arg.pack_end(); I != E; ++I) { TemplateArgument pack_arg(*I); - changed = GetFullyQualifiedTemplateArgument(Ctx,pack_arg); + changed = GetFullyQualifiedTemplateArgument(Ctx, Spec, pack_arg); desArgs.push_back(pack_arg); } if (changed) { @@ -285,6 +289,7 @@ namespace utils { static const Type* GetFullyQualifiedLocalType(const ASTContext& Ctx, + const ClassTemplateSpecializationDecl* Spec, const Type *typeptr) { // We really just want to handle the template parameter if any .... // In case of template specializations iterate over the arguments and @@ -301,7 +306,7 @@ namespace utils { // cheap to copy and potentially modified by // GetFullyQualifedTemplateArgument TemplateArgument arg(*I); - mightHaveChanged |= GetFullyQualifiedTemplateArgument(Ctx,arg); + mightHaveChanged |= GetFullyQualifiedTemplateArgument(Ctx, Spec, arg); desArgs.push_back(arg); } @@ -333,7 +338,8 @@ namespace utils { // cheap to copy and potentially modified by // GetFullyQualifedTemplateArgument TemplateArgument arg(templateArgs[I]); - mightHaveChanged |= GetFullyQualifiedTemplateArgument(Ctx,arg); + mightHaveChanged + |= GetFullyQualifiedTemplateArgument(Ctx, Spec, arg); desArgs.push_back(arg); } @@ -353,7 +359,9 @@ namespace utils { static NestedNameSpecifier* CreateOuterNNS(const ASTContext& Ctx, const Decl* D, - bool FullyQualify) { + bool FullyQualify, + const ClassTemplateSpecializationDecl* Spec) + { const DeclContext* DC = D->getDeclContext(); if (const NamespaceDecl* NS = dyn_cast(DC)) { while (NS && NS->isInline()) { @@ -364,23 +372,24 @@ namespace utils { return TypeName::CreateNestedNameSpecifier(Ctx, NS); return nullptr; // no starting '::', no anonymous } else if (const TagDecl* TD = dyn_cast(DC)) { - return TypeName::CreateNestedNameSpecifier(Ctx, TD, FullyQualify); + return TypeName::CreateNestedNameSpecifier(Ctx, TD, FullyQualify, Spec); } else if (const TypedefNameDecl* TDD = dyn_cast(DC)) { - return TypeName::CreateNestedNameSpecifier(Ctx, TDD, FullyQualify); + return TypeName::CreateNestedNameSpecifier(Ctx, TDD, FullyQualify, Spec); } return nullptr; // no starting '::' } static NestedNameSpecifier* GetFullyQualifiedNameSpecifier(const ASTContext& Ctx, - NestedNameSpecifier* scope) { + NestedNameSpecifier& scope) + { // Return a fully qualified version of this name specifier - if (scope->getKind() == NestedNameSpecifier::Global) { + if (scope.getKind() == NestedNameSpecifier::Global) { // Already fully qualified. - return scope; + return &scope; } - if (const Type *type = scope->getAsType()) { + if (const Type *type = scope.getAsType()) { // Find decl context. const TagDecl* TD = 0; if (const TagType* tagdecltype = dyn_cast(type)) { @@ -390,26 +399,29 @@ namespace utils { } if (TD) { return TypeName::CreateNestedNameSpecifier(Ctx, TD, - true /*FullyQualified*/); + true /*FullyQualified*/, + /*Spec*/ nullptr); } else if (const TypedefType* TDD = dyn_cast(type)) { return TypeName::CreateNestedNameSpecifier(Ctx, TDD->getDecl(), - true /*FullyQualified*/); + true /*FullyQualified*/, + /*Spec*/ nullptr); } - } else if (const NamespaceDecl* NS = scope->getAsNamespace()) { + } else if (const NamespaceDecl* NS = scope.getAsNamespace()) { return TypeName::CreateNestedNameSpecifier(Ctx, NS); - } else if (const NamespaceAliasDecl* alias = scope->getAsNamespaceAlias()) { + } else if (const NamespaceAliasDecl* alias = scope.getAsNamespaceAlias()) { const NamespaceDecl* CanonNS = alias->getNamespace()->getCanonicalDecl(); return TypeName::CreateNestedNameSpecifier(Ctx, CanonNS); } - return scope; + return &scope; } static NestedNameSpecifier* SelectPrefix(const ASTContext& Ctx, const DeclContext *declContext, NestedNameSpecifier *original_prefix, - const Transform::Config& TypeConfig) { + const Transform::Config& TypeConfig) + { // We have to also desugar the prefix. NestedNameSpecifier* prefix = 0; if (declContext) { @@ -434,7 +446,8 @@ namespace utils { if (old_ns == new_ns) { // This is the same namespace, use the original prefix // as a starting point. - prefix = GetFullyQualifiedNameSpecifier(Ctx,original_prefix); + assert(original_prefix && "Must have original_prefix!"); + prefix = GetFullyQualifiedNameSpecifier(Ctx, *original_prefix); } else { prefix = TypeName::CreateNestedNameSpecifier(Ctx, dyn_cast(new_ns)); @@ -456,7 +469,8 @@ namespace utils { const TagDecl *tdecl = dyn_cast(declContext); if (tdecl) { prefix = TypeName::CreateNestedNameSpecifier(Ctx, tdecl, - false /*FullyQualified*/); + false /*FullyQualified*/, + nullptr /*Spec*/); } } } else { @@ -466,12 +480,13 @@ namespace utils { const TagDecl *tdecl = dyn_cast(declContext); if (tdecl) { prefix = TypeName::CreateNestedNameSpecifier(Ctx,tdecl, - false /*FullyQualified*/); + false /*FullyQualified*/, + nullptr /*Spec*/); } } } } else { - prefix = GetFullyQualifiedNameSpecifier(Ctx,original_prefix); + prefix = GetFullyQualifiedNameSpecifier(Ctx, *original_prefix); } return prefix; } @@ -480,7 +495,8 @@ namespace utils { NestedNameSpecifier* SelectPrefix(const ASTContext& Ctx, const ElaboratedType *etype, NestedNameSpecifier *original_prefix, - const Transform::Config& TypeConfig) { + const Transform::Config& TypeConfig) + { // We have to also desugar the prefix. NestedNameSpecifier* prefix = etype->getQualifier(); @@ -526,12 +542,12 @@ namespace utils { if (old_ns == new_ns) { // This is the same namespace, use the original prefix // as a starting point. - prefix = GetFullyQualifiedNameSpecifier(Ctx,original_prefix); + prefix = GetFullyQualifiedNameSpecifier(Ctx, *original_prefix); } else { - prefix = GetFullyQualifiedNameSpecifier(Ctx,prefix); + prefix = GetFullyQualifiedNameSpecifier(Ctx, *prefix); } } else { - prefix = GetFullyQualifiedNameSpecifier(Ctx,prefix); + prefix = GetFullyQualifiedNameSpecifier(Ctx, *prefix); } } } @@ -552,7 +568,8 @@ namespace utils { QualType(scope_type,0), TypeConfig, /*qualifyType=*/false, - /*qualifyTmpltArg=*/true); + /*qualifyTmpltArg=*/true, + nullptr /*Spec*/); NestedNameSpecifier* outer_scope = scope->getPrefix(); const ElaboratedType* etype @@ -606,7 +623,7 @@ namespace utils { false /* template keyword wanted */, desugared.getTypePtr()); } else { - return GetFullyQualifiedNameSpecifier(Ctx,scope); + return GetFullyQualifiedNameSpecifier(Ctx, *scope); } } @@ -847,13 +864,14 @@ namespace utils { static bool GetPartiallyDesugaredTypeImpl(const ASTContext& Ctx, TemplateArgument &arg, const Transform::Config& TypeConfig, - bool fullyQualifyTmpltArg) { + bool fullyQualifyTmpltArg, + const ClassTemplateSpecializationDecl* Spec) { bool changed = false; if (arg.getKind() == TemplateArgument::Template) { TemplateName tname = arg.getAsTemplate(); // Note: should we not also desugar? - changed = GetFullyQualifiedTemplateName(Ctx, tname); + changed = GetFullyQualifiedTemplateName(Ctx, tname, Spec); if (changed) { arg = TemplateArgument(tname); } @@ -869,7 +887,7 @@ namespace utils { QualType PDQT = GetPartiallyDesugaredTypeImpl(Ctx, SubTy, TypeConfig, /*fullyQualifyType=*/true, - /*fullyQualifyTmpltArg=*/true); + /*fullyQualifyTmpltArg=*/true, Spec); arg = TemplateArgument(PDQT); } } else if (arg.getKind() == TemplateArgument::Pack) { @@ -878,7 +896,7 @@ namespace utils { TemplateArgument pack_arg(*I); changed = GetPartiallyDesugaredTypeImpl(Ctx,pack_arg, TypeConfig, - fullyQualifyTmpltArg); + fullyQualifyTmpltArg, Spec); desArgs.push_back(pack_arg); } if (changed) { @@ -932,7 +950,8 @@ namespace utils { static QualType GetPartiallyDesugaredTypeImpl(const ASTContext& Ctx, QualType QT, const Transform::Config& TypeConfig, - bool fullyQualifyType, bool fullyQualifyTmpltArg) + bool fullyQualifyType, bool fullyQualifyTmpltArg, + const ClassTemplateSpecializationDecl* Spec) { if (QT.isNull()) return QT; @@ -947,7 +966,8 @@ namespace utils { Qualifiers quals = QT.getQualifiers(); QualType nQT; nQT = GetPartiallyDesugaredTypeImpl(Ctx, QT->getPointeeType(), TypeConfig, - fullyQualifyType,fullyQualifyTmpltArg); + fullyQualifyType,fullyQualifyTmpltArg, + Spec); if (nQT == QT->getPointeeType()) return QT; QT = Ctx.getPointerType(nQT); @@ -974,7 +994,8 @@ namespace utils { Qualifiers quals = QT.getQualifiers(); QualType nQT; nQT = GetPartiallyDesugaredTypeImpl(Ctx, QT->getPointeeType(), TypeConfig, - fullyQualifyType,fullyQualifyTmpltArg); + fullyQualifyType,fullyQualifyTmpltArg, + Spec); if (nQT == QT->getPointeeType()) return QT; // Add the r- or l-value reference type back to the desugared one. @@ -998,7 +1019,8 @@ namespace utils { = dyn_cast(QT.getTypePtr()); QualType newQT = GetPartiallyDesugaredTypeImpl(Ctx,arr->getElementType(), TypeConfig, - fullyQualifyType,fullyQualifyTmpltArg); + fullyQualifyType,fullyQualifyTmpltArg, + Spec); if (newQT == arr->getElementType()) return QT; QT = Ctx.getConstantArrayType (newQT, arr->getSize(), @@ -1010,7 +1032,8 @@ namespace utils { = dyn_cast(QT.getTypePtr()); QualType newQT = GetPartiallyDesugaredTypeImpl(Ctx,arr->getElementType(), TypeConfig, - fullyQualifyType,fullyQualifyTmpltArg); + fullyQualifyType,fullyQualifyTmpltArg, + Spec); if (newQT == QT) return QT; QT = Ctx.getDependentSizedArrayType (newQT, arr->getSizeExpr(), @@ -1023,7 +1046,8 @@ namespace utils { = dyn_cast(QT.getTypePtr()); QualType newQT = GetPartiallyDesugaredTypeImpl(Ctx,arr->getElementType(), TypeConfig, - fullyQualifyType,fullyQualifyTmpltArg); + fullyQualifyType,fullyQualifyTmpltArg, + Spec); if (newQT == arr->getElementType()) return QT; QT = Ctx.getIncompleteArrayType (newQT, arr->getSizeModifier(), @@ -1034,7 +1058,8 @@ namespace utils { = dyn_cast(QT.getTypePtr()); QualType newQT = GetPartiallyDesugaredTypeImpl(Ctx,arr->getElementType(), TypeConfig, - fullyQualifyType,fullyQualifyTmpltArg); + fullyQualifyType,fullyQualifyTmpltArg, + Spec); if (newQT == arr->getElementType()) return QT; QT = Ctx.getVariableArrayType (newQT, arr->getSizeExpr(), @@ -1119,7 +1144,8 @@ namespace utils { isa(QT.getTypePtr())) { return GetPartiallyDesugaredTypeImpl(Ctx, QT, TypeConfig, fullyQualifyType, - fullyQualifyTmpltArg); + fullyQualifyTmpltArg, + Spec); } NestedNameSpecifier* prefix = 0; @@ -1183,7 +1209,7 @@ namespace utils { if (old_ns == new_ns) { // This is the same namespace, use the original prefix // as a starting point. - prefix = GetFullyQualifiedNameSpecifier(Ctx,original_prefix); + prefix = GetFullyQualifiedNameSpecifier(Ctx, *original_prefix); outer = 0; // Cancel the later creation. } } @@ -1203,7 +1229,8 @@ namespace utils { TagDecl *tdecl = dyn_cast(outer); if (tdecl) { prefix = TypeName::CreateNestedNameSpecifier(Ctx,tdecl, - false /*FullyQualified*/); + false /*FullyQualified*/, + Spec); prefix = GetPartiallyDesugaredNNS(Ctx,prefix,TypeConfig); } } @@ -1246,7 +1273,8 @@ namespace utils { // So for now just return move on with the least lose we can do return GetPartiallyDesugaredTypeImpl(Ctx, targetType, TypeConfig, fullyQualifyType, - fullyQualifyTmpltArg); + fullyQualifyTmpltArg, + Spec); } bool mightHaveChanged = false; @@ -1282,7 +1310,7 @@ namespace utils { if (I->getKind() == TemplateArgument::Template) { TemplateName tname = I->getAsTemplate(); // Note: should we not also desugar? - bool changed = GetFullyQualifiedTemplateName(Ctx, tname); + bool changed = GetFullyQualifiedTemplateName(Ctx, tname, Spec); if (changed) { desArgs.push_back(TemplateArgument(tname)); mightHaveChanged = true; @@ -1305,7 +1333,8 @@ namespace utils { QualType PDQT = GetPartiallyDesugaredTypeImpl(Ctx, SubTy, TypeConfig, fullyQualifyType, - fullyQualifyTmpltArg); + fullyQualifyTmpltArg, + Spec); mightHaveChanged |= (SubTy != PDQT); desArgs.push_back(TemplateArgument(PDQT)); } else { @@ -1341,13 +1370,13 @@ namespace utils { I != E; ++I) { #if 1 - // cheap to copy and potentially modified by // GetPartiallyDesugaredTypeImpl TemplateArgument arg(templateArgs[I]); mightHaveChanged |= GetPartiallyDesugaredTypeImpl(Ctx,arg, TypeConfig, - fullyQualifyTmpltArg); + fullyQualifyTmpltArg, + Spec); desArgs.push_back(arg); #else if (templateArgs[I].getKind() == TemplateArgument::Template) { @@ -1377,7 +1406,8 @@ namespace utils { QualType PDQT = GetPartiallyDesugaredTypeImpl(Ctx, SubTy, TypeConfig, /*fullyQualifyType=*/true, - /*fullyQualifyTmpltArg=*/true); + /*fullyQualifyTmpltArg=*/true, + Spec); desArgs.push_back(TemplateArgument(PDQT)); } else { desArgs.push_back(templateArgs[I]); @@ -1415,7 +1445,8 @@ namespace utils { { return GetPartiallyDesugaredTypeImpl(Ctx,QT,TypeConfig, /*qualifyType*/fullyQualify, - /*qualifyTmpltArg*/fullyQualify); + /*qualifyTmpltArg*/fullyQualify, + nullptr /*Spec*/); } NamespaceDecl* Lookup::Namespace(Sema* S, const char* Name, @@ -1504,55 +1535,63 @@ namespace utils { static NestedNameSpecifier* CreateNestedNameSpecifierForScopeOf(const ASTContext& Ctx, const Decl *decl, - bool FullyQualified) + bool FullyQualified, + const ClassTemplateSpecializationDecl* Spec) { // Create a nested name specifier for the declaring context of the type. assert(decl); - const NamedDecl* outer - = llvm::dyn_cast_or_null(decl->getDeclContext()); - const NamespaceDecl* outer_ns - = llvm::dyn_cast_or_null(decl->getDeclContext()); - if (outer && !(outer_ns && outer_ns->isAnonymousNamespace())) { - - if (const CXXRecordDecl *cxxdecl - = llvm::dyn_cast(decl->getDeclContext())) { - - if (ClassTemplateDecl *clTempl = cxxdecl->getDescribedClassTemplate()) { - // We are in the case of a type(def) that was declared in a - // class template but is *not* type dependent. In clang, it gets - // attached to the class template declaration rather than any - // specific class template instantiation. This result in 'odd' - // fully qualified typename: - // vector<_Tp,_Alloc>::size_type - // Make the situation is 'useable' but looking a bit odd by - // picking a random instance as the declaring context. - // FIXME: We should not use the iterators here to check if we are in - // a template specialization. clTempl != cxxdecl already tell us that - // is the case. It seems that we rely on a side-effect from triggering - // deserializations to support 'some' use-case. See ROOT-9709. - if (clTempl->spec_begin() != clTempl->spec_end()) { - decl = *(clTempl->spec_begin()); - outer = llvm::dyn_cast(decl); - outer_ns = llvm::dyn_cast(decl); - } + if (auto outer_ns = llvm::dyn_cast(decl->getDeclContext())) { + if (!outer_ns->isAnonymousNamespace()) + return TypeName::CreateNestedNameSpecifier(Ctx,outer_ns); + return nullptr; + } + + auto outer = llvm::dyn_cast(decl->getDeclContext()); + if (!outer) + return nullptr; + + if (auto cxxdecl = llvm::dyn_cast(outer)) { + if (ClassTemplateDecl *clTempl = cxxdecl->getDescribedClassTemplate()) { + assert(Spec && "Cannot determine type's scope"); + while (Spec && clTempl->getCanonicalDecl() != + Spec->getSpecializedTemplate()->getCanonicalDecl()) { + // This might be due to + // Spec: A::B + // decl: A::type + // Check Spec's scopes to see if any is a match. + Spec = llvm::dyn_cast( + Spec->getDeclContext()); } - } - if (outer_ns) { - return TypeName::CreateNestedNameSpecifier(Ctx,outer_ns); - } else if (const TagDecl* TD = llvm::dyn_cast(outer)) { - return TypeName::CreateNestedNameSpecifier(Ctx, TD, FullyQualified); + // We are in the case of a type(def) that was declared in a + // class template but is *not* type dependent. In clang, it gets + // attached to the class template declaration rather than any + // specific class template instantiation. This result in 'odd' + // fully qualified typename: + // vector<_Tp,_Alloc>::size_type + // Make the situation 'useable' by using the provided specialization + // as the declaring context. + assert(clTempl->getCanonicalDecl() + == Spec->getSpecializedTemplate()->getCanonicalDecl() + && "incompatible context"); + outer = Spec; } } - return 0; + + if (const TagDecl* TD = llvm::dyn_cast(outer)) { + return TypeName::CreateNestedNameSpecifier(Ctx, TD, FullyQualified, + nullptr /*Spec*/); + } + return nullptr; } static NestedNameSpecifier* CreateNestedNameSpecifierForScopeOf(const ASTContext& Ctx, const Type *TypePtr, - bool FullyQualified) + bool FullyQualified, + const ClassTemplateSpecializationDecl* Spec) { // Create a nested name specifier for the declaring context of the type. @@ -1573,7 +1612,7 @@ namespace utils { if (!decl) return 0; - return CreateNestedNameSpecifierForScopeOf(Ctx, decl, FullyQualified); + return CreateNestedNameSpecifierForScopeOf(Ctx, decl, FullyQualified, Spec); } NestedNameSpecifier* @@ -1587,35 +1626,44 @@ namespace utils { bool FullyQualified = true; // doesn't matter, DeclContexts are namespaces return NestedNameSpecifier::Create(Ctx, CreateOuterNNS(Ctx, Namesp, - FullyQualified), + FullyQualified, + nullptr /*Spec*/), Namesp); } NestedNameSpecifier* TypeName::CreateNestedNameSpecifier(const ASTContext& Ctx, const TypedefNameDecl* TD, - bool FullyQualify) { + bool FullyQualify, + const ClassTemplateSpecializationDecl* Spec) + { return NestedNameSpecifier::Create(Ctx, CreateOuterNNS(Ctx, TD, - FullyQualify), + FullyQualify, Spec), true /*Template*/, TD->getTypeForDecl()); } NestedNameSpecifier* TypeName::CreateNestedNameSpecifier(const ASTContext& Ctx, - const TagDecl *TD, bool FullyQualify) { + const TagDecl *TD, + bool FullyQualify, + const ClassTemplateSpecializationDecl* Spec) + { const Type* Ty = Ctx.getTypeDeclType(TD).getTypePtr(); if (FullyQualify) - Ty = GetFullyQualifiedLocalType(Ctx, Ty); + Ty = GetFullyQualifiedLocalType(Ctx, Spec, Ty); return NestedNameSpecifier::Create(Ctx, - CreateOuterNNS(Ctx, TD, FullyQualify), + CreateOuterNNS(Ctx, TD, FullyQualify, + Spec), false /* template keyword wanted */, Ty); } QualType - TypeName::GetFullyQualifiedType(QualType QT, const ASTContext& Ctx) { + TypeName::GetFullyQualifiedType(QualType QT, const ASTContext& Ctx, + const ClassTemplateSpecializationDecl* Spec) + { // Return the fully qualified type, if we need to recurse through any // template parameter, this needs to be merged somehow with // GetPartialDesugaredType. @@ -1625,7 +1673,7 @@ namespace utils { if (llvm::isa(QT.getTypePtr())) { // Get the qualifiers. Qualifiers quals = QT.getQualifiers(); - QT = GetFullyQualifiedType(QT->getPointeeType(), Ctx); + QT = GetFullyQualifiedType(QT->getPointeeType(), Ctx, Spec); QT = Ctx.getPointerType(QT); // Add back the qualifiers. QT = Ctx.getQualifiedType(QT, quals); @@ -1638,7 +1686,7 @@ namespace utils { // Get the qualifiers. bool isLValueRefTy = llvm::isa(QT.getTypePtr()); Qualifiers quals = QT.getQualifiers(); - QT = GetFullyQualifiedType(QT->getPointeeType(), Ctx); + QT = GetFullyQualifiedType(QT->getPointeeType(), Ctx, Spec); // Add the r- or l-value reference type back to the desugared one. if (isLValueRefTy) QT = Ctx.getLValueReferenceType(QT); @@ -1652,7 +1700,7 @@ namespace utils { // Strip deduced types. if (const AutoType* AutoTy = dyn_cast(QT.getTypePtr())) { if (!AutoTy->getDeducedType().isNull()) - return GetFullyQualifiedType(AutoTy->getDeducedType(), Ctx); + return GetFullyQualifiedType(AutoTy->getDeducedType(), Ctx, Spec); } // Remove the part of the type related to the type being a template @@ -1681,7 +1729,7 @@ namespace utils { if (prefix != NestedNameSpecifier::GlobalSpecifier(Ctx) && !(ns && ns->isAnonymousNamespace())) { prefix_qualifiers = QT.getLocalQualifiers(); - prefix = GetFullyQualifiedNameSpecifier(Ctx, prefix); + prefix = GetFullyQualifiedNameSpecifier(Ctx, *prefix); QT = QualType(etype_input->getNamedType().getTypePtr(),0); } else { prefix = 0; @@ -1692,7 +1740,8 @@ namespace utils { // Create a nested name specifier if needed (i.e. if the decl context // is not the global scope. prefix = CreateNestedNameSpecifierForScopeOf(Ctx,QT.getTypePtr(), - true /*FullyQualified*/); + true /*FullyQualified*/, + Spec); // move the qualifiers on the outer type (avoid 'std::const string'!) if (prefix) { @@ -1706,7 +1755,8 @@ namespace utils { if(llvm::isa(QT.getTypePtr())) { Qualifiers qualifiers = QT.getLocalQualifiers(); - const Type *TypePtr = GetFullyQualifiedLocalType(Ctx,QT.getTypePtr()); + const Type *TypePtr + = GetFullyQualifiedLocalType(Ctx, Spec, QT.getTypePtr()); QT = Ctx.getQualifiedType(TypePtr, qualifiers); } else if (llvm::isa(QT.getTypePtr())) { @@ -1715,7 +1765,8 @@ namespace utils { // its template argument, however we still need to fully qualify them. Qualifiers qualifiers = QT.getLocalQualifiers(); - const Type *TypePtr = GetFullyQualifiedLocalType(Ctx,QT.getTypePtr()); + const Type *TypePtr + = GetFullyQualifiedLocalType(Ctx, Spec, QT.getTypePtr()); QT = Ctx.getQualifiedType(TypePtr, qualifiers); } @@ -1729,8 +1780,9 @@ namespace utils { } std::string TypeName::GetFullyQualifiedName(QualType QT, - const ASTContext &Ctx) { - QualType FQQT = GetFullyQualifiedType(QT, Ctx); + const ASTContext &Ctx, + const ClassTemplateSpecializationDecl* Spec) { + QualType FQQT = GetFullyQualifiedType(QT, Ctx, Spec); PrintingPolicy Policy(Ctx.getPrintingPolicy()); Policy.SuppressScope = false; Policy.AnonymousTagLocations = false; diff --git a/interpreter/cling/test/AST/typeName.C b/interpreter/cling/test/AST/typeName.C new file mode 100644 index 0000000000000..22f9f5be226ea --- /dev/null +++ b/interpreter/cling/test/AST/typeName.C @@ -0,0 +1,44 @@ +//------------------------------------------------------------------------------ +// CLING - the C++ LLVM-based InterpreterG :) +// +// This file is dual-licensed: you can choose to license it under the University +// of Illinois Open Source License or the GNU Lesser General Public License. See +// LICENSE.TXT for details. +//------------------------------------------------------------------------------ + +// RUN: cat %s | %cling -I%p | FileCheck %s + +// This test covers cling::utils::GetFullyQualifiedName() interfaces. +// https://github.com/root-project/root/issues/7955 + + +#include "cling/Interpreter/Interpreter.h" +#include "cling/Interpreter/LookupHelper.h" +#include "cling/Utils/AST.h" + +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" + +#include "llvm/Support/Casting.h" + +.rawInput 1 + +template +struct Template1 { + using type1 = int; + void f0(type1); +}; + +Template1 xf1; + +.rawInput 0 + +const auto& lookup = gCling->getLookupHelper(); +using namespace clang; + +auto *T1s = llvm::dyn_cast(lookup.findScope("Template1", cling::LookupHelper::WithDiagnostics)); +// Find first function decl: +auto iFunc = std::find_if(T1s->decls_begin(), T1s->decls_end(), + [](const Decl* D) {if (auto FD = llvm::dyn_cast(D)) return FD->isUserProvided(); return false;}); +using cling::utils::TypeName::GetFullyQualifiedName; +GetFullyQualifiedName(llvm::dyn_cast(*iFunc)->getParamDecl(0)->getType(), T1s->getASTContext(), T1s) // CHECK: (std::string) "Template1::type1"