add test for Resolver.

This commit is contained in:
ykiko
2024-09-06 18:36:24 +08:00
parent 605d1f8042
commit 6f7f2bb8a0
9 changed files with 245 additions and 290 deletions

View File

@@ -15,8 +15,10 @@ struct ParsedAST {
std::unique_ptr<clang::FrontendAction> action;
std::unique_ptr<clang::CompilerInstance> instance;
static std::unique_ptr<ParsedAST>
build(llvm::StringRef filename, llvm::StringRef content, std::vector<const char*>& args, Preamble* preamble);
static std::unique_ptr<ParsedAST> build(llvm::StringRef filename,
llvm::StringRef content,
std::vector<const char*>& args,
Preamble* preamble = nullptr);
};
} // namespace clice

View File

@@ -1,249 +1,55 @@
#pragma once
#include "ParsedAST.h"
#include <stack>
#include <clang/Sema/Lookup.h>
#include <clang/Sema/Template.h>
#include <stack>
namespace clice {
/// This class is used to resolve dependent names in the AST.
/// For dependent names, we cannot know the any information about the name until the template is instantiated.
/// This can be frustrating, you cannot get completion, you cannot get go-to-definition, etc.
/// To avoid this, we just use some heuristics to simplify the dependent names as normal type/expression.
/// For dependent names, we cannot know the any information about the name until
/// the template is instantiated. This can be frustrating, you cannot get
/// completion, you cannot get go-to-definition, etc. To avoid this, we just use
/// some heuristics to simplify the dependent names as normal type/expression.
/// For example, `std::vector<T>::value_type` can be simplified as `T`.
class DependentNameResolver {
public:
DependentNameResolver(clang::Sema& sema, clang::ASTContext& context) : sema(sema), context(context) {}
std::vector<clang::TemplateArgument> resolve(llvm::ArrayRef<clang::TemplateArgument> arguments) {
std::vector<clang::TemplateArgument> result;
for(auto arg: arguments) {
if(arg.getKind() == clang::TemplateArgument::ArgKind::Type) {
// check whether it is a TemplateTypeParmType.
if(auto type = llvm::dyn_cast<clang::TemplateTypeParmType>(arg.getAsType())) {
const clang::TemplateTypeParmDecl* param = type->getDecl();
if(param && param->hasDefaultArgument()) {
result.push_back(param->getDefaultArgument().getArgument());
continue;
}
}
}
result.push_back(arg);
}
return result;
}
clang::QualType resolve(clang::QualType type);
clang::QualType dealias(clang::QualType type) {
if(auto DNT = type->getAs<clang::TemplateSpecializationType>()) {
return clang::QualType(DNT, 0);
} else if(auto DTST = type->getAs<clang::DependentTemplateSpecializationType>()) {
auto NNS = clang::NestedNameSpecifier::Create(
context,
nullptr,
false,
dealias(clang::QualType(DTST->getQualifier()->getAsType(), 0)).getTypePtr());
return context.getDependentTemplateSpecializationType(DTST->getKeyword(),
NNS,
DTST->getIdentifier(),
resolve(DTST->template_arguments()));
} else {
return type;
}
}
clang::QualType resolve(const clang::DependentNameType* DNT);
clang::QualType resolve(clang::QualType type) {
while(true) {
// llvm::outs() <<
// "--------------------------------------------------------------------\n";
// type.dump();
/// lookup member in a given nested name specifier
bool lookup(llvm::SmallVector<clang::NamedDecl*>& result,
const clang::NestedNameSpecifier* NNS,
const clang::IdentifierInfo* II);
clang::MultiLevelTemplateArgumentList list;
if(auto DNT = type->getAs<clang::DependentNameType>()) {
type = resolve(resolve(DNT->getQualifier(), DNT->getIdentifier()));
for(auto begin = arguments.rbegin(), end = arguments.rend(); begin != end; ++begin) {
list.addOuterTemplateArguments((*begin)->first, (*begin)->second, true);
}
type = sema.SubstType(dealias(type), list, {}, {});
arguments.clear();
bool lookup(llvm::SmallVector<clang::NamedDecl*>& result,
const clang::TemplateSpecializationType* TST,
const clang::IdentifierInfo* II);
} else if(auto DTST = type->getAs<clang::DependentTemplateSpecializationType>()) {
auto ND = resolve(DTST->getQualifier(), DTST->getIdentifier());
if(auto TATD = llvm::dyn_cast<clang::TypeAliasTemplateDecl>(ND)) {
auto args = resolve(DTST->template_arguments());
// lookup member in a given class template
// FIXME: search in base classes
bool lookup(llvm::SmallVector<clang::NamedDecl*>& result,
clang::ClassTemplateDecl* CTD,
const clang::IdentifierInfo* II,
llvm::ArrayRef<clang::TemplateArgument> arguments);
clang::Sema::CodeSynthesisContext context;
context.Entity = TATD;
context.Kind = clang::Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation;
context.TemplateArgs = args.data();
sema.pushCodeSynthesisContext(context);
list.addOuterTemplateArguments(TATD, args, true);
for(auto begin = arguments.rbegin(), end = arguments.rend(); begin != end; ++begin) {
list.addOuterTemplateArguments((*begin)->first, (*begin)->second, true);
}
// llvm::outs() << "before:
// ----------------------------------------------------------------\n";
// TATD->getTemplatedDecl()->getUnderlyingType().dump();
type = dealias(TATD->getTemplatedDecl()->getUnderlyingType());
// llvm::outs() << "arguments:
// -------------------------------------------------------------\n";
// list.dump();
type = sema.SubstType(type, list, {}, {});
// type.dump();
arguments.clear();
} else {
ND->dump();
std::terminate();
}
// return resolve(DTST);
} else if(auto LRT = type->getAs<clang::LValueReferenceType>()) {
type = context.getLValueReferenceType(resolve(LRT->getPointeeType()));
} else {
return type;
}
}
}
clang::QualType resolve(clang::NamedDecl* ND) {
if(auto TD = llvm::dyn_cast<clang::TypedefDecl>(ND)) {
return TD->getUnderlyingType();
} else if(auto TAD = llvm::dyn_cast<clang::TypeAliasDecl>(ND)) {
return TAD->getUnderlyingType();
} else {
ND->dump();
std::terminate();
}
}
clang::NamedDecl* resolve(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: {
return lookup(clang::QualType(NNS->getAsType(), 0), II);
}
default: {
NNS->dump();
std::terminate();
}
}
}
clang::NamedDecl* lookup(clang::QualType Type, const clang::IdentifierInfo* Name) {
clang::NamedDecl* TemplateDecl;
clang::ArrayRef<clang::TemplateArgument> arguments;
// llvm::outs() << "--------------------------------------------------------------------\n";
if(auto TTPT = Type->getAs<clang::TemplateTypeParmType>()) {
Type->dump();
std::terminate();
} else if(auto TST = Type->getAs<clang::TemplateSpecializationType>()) {
auto TemplateName = TST->getTemplateName();
TemplateDecl = TemplateName.getAsTemplateDecl();
arguments = TST->template_arguments();
} else if(auto DTST = Type->getAs<clang::DependentTemplateSpecializationType>()) {
TemplateDecl = resolve(DTST->getQualifier(), DTST->getIdentifier());
arguments = DTST->template_arguments();
} else if(auto RT = Type->getAs<clang::RecordType>()) {
return RT->getDecl()->lookup(Name).front();
} else {
Type->dump();
std::terminate();
}
clang::NamedDecl* result;
clang::Sema::CodeSynthesisContext context;
if(auto CTD = llvm::dyn_cast<clang::ClassTemplateDecl>(TemplateDecl)) {
// always we check main template first
result = CTD->getTemplatedDecl()->lookup(Name).front();
if(result) {
this->arguments.push_back(new std::pair<clang::Decl*, std::vector<clang::TemplateArgument>>{
TemplateDecl,
resolve(arguments),
});
context.Entity = TemplateDecl;
context.Kind = clang::Sema::CodeSynthesisContext::TemplateInstantiation;
context.TemplateArgs = this->arguments.back()->second.data();
sema.pushCodeSynthesisContext(context);
} else {
llvm::SmallVector<clang::ClassTemplatePartialSpecializationDecl*> paritals;
CTD->getPartialSpecializations(paritals);
clang::sema::TemplateDeductionInfo info(CTD->getLocation());
for(auto partial: paritals) {
auto deduction = sema.DeduceTemplateArguments(partial, arguments, info);
if(deduction == clang::TemplateDeductionResult::Success) {
auto decl = partial->lookup(Name).front();
if(decl) {
result = decl;
// TODO: fix resugar
// std::vector<clang::TemplateArgument> arguments;
auto arguments = info.takeSugared();
for(auto& arg: arguments->asArray()) {
arg.dump();
// if(arg.getKind() == clang::TemplateArgument::ArgKind::Type) {
// auto type = arg.getAsType();
// type->dump();
// if(type.isCanonical()) {
// auto TemplateDecl =
// llvm::dyn_cast<clang::TemplateDecl>(this->arguments.back()->first);
// auto params = TemplateDecl->getTemplateParameters();
// auto TTPT = type->getAs<clang::TemplateTypeParmType>();
// unsigned Depth = TTPT->getDepth();
// unsigned Index = TTPT->getIndex();
// auto* TTPD = dyn_cast<clang::TemplateTypeParmDecl>(params->getParam(Index));
// arguments.push_back(
// clang::TemplateArgument(this->context.getTypeDeclType(TTPD)));
// } else {
// arguments.push_back(arg);
// }
// } else {
// arguments.push_back(arg);
// }
}
this->arguments.push_back(new std::pair<clang::Decl*, std::vector<clang::TemplateArgument>>{
partial,
arguments->asArray(),
});
context.Entity = partial;
context.Kind = clang::Sema::CodeSynthesisContext::TemplateInstantiation;
context.TemplateArgs = this->arguments.back()->second.data();
sema.pushCodeSynthesisContext(context);
break;
}
}
}
}
} else if(auto TATD = llvm::dyn_cast<clang::TypeAliasTemplateDecl>(TemplateDecl)) {
result = lookup(TATD->getTemplatedDecl()->getUnderlyingType(), Name);
}
if(result == nullptr) {
Type.dump();
std::terminate();
}
return result;
}
// replace the template arguments in the type, using the arguments in the frame
clang::QualType substitute(clang::QualType type);
private:
struct Frame {
clang::NamedDecl* decl;
std::vector<clang::TemplateArgument> arguments;
};
clang::Sema& sema;
clang::ASTContext& context;
std::vector<std::pair<clang::Decl*, std::vector<clang::TemplateArgument>>*> arguments;
std::vector<Frame> frames;
};
} // namespace clice