Basic support for calling class methods imported from C++. (#5796)

Create a `self` parameter when importing a C++ non-static member
function. That seems to be all we need to get basic method calls
working! For now, `const` methods have by-value self parameters, and
non-`const` methods get an `addr self: Self*` parameter.
This commit is contained in:
Richard Smith
2025-07-15 16:05:01 +00:00
committed by GitHub
parent 83b2924432
commit f204bdf094
8 changed files with 850 additions and 245 deletions
+102 -12
View File
@@ -27,6 +27,7 @@
#include "toolchain/check/convert.h"
#include "toolchain/check/diagnostic_helpers.h"
#include "toolchain/check/eval.h"
#include "toolchain/check/function.h"
#include "toolchain/check/import.h"
#include "toolchain/check/inst.h"
#include "toolchain/check/literal.h"
@@ -805,6 +806,87 @@ static auto MapType(Context& context, SemIR::LocId loc_id, clang::QualType type)
return mapped;
}
// Returns a block for the implicit parameters of the given function
// declaration. Because function templates are not yet supported, this currently
// only contains the `self` parameter. On error, produces a diagnostic and
// returns None.
static auto MakeImplicitParamPatternsBlockId(
Context& context, SemIR::LocId loc_id,
const clang::FunctionDecl& clang_decl) -> SemIR::InstBlockId {
const auto* method_decl = dyn_cast<clang::CXXMethodDecl>(&clang_decl);
if (!method_decl || method_decl->isStatic()) {
return SemIR::InstBlockId::Empty;
}
// Build a `self` parameter from the object parameter.
BeginSubpattern(context);
// Perform some special-case mapping for the object parameter:
//
// - If it's a const reference to T, produce a by-value `self: T` parameter.
// - If it's a non-const reference to T, produce an `addr self: T*`
// parameter.
// - Otherwise, map it directly, which will currently fail for `&&`-qualified
// methods.
//
// TODO: Some of this mapping should be performed for all parameters.
clang::QualType param_type =
method_decl->getFunctionObjectParameterReferenceType();
bool addr_self = false;
if (param_type->isLValueReferenceType()) {
param_type = param_type.getNonReferenceType();
if (param_type.isConstQualified()) {
// TODO: Consider only doing this if `const` is the only qualifier. For
// now, any other qualifier will fail when mapping the type.
auto split_type = param_type.getSplitUnqualifiedType();
split_type.Quals.removeConst();
param_type = method_decl->getASTContext().getQualifiedType(split_type);
} else {
addr_self = true;
}
}
auto [type_inst_id, type_id] = MapType(context, loc_id, param_type);
SemIR::ExprRegionId type_expr_region_id =
EndSubpatternAsExpr(context, type_inst_id);
if (!type_id.has_value()) {
context.TODO(loc_id,
llvm::formatv("Unsupported: object parameter type: {0}",
param_type.getAsString()));
return SemIR::InstBlockId::None;
}
if (addr_self) {
type_id = GetPointerType(context, type_inst_id);
}
SemIR::InstId pattern_id =
// TODO: Fill in a location once available.
AddBindingPattern(context, SemIR::LocId::None, SemIR::NameId::SelfValue,
type_id, type_expr_region_id, /*is_generic*/ false,
/*is_template*/ false)
.pattern_id;
// TODO: Fill in a location once available.
pattern_id = AddPatternInst<SemIR::ValueParamPattern>(
context, SemIR::LocId::None,
{.type_id = context.insts().Get(pattern_id).type_id(),
.subpattern_id = pattern_id,
.index = SemIR::CallParamIndex::None});
// If we're building `addr self: Self*`, do that now.
if (addr_self) {
// TODO: Fill in a location once available.
pattern_id = AddPatternInst<SemIR::AddrPattern>(
context, SemIR::LocId::None,
{.type_id = GetPatternType(context, SemIR::AutoType::TypeId),
.inner_id = pattern_id});
}
return context.inst_blocks().Add({pattern_id});
}
// Returns a block id for the explicit parameters of the given function
// declaration. If the function declaration has no parameters, it returns
// `SemIR::InstBlockId::Empty`. In the case of an unsupported parameter type, it
@@ -909,6 +991,7 @@ namespace {
// Represents the parameter patterns block id, the return slot pattern id and
// the call parameters block id for a function declaration.
struct FunctionParamsInsts {
SemIR::InstBlockId implicit_param_patterns_id;
SemIR::InstBlockId param_patterns_id;
SemIR::InstId return_slot_pattern_id;
SemIR::InstBlockId call_params_id;
@@ -927,6 +1010,16 @@ struct FunctionParamsInsts {
static auto CreateFunctionParamsInsts(Context& context, SemIR::LocId loc_id,
const clang::FunctionDecl* clang_decl)
-> std::optional<FunctionParamsInsts> {
if (isa<clang::CXXConstructorDecl, clang::CXXDestructorDecl>(clang_decl)) {
context.TODO(loc_id, "Unsupported: Constructor/Destructor");
return std::nullopt;
}
auto implicit_param_patterns_id =
MakeImplicitParamPatternsBlockId(context, loc_id, *clang_decl);
if (!implicit_param_patterns_id.has_value()) {
return std::nullopt;
}
auto param_patterns_id =
MakeParamPatternsBlockId(context, loc_id, *clang_decl);
if (!param_patterns_id.has_value()) {
@@ -937,12 +1030,12 @@ static auto CreateFunctionParamsInsts(Context& context, SemIR::LocId loc_id,
return std::nullopt;
}
// TODO: Add support for implicit parameters.
auto call_params_id = CalleePatternMatch(
context, /*implicit_param_patterns_id=*/SemIR::InstBlockId::None,
param_patterns_id, return_slot_pattern_id);
auto call_params_id =
CalleePatternMatch(context, implicit_param_patterns_id, param_patterns_id,
return_slot_pattern_id);
return {{.param_patterns_id = param_patterns_id,
return {{.implicit_param_patterns_id = implicit_param_patterns_id,
.param_patterns_id = param_patterns_id,
.return_slot_pattern_id = return_slot_pattern_id,
.call_params_id = call_params_id}};
}
@@ -959,11 +1052,6 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
MarkFailedDecl(context, clang_decl);
return SemIR::ErrorInst::InstId;
}
if (!clang_decl->isGlobal()) {
context.TODO(loc_id, "Unsupported: Non-global function");
MarkFailedDecl(context, clang_decl);
return SemIR::ErrorInst::InstId;
}
if (clang_decl->getTemplatedKind() != clang::FunctionDecl::TK_NonTemplate) {
context.TODO(loc_id, "Unsupported: Template function");
MarkFailedDecl(context, clang_decl);
@@ -999,7 +1087,8 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
.first_param_node_id = Parse::NodeId::None,
.last_param_node_id = Parse::NodeId::None,
.pattern_block_id = pattern_block_id,
.implicit_param_patterns_id = SemIR::InstBlockId::Empty,
.implicit_param_patterns_id =
function_params_insts->implicit_param_patterns_id,
.param_patterns_id = function_params_insts->param_patterns_id,
.is_extern = false,
.extern_library_id = SemIR::LibraryNameId::None,
@@ -1009,7 +1098,8 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
{.call_params_id = function_params_insts->call_params_id,
.return_slot_pattern_id = function_params_insts->return_slot_pattern_id,
.virtual_modifier = SemIR::FunctionFields::VirtualModifier::None,
.self_param_id = SemIR::InstId::None,
.self_param_id = FindSelfPattern(
context, function_params_insts->implicit_param_patterns_id),
.clang_decl_id = context.sem_ir().clang_decls().Add(
{.decl = clang_decl, .inst_id = decl_id})}};