// 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 "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/deduce.h" #include "toolchain/check/function.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/ids.h" #include "toolchain/sem_ir/inst.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, }; } // namespace // Resolves the callee expression in a call to a specific callee, or diagnoses // if no specific callee can be identified. This verifies the arity of the // callee and determines any compile-time arguments, but doesn't check that the // runtime arguments are convertible to the parameter types. // // `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_type_id, SemIR::InstId self_id, llvm::ArrayRef arg_ids) -> std::optional { // Check that the arity matches. auto params = context.inst_blocks().GetOrEmpty(entity.param_patterns_id); if (arg_ids.size() != params.size()) { CARBON_DIAGNOSTIC(CallArgCountMismatch, Error, "{0} argument{0:s} passed to " "{1:=0:function|=1:generic class|=2:generic interface}" " expecting {2} argument{2:s}", IntAsSelect, IntAsSelect, IntAsSelect); CARBON_DIAGNOSTIC( InCallToEntity, Note, "calling {0:=0:function|=1:generic class|=2:generic interface}" " declared here", IntAsSelect); context.emitter() .Build(loc_id, CallArgCountMismatch, arg_ids.size(), static_cast(entity_kind_for_diagnostic), params.size()) .Note(entity.latest_decl_id(), InCallToEntity, static_cast(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, self_type_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 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_type_id=*/SemIR::InstId::None, /*self_id=*/SemIR::InstId::None, arg_ids); if (!callee_specific_id) { return SemIR::ErrorInst::SingletonInstId; } return context.GetOrAddInst( loc_id, {.type_id = SemIR::TypeType::SingletonTypeId, .class_id = class_id, .specific_id = *callee_specific_id}); } // Performs a call where the callee is the name of a generic interface, such as // `AddWith(i32)`. static auto PerformCallToGenericInterface( Context& context, SemIR::LocId loc_id, SemIR::InterfaceId interface_id, SemIR::SpecificId enclosing_specific_id, llvm::ArrayRef arg_ids) -> SemIR::InstId { const auto& interface = context.interfaces().Get(interface_id); auto callee_specific_id = ResolveCalleeInCall(context, loc_id, interface, EntityKind::GenericInterface, enclosing_specific_id, /*self_type_id=*/SemIR::InstId::None, /*self_id=*/SemIR::InstId::None, arg_ids); if (!callee_specific_id) { return SemIR::ErrorInst::SingletonInstId; } return context.GetOrAddInst(loc_id, context.FacetTypeFromInterface( interface_id, *callee_specific_id)); } auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id, llvm::ArrayRef arg_ids) -> SemIR::InstId { // Identify the function we're calling. auto callee_function = GetCalleeFunction(context.sem_ir(), callee_id); if (!callee_function.function_id.has_value()) { auto type_inst = context.types().GetAsInst(context.insts().Get(callee_id).type_id()); CARBON_KIND_SWITCH(type_inst) { case CARBON_KIND(SemIR::GenericClassType generic_class): { return PerformCallToGenericClass( context, loc_id, generic_class.class_id, generic_class.enclosing_specific_id, arg_ids); } case CARBON_KIND(SemIR::GenericInterfaceType generic_interface): { return PerformCallToGenericInterface( context, loc_id, generic_interface.interface_id, generic_interface.enclosing_specific_id, arg_ids); } default: { if (!callee_function.is_error) { CARBON_DIAGNOSTIC(CallToNonCallable, Error, "value of type {0} is not callable", TypeOfInstId); context.emitter().Emit(loc_id, CallToNonCallable, callee_id); } return SemIR::ErrorInst::SingletonInstId; } } } // If the callee is a generic function, determine the generic argument values // for the call. auto callee_specific_id = ResolveCalleeInCall( context, loc_id, context.functions().Get(callee_function.function_id), EntityKind::Function, callee_function.enclosing_specific_id, callee_function.self_type_id, callee_function.self_id, arg_ids); if (!callee_specific_id) { return SemIR::ErrorInst::SingletonInstId; } if (callee_specific_id->has_value()) { callee_id = context.GetOrAddInst( context.insts().GetLocId(callee_id), SemIR::SpecificFunction{ .type_id = context.GetSingletonType( SemIR::SpecificFunctionType::SingletonInstId), .callee_id = callee_id, .specific_id = *callee_specific_id}); if (callee_function.self_type_id.has_value()) { // This is an associated function, and will be required to be defined as // part of checking that the impl is complete. } else { context.definitions_required().push_back(callee_id); } } // If there is a return slot, build storage for the result. SemIR::InstId return_slot_arg_id = SemIR::InstId::None; SemIR::ReturnTypeInfo return_info = [&] { auto& function = context.functions().Get(callee_function.function_id); DiagnosticAnnotationScope annotate_diagnostics( &context.emitter(), [&](auto& builder) { CARBON_DIAGNOSTIC(IncompleteReturnTypeHere, Note, "return type declared here"); builder.Note(function.return_slot_pattern_id, IncompleteReturnTypeHere); }); return CheckFunctionReturnType(context, loc_id, function, *callee_specific_id); }(); switch (return_info.init_repr.kind) { case SemIR::InitRepr::InPlace: // Tentatively put storage for a temporary in the function's return slot. // This will be replaced if necessary when we perform initialization. return_slot_arg_id = context.AddInst( loc_id, {.type_id = return_info.type_id}); break; case SemIR::InitRepr::None: // For functions with an implicit return type, the return type is the // empty tuple type. if (!return_info.type_id.has_value()) { return_info.type_id = context.GetTupleType({}); } break; case SemIR::InitRepr::ByCopy: break; case SemIR::InitRepr::Incomplete: // Don't form an initializing expression with an incomplete type. // CheckFunctionReturnType will have diagnosed this for us if needed. return_info.type_id = SemIR::ErrorInst::SingletonTypeId; break; } // Convert the arguments to match the parameters. auto converted_args_id = ConvertCallArgs( context, loc_id, callee_function.self_id, arg_ids, return_slot_arg_id, context.functions().Get(callee_function.function_id), *callee_specific_id); auto call_inst_id = context.GetOrAddInst(loc_id, {.type_id = return_info.type_id, .callee_id = callee_id, .args_id = converted_args_id}); return call_inst_id; } } // namespace Carbon::Check