Files
clice/src/semantic/resolver.cpp
2026-03-22 23:37:08 +08:00

822 lines
31 KiB
C++

#include "semantic/resolver.h"
#include <print>
#include <ranges>
#include "clang/Sema/Template.h"
#include "clang/Sema/TemplateDeduction.h"
#include "clang/Sema/TreeTransform.h"
namespace clice {
namespace {
template <typename T>
constexpr inline bool dependent_false = false;
template <typename Callback>
void visitTemplateDeclContexts(clang::Decl* decl, const Callback& callback) {
while(true) {
if(llvm::isa<clang::TranslationUnitDecl>(decl)) {
break;
}
clang::TemplateParameterList* params = nullptr;
if(auto TD = decl->getDescribedTemplate()) {
params = TD->getTemplateParameters();
}
if(auto CTPSD = llvm::dyn_cast<clang::ClassTemplatePartialSpecializationDecl>(decl)) {
params = CTPSD->getTemplateParameters();
}
if(auto VTPSD = llvm::dyn_cast<clang::VarTemplatePartialSpecializationDecl>(decl)) {
params = VTPSD->getTemplateParameters();
}
if(params) {
callback(decl, params);
}
decl = llvm::dyn_cast<clang::Decl>(decl->getDeclContext());
}
}
/// `Sema::SubstType` will not substitute template arguments in aliased types.
/// For example:
///
/// ```cpp
/// template <typename T>
/// struct A {
/// using base = std::vector<T>;
/// using type = typename base::reference;
/// };
/// ```
///
/// In this case, if you call `SubstType` on `type`, the alias `base` will remain with
/// the original type parameter `T`, without substituting it. Therefore, we need to
/// manually resolve the alias before calling `SubstType`, which is what `DesugarOnly`
/// aims to achieve.
class DesugarOnly : public clang::TreeTransform<DesugarOnly> {
using Base = clang::TreeTransform<DesugarOnly>;
public:
DesugarOnly(clang::Sema& sema) : Base(sema), context(sema.getASTContext()) {}
// FIXME: desugar more types, e.g `UsingType`.
clang::QualType TransformTypedefType(clang::TypeLocBuilder& TLB, clang::TypedefTypeLoc TL) {
if(clang::TypedefNameDecl* TND = TL.getTypedefNameDecl()) {
auto type = TransformType(TND->getUnderlyingType());
if(auto ET = llvm::dyn_cast<clang::ElaboratedType>(type)) {
type = ET->getNamedType();
}
TLB.pushTrivial(context, type, {});
return type;
}
return clang::QualType();
}
clang::QualType TransformElaboratedType(clang::TypeLocBuilder& TLB,
clang::ElaboratedTypeLoc TL) {
clang::QualType type = TransformType(TL.getNamedTypeLoc().getType());
TLB.pushTrivial(context, type, {});
return type;
}
clang::QualType TransformInjectedClassNameType(clang::TypeLocBuilder& TLB,
clang::InjectedClassNameTypeLoc TL) {
auto ICT = TL.getTypePtr();
clang::QualType type = TransformType(ICT->getInjectedSpecializationType());
TLB.pushTrivial(context, type, {});
return type;
}
using Base::TransformTemplateSpecializationType;
clang::QualType TransformTemplateSpecializationType(clang::TypeLocBuilder& TLB,
clang::TemplateSpecializationTypeLoc TL) {
if(TL.getTypePtr()->isTypeAlias()) {
clang::QualType type = TransformType(TL.getTypePtr()->desugar());
TLB.pushTrivial(context, type, {});
return type;
}
return Base::TransformTemplateSpecializationType(TLB, TL);
}
private:
clang::ASTContext& context;
};
/// When deduce `TemplateSpecializationType` for partial specialization, `TemplateTypeParmType`
/// will be deduced as canonical type.
///
/// For example:
/// ```cpp
/// template <typename T>
/// struct A {};
///
/// template <typename T>
/// struct A<T*> {};
/// ```
/// If you use deduce `A` with `A<U*>`, you will get `T = type-parameter-0-0` instead of `U`.
///
/// For code completion, we don't care about the whether it's a canonical type or not. But
/// sometimes, the type may be needed to display to the user, e.g. inlay hints. In this case,
/// we need to resugar the type to make it more readable.
class ResugarOnly : public clang::TreeTransform<ResugarOnly> {
public:
ResugarOnly(clang::Sema& sema, clang::Decl* decl) :
TreeTransform(sema), context(sema.getASTContext()) {
visitTemplateDeclContexts(decl,
[&](clang::Decl* decl, clang::TemplateParameterList* params) {
lists.push_back(params);
});
std::ranges::reverse(lists);
}
clang::QualType TransformTemplateTypeParmType(clang::TypeLocBuilder& TLB,
clang::TemplateTypeParmTypeLoc TL,
bool = false) {
clang::QualType type = TL.getType();
auto TTPT = TL.getTypePtr();
if(!TTPT->getDecl()) {
auto depth = TTPT->getDepth();
auto index = TTPT->getIndex();
auto isPack = TTPT->isParameterPack();
auto param = llvm::cast<clang::TemplateTypeParmDecl>(lists[depth]->getParam(index));
type = context.getTemplateTypeParmType(depth, index, isPack, param);
}
return TLB.push<clang::TemplateTypeParmTypeLoc>(type).getType();
}
private:
clang::ASTContext& context;
llvm::SmallVector<clang::TemplateParameterList*> lists;
};
/// A helper class to record the instantiation stack.
struct InstantiationStack {
using Arguments = llvm::SmallVector<clang::TemplateArgument, 4>;
using TemplateArguments = llvm::ArrayRef<clang::TemplateArgument>;
llvm::SmallVector<std::pair<clang::Decl*, Arguments>> data;
void clear() {
data.clear();
}
bool empty() const {
return data.empty();
}
auto state() const {
return data;
}
void rewind(auto& point) {
data = std::move(point);
}
void push(clang::Decl* decl, TemplateArguments arguments) {
data.emplace_back(decl, arguments);
}
void pop() {
data.pop_back();
}
auto& frames() {
return data;
}
};
/// The core class that performs pseudo template instantiation.
class PseudoInstantiator : public clang::TreeTransform<PseudoInstantiator> {
public:
using Base = clang::TreeTransform<PseudoInstantiator>;
using TemplateArguments = llvm::ArrayRef<clang::TemplateArgument>;
using TemplateDeductionInfo = clang::sema::TemplateDeductionInfo;
PseudoInstantiator(clang::Sema& sema, llvm::DenseMap<const void*, clang::QualType>& resolved) :
Base(sema), sema(sema), context(sema.getASTContext()), resolved(resolved) {}
public:
/// Check whether the given template arguments match the template parameters and
/// complete the default template arguments if necessary.
bool checkTemplateArguments(clang::TemplateDecl* TD,
TemplateArguments& arguments,
llvm::SmallVectorImpl<clang::TemplateArgument>& out) {
auto list = TD->getTemplateParameters();
out.reserve(list->size());
for(auto arg: arguments) {
out.emplace_back(arg);
}
if(out.size() != list->size()) {
for(auto i = out.size(); i < list->size(); ++i) {
auto param = list->getParam(i);
auto TTPD = llvm::dyn_cast<clang::TemplateTypeParmDecl>(param);
if(TTPD && TTPD->hasDefaultArgument()) {
auto type = TTPD->getDefaultArgument().getArgument().getAsType();
auto state = stack.state();
stack.push(TD, out);
auto result = TransformType(instantiate(type));
if(result.isNull()) {
stack.rewind(state);
return false;
}
out.emplace_back(result);
stack.rewind(state);
}
}
}
if(out.size() != list->size()) {
return false;
}
return true;
}
/// Deduce the template arguments for the given declaration. If deduction succeeds, push the
/// declaration and its deduced template arguments to the instantiation stack.
template <typename Decl>
bool deduceTemplateArguments(Decl* decl, TemplateArguments arguments) {
clang::TemplateParameterList* list = nullptr;
TemplateArguments params = {};
if constexpr(std::is_same_v<Decl, clang::ClassTemplateDecl>) {
const clang::ClassTemplateDecl* CTD = decl;
list = CTD->getTemplateParameters();
params = list->getInjectedTemplateArgs(context);
} else if constexpr(std::is_same_v<Decl, clang::ClassTemplatePartialSpecializationDecl>) {
const clang::ClassTemplatePartialSpecializationDecl* CTPSD = decl;
list = CTPSD->getTemplateParameters();
params = CTPSD->getTemplateArgs().asArray();
} else if constexpr(std::is_same_v<Decl, clang::TypeAliasTemplateDecl>) {
const clang::TypeAliasTemplateDecl* TATD = decl;
list = TATD->getTemplateParameters();
params = list->getInjectedTemplateArgs(context);
} else {
static_assert(dependent_false<Decl>, "Unknown declaration type");
}
assert(list && "No template parameters found");
TemplateDeductionInfo info = {clang::SourceLocation(), list->getDepth()};
llvm::SmallVector<clang::DeducedTemplateArgument, 4> deduced(list->size());
auto result = sema.DeduceTemplateArguments(list, params, arguments, info, deduced, true);
bool success =
result == clang::TemplateDeductionResult::Success && !info.hasSFINAEDiagnostic();
if(!success) {
return false;
}
/// made up class template context.
if(stack.empty()) {
visitTemplateDeclContexts(llvm::dyn_cast<clang::Decl>(decl->getDeclContext()),
[&](clang::Decl* decl, clang::TemplateParameterList* params) {
stack.push(decl,
params->getInjectedTemplateArgs(context));
});
std::ranges::reverse(stack.frames());
}
llvm::SmallVector<clang::TemplateArgument, 4> output(deduced.begin(), deduced.end());
stack.push(decl, output);
return true;
}
using lookup_result = clang::DeclContext::lookup_result;
/// If this class and its base class have members with the same name, `DeclContext::lookup`
/// will return multiple declarations in order from the base class to the derived class, so we
/// use the last declaration.
clang::Decl* preferred(lookup_result members) {
clang::Decl* decl = nullptr;
std::ranges::for_each(members, [&](auto member) { decl = member; });
return decl;
}
lookup_result lookup(clang::QualType type, clang::DeclarationName name) {
clang::Decl* TD = nullptr;
llvm::ArrayRef<clang::TemplateArgument> args;
type = TransformType(type);
if(type.isNull()) {
return lookup_result();
}
if(auto TST = type->getAs<clang::TemplateSpecializationType>()) {
TD = TST->getTemplateName().getAsTemplateDecl();
args = TST->template_arguments();
} else if(auto DTST = type->getAs<clang::DependentTemplateSpecializationType>()) {
auto& template_name = DTST->getDependentTemplateName();
/// FIXME: operators does't have the name.
auto name = template_name.getName().getIdentifier();
if(!name) {
return {};
}
if(auto decl = preferred(lookup(template_name.getQualifier(), name))) {
TD = decl;
args = DTST->template_arguments();
}
}
if(!TD) {
return lookup_result();
}
#ifndef NDEBUG
if(TemplateResolver::debug) {
llvm::outs() << "--------------------------------------------------------------\n";
llvm::outs() << "lookup: { " << name << " } in { " << type.getAsString() << " }\n";
}
#endif
if(auto CTD = llvm::dyn_cast<clang::ClassTemplateDecl>(TD)) {
return lookup(CTD, name, args);
} else if(auto TATD = llvm::dyn_cast<clang::TypeAliasTemplateDecl>(TD)) {
if(deduceTemplateArguments(TATD, args)) {
return lookup(instantiate(TATD->getTemplatedDecl()->getUnderlyingType()), name);
}
}
return lookup_result();
}
/// Look up the name in the given nested name specifier.
lookup_result lookup(const clang::NestedNameSpecifier* NNS, clang::DeclarationName name) {
if(!NNS) {
return lookup_result();
}
/// Search the resolved entities first.
if(auto iter = resolved.find(NNS); iter != resolved.end()) {
return lookup(iter->second, name);
}
switch(NNS->getKind()) {
case clang::NestedNameSpecifier::Identifier: {
/// If the prefix is `Identifier`, it must be a dependent name.
/// For example: `std::vector<T>::value_type::type`
/// ^~~~~~~~~~~~~~~~~~~~~~~~~^
/// identifier
///
/// So resolve it recursively.
auto type =
instantiate(preferred(lookup(NNS->getPrefix(), NNS->getAsIdentifier())));
resolved.try_emplace(NNS, type);
return lookup(type, name);
}
case clang::NestedNameSpecifier::TypeSpec: {
/// If the prefix is `TypeSpec`, it must be a type.
return lookup(clang::QualType(NNS->getAsType(), 0), name);
}
case clang::NestedNameSpecifier::Global:
case clang::NestedNameSpecifier::Namespace:
case clang::NestedNameSpecifier::NamespaceAlias:
case clang::NestedNameSpecifier::Super: {
llvm::errs() << "Unexpected name specifier\n";
std::abort();
}
}
return lookup_result();
}
/// Look up the name in the bases of the given class. Keep stack unchanged.
lookup_result lookupInBases(clang::CXXRecordDecl* CRD, clang::DeclarationName name) {
if(!CRD->hasDefinition()) {
return lookup_result();
}
for(auto base: CRD->bases()) {
if(auto type = base.getType(); type->isDependentType()) {
auto state = stack.state();
if(auto members = lookup(instantiate(type), name); !members.empty()) {
return members;
}
stack.rewind(state);
}
}
return lookup_result();
}
/// Look up the name in the given class template. We first search the name in the
/// primary template, if failed, try dependent base classes, if still failed, try
/// partial specializations. **Note that this function will be responsible for pushing
/// the class template and its template arguments to the instantiation stack**.
lookup_result lookup(clang::ClassTemplateDecl* CTD,
clang::DeclarationName name,
TemplateArguments visibleArguments) {
llvm::SmallVector<clang::TemplateArgument, 4> arguments;
if(!checkTemplateArguments(CTD, visibleArguments, arguments)) {
return lookup_result();
}
/// Try to find the name in the partial specializations.
llvm::SmallVector<clang::ClassTemplatePartialSpecializationDecl*> partials;
CTD->getPartialSpecializations(partials);
for(auto partial: partials) {
if(deduceTemplateArguments(partial, arguments)) {
if(auto members = partial->lookup(name); !members.empty()) {
return members;
}
if(auto members = lookupInBases(partial, name); !members.empty()) {
return members;
}
stack.pop();
}
}
if(deduceTemplateArguments(CTD, arguments)) {
auto CRD = CTD->getTemplatedDecl();
/// First, try to find the name in the primary template.
if(auto members = CRD->lookup(name); !members.empty()) {
return members;
}
/// If failed, try to find the name in the dependent base classes.
if(auto members = lookupInBases(CRD, name); !members.empty()) {
return members;
}
/// If failed, pop the decl and deduced template arguments.
stack.pop();
}
/// FIXME: try full specializations?.
return lookup_result();
}
/// Instantiate the given type and clear the instantiation stack.
clang::QualType instantiate(clang::QualType type) {
if(!type->isDependentType()) {
return type;
}
auto& contexts = sema.CodeSynthesisContexts;
assert(contexts.empty() && "CodeSynthesisContexts should be empty");
assert(!stack.frames().empty() && "Instantiation stack should not be empty");
std::ranges::for_each(stack.frames(), [&](auto& frame) {
clang::Sema::CodeSynthesisContext context;
context.Entity = frame.first;
context.TemplateArgs = frame.second.data();
context.Kind = clang::Sema::CodeSynthesisContext::TemplateInstantiation;
contexts.push_back(context);
});
clang::MultiLevelTemplateArgumentList list;
std::ranges::for_each(stack.frames() | std::views::reverse, [&](auto& frame) {
list.addOuterTemplateArguments(frame.first, frame.second, true);
});
type = DesugarOnly(sema).TransformType(type);
#ifndef NDEBUG
if(TemplateResolver::debug) {
llvm::SmallString<128> args;
for(auto& frame: stack.frames()) {
args += "<";
for(auto& arg: frame.second) {
switch(arg.getKind()) {
case clang::TemplateArgument::Null:
case clang::TemplateArgument::Type: {
args += arg.getAsType().getAsString();
break;
}
case clang::TemplateArgument::Declaration: {
args += arg.getAsDecl()->getNameAsString();
break;
}
case clang::TemplateArgument::NullPtr: {
args += "nullptr";
break;
}
case clang::TemplateArgument::Integral: {
arg.getAsIntegral().toString(args, 10);
break;
}
case clang::TemplateArgument::StructuralValue: {
args += arg.getAsStructuralValue().getAsString(
context,
arg.getStructuralValueType());
break;
}
case clang::TemplateArgument::Template: {
args += arg.getAsTemplate().getAsTemplateDecl()->getNameAsString();
break;
}
case clang::TemplateArgument::TemplateExpansion: {
args += arg.getAsTemplateOrTemplatePattern()
.getAsTemplateDecl()
->getNameAsString();
break;
}
case clang::TemplateArgument::Expression: {
args += arg.getAsExpr()->getStmtClassName();
break;
}
case clang::TemplateArgument::Pack: {
args += "pack";
break;
}
}
args += ", ";
}
args += ">, ";
}
std::print("try instantiate [{}] with arguments: {}\n",
type.getAsString(),
args.str().str());
}
#endif
auto result = sema.SubstType(type, list, {}, {});
#ifndef NDEBUG
if(TemplateResolver::debug) {
std::print("instantiate success: {}\n", result.getAsString());
}
#endif
stack.clear();
contexts.clear();
return result;
}
clang::QualType instantiate(clang::Decl* decl) {
if(!decl) {
return clang::QualType();
}
if(auto TND = llvm::dyn_cast<clang::TypedefNameDecl>(decl)) {
return instantiate(TND->getUnderlyingType());
} else if(auto RD = llvm::dyn_cast<clang::RecordDecl>(decl)) {
return clang::QualType(RD->getTypeForDecl(), 0);
}
// FIXME: more possibilities?
return clang::QualType();
}
/// FIXME: Use a general method to dig holes.
clang::QualType hole(clang::NestedNameSpecifier* NNS,
const clang::IdentifierInfo* member,
TemplateArguments arguments) {
if(NNS->getKind() != clang::NestedNameSpecifier::TypeSpec) {
return clang::QualType();
}
auto TST = NNS->getAsType()->getAs<clang::TemplateSpecializationType>();
if(!TST) {
return clang::QualType();
}
auto TD = TST->getTemplateName().getAsTemplateDecl();
if(!TD->getDeclContext()->isStdNamespace()) {
return clang::QualType();
}
if(TD->getName() == "allocator_traits") {
assert(TST->template_arguments().size() == 1 && "Invalid template arguments");
auto Alloc = TST->template_arguments()[0].getAsType();
if(member->getName() == "rebind_alloc") {
auto T = arguments[0].getAsType();
/// Alloc::rebind<T>::other
auto prefix =
clang::NestedNameSpecifier::Create(context, nullptr, Alloc.getTypePtr());
auto rebind = sema.getPreprocessor().getIdentifierInfo("rebind");
auto DTST = context.getDependentTemplateSpecializationType(
clang::ElaboratedTypeKeyword::None,
clang::DependentTemplateStorage(prefix, rebind, false),
arguments);
prefix = clang::NestedNameSpecifier::Create(context, prefix, DTST.getTypePtr());
auto other = sema.getPreprocessor().getIdentifierInfo("other");
auto DNT = context.getDependentNameType(clang::ElaboratedTypeKeyword::Typename,
prefix,
other);
auto result = PseudoInstantiator(sema, resolved).TransformType(DNT);
if(!result.isNull()) {
return result;
}
/// SomeAllocator<U, Args> -> SomeAllocator<T, Args>
if(auto TST = Alloc->getAs<clang::TemplateSpecializationType>()) {
llvm::SmallVector<clang::TemplateArgument, 1> replaceArguments = {T};
llvm::SmallVector<clang::TemplateArgument, 1> canonicalArguments;
for(auto& arg: replaceArguments) {
canonicalArguments.emplace_back(context.getCanonicalTemplateArgument(arg));
}
return context.getTemplateSpecializationType(TST->getTemplateName(),
replaceArguments,
canonicalArguments);
}
}
}
return clang::QualType();
}
public:
using Base::TransformType;
clang::QualType TransformType(clang::QualType type) {
if(type.isNull()) {
return clang::QualType();
}
if(!type->isDependentType()) {
return type;
}
return Base::TransformType(DesugarOnly(sema).TransformType(type));
}
/// Sometimes the outer argument is just a simple type `T` and actually cannot make
/// instantiation continue. In this case, we try to use its default argument to replace it,
/// which may make the instantiation continue.
/// For example: `template <typename T = std::vector<T>> using type = T::value_type`.
clang::QualType TransformTemplateTypeParmType(clang::TypeLocBuilder& TLB,
clang::TemplateTypeParmTypeLoc TL,
bool = false) {
if(clang::TemplateTypeParmDecl* TTPD = TL.getDecl()) {
if(TTPD->hasDefaultArgument()) {
const clang::TemplateArgument& argument = TTPD->getDefaultArgument().getArgument();
clang::QualType type = TransformType(argument.getAsType());
TLB.pushTrivial(context, type, clang::SourceLocation());
return type;
}
}
TLB.push<clang::TemplateTypeParmTypeLoc>(TL.getType());
return TL.getType();
}
clang::QualType TransformDependentNameType(clang::TypeLocBuilder& TLB,
clang::DependentNameTypeLoc TL,
bool DeducedTSTContext = false) {
auto DNT = TL.getTypePtr();
/// Search the resolved entities first.
if(auto iter = resolved.find(DNT); iter != resolved.end()) {
TLB.pushTrivial(context, iter->second, {});
return iter->second;
}
auto NNS = TransformNestedNameSpecifierLoc(TL.getQualifierLoc()).getNestedNameSpecifier();
auto type = TransformType(instantiate(preferred(lookup(NNS, DNT->getIdentifier()))));
resolved.try_emplace(DNT, type);
TLB.pushTrivial(context, type, {});
return type;
}
using Base::TransformDependentTemplateSpecializationType;
/// For a `DependentTemplateSpecializationType`, the template name can be either an alias
/// template or a class template. If it is an alias template, we can simplify it directly
/// by transforming the alias template's underlying type. However, if it is a class
/// template, we need additional context (e.g., suffix name) to simplify it correctly. In
/// this case, we defer further transformation to `TransformDependentNameType`, which can
/// handle this scenario. Thus, if the template is not an alias template, we keep it
/// unchanged here.
clang::QualType TransformDependentTemplateSpecializationType(
clang::TypeLocBuilder& TLB,
clang::DependentTemplateSpecializationTypeLoc TL) {
auto DTST = TL.getTypePtr();
/// Search the resolved entities first.
if(auto iter = resolved.find(DTST); iter != resolved.end()) {
TLB.pushTrivial(context, iter->second, {});
return iter->second;
}
auto NNS = TransformNestedNameSpecifierLoc(TL.getQualifierLoc()).getNestedNameSpecifier();
if(!NNS) {
return clang::QualType();
}
/// FIXME: figure out here.
clang::TemplateArgumentListInfo info;
using iterator = clang::TemplateArgumentLocContainerIterator<
clang::DependentTemplateSpecializationTypeLoc>;
TransformTemplateArguments(iterator(TL, 0), iterator(TL, TL.getNumArgs()), info);
llvm::SmallVector<clang::TemplateArgument, 4> arguments;
for(auto& arg: info.arguments()) {
arguments.push_back(arg.getArgument());
}
/// FIXME: operator does't have a name.
auto name = DTST->getDependentTemplateName().getName().getIdentifier();
if(!name) {
return clang::QualType();
}
/// Try resolve the hole.
if(auto result = hole(NNS, name, arguments); !result.isNull()) {
resolved.try_emplace(DTST, result);
TLB.pushTrivial(context, result, {});
return result;
}
/// The `lookup` may change the instantiation stack, save the current state.
auto state = stack.state();
if(auto decl = preferred(lookup(NNS, name))) {
/// FIXME: Current implementation results in duplicated lookup.
/// Cache the result of `lookup` to avoid duplicated lookup.
if(auto TATD = llvm::dyn_cast<clang::TypeAliasTemplateDecl>(decl)) {
if(deduceTemplateArguments(TATD, DTST->template_arguments())) {
clang::QualType type =
TransformType(instantiate(TATD->getTemplatedDecl()->getUnderlyingType()));
resolved.try_emplace(DTST, type);
TLB.pushTrivial(context, type, {});
return type;
}
}
stack.rewind(state);
}
/// FIXME: figure out here.
auto result = context.getDependentTemplateSpecializationType(
DTST->getKeyword(),
clang::DependentTemplateStorage(NNS, name, false),
arguments);
return TLB.push<clang::DependentTemplateSpecializationTypeLoc>(result).getType();
}
/// FIXME: handle more cases.
clang::QualType TransformDecltypeType(clang::TypeLocBuilder& TLB, clang::DecltypeTypeLoc TL) {
auto expr = TL.getTypePtr()->getUnderlyingExpr();
if(auto DRE = llvm::dyn_cast<clang::DeclRefExpr>(expr)) {
if(auto decl = DRE->getDecl(); llvm::isa<clang::VarDecl>(decl)) {
auto type = TransformType(decl->getType());
TLB.pushTrivial(context, type, {});
return type;
}
}
return Base::TransformDecltypeType(TLB, TL);
}
private:
clang::Sema& sema;
clang::ASTContext& context;
InstantiationStack stack;
llvm::DenseMap<const void*, clang::QualType>& resolved;
};
} // namespace
clang::QualType TemplateResolver::resolve(clang::QualType type) {
PseudoInstantiator instantiator(sema, resolved);
return instantiator.TransformType(type);
}
clang::QualType TemplateResolver::resugar(clang::QualType type, clang::Decl* decl) {
ResugarOnly resugar(sema, decl);
return resugar.TransformType(type);
}
TemplateResolver::lookup_result TemplateResolver::lookup(const clang::NestedNameSpecifier* NNS,
clang::DeclarationName name) {
PseudoInstantiator instantiator(sema, resolved);
return instantiator.lookup(NNS, name);
}
} // namespace clice