mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Add interop support for naming and "calling" C++ templates. (#6474)
Expose C++ class templates, variable templates, alias templates, and concepts as callable values in Carbon, and map calls to them into template-id formation, mirroring how Carbon generics behave. For now, only type template parameters are supported; non-type and template template parameters produce a TODO error.
This commit is contained in:
@@ -4,10 +4,15 @@
|
||||
|
||||
#include "toolchain/check/cpp/call.h"
|
||||
|
||||
#include "clang/Sema/Sema.h"
|
||||
#include "toolchain/base/kind_switch.h"
|
||||
#include "toolchain/check/call.h"
|
||||
#include "toolchain/check/cpp/import.h"
|
||||
#include "toolchain/check/cpp/location.h"
|
||||
#include "toolchain/check/cpp/operators.h"
|
||||
#include "toolchain/check/cpp/overload_resolution.h"
|
||||
#include "toolchain/check/cpp/type_mapping.h"
|
||||
#include "toolchain/check/literal.h"
|
||||
#include "toolchain/sem_ir/function.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
#include "toolchain/sem_ir/typed_insts.h"
|
||||
@@ -43,4 +48,148 @@ auto PerformCallToCppFunction(Context& context, SemIR::LocId loc_id,
|
||||
}
|
||||
}
|
||||
|
||||
// Converts an argument in a call to a C++ template name into a corresponding
|
||||
// clang template argument, given the template parameter it will be matched
|
||||
// against.
|
||||
static auto ConvertArgToTemplateArg(Context& context,
|
||||
const clang::NamedDecl* param_decl,
|
||||
SemIR::InstId arg_id)
|
||||
-> std::optional<clang::TemplateArgumentLoc> {
|
||||
if (isa<clang::TemplateTypeParmDecl>(param_decl)) {
|
||||
auto type = ExprAsType(context, SemIR::LocId(arg_id), arg_id);
|
||||
if (type.type_id == SemIR::ErrorInst::TypeId) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto clang_type = MapToCppType(context, type.type_id);
|
||||
if (clang_type.isNull()) {
|
||||
context.TODO(arg_id, "unsupported type used as template argument");
|
||||
return std::nullopt;
|
||||
}
|
||||
return clang::TemplateArgumentLoc(
|
||||
clang_type,
|
||||
context.ast_context().getTrivialTypeSourceInfo(
|
||||
clang_type, GetCppLocation(context, SemIR::LocId(arg_id))));
|
||||
}
|
||||
|
||||
if (isa<clang::TemplateTemplateParmDecl>(param_decl)) {
|
||||
// TODO: Check the type of the argument `CppTemplateNameType` and
|
||||
// convert it to a `clang::TemplateName`.
|
||||
context.TODO(arg_id, "argument for template template parameter");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (isa<clang::NonTypeTemplateParmDecl>(param_decl)) {
|
||||
// TODO: Check the argument has a concrete constant value, and convert it to
|
||||
// a Clang constant value.
|
||||
context.TODO(arg_id, "argument for non-type template parameter");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
CARBON_FATAL("Unknown declaration kind for template parameter");
|
||||
}
|
||||
|
||||
// Converts a call argument list into a Clang template argument list for a given
|
||||
// template. Returns true on success, or false if an error was diagnosed.
|
||||
static auto ConvertArgsToTemplateArgs(Context& context,
|
||||
clang::TemplateDecl* template_decl,
|
||||
llvm::ArrayRef<SemIR::InstId> arg_ids,
|
||||
clang::TemplateArgumentListInfo& arg_list)
|
||||
-> bool {
|
||||
for (auto* param_decl : template_decl->getTemplateParameters()->asArray()) {
|
||||
if (arg_ids.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// A parameter pack consumes all remaining arguments; otherwise, it consumes
|
||||
// a single argument.
|
||||
// TODO: Handle expanded template parameter packs, which have a known, fixed
|
||||
// arity.
|
||||
llvm::ArrayRef<SemIR::InstId> args_for_param =
|
||||
param_decl->isTemplateParameterPack() ? std::exchange(arg_ids, {})
|
||||
: arg_ids.consume_front();
|
||||
for (auto arg_id : args_for_param) {
|
||||
if (auto arg = ConvertArgToTemplateArg(context, param_decl, arg_id)) {
|
||||
arg_list.addArgument(*arg);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there are any remaining arguments, that's an error; convert them to
|
||||
// placeholder template arguments so that Clang will diagnose it for us.
|
||||
for (auto arg_id : arg_ids) {
|
||||
// Synthesize a placeholder `void{}` template argument.
|
||||
auto arg_loc = GetCppLocation(context, SemIR::LocId(arg_id));
|
||||
auto void_type = context.ast_context().VoidTy;
|
||||
auto* arg = new (context.ast_context()) clang::CXXScalarValueInitExpr(
|
||||
void_type,
|
||||
context.ast_context().getTrivialTypeSourceInfo(void_type, arg_loc),
|
||||
arg_loc);
|
||||
arg_list.addArgument(clang::TemplateArgumentLoc(
|
||||
clang::TemplateArgument(arg, /*IsCanonical=*/false), arg));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Given a template and an template argument list, builds a Carbon value
|
||||
// describing the corresponding C++ template-id.
|
||||
static auto BuildTemplateId(Context& context, SemIR::LocId loc_id,
|
||||
clang::SourceLocation loc,
|
||||
clang::TemplateDecl* template_decl,
|
||||
clang::TemplateArgumentListInfo& arg_list)
|
||||
-> SemIR::InstId {
|
||||
if (auto* var_template_decl =
|
||||
dyn_cast<clang::VarTemplateDecl>(template_decl)) {
|
||||
auto decl_result = context.clang_sema().CheckVarTemplateId(
|
||||
var_template_decl, /*TemplateLoc=*/clang::SourceLocation(), loc,
|
||||
arg_list, /*SetWrittenArgs=*/false);
|
||||
return decl_result.isInvalid()
|
||||
? SemIR::ErrorInst::InstId
|
||||
: ImportCppDecl(context, loc_id,
|
||||
SemIR::ClangDeclKey::ForNonFunctionDecl(
|
||||
decl_result.get()));
|
||||
}
|
||||
|
||||
if (auto* concept_decl = dyn_cast<clang::ConceptDecl>(template_decl)) {
|
||||
auto expr_result = context.clang_sema().CheckConceptTemplateId(
|
||||
clang::CXXScopeSpec(), /*TemplateKWLoc=*/clang::SourceLocation(),
|
||||
clang::DeclarationNameInfo(concept_decl->getDeclName(), loc),
|
||||
concept_decl, concept_decl, &arg_list);
|
||||
if (expr_result.isInvalid()) {
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
auto* expr = expr_result.getAs<clang::ConceptSpecializationExpr>();
|
||||
return MakeBoolLiteral(context, loc_id,
|
||||
SemIR::BoolValue::From(expr->isSatisfied()));
|
||||
}
|
||||
|
||||
clang::TemplateName template_name(template_decl);
|
||||
auto clang_type = context.clang_sema().CheckTemplateIdType(
|
||||
clang::ElaboratedTypeKeyword::None, template_name, loc, arg_list,
|
||||
/*Scope=*/nullptr, /*ForNestedNameSpecifier=*/false);
|
||||
if (clang_type.isNull()) {
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
return ImportCppType(context, loc_id, clang_type).inst_id;
|
||||
}
|
||||
|
||||
auto PerformCallToCppTemplateName(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::ClangDeclId template_decl_id,
|
||||
llvm::ArrayRef<SemIR::InstId> arg_ids)
|
||||
-> SemIR::InstId {
|
||||
auto* template_decl = dyn_cast<clang::TemplateDecl>(
|
||||
context.clang_decls().Get(template_decl_id).key.decl);
|
||||
auto loc = GetCppLocation(context, loc_id);
|
||||
|
||||
// Form a template argument list for this template.
|
||||
clang::TemplateArgumentListInfo arg_list(loc, loc);
|
||||
if (!ConvertArgsToTemplateArgs(context, template_decl, arg_ids, arg_list)) {
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
|
||||
return BuildTemplateId(context, loc_id, loc, template_decl, arg_list);
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
Reference in New Issue
Block a user