mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-11-15 09:48:32 +00:00
A `where` expression can have symbolic references to `.Self` inside it, but those references do not represent symbolic instructions that should be part of an enclosing generic eval block. They are internal details of the facet type, and as such they are PeriodSelfSymbolic, not Symbolic. The WhereExpr evaluation is careful to produce a Symbolic constant value only if it has symbolic dependencies on the LHS of the `where`, which avoids leaking the symbolic nature of `.Self` into he facet type's constant value. But until now, those PeriodSelfSymbolic insts could still leak into the enclosing eval block if they ended up in an operand of a Call expression. A compile-time Call expr evaluates to a TemplateSymbolic phase constant if it's not concrete, which includes PeriodSelfSymbolic, because we don't want to evaluate the Call expr until the `.Self` gets replaced during identify. But that means the Call constant value, and its operands including PeriodSelfSymbolic constants, all end up in the eval block. We fix this by making Call evaluate to a PeriodSelfSymbolic phase if its input phase is PeriodSelfSymbolic. This keeps the RHS of the `where` expression self-contained. If the facet type is concrete, then it doesn't add any instructions to the enclosing eval block (which may not exist at all in a non-generic context). Regardless, when we identify the facet type and replace `.Self`, the dependent Call expression will be rebuilt as well and it will evaluate as appropriate. We also enclose the `where` expression in its own generic scope. This means that instructions which are not part of the facet type do not end up in an eval block. They may be symbolic instructions, and they will not get updated by a specific when it's applied the enclosing generic anymore. But since they are not part of the facet type their substitution is not required. The new test had an incorrectly elided SpecificImplFunction instruction in the resulting semir, as SubstPeriodSelf was using the location of the input canonical instructions as the location for the results which resulted in an instruction without a location. We fix that by using `loc_id_` in the subst rebuild. Formatting now saw the initialization of a value from an `eval fn` call to a function in a witness table, so it needed to be taught to look through ImplWitnessAssociatedConstant (which is what we find in the witness table).
529 lines
23 KiB
C++
529 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_LABEL(
|
|
InCallToEntity, Info,
|
|
"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)
|
|
.Attach(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<SemIR::SpecificImplFunction>(
|
|
context, SemIR::LocId(generic_callee_id),
|
|
{.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<SemIR::SpecificFunction>(
|
|
context, SemIR::LocId(generic_callee_id),
|
|
{.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. Within
|
|
// a generic function's body, after its return type is deduced, it can still
|
|
// only be called with its own generic parameters as arguments. And when a
|
|
// generic function is instantiated, the instantiation can't call the same
|
|
// specific before reaching the point where its return type is deduced.
|
|
if (!ResolveReturnTypeInSpecific(context, loc_id, callee,
|
|
*callee_specific_id)) {
|
|
CARBON_DIAGNOSTIC(CallBeforeReturnTypeDeduced, Error,
|
|
"call to `{0}` before its return type is known",
|
|
SemIR::NameId);
|
|
CARBON_DIAGNOSTIC_LABEL(
|
|
ReturnTypeDeducedFromDefinition, Info,
|
|
"return type of `{0}` is deduced from its definition", SemIR::NameId);
|
|
context.emitter()
|
|
.Build(loc_id, CallBeforeReturnTypeDeduced, callee.name_id)
|
|
.Attach(callee.latest_decl_id(), ReturnTypeDeducedFromDefinition,
|
|
callee.name_id)
|
|
.Emit();
|
|
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_LABEL(IncompleteReturnTypeHere, Info,
|
|
"return type declared here");
|
|
builder.Attach(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, loc_id, 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, callee_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
|