Fix C++ function params and return values printed in SemIR (#5468)

Fix `pattern_block_id` and `call_params_id` for C++ function decl
import, to get the correct SemIR printout for the C++ functions i.e.

1) Fill in missing parameter info in the function decls:
```
// CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {
// CHECK:STDOUT:     %a.patt: %pattern_type.2f8 = binding_pattern a [concrete]
// CHECK:STDOUT:     %a.param_patt: %pattern_type.2f8 = value_param_pattern %a.patt, call_param0 [concrete]
// CHECK:STDOUT:   } {
// CHECK:STDOUT:     %a.param: %i16 = value_param call_param0
// CHECK:STDOUT:     %.1: type = splice_block %i16 [concrete = constants.%i16] {
// CHECK:STDOUT:       %int_16: Core.IntLiteral = int_value 16 [concrete = constants.%int_16]
// CHECK:STDOUT:       %i16: type = class_type @Int, @Int(constants.%int_16) [concrete = constants.%i16]
// CHECK:STDOUT:     }
// CHECK:STDOUT:     %a: %i16 = bind_name a, %a.param
// CHECK:STDOUT:   }
```
2) Print the params and return values:

```
// CHECK:STDOUT: fn @foo_short() -> %i16;
```

```
// CHECK:STDOUT: fn @foo(%a.param: %i16);
```

Closes #5449
This commit is contained in:
Ivana Ivanovska
2025-06-10 17:17:19 +00:00
committed by GitHub
parent e4c8150f2c
commit 3d603fced7
5 changed files with 491 additions and 171 deletions
+95 -34
View File
@@ -25,6 +25,8 @@
#include "toolchain/check/import.h"
#include "toolchain/check/inst.h"
#include "toolchain/check/literal.h"
#include "toolchain/check/pattern.h"
#include "toolchain/check/pattern_match.h"
#include "toolchain/check/type.h"
#include "toolchain/diagnostics/diagnostic.h"
#include "toolchain/diagnostics/format_providers.h"
@@ -341,53 +343,67 @@ static auto MapType(Context& context, clang::QualType type) -> TypeExpr {
// declaration. If the function declaration has no parameters, it returns
// `SemIR::InstBlockId::Empty`. In the case of an unsupported parameter type, it
// returns `SemIR::InstBlockId::None`.
// TODO: Consider refactoring to extract and reuse more logic from
// `HandleAnyBindingPattern()`.
static auto MakeParamPatternsBlockId(Context& context, SemIR::LocId loc_id,
const clang::FunctionDecl& clang_decl)
-> SemIR::InstBlockId {
if (clang_decl.parameters().empty()) {
return SemIR::InstBlockId::Empty;
}
SemIR::CallParamIndex next_index(0);
llvm::SmallVector<SemIR::InstId> params;
params.reserve(clang_decl.parameters().size());
for (const clang::ParmVarDecl* param : clang_decl.parameters()) {
clang::QualType param_type = param->getType().getCanonicalType();
SemIR::TypeId type_id =
GetPatternType(context, MapType(context, param_type).type_id);
// Mark the start of a region of insts, needed for the type expression
// created later with the call of `EndSubpatternAsExpr()`.
BeginSubpattern(context);
auto [type_inst_id, type_id] = MapType(context, param_type);
// Type expression of the binding pattern - a single-entry/single-exit
// region that allows control flow in the type expression e.g. fn F(x: if C
// then i32 else i64).
SemIR::ExprRegionId type_expr_region_id =
EndSubpatternAsExpr(context, type_inst_id);
if (type_id == SemIR::ErrorInst::TypeId) {
context.TODO(loc_id, llvm::formatv("Unsupported: parameter type: {0}",
param_type.getAsString()));
return SemIR::InstBlockId::None;
}
llvm::StringRef param_name = param->getName();
SemIR::EntityNameId entity_name_id = context.entity_names().Add(
{.name_id =
(param_name.empty())
// Translate an unnamed parameter to an underscore to
// match Carbon's naming of unnamed/unused function params.
? SemIR::NameId::Underscore
: SemIR::NameId::ForIdentifier(
context.sem_ir().identifiers().Add(param_name)),
.parent_scope_id = SemIR::NameScopeId::None});
SemIR::InstId binding_pattern_id = AddInstInNoBlock(
SemIR::NameId name_id =
param_name.empty()
// Translate an unnamed parameter to an underscore to
// match Carbon's naming of unnamed/unused function params.
? SemIR::NameId::Underscore
: SemIR::NameId::ForIdentifier(
context.sem_ir().identifiers().Add(param_name));
// TODO: Fix this once templates are supported.
bool is_template = false;
// TODO: Fix this once generics are supported.
bool is_generic = false;
SemIR::InstId binding_pattern_id =
// TODO: Fill in a location once available.
context, SemIR::LocIdAndInst::NoLoc(SemIR::BindingPattern(
{.type_id = type_id, .entity_name_id = entity_name_id})));
SemIR::InstId var_pattern_id = AddInstInNoBlock(
AddBindingPattern(context, SemIR::LocId::None, name_id, type_id,
type_expr_region_id, is_generic, is_template)
.pattern_id;
SemIR::InstId var_pattern_id = AddPatternInst(
context,
// TODO: Fill in a location once available.
SemIR::LocIdAndInst::NoLoc(SemIR::ValueParamPattern(
{.type_id = context.insts().Get(binding_pattern_id).type_id(),
.subpattern_id = binding_pattern_id,
.index = next_index})));
++next_index.index;
.index = SemIR::CallParamIndex::None})));
params.push_back(var_pattern_id);
}
return context.inst_blocks().Add(params);
}
// Returns the return type of the given function declaration.
// Currently only void and 32-bit int are supported.
// Returns the return type of the given function declaration. In case of an
// unsupported return type, it returns `SemIR::ErrorInst::InstId`.
// TODO: Support more return types.
static auto GetReturnType(Context& context, SemIR::LocId loc_id,
const clang::FunctionDecl* clang_decl)
@@ -396,6 +412,7 @@ static auto GetReturnType(Context& context, SemIR::LocId loc_id,
if (ret_type->isVoidType()) {
return SemIR::InstId::None;
}
auto [type_inst_id, type_id] = MapType(context, ret_type);
if (type_id == SemIR::ErrorInst::TypeId) {
context.TODO(loc_id, llvm::formatv("Unsupported: return type: {0}",
@@ -403,12 +420,12 @@ static auto GetReturnType(Context& context, SemIR::LocId loc_id,
return SemIR::ErrorInst::InstId;
}
auto pattern_type_id = GetPatternType(context, type_id);
SemIR::InstId return_slot_pattern_id = AddInstInNoBlock(
SemIR::InstId return_slot_pattern_id = AddPatternInst(
// TODO: Fill in a location for the return type once available.
context,
SemIR::LocIdAndInst::NoLoc(SemIR::ReturnSlotPattern(
{.type_id = pattern_type_id, .type_inst_id = type_inst_id})));
SemIR::InstId param_pattern_id = AddInstInNoBlock(
SemIR::InstId param_pattern_id = AddPatternInst(
// TODO: Fill in a location for the return type once available.
context, SemIR::LocIdAndInst::NoLoc(SemIR::OutParamPattern(
{.type_id = pattern_type_id,
@@ -417,6 +434,46 @@ static auto GetReturnType(Context& context, SemIR::LocId loc_id,
return param_pattern_id;
}
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 param_patterns_id;
SemIR::InstId return_slot_pattern_id;
SemIR::InstBlockId call_params_id;
};
} // namespace
// Creates a block containing the parameter pattern instructions for the
// explicit parameters, a parameter pattern instruction for the return type and
// a block containing the call parameters of the function. Emits a callee
// pattern-match for the explicit parameter patterns and the return slot pattern
// to create the Call parameters instructions block. Currently the implicit
// parameter patterns are not taken into account. Returns the parameter patterns
// block id, the return slot pattern id, and the call parameters block id.
// Returns `std::nullopt` if the function declaration has an unsupported
// parameter type.
static auto CreateFunctionParamsInsts(Context& context, SemIR::LocId loc_id,
const clang::FunctionDecl* clang_decl)
-> std::optional<FunctionParamsInsts> {
auto param_patterns_id =
MakeParamPatternsBlockId(context, loc_id, *clang_decl);
if (!param_patterns_id.has_value()) {
return std::nullopt;
}
auto return_slot_pattern_id = GetReturnType(context, loc_id, clang_decl);
if (SemIR::ErrorInst::InstId == return_slot_pattern_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);
return {{.param_patterns_id = param_patterns_id,
.return_slot_pattern_id = return_slot_pattern_id,
.call_params_id = call_params_id}};
}
// Imports a function declaration from Clang to Carbon. If successful, returns
// the new Carbon function declaration `InstId`.
static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
@@ -436,18 +493,22 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
context.TODO(loc_id, "Unsupported: Template function");
return SemIR::ErrorInst::InstId;
}
auto param_patterns_id =
MakeParamPatternsBlockId(context, loc_id, *clang_decl);
if (!param_patterns_id.has_value()) {
return SemIR::ErrorInst::InstId;
}
auto return_slot_pattern_id = GetReturnType(context, loc_id, clang_decl);
if (SemIR::ErrorInst::InstId == return_slot_pattern_id) {
context.inst_block_stack().Push();
context.pattern_block_stack().Push();
auto function_params_insts =
CreateFunctionParamsInsts(context, loc_id, clang_decl);
auto pattern_block_id = context.pattern_block_stack().Pop();
auto decl_block_id = context.inst_block_stack().Pop();
if (!function_params_insts.has_value()) {
return SemIR::ErrorInst::InstId;
}
auto function_decl = SemIR::FunctionDecl{
SemIR::TypeId::None, SemIR::FunctionId::None, SemIR::InstBlockId::Empty};
SemIR::TypeId::None, SemIR::FunctionId::None, decl_block_id};
auto decl_id =
AddPlaceholderInstInNoBlock(context, Parse::NodeId::None, function_decl);
context.imports().push_back(decl_id);
@@ -458,16 +519,16 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
.generic_id = SemIR::GenericId::None,
.first_param_node_id = Parse::NodeId::None,
.last_param_node_id = Parse::NodeId::None,
.pattern_block_id = SemIR::InstBlockId::Empty,
.pattern_block_id = pattern_block_id,
.implicit_param_patterns_id = SemIR::InstBlockId::Empty,
.param_patterns_id = param_patterns_id,
.param_patterns_id = function_params_insts->param_patterns_id,
.is_extern = false,
.extern_library_id = SemIR::LibraryNameId::None,
.non_owning_decl_id = SemIR::InstId::None,
.first_owning_decl_id = decl_id,
.definition_id = SemIR::InstId::None},
{.call_params_id = SemIR::InstBlockId::Empty,
.return_slot_pattern_id = return_slot_pattern_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,
.clang_decl_id = context.sem_ir().clang_decls().Add(clang_decl)}};