From eb9913234b3fd75472fb617e11cbe4482a15b475 Mon Sep 17 00:00:00 2001 From: ykiko Date: Fri, 6 Sep 2024 22:40:47 +0800 Subject: [PATCH] add some new test for Resolver. --- .vscode/launch.json | 2 +- include/AST/Resolver.h | 6 +- src/AST/Resolver.cpp | 141 +++++++++++++++++++++++++++-------------- tests/Resolver.cpp | 133 +++++++++++++++++++++++++++++--------- 4 files changed, 205 insertions(+), 77 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 5d947f75..56e55913 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -15,7 +15,7 @@ "type": "lldb", "request": "launch", "name": "DependentNameResolver", - "program": "${workspaceFolder}/build/bin/test", + "program": "${workspaceFolder}/build/bin/clice_test", "cwd": "${workspaceFolder}" } ] diff --git a/include/AST/Resolver.h b/include/AST/Resolver.h index 35db5f7a..72b55470 100644 --- a/include/AST/Resolver.h +++ b/include/AST/Resolver.h @@ -18,6 +18,8 @@ class DependentNameResolver { public: DependentNameResolver(clang::Sema& sema, clang::ASTContext& context) : sema(sema), context(context) {} + clang::QualType resolve(clang::NamedDecl* ND); + clang::QualType resolve(clang::QualType type); clang::QualType resolve(const clang::DependentNameType* DNT); @@ -28,7 +30,7 @@ public: const clang::IdentifierInfo* II); bool lookup(llvm::SmallVector& result, - const clang::TemplateSpecializationType* TST, + const clang::QualType type, const clang::IdentifierInfo* II); // lookup member in a given class template @@ -41,6 +43,8 @@ public: // replace the template arguments in the type, using the arguments in the frame clang::QualType substitute(clang::QualType type); + clang::Decl* substitute(clang::Decl* decl); + private: struct Frame { clang::NamedDecl* decl; diff --git a/src/AST/Resolver.cpp b/src/AST/Resolver.cpp index cfd16836..488fd107 100644 --- a/src/AST/Resolver.cpp +++ b/src/AST/Resolver.cpp @@ -2,29 +2,85 @@ namespace clice { -clang::QualType DependentNameResolver::resolve(clang::QualType type) { - if(auto DNT = clang::dyn_cast(type)) { - return resolve(DNT); +clang::QualType DependentNameResolver::resolve(clang::NamedDecl* ND) { + auto decl = substitute(ND); + if(auto TAD = llvm::dyn_cast(decl)) { + return resolve(TAD->getUnderlyingType()); + } else if(auto TND = llvm::dyn_cast(decl)) { + return resolve(TND->getUnderlyingType()); } else { std::terminate(); } } -clang::QualType DependentNameResolver::resolve(const clang::DependentNameType* DNT) { - auto prefix = DNT->getQualifier(); - auto II = DNT->getIdentifier(); - llvm::SmallVector result; - lookup(result, prefix, II); - assert(result.size() == 1); +clang::QualType DependentNameResolver::resolve(clang::QualType type) { + if(!type->isDependentType()) { + return type; + } - // TODO: substitute template arguments - if(auto TAD = clang::dyn_cast(result.front())) { - return substitute(TAD->getUnderlyingType()); + type = type.getDesugaredType(context); + + if(auto DNT = llvm::dyn_cast(type)) { + return resolve(DNT); } else { + return type; + } +} + +clang::QualType DependentNameResolver::resolve(const clang::DependentNameType* DNT) { + llvm::SmallVector result; + if(lookup(result, DNT->getQualifier(), DNT->getIdentifier()) && result.size() == 1) { + return resolve(result.front()); + } else { + DNT->dump(); std::terminate(); } } +bool DependentNameResolver::lookup(llvm::SmallVector& result, + const clang::NestedNameSpecifier* NNS, + const clang::IdentifierInfo* II) { + switch(NNS->getKind()) { + // prefix is an identifier, e.g. <...>::name:: + case clang::NestedNameSpecifier::SpecifierKind::Identifier: { + if(lookup(result, NNS->getPrefix(), NNS->getAsIdentifier()) && result.size() == 1) { + auto type = resolve(result.front()); + result.clear(); + return lookup(result, type, II); + } else { + NNS->dump(); + std::terminate(); + } + } + + // prefix is a type, e.g. <...>::typename name:: + case clang::NestedNameSpecifier::SpecifierKind::TypeSpec: + case clang::NestedNameSpecifier::SpecifierKind::TypeSpecWithTemplate: { + return lookup(result, clang::QualType(NNS->getAsType(), 0), II); + } + + default: { + NNS->dump(); + std::terminate(); + } + } +} + +bool DependentNameResolver::lookup(llvm::SmallVector& result, + const clang::QualType type, + const clang::IdentifierInfo* II) { + if(auto TST = type->getAs()) { + auto TD = TST->getTemplateName().getAsTemplateDecl(); + // FIXME: consider default arguments + auto args = TST->template_arguments(); + if(auto CTD = llvm::dyn_cast(TD)) { + return lookup(result, CTD, II, args); + } + } + + std::terminate(); +} + bool DependentNameResolver::lookup(llvm::SmallVector& result, clang::ClassTemplateDecl* CTD, const clang::IdentifierInfo* II, @@ -33,6 +89,7 @@ bool DependentNameResolver::lookup(llvm::SmallVector& result, // main template first auto decls = CTD->getTemplatedDecl()->lookup(II); if(!decls.empty()) { + std::size_t count = 0; for(auto decl: decls) { result.push_back(decl); } @@ -48,56 +105,26 @@ bool DependentNameResolver::lookup(llvm::SmallVector& result, for(auto partial: partials) { clang::sema::TemplateDeductionInfo info(partial->getLocation()); if(sema.DeduceTemplateArguments(partial, arguments, info) != clang::TemplateDeductionResult::Success) { - return false; + break; } auto decls = partial->lookup(II); if(decls.empty()) { - return false; + break; } for(auto decl: decls) { result.push_back(decl); } + // NOTE: takeSugared will take the ownership of the list auto list = info.takeSugared(); frames.emplace_back(partial, list->asArray()); delete list; return true; } -} -bool DependentNameResolver::lookup(llvm::SmallVector& result, - const clang::NestedNameSpecifier* NNS, - const clang::IdentifierInfo* II) { - switch(NNS->getKind()) { - // prefix is an identifier, e.g. <...>::name:: - case clang::NestedNameSpecifier::SpecifierKind::Identifier: { - // return lookup(resolve(resolve(NNS->getPrefix(), NNS->getAsIdentifier())), - // II); - } - - // prefix is a type, e.g. <...>::typename name:: - case clang::NestedNameSpecifier::SpecifierKind::TypeSpec: - case clang::NestedNameSpecifier::SpecifierKind::TypeSpecWithTemplate: { - auto type = NNS->getAsType(); - if(auto TST = type->getAs()) { - auto TD = TST->getTemplateName().getAsTemplateDecl(); - // FIXME: consider default arguments - auto args = TST->template_arguments(); - if(auto CTD = llvm::dyn_cast(TD)) { - return lookup(result, CTD, II, args); - } - } - - std::terminate(); - } - - default: { - NNS->dump(); - std::terminate(); - } - } + return false; } clang::QualType DependentNameResolver::substitute(clang::QualType type) { @@ -121,4 +148,26 @@ clang::QualType DependentNameResolver::substitute(clang::QualType type) { return result; } +clang::Decl* DependentNameResolver::substitute(clang::Decl* decl) { + clang::MultiLevelTemplateArgumentList list; + for(auto begin = frames.rbegin(), end = frames.rend(); begin != end; ++begin) { + list.addOuterTemplateArguments(begin->decl, begin->arguments, true); + } + + for(auto frame: frames) { + clang::Sema::CodeSynthesisContext context; + context.Entity = frame.decl; + context.TemplateArgs = frame.arguments.data(); + context.Kind = clang::Sema::CodeSynthesisContext::TemplateInstantiation; + sema.pushCodeSynthesisContext(context); + } + + // FIXME: use fake TU + auto result = sema.SubstDecl(decl, context.getTranslationUnitDecl(), list); + + frames.clear(); + + return result; +} + } // namespace clice diff --git a/tests/Resolver.cpp b/tests/Resolver.cpp index 79b508a1..293732d6 100644 --- a/tests/Resolver.cpp +++ b/tests/Resolver.cpp @@ -5,10 +5,18 @@ using namespace clice; namespace { -struct ASTVisitor : public clang::RecursiveASTVisitor { - clang::Sema& sema; - clang::QualType& result; - clang::ASTContext& context; +std::vector compileArgs = { + "clang++", + "-std=c++20", + "main.cpp", + "-resource-dir=../build/lib/clang/20", +}; + +struct Visitor : public clang::RecursiveASTVisitor { + clang::QualType result; + std::unique_ptr parsedAST; + + Visitor(const char* code) : parsedAST(ParsedAST::build("main.cpp", code, compileArgs)) {} bool VisitTypeAliasDecl(clang::TypeAliasDecl* decl) { if(decl->getName() == "result") { @@ -18,23 +26,22 @@ struct ASTVisitor : public clang::RecursiveASTVisitor { context.Kind = clang::Sema::CodeSynthesisContext::TemplateInstantiation; context.Entity = decl; context.TemplateArgs = nullptr; - sema.pushCodeSynthesisContext(context); + parsedAST->sema.pushCodeSynthesisContext(context); } - auto resolver = DependentNameResolver(sema, context); + auto resolver = DependentNameResolver(parsedAST->sema, parsedAST->context); result = resolver.resolve(type); } return true; } + + clang::QualType test() { + auto decl = parsedAST->context.getTranslationUnitDecl(); + TraverseDecl(decl); + return result; + } }; -TEST(DependentNameResolver, resolve) { - std::vector args{ - "clang++", - "-std=c++20", - "main.cpp", - "-resource-dir=../build/lib/clang/20", - }; - +TEST(DependentNameResolver, single_level_dependent_name) { const char* code = R"( template struct type_list {}; @@ -48,27 +55,95 @@ template struct test { using result = typename A::type; }; + )"; - auto parsedAST = ParsedAST::build("main.cpp", code, args); - auto decl = parsedAST->context.getTranslationUnitDecl(); + Visitor visitor(code); + auto result = visitor.test(); - clang::QualType result; + auto TST = result->getAs(); + ASSERT_TRUE(TST); + ASSERT_EQ(TST->getTemplateName().getAsTemplateDecl()->getName(), "type_list"); - ASTVisitor visitor{{}, parsedAST->sema, result, parsedAST->context}; - visitor.TraverseDecl(decl); + auto args = TST->template_arguments(); + ASSERT_EQ(args.size(), 1); - { - auto TST = result->getAs(); - ASSERT_TRUE(TST); - ASSERT_EQ(TST->getTemplateName().getAsTemplateDecl()->getName(), "type_list"); + auto T = llvm::dyn_cast(args[0].getAsType()); + ASSERT_TRUE(T); + ASSERT_EQ(T->getDecl()->getName(), "X"); +} - auto args = TST->template_arguments(); - ASSERT_EQ(args.size(), 1); - auto T = llvm::dyn_cast(args[0].getAsType()); - ASSERT_TRUE(T); - ASSERT_EQ(T->getDecl()->getName(), "X"); - } +TEST(DependentNameResolver, multi_level_dependent_name) { + const char* code = R"( +template +struct type_list {}; + +template +struct A { + using type = type_list; +}; + +template +struct B { + using type = typename A::type; +}; + +template +struct C { + using type = typename B::type; +}; + +template +struct test { + using result = typename C::type; +}; + +)"; + Visitor visitor(code); + clang::QualType result = visitor.test(); + + auto TST = result->getAs(); + ASSERT_TRUE(TST); + ASSERT_EQ(TST->getTemplateName().getAsTemplateDecl()->getName(), "type_list"); + + auto args = TST->template_arguments(); + ASSERT_EQ(args.size(), 1); + + auto T = llvm::dyn_cast(args[0].getAsType()); + ASSERT_TRUE(T); + ASSERT_EQ(T->getDecl()->getName(), "X"); +} + +TEST(DependentNameResolver, dependent_dependent_dependent_name) { + const char* code = R"( +template +struct type_list {}; + +template +struct A { + using self = A; + using type = type_list; +}; + +template +struct test { + using result = typename A::self::self::self::self::self::type; +}; + +)"; + Visitor visitor(code); + clang::QualType result = visitor.test(); + + auto TST = result->getAs(); + ASSERT_TRUE(TST); + ASSERT_EQ(TST->getTemplateName().getAsTemplateDecl()->getName(), "type_list"); + + auto args = TST->template_arguments(); + ASSERT_EQ(args.size(), 1); + + auto T = llvm::dyn_cast(args[0].getAsType()); + ASSERT_TRUE(T); + ASSERT_EQ(T->getDecl()->getName(), "X"); } } // namespace