mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-11-15 05:38:33 +00:00
The return type is deduced from the function's `return` statements (currently required to all have the same type), or from its first `returned var`. Calls to generic functions with deduced return types are not yet supported. When a function has a deduced return type, we defer the return type portion of the callee pattern match until deduction, and during deduction we extend the decl block and the pattern block with the return-related instructions. Conversions for `return expr;` statements are deferred until the return type has been deduced. The design requires that we take the common type of the returned types as the deduced return type, so we can't in general perform any conversions for returned expressions until we reach the end of the function. This is accomplished by inserting a `splice_block` where the conversions will go, and later updating that instruction to contain the conversion. Assisted-by: Claude Opus 5.5 via Antigravity
530 lines
23 KiB
C++
530 lines
23 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#include "toolchain/check/call.h"
|
|
|
|
#include <optional>
|
|
|
|
#include "toolchain/base/kind_switch.h"
|
|
#include "toolchain/check/action.h"
|
|
#include "toolchain/check/context.h"
|
|
#include "toolchain/check/control_flow.h"
|
|
#include "toolchain/check/convert.h"
|
|
#include "toolchain/check/cpp/call.h"
|
|
#include "toolchain/check/cpp/import.h"
|
|
#include "toolchain/check/cpp/thunk.h"
|
|
#include "toolchain/check/deduce.h"
|
|
#include "toolchain/check/facet_type.h"
|
|
#include "toolchain/check/function.h"
|
|
#include "toolchain/check/import_ref.h"
|
|
#include "toolchain/check/inst.h"
|
|
#include "toolchain/check/thunk.h"
|
|
#include "toolchain/check/type.h"
|
|
#include "toolchain/diagnostics/format_providers.h"
|
|
#include "toolchain/sem_ir/builtin_function_kind.h"
|
|
#include "toolchain/sem_ir/entity_with_params_base.h"
|
|
#include "toolchain/sem_ir/function.h"
|
|
#include "toolchain/sem_ir/ids.h"
|
|
#include "toolchain/sem_ir/inst.h"
|
|
#include "toolchain/sem_ir/pattern.h"
|
|
#include "toolchain/sem_ir/typed_insts.h"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
namespace {
|
|
// Entity kinds, for diagnostics. Converted to an int for a select.
|
|
enum class EntityKind : uint8_t {
|
|
Function = 0,
|
|
GenericClass = 1,
|
|
GenericInterface = 2,
|
|
GenericNamedConstraint = 3,
|
|
};
|
|
} // namespace
|
|
|
|
// Resolves the callee expression in a call to a specific callee, or diagnoses
|
|
// if no specific callee can be identified. This determines any compile-time
|
|
// arguments, but doesn't check that the runtime arguments are convertible to
|
|
// the parameter types. It also verifies that the number of arguments is within
|
|
// the range [callee_arity - arity_lower_bound_margin, callee_arity]. This
|
|
// allows arity matching when the callee has default arguments for some
|
|
// subpatterns. In all other cases supply the default value `0` for exact arity
|
|
// checking.
|
|
//
|
|
// `self_id` and `arg_ids` are the self argument and explicit arguments in the
|
|
// call.
|
|
//
|
|
// Returns a `SpecificId` for the specific callee, `SpecificId::None` if the
|
|
// callee is not generic, or `nullopt` if an error has been diagnosed.
|
|
static auto ResolveCalleeInCall(Context& context, SemIR::LocId loc_id,
|
|
const SemIR::EntityWithParamsBase& entity,
|
|
EntityKind entity_kind_for_diagnostic,
|
|
SemIR::SpecificId enclosing_specific_id,
|
|
SemIR::InstId self_id,
|
|
llvm::ArrayRef<SemIR::InstId> arg_ids,
|
|
int32_t arity_lower_bound_margin = 0)
|
|
-> std::optional<SemIR::SpecificId> {
|
|
// Check that the arity exactly matches or is the upper bound of the explicit
|
|
// arguments.
|
|
auto param_patterns =
|
|
context.inst_blocks().GetOrEmpty(entity.param_patterns_id);
|
|
size_t expected_args_size =
|
|
param_patterns.size() - (self_id.has_value() ? 1 : 0);
|
|
CARBON_CHECK(static_cast<size_t>(arity_lower_bound_margin) <=
|
|
expected_args_size);
|
|
size_t size_lower_bound =
|
|
expected_args_size - static_cast<size_t>(arity_lower_bound_margin);
|
|
if (arg_ids.size() < size_lower_bound ||
|
|
arg_ids.size() > expected_args_size) {
|
|
CARBON_DIAGNOSTIC(CallArgCountMismatch, Error,
|
|
"{0} argument{0:s} passed to "
|
|
"{1:=0:function|=1:generic class|=2:generic "
|
|
"interface|=3:generic constraint}"
|
|
" expecting {2} argument{2:s}",
|
|
Diagnostics::IntAsSelect, Diagnostics::IntAsSelect,
|
|
Diagnostics::IntAsSelect);
|
|
CARBON_DIAGNOSTIC(InCallToEntity, Note,
|
|
"calling {0:=0:function|=1:generic class|=2:generic "
|
|
"interface|=3:generic constraint}"
|
|
" declared here",
|
|
Diagnostics::IntAsSelect);
|
|
context.emitter()
|
|
.Build(loc_id, CallArgCountMismatch, arg_ids.size(),
|
|
static_cast<int>(entity_kind_for_diagnostic), expected_args_size)
|
|
.Note(entity.latest_decl_id(), InCallToEntity,
|
|
static_cast<int>(entity_kind_for_diagnostic))
|
|
.Emit();
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Perform argument deduction.
|
|
auto specific_id = SemIR::SpecificId::None;
|
|
if (entity.generic_id.has_value()) {
|
|
specific_id = DeduceGenericCallArguments(
|
|
context, loc_id, entity.generic_id, enclosing_specific_id,
|
|
entity.implicit_param_patterns_id, entity.param_patterns_id, self_id,
|
|
arg_ids);
|
|
if (!specific_id.has_value()) {
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
return specific_id;
|
|
}
|
|
|
|
// Performs a call where the callee is the name of a generic class, such as
|
|
// `Vector(i32)`.
|
|
static auto PerformCallToGenericClass(Context& context, SemIR::LocId loc_id,
|
|
SemIR::ClassId class_id,
|
|
SemIR::SpecificId enclosing_specific_id,
|
|
llvm::ArrayRef<SemIR::InstId> arg_ids)
|
|
-> SemIR::InstId {
|
|
const auto& generic_class = context.classes().Get(class_id);
|
|
auto callee_specific_id =
|
|
ResolveCalleeInCall(context, loc_id, generic_class,
|
|
EntityKind::GenericClass, enclosing_specific_id,
|
|
/*self_id=*/SemIR::InstId::None, arg_ids);
|
|
if (!callee_specific_id) {
|
|
return SemIR::ErrorInst::InstId;
|
|
}
|
|
return GetOrAddInst<SemIR::ClassType>(context, loc_id,
|
|
{.type_id = SemIR::TypeType::TypeId,
|
|
.class_id = class_id,
|
|
.specific_id = *callee_specific_id});
|
|
}
|
|
|
|
static auto EntityFromInterfaceOrNamedConstraint(
|
|
Context& context, SemIR::InterfaceId interface_id)
|
|
-> const SemIR::EntityWithParamsBase& {
|
|
return context.interfaces().Get(interface_id);
|
|
}
|
|
|
|
static auto EntityFromInterfaceOrNamedConstraint(
|
|
Context& context, SemIR::NamedConstraintId named_constraint_id)
|
|
-> const SemIR::EntityWithParamsBase& {
|
|
return context.named_constraints().Get(named_constraint_id);
|
|
}
|
|
|
|
// Performs a call where the callee is the name of a generic interface or named
|
|
// constraint, such as `AddWith(i32)`.
|
|
template <typename IdT>
|
|
requires SameAsOneOf<IdT, SemIR::InterfaceId, SemIR::NamedConstraintId>
|
|
static auto PerformCallToGenericInterfaceOrNamedConstaint(
|
|
Context& context, SemIR::LocId loc_id, IdT id,
|
|
SemIR::SpecificId enclosing_specific_id,
|
|
llvm::ArrayRef<SemIR::InstId> arg_ids) -> SemIR::InstId {
|
|
const auto& entity = EntityFromInterfaceOrNamedConstraint(context, id);
|
|
|
|
auto entity_kind_for_diagnostic = EntityKind::GenericInterface;
|
|
if constexpr (std::same_as<IdT, SemIR::NamedConstraintId>) {
|
|
entity_kind_for_diagnostic = EntityKind::GenericNamedConstraint;
|
|
}
|
|
auto callee_specific_id =
|
|
ResolveCalleeInCall(context, loc_id, entity, entity_kind_for_diagnostic,
|
|
enclosing_specific_id,
|
|
/*self_id=*/SemIR::InstId::None, arg_ids);
|
|
if (!callee_specific_id) {
|
|
return SemIR::ErrorInst::InstId;
|
|
}
|
|
std::optional<SemIR::FacetType> facet_type;
|
|
if constexpr (std::same_as<IdT, SemIR::InterfaceId>) {
|
|
facet_type = FacetTypeFromInterface(context, id, *callee_specific_id);
|
|
} else {
|
|
facet_type = FacetTypeFromNamedConstraint(context, id, *callee_specific_id);
|
|
}
|
|
return GetOrAddInst(context, loc_id, *facet_type);
|
|
}
|
|
|
|
// Builds an appropriate specific function for the callee, also handling
|
|
// instance binding.
|
|
static auto BuildCalleeSpecificFunction(
|
|
Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
|
|
SemIR::InstId callee_function_self_type_id,
|
|
SemIR::SpecificId callee_specific_id) -> SemIR::InstId {
|
|
auto generic_callee_id = callee_id;
|
|
|
|
// Strip off a bound_method so that we can form a constant specific callee.
|
|
auto bound_method = SemIR::TryGetCalleeAsBoundMethod(
|
|
context.sem_ir(), callee_id, SemIR::SpecificId::None);
|
|
if (bound_method) {
|
|
generic_callee_id = bound_method->function_decl_id;
|
|
}
|
|
|
|
// Form a specific callee.
|
|
if (callee_function_self_type_id.has_value()) {
|
|
// This is an associated function in an interface; the callee is the
|
|
// specific function in the impl that corresponds to the specific function
|
|
// we deduced.
|
|
callee_id =
|
|
GetOrAddInst(context, SemIR::LocId(generic_callee_id),
|
|
SemIR::SpecificImplFunction{
|
|
.type_id = GetSingletonType(
|
|
context, SemIR::SpecificFunctionType::TypeInstId),
|
|
.callee_id = generic_callee_id,
|
|
.specific_id = callee_specific_id});
|
|
} else {
|
|
// This is a regular generic function. The callee is the specific function
|
|
// we deduced.
|
|
callee_id =
|
|
GetOrAddInst(context, SemIR::LocId(generic_callee_id),
|
|
SemIR::SpecificFunction{
|
|
.type_id = GetSingletonType(
|
|
context, SemIR::SpecificFunctionType::TypeInstId),
|
|
.callee_id = generic_callee_id,
|
|
.specific_id = callee_specific_id});
|
|
}
|
|
|
|
// Add the `self` argument back if there was one.
|
|
if (bound_method) {
|
|
callee_id =
|
|
GetOrAddInst<SemIR::BoundMethod>(context, loc_id,
|
|
{.type_id = bound_method->type_id,
|
|
.object_id = bound_method->object_id,
|
|
.function_decl_id = callee_id});
|
|
}
|
|
|
|
return callee_id;
|
|
}
|
|
|
|
auto PerformCallToFunction(Context& context, SemIR::LocId loc_id,
|
|
SemIR::InstId callee_id,
|
|
const SemIR::CalleeFunction& callee_function,
|
|
llvm::ArrayRef<SemIR::InstId> arg_ids,
|
|
bool is_desugared) -> SemIR::InstId {
|
|
// If the callee is a generic function, determine the generic argument values
|
|
// for the call. Also check the arity of the function against the arguments,
|
|
// with allowance for default argument values.
|
|
const auto& function = context.functions().Get(callee_function.function_id);
|
|
auto callee_specific_id = ResolveCalleeInCall(
|
|
context, loc_id, function, EntityKind::Function,
|
|
callee_function.enclosing_specific_id, callee_function.self_id, arg_ids,
|
|
function.default_value_arity);
|
|
if (!callee_specific_id) {
|
|
return SemIR::ErrorInst::InstId;
|
|
}
|
|
|
|
if (callee_specific_id->has_value()) {
|
|
callee_id = BuildCalleeSpecificFunction(context, loc_id, callee_id,
|
|
callee_function.self_type_id,
|
|
*callee_specific_id);
|
|
}
|
|
|
|
auto& callee = context.functions().Get(callee_function.function_id);
|
|
|
|
// We can't call a function before its return type is known. This happens for
|
|
// a function with a deduced return type that is called from within its own
|
|
// body, or from the body of a class member function that precedes it.
|
|
if (callee.has_pending_deduced_return_type()) {
|
|
CARBON_DIAGNOSTIC(CallBeforeReturnTypeDeduced, Error,
|
|
"call to `{0}` before its return type is deduced",
|
|
SemIR::NameId);
|
|
CARBON_DIAGNOSTIC(ReturnTypeDeducedFromDefinition, Note,
|
|
"return type of `{0}` is deduced from its definition",
|
|
SemIR::NameId);
|
|
context.emitter()
|
|
.Build(loc_id, CallBeforeReturnTypeDeduced, callee.name_id)
|
|
.Note(callee.latest_decl_id(), ReturnTypeDeducedFromDefinition,
|
|
callee.name_id)
|
|
.Emit();
|
|
return SemIR::ErrorInst::InstId;
|
|
}
|
|
if (callee.has_deduced_return_type && callee_specific_id->has_value()) {
|
|
// TODO: Resolve the specific definition to determine the return type.
|
|
context.TODO(loc_id, "call to generic function with deduced return type");
|
|
return SemIR::ErrorInst::InstId;
|
|
}
|
|
|
|
auto return_type_id =
|
|
callee.GetDeclaredReturnType(context.sem_ir(), *callee_specific_id);
|
|
if (!return_type_id.has_value()) {
|
|
return_type_id = GetTupleType(context, {});
|
|
}
|
|
|
|
auto return_arg_id = SemIR::InstId::None;
|
|
if (callee.return_pattern_id.has_value()) {
|
|
Diagnostics::AnnotationScope annotate_diagnostics(
|
|
&context.emitter(), [&](auto& builder) {
|
|
CARBON_DIAGNOSTIC(IncompleteReturnTypeHere, Note,
|
|
"return type declared here");
|
|
builder.Note(callee.return_pattern_id, IncompleteReturnTypeHere);
|
|
});
|
|
auto arg_type_id = CheckFunctionReturnPatternType(
|
|
context, loc_id, callee.return_pattern_id, *callee_specific_id);
|
|
if (arg_type_id == SemIR::ErrorInst::TypeId) {
|
|
return_type_id = SemIR::ErrorInst::TypeId;
|
|
} else if (callee.call_param_ranges.return_size() > 0 &&
|
|
SemIR::InitRepr::ForType(context.sem_ir(), arg_type_id)
|
|
.MightBeInPlace()) {
|
|
// Tentatively use storage for a temporary as the return argument.
|
|
// This will be replaced if necessary when we perform initialization.
|
|
// Only do this if the callee has a return parameter to receive it;
|
|
// functions that return by reference or by value don't.
|
|
return_arg_id = AddInst<SemIR::TemporaryStorage>(
|
|
context, loc_id, {.type_id = arg_type_id});
|
|
}
|
|
}
|
|
// Convert the arguments to match the parameters.
|
|
auto converted_args_id =
|
|
ConvertCallArgs(context, callee_function.self_id, arg_ids, return_arg_id,
|
|
callee, *callee_specific_id, is_desugared);
|
|
switch (callee.special_function_kind) {
|
|
case SemIR::Function::SpecialFunctionKind::Thunk: {
|
|
// If we're about to form a direct call to a thunk, inline it.
|
|
const auto& thunk_info = context.sem_ir().thunks().Get(callee.thunk_id());
|
|
LoadImportRef(context, thunk_info.callee_id);
|
|
|
|
// Name the thunk target within the enclosing scope of the thunk.
|
|
auto thunk_ref_id =
|
|
BuildNameRef(context, loc_id, callee.name_id, thunk_info.callee_id,
|
|
callee_function.enclosing_specific_id);
|
|
|
|
auto param_pattern_ids =
|
|
context.inst_blocks().Get(context.functions()
|
|
.Get(callee_function.function_id)
|
|
.param_patterns_id);
|
|
|
|
// This recurses back into `PerformCall`. However, we never form a thunk
|
|
// to a thunk, so we only recurse once.
|
|
return PerformThunkCall(context, loc_id, callee_function.function_id,
|
|
param_pattern_ids,
|
|
context.inst_blocks().Get(converted_args_id),
|
|
thunk_ref_id, thunk_info.override_self_type_id);
|
|
}
|
|
|
|
case SemIR::Function::SpecialFunctionKind::HasCppThunk: {
|
|
return PerformCppThunkCall(context, loc_id, callee_function.function_id,
|
|
context.inst_blocks().Get(converted_args_id),
|
|
callee.cpp_thunk_decl_id());
|
|
}
|
|
|
|
case SemIR::Function::SpecialFunctionKind::None:
|
|
case SemIR::Function::SpecialFunctionKind::Builtin:
|
|
case SemIR::Function::SpecialFunctionKind::Generated:
|
|
case SemIR::Function::SpecialFunctionKind::CppThunk:
|
|
case SemIR::Function::SpecialFunctionKind::CppFunctionPointerThunk: {
|
|
return GetOrAddInst<SemIR::Call>(context, loc_id,
|
|
{.type_id = return_type_id,
|
|
.callee_id = callee_id,
|
|
.args_id = converted_args_id});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Determines whether a C++ template call can be performed immediately
|
|
// (i.e. whether it is non-template-dependent).
|
|
static auto IsCppTemplateCallPerformable(Context& context,
|
|
SemIR::InstId callee_id,
|
|
llvm::ArrayRef<SemIR::InstId> arg_ids)
|
|
-> bool {
|
|
CARBON_CHECK(OperandDependence(context, callee_id) <
|
|
SemIR::ConstantDependence::Template);
|
|
|
|
for (auto arg_id : arg_ids) {
|
|
if (context.constant_values().Get(arg_id).is_symbolic()) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Performs a call where the callee is a generic type. If it's not a generic
|
|
// type, produces a diagnostic.
|
|
static auto PerformCallToNonFunction(Context& context, SemIR::LocId loc_id,
|
|
SemIR::InstId callee_id,
|
|
llvm::ArrayRef<SemIR::InstId> arg_ids)
|
|
-> SemIR::InstId {
|
|
auto type_inst =
|
|
context.types().GetAsInst(context.insts().Get(callee_id).type_id());
|
|
CARBON_KIND_SWITCH(type_inst) {
|
|
case CARBON_KIND(SemIR::CppTemplateNameType template_name): {
|
|
if (IsCppTemplateCallPerformable(context, callee_id, arg_ids)) {
|
|
return PerformCallToCppTemplateName(context, loc_id,
|
|
template_name.decl_id, arg_ids);
|
|
}
|
|
|
|
llvm::SmallVector<SemIR::InstId> inst_ids;
|
|
inst_ids.push_back(callee_id);
|
|
// Wrap symbolic template args in a `TemplateInst` so that all symbolic
|
|
// arguments are treated as templates.
|
|
for (auto arg_id : arg_ids) {
|
|
if (context.constant_values().Get(arg_id).is_symbolic()) {
|
|
auto arg = context.insts().Get(arg_id);
|
|
inst_ids.push_back(
|
|
AddInst(context, SemIR::LocId(arg_id),
|
|
SemIR::TemplateInst{.type_id = arg.type_id(),
|
|
.inst_id = arg_id}));
|
|
} else {
|
|
inst_ids.push_back(arg_id);
|
|
}
|
|
}
|
|
|
|
auto inst_block_id = context.inst_blocks().Add(inst_ids);
|
|
return AddDependentActionSplice(
|
|
context, loc_id,
|
|
SemIR::CallAction{.type_id = SemIR::InstType::TypeId,
|
|
.inst_block_id = inst_block_id,
|
|
// Unused for non-function calls.
|
|
.is_desugared = SemIR::BoolValue::From(false)},
|
|
SemIR::TypeInstId::None);
|
|
}
|
|
case CARBON_KIND(SemIR::ParameterizedClassType param_class): {
|
|
return PerformCallToGenericClass(context, loc_id, param_class.class_id,
|
|
param_class.enclosing_specific_id,
|
|
arg_ids);
|
|
}
|
|
case CARBON_KIND(SemIR::GenericInterfaceType generic_interface): {
|
|
return PerformCallToGenericInterfaceOrNamedConstaint(
|
|
context, loc_id, generic_interface.interface_id,
|
|
generic_interface.enclosing_specific_id, arg_ids);
|
|
}
|
|
case CARBON_KIND(SemIR::GenericNamedConstraintType generic_constraint): {
|
|
return PerformCallToGenericInterfaceOrNamedConstaint(
|
|
context, loc_id, generic_constraint.named_constraint_id,
|
|
generic_constraint.enclosing_specific_id, arg_ids);
|
|
}
|
|
default: {
|
|
CARBON_DIAGNOSTIC(CallToNonCallable, Error,
|
|
"value of type {0} is not callable", TypeOfInstId);
|
|
context.emitter().Emit(loc_id, CallToNonCallable, callee_id);
|
|
return SemIR::ErrorInst::InstId;
|
|
}
|
|
}
|
|
}
|
|
|
|
static auto PerformCallToCppFunctionPointer(
|
|
Context& context, SemIR::LocId loc_id, SemIR::InstId function_ptr_id,
|
|
SemIR::CalleeCppFunctionPointer fn_ptr,
|
|
llvm::ArrayRef<SemIR::InstId> arg_ids) -> SemIR::InstId {
|
|
auto pointer_info =
|
|
ImportFunctionPointerInvoke(context, loc_id, fn_ptr.function_type_id);
|
|
SemIR::CalleeFunction callee_function = {
|
|
.function_id = pointer_info.function_id,
|
|
.enclosing_specific_id = SemIR::SpecificId::None,
|
|
.resolved_specific_id = SemIR::SpecificId::None,
|
|
.self_type_id = SemIR::InstId::None,
|
|
.self_id = function_ptr_id};
|
|
|
|
return PerformCallToFunction(context, loc_id, pointer_info.decl_id,
|
|
callee_function, arg_ids, /*is_desugared=*/true);
|
|
}
|
|
|
|
// Determines whether a call can be performed immediately (i.e. whether it is
|
|
// non-template-dependent).
|
|
static auto IsCallPerformable(Context& context, SemIR::InstId callee_id,
|
|
llvm::ArrayRef<SemIR::InstId> arg_ids) -> bool {
|
|
if (OperandDependence(context, callee_id) ==
|
|
SemIR::ConstantDependence::Template) {
|
|
return false;
|
|
}
|
|
for (auto arg_id : arg_ids) {
|
|
if (OperandDependence(context, arg_id) ==
|
|
SemIR::ConstantDependence::Template) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Common logic for `PerformCall` and `PerformAction`.
|
|
static auto PerformCallHelper(Context& context, SemIR::LocId loc_id,
|
|
SemIR::InstId callee_id,
|
|
llvm::ArrayRef<SemIR::InstId> arg_ids,
|
|
bool is_desugared) {
|
|
// Try treating the callee as a function first.
|
|
auto callee = GetCallee(context.sem_ir(), callee_id);
|
|
CARBON_KIND_SWITCH(callee) {
|
|
case CARBON_KIND(SemIR::CalleeError _): {
|
|
return SemIR::ErrorInst::InstId;
|
|
}
|
|
case CARBON_KIND(SemIR::CalleeFunction fn): {
|
|
return PerformCallToFunction(context, loc_id, callee_id, fn, arg_ids,
|
|
is_desugared);
|
|
}
|
|
case CARBON_KIND(SemIR::CalleeCppFunctionPointer fn_ptr): {
|
|
return PerformCallToCppFunctionPointer(context, loc_id, callee_id, fn_ptr,
|
|
arg_ids);
|
|
}
|
|
case CARBON_KIND(SemIR::CalleeNonFunction _): {
|
|
return PerformCallToNonFunction(context, loc_id, callee_id, arg_ids);
|
|
}
|
|
|
|
case CARBON_KIND(SemIR::CalleeCppOverloadSet overload): {
|
|
return PerformCallToCppFunction(context, loc_id,
|
|
overload.cpp_overload_set_id,
|
|
overload.self_id, arg_ids, is_desugared);
|
|
}
|
|
}
|
|
}
|
|
|
|
auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
|
|
llvm::ArrayRef<SemIR::InstId> arg_ids, bool is_desugared)
|
|
-> SemIR::InstId {
|
|
if (IsCallPerformable(context, callee_id, arg_ids)) {
|
|
return PerformCallHelper(context, loc_id, callee_id, arg_ids, is_desugared);
|
|
}
|
|
|
|
// Pack the callee and args into an inst block. This is an optimization to
|
|
// avoid needing a Bundle in the `CallAction`.
|
|
llvm::SmallVector<SemIR::InstId> inst_ids;
|
|
inst_ids.reserve(1 + arg_ids.size());
|
|
inst_ids.push_back(callee_id);
|
|
inst_ids.append(arg_ids.begin(), arg_ids.end());
|
|
auto inst_block_id = context.inst_blocks().Add(inst_ids);
|
|
|
|
return HandleAction<SemIR::CallAction>(
|
|
context, loc_id, SemIR::TypeInstId::None,
|
|
{.type_id = SemIR::InstType::TypeId,
|
|
.inst_block_id = inst_block_id,
|
|
.is_desugared = SemIR::BoolValue::From(is_desugared)});
|
|
}
|
|
|
|
auto PerformAction(Context& context, SemIR::LocId loc_id,
|
|
SemIR::CallAction action) -> SemIR::InstId {
|
|
auto inst_ids = context.inst_blocks().Get(action.inst_block_id);
|
|
auto callee_id = inst_ids[0];
|
|
auto arg_ids = inst_ids.slice(1);
|
|
return PerformCallHelper(context, loc_id, callee_id, arg_ids,
|
|
action.is_desugared.ToBool());
|
|
}
|
|
|
|
} // namespace Carbon::Check
|