diff --git a/core/clingutils/res/TClingUtils.h b/core/clingutils/res/TClingUtils.h index de5a174ac835b..bac84e1414f77 100644 --- a/core/clingutils/res/TClingUtils.h +++ b/core/clingutils/res/TClingUtils.h @@ -613,12 +613,14 @@ static inline std::string DemangleNameForDlsym(const std::string& name) //______________________________________________________________________________ // 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 b977d67cd193b..5b92bf16ec835 100644 --- a/core/clingutils/src/TClingUtils.cxx +++ b/core/clingutils/src/TClingUtils.cxx @@ -1358,7 +1358,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)); } //---- @@ -1473,6 +1474,7 @@ static void CreateNameTypeMap(const clang::CXXRecordDecl &cl, ROOT::MembersTypeM 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(); @@ -1497,7 +1499,7 @@ static void CreateNameTypeMap(const clang::CXXRecordDecl &cl, ROOT::MembersTypeM } } - ROOT::TMetaUtils::GetFullyQualifiedTypeName(typenameStr, fieldType, astContext); + ROOT::TMetaUtils::GetFullyQualifiedTypeName(typenameStr, fieldType, astContext, CTSD); nameType[field_iter->getName().str()] = ROOT::Internal::TSchemaType(typenameStr.c_str(),dims.str().c_str()); } @@ -3489,9 +3491,10 @@ std::string 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); @@ -3501,7 +3504,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 @@ -3510,7 +3514,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 b034c9a0b5ce2..9cac67101da2c 100644 --- a/core/dictgen/src/DictSelectionReader.cxx +++ b/core/dictgen/src/DictSelectionReader.cxx @@ -383,12 +383,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/TClingDataMemberInfo.cxx b/core/metacling/src/TClingDataMemberInfo.cxx index 57d64ef3c7c32..69f4f22051102 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" @@ -586,7 +587,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 f796ec3fde688..656fe04bb6881 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 nullptr; } - 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 baee85922eee5..55797f36a40d5 100644 --- a/core/metacling/src/TClingMethodInfo.cxx +++ b/core/metacling/src/TClingMethodInfo.cxx @@ -634,7 +634,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())) { @@ -688,9 +690,17 @@ const char *TClingMethodInfo::TypeName() const // The next *two* calls lock the interpreter mutex. Lock here first instead // of locking/unlocking twice. R__LOCKGUARD(gInterpreterMutex); - 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 46cbeedf86618..563ebf1479903 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 e5bb88fda7468..7136ff107e702 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" @@ -97,7 +98,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 // TODO: This needs to be locked, but the lock cannot be placed in TClingUtils.cxx as it cannot depend from // TInterpreter.h for the declaration of gInterpreterMutex. Or can it? 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 c68a9ba7b2cf2..f66fc4cd71148 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; } @@ -65,7 +69,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 de3da8684afa3..74a2235b63944 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"); @@ -555,3 +558,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/core/metacling/test/TClingTests.cxx b/core/metacling/test/TClingTests.cxx index 674ec865c761e..c94cc0815f7f7 100644 --- a/core/metacling/test/TClingTests.cxx +++ b/core/metacling/test/TClingTests.cxx @@ -4,6 +4,8 @@ #include "TInterpreter.h" #include "TROOT.h" #include "TSystem.h" +#include "TMethod.h" +#include "TMethodArg.h" #include "gmock/gmock.h" @@ -364,3 +366,13 @@ auto val2 = Ln10_pushforward(); EXPECT_DOUBLE_EQ(2.3025850929940459, val1); EXPECT_DOUBLE_EQ(2.3025850929940459, val2); } + +// https://github.com/root-project/root/issues/7955 +TEST(ClingAST, Issue7955) +{ + auto cls = TClass::GetClass("std::vector"); + auto meth = cls->GetMethodWithPrototype("operator[]","int",true, ROOT::kConversionMatch); + auto args = meth->GetListOfMethodArgs(); + auto methArg = dynamic_cast(args->First()); + EXPECT_STREQ(methArg->GetTypeName(), "vector::size_type"); +} diff --git a/interpreter/cling/include/cling/Utils/AST.h b/interpreter/cling/include/cling/Utils/AST.h index c287626299b4c..46d2a32f72804 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; @@ -301,7 +302,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. @@ -310,7 +312,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. @@ -332,7 +335,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. @@ -345,7 +349,8 @@ namespace utils { clang::NestedNameSpecifier* CreateNestedNameSpecifier(const clang::ASTContext& Ctx, const clang::TypedefNameDecl *TD, - bool FullyQualify); + bool FullyQualify, + const clang::ClassTemplateSpecializationDecl* Spec = nullptr); ///\brief Create a NestedNameSpecifier for UsingShadowDecl and its enclosing /// scopes. @@ -358,7 +363,8 @@ namespace utils { clang::NestedNameSpecifier* CreateNestedNameSpecifier(const clang::ASTContext& Ctx, const clang::UsingShadowDecl *USD, - 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 80aba72672e90..7b6519f5f656f 100644 --- a/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.cpp +++ b/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.cpp @@ -113,7 +113,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()); } @@ -938,7 +940,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(nullptr, "type template param default failed"); @@ -1094,6 +1098,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 abe241867bed5..3392e27d01d46 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 ef34d9595c710..d41fc821ed7aa 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) @@ -196,7 +198,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 = nullptr; @@ -206,7 +209,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; @@ -214,7 +217,7 @@ namespace utils { NNS = nullptr; } } else { - NNS = CreateNestedNameSpecifierForScopeOf(Ctx, argtdecl, true); + NNS = CreateNestedNameSpecifierForScopeOf(Ctx, argtdecl, true, Spec); } if (NNS) { TemplateName UnderlyingTN(argtdecl); @@ -230,6 +233,7 @@ namespace utils { static bool GetFullyQualifiedTemplateArgument(const ASTContext& Ctx, + const ClassTemplateSpecializationDecl* Spec, TemplateArgument &arg) { bool changed = false; @@ -239,14 +243,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; @@ -255,7 +259,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) { @@ -271,6 +275,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 @@ -284,7 +289,7 @@ namespace utils { // cheap to copy and potentially modified by // GetFullyQualifedTemplateArgument TemplateArgument CopiedArg = Arg; - mightHaveChanged |= GetFullyQualifiedTemplateArgument(Ctx, CopiedArg); + mightHaveChanged |= GetFullyQualifiedTemplateArgument(Ctx, Spec, CopiedArg); desArgs.push_back(CopiedArg); } @@ -316,7 +321,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); } @@ -336,7 +342,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()) { @@ -347,23 +355,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 = nullptr; if (const TagType* tagdecltype = dyn_cast(type)) { @@ -373,29 +382,32 @@ 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 UsingType* UT = dyn_cast(type)) { return TypeName::CreateNestedNameSpecifier(Ctx, UT->getFoundDecl(), true /*FullyQualified*/); } - } 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 = nullptr; if (declContext) { @@ -420,7 +432,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)); @@ -442,7 +455,8 @@ namespace utils { const TagDecl *tdecl = dyn_cast(declContext); if (tdecl) { prefix = TypeName::CreateNestedNameSpecifier(Ctx, tdecl, - false /*FullyQualified*/); + false /*FullyQualified*/, + nullptr /*Spec*/); } } } else { @@ -452,12 +466,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; } @@ -466,7 +481,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(); @@ -512,12 +528,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); } } } @@ -538,7 +554,8 @@ namespace utils { QualType(scope_type,0), TypeConfig, /*qualifyType=*/false, - /*qualifyTmpltArg=*/true); + /*qualifyTmpltArg=*/true, + nullptr /*Spec*/); NestedNameSpecifier* outer_scope = scope->getPrefix(); const ElaboratedType* etype @@ -596,7 +613,7 @@ namespace utils { false /* template keyword wanted */, desugared.getTypePtr()); } else { - return GetFullyQualifiedNameSpecifier(Ctx,scope); + return GetFullyQualifiedNameSpecifier(Ctx, *scope); } } @@ -841,13 +858,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); } @@ -864,7 +882,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) { @@ -873,7 +891,7 @@ namespace utils { TemplateArgument pack_arg(*I); changed = GetPartiallyDesugaredTypeImpl(Ctx,pack_arg, TypeConfig, - fullyQualifyTmpltArg); + fullyQualifyTmpltArg, Spec); desArgs.push_back(pack_arg); } if (changed) { @@ -927,7 +945,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; @@ -942,7 +961,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); @@ -969,7 +989,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. @@ -993,7 +1014,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(), @@ -1006,7 +1028,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(), @@ -1019,7 +1042,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(), @@ -1030,7 +1054,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(), @@ -1115,7 +1140,8 @@ namespace utils { isa(QT.getTypePtr())) { return GetPartiallyDesugaredTypeImpl(Ctx, QT, TypeConfig, fullyQualifyType, - fullyQualifyTmpltArg); + fullyQualifyTmpltArg, + Spec); } NestedNameSpecifier* prefix = nullptr; @@ -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 = nullptr; // 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; @@ -1284,7 +1312,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; @@ -1308,7 +1336,8 @@ namespace utils { QualType PDQT = GetPartiallyDesugaredTypeImpl(Ctx, SubTy, TypeConfig, fullyQualifyType, - fullyQualifyTmpltArg); + fullyQualifyTmpltArg, + Spec); mightHaveChanged |= (SubTy != PDQT); desArgs.push_back(TemplateArgument(PDQT)); } else { @@ -1344,13 +1373,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) { @@ -1381,7 +1410,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]); @@ -1419,7 +1449,8 @@ namespace utils { { return GetPartiallyDesugaredTypeImpl(Ctx,QT,TypeConfig, /*qualifyType*/fullyQualify, - /*qualifyTmpltArg*/fullyQualify); + /*qualifyTmpltArg*/fullyQualify, + nullptr /*Spec*/); } NamespaceDecl* Lookup::Namespace(Sema* S, const char* Name, @@ -1508,55 +1539,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; } } + + 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. @@ -1579,7 +1618,7 @@ namespace utils { if (!decl) return nullptr; - return CreateNestedNameSpecifierForScopeOf(Ctx, decl, FullyQualified); + return CreateNestedNameSpecifierForScopeOf(Ctx, decl, FullyQualified, Spec); } NestedNameSpecifier* @@ -1593,16 +1632,19 @@ 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()); } @@ -1610,28 +1652,35 @@ namespace utils { NestedNameSpecifier* TypeName::CreateNestedNameSpecifier(const ASTContext& Ctx, const UsingShadowDecl* USD, - bool FullyQualify) { + bool FullyQualify, + const ClassTemplateSpecializationDecl* Spec) { return NestedNameSpecifier::Create(Ctx, CreateOuterNNS(Ctx, USD, - FullyQualify), + FullyQualify, Spec), true /*Template*/, cast(USD)->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. @@ -1641,7 +1690,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); @@ -1654,7 +1703,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); @@ -1668,7 +1717,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 @@ -1696,7 +1745,9 @@ namespace utils { const NamespaceDecl *ns = prefix->getAsNamespace(); if (prefix != NestedNameSpecifier::GlobalSpecifier(Ctx) && !(ns && ns->isAnonymousNamespace())) { - prefix = GetFullyQualifiedNameSpecifier(Ctx, prefix); + prefix_qualifiers = QT.getLocalQualifiers(); + prefix = GetFullyQualifiedNameSpecifier(Ctx, *prefix); + QT = QualType(etype_input->getNamedType().getTypePtr(),0); } else { prefix = nullptr; } @@ -1707,7 +1758,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) { @@ -1721,7 +1773,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())) { @@ -1730,7 +1783,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); } @@ -1746,8 +1800,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"