| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511 |
- // 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/function.h"
- #include "common/find.h"
- #include "toolchain/base/kind_switch.h"
- #include "toolchain/check/convert.h"
- #include "toolchain/check/generic.h"
- #include "toolchain/check/inst.h"
- #include "toolchain/check/merge.h"
- #include "toolchain/check/pattern.h"
- #include "toolchain/check/pattern_match.h"
- #include "toolchain/check/scope_stack.h"
- #include "toolchain/check/type.h"
- #include "toolchain/check/type_completion.h"
- #include "toolchain/diagnostics/format_providers.h"
- #include "toolchain/sem_ir/builtin_function_kind.h"
- #include "toolchain/sem_ir/ids.h"
- #include "toolchain/sem_ir/pattern.h"
- namespace Carbon::Check {
- auto FindSelfPattern(Context& context,
- SemIR::InstBlockId implicit_param_patterns_id)
- -> SemIR::InstId {
- auto implicit_param_patterns =
- context.inst_blocks().GetOrEmpty(implicit_param_patterns_id);
- return FindIfOrNone(implicit_param_patterns, [&](auto implicit_param_id) {
- return SemIR::IsSelfPattern(context.sem_ir(), implicit_param_id);
- });
- }
- auto AddReturnPattern(Context& context, SemIR::LocId loc_id,
- Context::FormExpr form_expr) -> SemIR::InstId {
- auto result_id = SemIR::InstId::None;
- auto result_type_id = SemIR::TypeId::None;
- auto form_inst = context.insts().Get(
- context.constant_values().GetConstantInstId(form_expr.form_inst_id));
- CARBON_KIND_SWITCH(form_inst) {
- case SemIR::RefForm::Kind: {
- result_type_id = GetPatternType(context, form_expr.type_component_id);
- result_id = AddInst<SemIR::RefReturnPattern>(context, loc_id,
- {.type_id = result_type_id});
- break;
- }
- case SemIR::ValueForm::Kind: {
- result_type_id = GetPatternType(context, form_expr.type_component_id);
- result_id = AddInst<SemIR::ValueReturnPattern>(
- context, loc_id, {.type_id = result_type_id});
- break;
- }
- case CARBON_KIND(SemIR::InitForm _): {
- result_type_id = GetPatternType(context, form_expr.type_component_id);
- result_id = AddInst<SemIR::OutParamPattern>(
- context, SemIR::LocId(form_expr.form_inst_id),
- {.type_id = result_type_id,
- .pretty_name_id = SemIR::NameId::ReturnSlot});
- break;
- }
- case SemIR::SymbolicBinding::Kind:
- CARBON_CHECK(
- context.constant_values().Get(form_expr.form_inst_id).is_symbolic());
- context.TODO(loc_id, "Support symbolic return forms");
- result_type_id = SemIR::ErrorInst::TypeId;
- result_id = SemIR::ErrorInst::InstId;
- break;
- case SemIR::ErrorInst::Kind:
- result_type_id = SemIR::ErrorInst::TypeId;
- result_id = SemIR::ErrorInst::InstId;
- break;
- default:
- CARBON_FATAL("unexpected inst kind: {0}", form_inst);
- }
- return AddInst<SemIR::ReturnSlotPattern>(
- context, SemIR::LocId(form_expr.form_inst_id),
- {.type_id = result_type_id,
- .subpattern_id = result_id,
- .type_inst_id = form_expr.type_component_inst_id});
- }
- auto IsValidBuiltinDeclaration(Context& context,
- const SemIR::Function& function,
- SemIR::BuiltinFunctionKind builtin_kind)
- -> bool {
- if (!function.call_params_id.has_value()) {
- // For now, we have no builtins that support positional parameters.
- return false;
- }
- // Find the list of call parameters other than the implicit return slots.
- auto call_params =
- context.inst_blocks()
- .Get(function.call_params_id)
- .take_front(function.call_param_ranges.explicit_end().index);
- // Get the return type. This is `()` if none was specified.
- auto return_type_id = function.GetDeclaredReturnType(context.sem_ir());
- if (!return_type_id.has_value()) {
- return_type_id = GetTupleType(context, {});
- }
- return builtin_kind.IsValidType(context.sem_ir(), call_params,
- return_type_id);
- }
- namespace {
- // Function signature fields for `MakeFunctionSignature`.
- struct FunctionSignatureInsts {
- SemIR::InstBlockId decl_block_id = SemIR::InstBlockId::None;
- SemIR::InstBlockId pattern_block_id = SemIR::InstBlockId::None;
- SemIR::InstBlockId implicit_param_patterns_id = SemIR::InstBlockId::None;
- SemIR::InstBlockId param_patterns_id = SemIR::InstBlockId::None;
- SemIR::InstBlockId call_param_patterns_id = SemIR::InstBlockId::None;
- SemIR::InstBlockId call_params_id = SemIR::InstBlockId::None;
- SemIR::Function::CallParamIndexRanges call_param_ranges =
- SemIR::Function::CallParamIndexRanges::Empty;
- SemIR::TypeInstId return_type_inst_id = SemIR::TypeInstId::None;
- SemIR::InstId return_form_inst_id = SemIR::InstId::None;
- SemIR::InstId return_pattern_id = SemIR::InstId::None;
- SemIR::InstId self_param_id = SemIR::InstId::None;
- };
- } // namespace
- // Handles construction of the signature's parameter and return types.
- static auto MakeFunctionSignature(Context& context, SemIR::LocId loc_id,
- const FunctionDeclArgs& args)
- -> FunctionSignatureInsts {
- FunctionSignatureInsts insts;
- StartFunctionSignature(context);
- // Build and add a `self: Self` or `ref self: Self` parameter if needed.
- if (args.self_type_id.has_value()) {
- context.full_pattern_stack().StartImplicitParamList();
- BeginSubpattern(context);
- auto self_type_region_id = ConsumeSubpatternExpr(
- context, context.types().GetTypeInstId(args.self_type_id));
- EndEmptySubpattern(context);
- insts.self_param_id = AddParamPattern(
- context, loc_id, SemIR::NameId::SelfValue, self_type_region_id,
- args.self_type_id, args.self_is_ref);
- insts.implicit_param_patterns_id =
- context.inst_blocks().Add({insts.self_param_id});
- context.full_pattern_stack().EndImplicitParamList();
- }
- // Build and add any explicit parameters. Whether these are references
- // or not is controlled by `args.params_are_refs`.
- context.full_pattern_stack().StartExplicitParamList();
- if (args.param_type_ids.empty()) {
- insts.param_patterns_id = SemIR::InstBlockId::Empty;
- } else {
- context.inst_block_stack().Push();
- for (auto param_type_id : args.param_type_ids) {
- BeginSubpattern(context);
- auto param_type_region_id = ConsumeSubpatternExpr(
- context, context.types().GetTypeInstId(param_type_id));
- EndEmptySubpattern(context);
- context.inst_block_stack().AddInstId(AddParamPattern(
- context, loc_id, SemIR::NameId::Underscore, param_type_region_id,
- param_type_id, /*is_ref=*/args.params_are_refs));
- }
- insts.param_patterns_id = context.inst_block_stack().Pop();
- }
- context.full_pattern_stack().EndExplicitParamList();
- // Build and add the return type. We always use an initializing form for now.
- if (args.return_type_id.has_value()) {
- auto return_form = ReturnExprAsForm(
- context, loc_id, context.types().GetTypeInstId(args.return_type_id));
- insts.return_type_inst_id = return_form.type_component_inst_id;
- insts.return_form_inst_id = return_form.form_inst_id;
- insts.return_pattern_id = AddReturnPattern(context, loc_id, return_form);
- }
- auto match_results =
- CalleePatternMatch(context, insts.implicit_param_patterns_id,
- insts.param_patterns_id, insts.return_pattern_id);
- insts.call_param_patterns_id = match_results.call_param_patterns_id;
- insts.call_params_id = match_results.call_params_id;
- insts.call_param_ranges = match_results.param_ranges;
- auto [pattern_block_id, decl_block_id] =
- FinishFunctionSignature(context, /*check_unused=*/false);
- insts.pattern_block_id = pattern_block_id;
- insts.decl_block_id = decl_block_id;
- return insts;
- }
- auto MakeGeneratedFunctionDecl(Context& context, SemIR::LocId loc_id,
- const FunctionDeclArgs& args)
- -> std::pair<SemIR::InstId, SemIR::FunctionId> {
- auto insts = MakeFunctionSignature(context, loc_id, args);
- // Add the function declaration.
- auto [decl_id, function_id] = MakeFunctionDecl(
- context, loc_id, insts.decl_block_id, /*build_generic=*/false,
- /*is_definition=*/true,
- SemIR::Function{
- {
- .name_id = args.name_id,
- .parent_scope_id = args.parent_scope_id,
- .generic_id = SemIR::GenericId::None,
- .first_param_node_id = Parse::NodeId::None,
- .last_param_node_id = Parse::NodeId::None,
- .pattern_block_id = insts.pattern_block_id,
- .implicit_param_patterns_id = insts.implicit_param_patterns_id,
- .param_patterns_id = insts.param_patterns_id,
- .is_extern = false,
- .extern_library_id = SemIR::LibraryNameId::None,
- .non_owning_decl_id = SemIR::InstId::None,
- // Set by `MakeFunctionDecl`.
- .first_owning_decl_id = SemIR::InstId::None,
- },
- {
- .call_param_patterns_id = insts.call_param_patterns_id,
- .call_params_id = insts.call_params_id,
- .call_param_ranges = insts.call_param_ranges,
- .return_type_inst_id = insts.return_type_inst_id,
- .return_form_inst_id = insts.return_form_inst_id,
- .return_pattern_id = insts.return_pattern_id,
- .self_param_id = insts.self_param_id,
- }});
- context.generated().push_back(decl_id);
- return {decl_id, function_id};
- }
- auto CheckFunctionReturnTypeMatches(Context& context,
- const SemIR::Function& new_function,
- const SemIR::Function& prev_function,
- SemIR::SpecificId prev_specific_id,
- bool diagnose) -> bool {
- // TODO: Pass a specific ID for `prev_function` instead of substitutions and
- // use it here.
- auto new_return_type_id =
- new_function.GetDeclaredReturnType(context.sem_ir());
- auto prev_return_type_id =
- prev_function.GetDeclaredReturnType(context.sem_ir(), prev_specific_id);
- if (new_return_type_id == SemIR::ErrorInst::TypeId ||
- prev_return_type_id == SemIR::ErrorInst::TypeId) {
- return false;
- }
- if (!context.types().AreEqualAcrossDeclarations(new_return_type_id,
- prev_return_type_id)) {
- if (!diagnose) {
- return false;
- }
- CARBON_DIAGNOSTIC(
- FunctionRedeclReturnTypeDiffers, Error,
- "function redeclaration differs because return type is {0}",
- SemIR::TypeId);
- CARBON_DIAGNOSTIC(
- FunctionRedeclReturnTypeDiffersNoReturn, Error,
- "function redeclaration differs because no return type is provided");
- auto diag =
- new_return_type_id.has_value()
- ? context.emitter().Build(new_function.latest_decl_id(),
- FunctionRedeclReturnTypeDiffers,
- new_return_type_id)
- : context.emitter().Build(new_function.latest_decl_id(),
- FunctionRedeclReturnTypeDiffersNoReturn);
- if (prev_return_type_id.has_value()) {
- CARBON_DIAGNOSTIC(FunctionRedeclReturnTypePrevious, Note,
- "previously declared with return type {0}",
- SemIR::TypeId);
- diag.Note(prev_function.latest_decl_id(),
- FunctionRedeclReturnTypePrevious, prev_return_type_id);
- } else {
- CARBON_DIAGNOSTIC(FunctionRedeclReturnTypePreviousNoReturn, Note,
- "previously declared with no return type");
- diag.Note(prev_function.latest_decl_id(),
- FunctionRedeclReturnTypePreviousNoReturn);
- }
- diag.Emit();
- return false;
- }
- return true;
- }
- // Checks that a function declaration's evaluation mode matches the previous
- // declaration's evaluation mode. Returns `false` and optionally produces a
- // diagnostic on mismatch.
- static auto CheckFunctionEvaluationModeMatches(
- Context& context, const SemIR::Function& new_function,
- const SemIR::Function& prev_function, bool diagnose) -> bool {
- if (prev_function.evaluation_mode == new_function.evaluation_mode) {
- return true;
- }
- if (!diagnose) {
- return false;
- }
- auto eval_mode_index = [](SemIR::Function::EvaluationMode mode) {
- switch (mode) {
- case SemIR::Function::EvaluationMode::None:
- return 0;
- case SemIR::Function::EvaluationMode::Eval:
- return 1;
- case SemIR::Function::EvaluationMode::MustEval:
- return 2;
- }
- };
- auto prev_eval_mode_index = eval_mode_index(prev_function.evaluation_mode);
- auto new_eval_mode_index = eval_mode_index(new_function.evaluation_mode);
- CARBON_DIAGNOSTIC(
- FunctionRedeclEvaluationModeDiffers, Error,
- "function redeclaration differs because new function is "
- "{0:=-1:not `eval`|=-2:not `musteval`|=1:`eval`|=2:`musteval`}",
- Diagnostics::IntAsSelect);
- CARBON_DIAGNOSTIC(FunctionRedeclEvaluationModePrevious, Note,
- "previously {0:<0:not |:}declared as "
- "{0:=-1:`eval`|=-2:`musteval`|=1:`eval`|=2:`musteval`}",
- Diagnostics::IntAsSelect);
- context.emitter()
- .Build(new_function.latest_decl_id(), FunctionRedeclEvaluationModeDiffers,
- new_eval_mode_index ? new_eval_mode_index : -prev_eval_mode_index)
- .Note(prev_function.latest_decl_id(),
- FunctionRedeclEvaluationModePrevious,
- prev_eval_mode_index ? prev_eval_mode_index : -new_eval_mode_index)
- .Emit();
- return false;
- }
- auto CheckFunctionTypeMatches(Context& context,
- const SemIR::Function& new_function,
- const SemIR::Function& prev_function,
- SemIR::SpecificId prev_specific_id,
- bool check_syntax, bool check_self, bool diagnose)
- -> bool {
- if (!CheckRedeclParamsMatch(context, DeclParams(new_function),
- DeclParams(prev_function), prev_specific_id,
- diagnose, check_syntax, check_self)) {
- return false;
- }
- if (!CheckFunctionReturnTypeMatches(context, new_function, prev_function,
- prev_specific_id, diagnose)) {
- return false;
- }
- if (!CheckFunctionEvaluationModeMatches(context, new_function, prev_function,
- diagnose)) {
- return false;
- }
- return true;
- }
- auto CheckFunctionReturnPatternType(Context& context, SemIR::LocId loc_id,
- SemIR::InstId return_pattern_id,
- SemIR::SpecificId specific_id)
- -> SemIR::TypeId {
- auto arg_type_id = SemIR::ExtractScrutineeType(
- context.sem_ir(), SemIR::GetTypeOfInstInSpecific(
- context.sem_ir(), specific_id, return_pattern_id));
- auto init_repr = SemIR::InitRepr::ForType(context.sem_ir(), arg_type_id);
- if (!init_repr.is_valid()) {
- // TODO: Consider suppressing the diagnostics if we've already diagnosed a
- // definition or call to this function.
- if (!RequireConcreteType(
- context, arg_type_id, SemIR::LocId(return_pattern_id),
- [&](auto& builder) {
- CARBON_DIAGNOSTIC(IncompleteTypeInFunctionReturnType, Context,
- "function returns incomplete type {0}",
- SemIR::TypeId);
- builder.Context(loc_id, IncompleteTypeInFunctionReturnType,
- arg_type_id);
- },
- [&](auto& builder) {
- CARBON_DIAGNOSTIC(AbstractTypeInFunctionReturnType, Context,
- "function returns abstract type {0}",
- SemIR::TypeId);
- builder.Context(loc_id, AbstractTypeInFunctionReturnType,
- arg_type_id);
- })) {
- return SemIR::ErrorInst::TypeId;
- }
- }
- return arg_type_id;
- }
- auto CheckFunctionDefinitionSignature(Context& context,
- SemIR::FunctionId function_id) -> void {
- auto& function = context.functions().Get(function_id);
- auto params_to_complete =
- context.inst_blocks().GetOrEmpty(function.call_params_id);
- // The return parameter will be diagnosed after and differently from other
- // parameters.
- auto return_call_param = SemIR::InstId::None;
- if (!params_to_complete.empty() && function.return_pattern_id.has_value()) {
- return_call_param = params_to_complete.consume_back();
- }
- // Check the parameter types are complete.
- for (auto param_ref_id : params_to_complete) {
- if (param_ref_id == SemIR::ErrorInst::InstId) {
- continue;
- }
- // The parameter types need to be complete.
- RequireCompleteType(
- context, context.insts().GetAs<SemIR::AnyParam>(param_ref_id).type_id,
- SemIR::LocId(param_ref_id), [&](auto& builder) {
- CARBON_DIAGNOSTIC(
- IncompleteTypeInFunctionParam, Context,
- "parameter has incomplete type {0} in function definition",
- TypeOfInstId);
- builder.Context(param_ref_id, IncompleteTypeInFunctionParam,
- param_ref_id);
- });
- }
- // Check the return type is complete.
- if (function.return_pattern_id.has_value()) {
- CheckFunctionReturnPatternType(
- context, SemIR::LocId(function.return_pattern_id),
- function.return_pattern_id, SemIR::SpecificId::None);
- // `CheckFunctionReturnPatternType` should have diagnosed incomplete types,
- // so don't `RequireCompleteType` on the return type.
- if (return_call_param.has_value()) {
- // TODO: If the types are already checked for completeness then this does
- // nothing?
- TryToCompleteType(
- context,
- context.insts().GetAs<SemIR::AnyParam>(return_call_param).type_id,
- SemIR::LocId(return_call_param));
- }
- }
- }
- auto StartFunctionSignature(Context& context) -> void {
- context.scope_stack().PushForDeclName();
- context.inst_block_stack().Push();
- context.pattern_block_stack().Push();
- context.full_pattern_stack().PushParameterizedDecl();
- }
- auto FinishFunctionSignature(Context& context, bool check_unused)
- -> FinishFunctionSignatureResult {
- context.full_pattern_stack().PopFullPattern();
- auto pattern_block_id = context.pattern_block_stack().Pop();
- auto decl_block_id = context.inst_block_stack().Pop();
- context.scope_stack().Pop(check_unused);
- return {.pattern_block_id = pattern_block_id, .decl_block_id = decl_block_id};
- }
- auto MakeFunctionDecl(Context& context, SemIR::LocId loc_id,
- SemIR::InstBlockId decl_block_id, bool build_generic,
- bool is_definition, SemIR::Function function)
- -> std::pair<SemIR::InstId, SemIR::FunctionId> {
- CARBON_CHECK(!function.first_owning_decl_id.has_value());
- SemIR::FunctionDecl function_decl = {SemIR::TypeId::None,
- SemIR::FunctionId::None, decl_block_id};
- auto decl_id = AddPlaceholderInstInNoBlock(
- context, SemIR::LocIdAndInst::RuntimeVerified(context.sem_ir(), loc_id,
- function_decl));
- function.first_owning_decl_id = decl_id;
- if (is_definition) {
- function.definition_id = decl_id;
- }
- if (build_generic) {
- function.generic_id = BuildGenericDecl(context, decl_id);
- }
- // Create the `Function` object.
- function_decl.function_id = context.functions().Add(std::move(function));
- function_decl.type_id =
- GetFunctionType(context, function_decl.function_id,
- build_generic ? context.scope_stack().PeekSpecificId()
- : SemIR::SpecificId::None);
- ReplaceInstBeforeConstantUse(context, decl_id, function_decl);
- return {decl_id, function_decl.function_id};
- }
- auto StartFunctionDefinition(Context& context, SemIR::InstId decl_id,
- SemIR::FunctionId function_id) -> void {
- // Create the function scope and the entry block.
- context.scope_stack().PushForFunctionBody(decl_id);
- context.inst_block_stack().Push();
- context.region_stack().PushRegion(context.inst_block_stack().PeekOrAdd());
- StartGenericDefinition(context,
- context.functions().Get(function_id).generic_id);
- CheckFunctionDefinitionSignature(context, function_id);
- }
- auto FinishFunctionDefinition(Context& context, SemIR::FunctionId function_id)
- -> void {
- context.inst_block_stack().Pop();
- context.scope_stack().Pop(/*check_unused=*/true);
- auto& function = context.functions().Get(function_id);
- function.body_block_ids = context.region_stack().PopRegion();
- // If this is a generic function, collect information about the definition.
- FinishGenericDefinition(context, function.generic_id);
- }
- } // namespace Carbon::Check
|