diff --git a/toolchain/check/call.cpp b/toolchain/check/call.cpp index 267dfafb8108..ee90a1d4ec80 100644 --- a/toolchain/check/call.cpp +++ b/toolchain/check/call.cpp @@ -211,21 +211,6 @@ static auto BuildCalleeSpecificFunction( return callee_id; } -// Returns the return type, with a scoped annotation for any diagnostics. -static auto CheckCalleeFunctionReturnType(Context& context, SemIR::LocId loc_id, - SemIR::FunctionId callee_function_id, - SemIR::SpecificId callee_specific_id) - -> SemIR::TypeId { - auto& function = context.functions().Get(callee_function_id); - Diagnostics::AnnotationScope annotate_diagnostics( - &context.emitter(), [&](auto& builder) { - CARBON_DIAGNOSTIC(IncompleteReturnTypeHere, Note, - "return type declared here"); - builder.Note(function.return_type_inst_id, IncompleteReturnTypeHere); - }); - return CheckFunctionReturnType(context, loc_id, function, callee_specific_id); -} - auto PerformCallToFunction(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id, const SemIR::CalleeFunction& callee_function, @@ -247,19 +232,27 @@ auto PerformCallToFunction(Context& context, SemIR::LocId loc_id, *callee_specific_id); } - auto return_type_id = CheckCalleeFunctionReturnType( - context, loc_id, callee_function.function_id, *callee_specific_id); - auto& callee = context.functions().Get(callee_function.function_id); + auto return_type_id = + callee.GetDeclaredReturnType(context.sem_ir(), *callee_specific_id); + if (!return_type_id.has_value()) { + return_type_id = GetTupleType(context, {}); + } - // Build storage for any output parameters. llvm::SmallVector return_arg_ids; for (auto return_pattern_id : context.inst_blocks().GetOrEmpty(callee.return_patterns_id)) { - auto arg_type_id = SemIR::ExtractScrutineeType( - context.sem_ir(), - SemIR::GetTypeOfInstInSpecific(context.sem_ir(), *callee_specific_id, - return_pattern_id)); + Diagnostics::AnnotationScope annotate_diagnostics( + &context.emitter(), [&](auto& builder) { + CARBON_DIAGNOSTIC(IncompleteReturnTypeHere, Note, + "return type declared here"); + builder.Note(return_pattern_id, IncompleteReturnTypeHere); + }); + auto arg_type_id = CheckFunctionReturnPatternType( + context, loc_id, return_pattern_id, *callee_specific_id); + if (arg_type_id == SemIR::ErrorInst::TypeId) { + return_type_id = SemIR::ErrorInst::TypeId; + } switch (SemIR::InitRepr::ForType(context.sem_ir(), arg_type_id).kind) { case SemIR::InitRepr::InPlace: case SemIR::InitRepr::Dependent: diff --git a/toolchain/check/function.cpp b/toolchain/check/function.cpp index 3f756be706d5..550f113572c3 100644 --- a/toolchain/check/function.cpp +++ b/toolchain/check/function.cpp @@ -92,47 +92,38 @@ auto CheckFunctionTypeMatches(Context& context, prev_specific_id, diagnose); } -auto CheckFunctionReturnType(Context& context, SemIR::LocId loc_id, - const SemIR::Function& function, - SemIR::SpecificId specific_id) -> SemIR::TypeId { - auto return_info = SemIR::ReturnTypeInfo::ForFunction(context.sem_ir(), - function, specific_id); - - // If we couldn't determine the return information due to the return type - // being incomplete, try to complete it now. - if (return_info.init_repr.kind == SemIR::InitRepr::Incomplete || - return_info.init_repr.kind == SemIR::InitRepr::Abstract) { +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()) { auto diagnose_incomplete_return_type = [&] { CARBON_DIAGNOSTIC(IncompleteTypeInFunctionReturnType, Error, "function returns incomplete type {0}", SemIR::TypeId); return context.emitter().Build(loc_id, IncompleteTypeInFunctionReturnType, - return_info.type_id); + arg_type_id); }; auto diagnose_abstract_return_type = [&] { CARBON_DIAGNOSTIC(AbstractTypeInFunctionReturnType, Error, "function returns abstract type {0}", SemIR::TypeId); return context.emitter().Build(loc_id, AbstractTypeInFunctionReturnType, - return_info.type_id); + arg_type_id); }; // TODO: Consider suppressing the diagnostic if we've already diagnosed a // definition or call to this function. - if (RequireConcreteType(context, return_info.type_id, loc_id, - diagnose_incomplete_return_type, - diagnose_abstract_return_type)) { - return_info = SemIR::ReturnTypeInfo::ForFunction(context.sem_ir(), - function, specific_id); + if (!RequireConcreteType( + context, arg_type_id, SemIR::LocId(return_pattern_id), + diagnose_incomplete_return_type, diagnose_abstract_return_type)) { + return SemIR::ErrorInst::TypeId; } } - if (return_info.init_repr.kind == SemIR::InitRepr::Incomplete || - return_info.init_repr.kind == SemIR::InitRepr::Abstract) { - return SemIR::ErrorInst::TypeId; - } - if (!return_info.type_id.has_value()) { - return GetTupleType(context, {}); - } - return return_info.type_id; + return arg_type_id; } auto CheckFunctionDefinitionSignature(Context& context, @@ -143,9 +134,10 @@ auto CheckFunctionDefinitionSignature(Context& context, context.inst_blocks().GetOrEmpty(function.call_params_id); // Check the return type is complete. - if (function.return_type_inst_id.has_value()) { - CheckFunctionReturnType(context, SemIR::LocId(function.return_type_inst_id), - function, SemIR::SpecificId::None); + for (auto return_pattern_id : + context.inst_blocks().GetOrEmpty(function.return_patterns_id)) { + CheckFunctionReturnPatternType(context, SemIR::LocId(return_pattern_id), + return_pattern_id, SemIR::SpecificId::None); } // Check the parameter types are complete. diff --git a/toolchain/check/function.h b/toolchain/check/function.h index 6cdd0792386a..794375103c6b 100644 --- a/toolchain/check/function.h +++ b/toolchain/check/function.h @@ -53,12 +53,14 @@ inline auto CheckFunctionTypeMatches(Context& context, /*check_syntax=*/true, /*check_self=*/true); } -// Checks that the return type of the specified function is complete, issuing an -// error if not. This computes the return slot usage for the function if -// necessary, and returns the function's return type. -auto CheckFunctionReturnType(Context& context, SemIR::LocId loc_id, - const SemIR::Function& function, - SemIR::SpecificId specific_id) -> SemIR::TypeId; +// Checks that the scrutinee type of `return_pattern_id` in `specific_id` is +// concrete. If so, it returns that type; if not, it issues an error and returns +// SemIR::ErrorInst::TypeId. `return_pattern_id` must be part of a function's +// return form, or the error message will be nonsensical. +auto CheckFunctionReturnPatternType(Context& context, SemIR::LocId loc_id, + SemIR::InstId return_pattern_id, + SemIR::SpecificId specific_id) + -> SemIR::TypeId; // Checks that a function declaration's signature is suitable to support a // function definition. This requires the parameter types to be complete and the diff --git a/toolchain/check/return.cpp b/toolchain/check/return.cpp index a46c0f34a391..0f0766186f7f 100644 --- a/toolchain/check/return.cpp +++ b/toolchain/check/return.cpp @@ -75,15 +75,10 @@ auto RegisterReturnedVar(Context& context, Parse::NodeId returned_node, Parse::NodeId type_node, SemIR::TypeId type_id, SemIR::InstId bind_id) -> void { auto& function = GetCurrentFunctionForReturn(context); - auto return_info = - SemIR::ReturnTypeInfo::ForFunction(context.sem_ir(), function); - if (!return_info.is_valid()) { - // We already diagnosed this when we started defining the function. - return; - } + auto return_type_id = function.GetDeclaredReturnType(context.sem_ir()); // A `returned var` requires an explicit return type. - if (!return_info.type_id.has_value()) { + if (!return_type_id.has_value()) { CARBON_DIAGNOSTIC(ReturnedVarWithNoReturnType, Error, "cannot declare a `returned var` in this function"); auto diag = @@ -94,7 +89,7 @@ auto RegisterReturnedVar(Context& context, Parse::NodeId returned_node, } // The declared type of the var must match the return type of the function. - if (return_info.type_id != type_id) { + if (return_type_id != type_id) { CARBON_DIAGNOSTIC(ReturnedVarWrongType, Error, "type {0} of `returned var` does not match " "return type of enclosing function", @@ -105,6 +100,17 @@ auto RegisterReturnedVar(Context& context, Parse::NodeId returned_node, diag.Emit(); } + auto form_inst_id = function.GetDeclaredReturnForm(context.sem_ir()); + if (!context.insts().Is(form_inst_id)) { + CARBON_DIAGNOSTIC(ReturnedVarNotInit, Error, + "`returned var` declaration in function with " + "non-initializing return form"); + auto diag = context.emitter().Build(returned_node, ReturnedVarNotInit); + CARBON_DIAGNOSTIC(ReturnFormHereNote, Note, "return form declared here"); + diag.Note(function.return_form_inst_id, ReturnFormHereNote); + diag.Emit(); + } + auto existing_id = context.scope_stack().SetReturnedVarOrGetExisting(bind_id); if (existing_id.has_value()) { CARBON_DIAGNOSTIC(ReturnedVarShadowed, Error, diff --git a/toolchain/check/testdata/interop/cpp/function/class.carbon b/toolchain/check/testdata/interop/cpp/function/class.carbon index fb40f10545d4..c0c35914a5e0 100644 --- a/toolchain/check/testdata/interop/cpp/function/class.carbon +++ b/toolchain/check/testdata/interop/cpp/function/class.carbon @@ -452,9 +452,9 @@ fn F() { // CHECK:STDERR: class C; // CHECK:STDERR: ^ // CHECK:STDERR: fail_import_decl_value_return_type.carbon:[[@LINE-14]]:10: in file included here [InCppInclude] - // CHECK:STDERR: ./decl_value_return_type.h:2:7: note: return type declared here [IncompleteReturnTypeHere] - // CHECK:STDERR: class C; - // CHECK:STDERR: ^ + // CHECK:STDERR: ./decl_value_return_type.h:4:1: note: return type declared here [IncompleteReturnTypeHere] + // CHECK:STDERR: auto foo() -> C; + // CHECK:STDERR: ^ // CHECK:STDERR: Cpp.foo(); } diff --git a/toolchain/check/testdata/interop/cpp/function/struct.carbon b/toolchain/check/testdata/interop/cpp/function/struct.carbon index 979971f64530..6c1d9f7dff1e 100644 --- a/toolchain/check/testdata/interop/cpp/function/struct.carbon +++ b/toolchain/check/testdata/interop/cpp/function/struct.carbon @@ -451,9 +451,9 @@ fn F() { // CHECK:STDERR: struct S; // CHECK:STDERR: ^ // CHECK:STDERR: fail_import_decl_value_return_type.carbon:[[@LINE-14]]:10: in file included here [InCppInclude] - // CHECK:STDERR: ./decl_value_return_type.h:2:8: note: return type declared here [IncompleteReturnTypeHere] - // CHECK:STDERR: struct S; - // CHECK:STDERR: ^ + // CHECK:STDERR: ./decl_value_return_type.h:4:1: note: return type declared here [IncompleteReturnTypeHere] + // CHECK:STDERR: auto foo() -> S; + // CHECK:STDERR: ^ // CHECK:STDERR: Cpp.foo(); } diff --git a/toolchain/check/testdata/interop/cpp/function/union.carbon b/toolchain/check/testdata/interop/cpp/function/union.carbon index baffcc95ca50..98cbbd4b92ee 100644 --- a/toolchain/check/testdata/interop/cpp/function/union.carbon +++ b/toolchain/check/testdata/interop/cpp/function/union.carbon @@ -413,9 +413,9 @@ fn F() { // CHECK:STDERR: union U; // CHECK:STDERR: ^ // CHECK:STDERR: fail_import_decl_value_return_type.carbon:[[@LINE-14]]:10: in file included here [InCppInclude] - // CHECK:STDERR: ./decl_value_return_type.h:2:7: note: return type declared here [IncompleteReturnTypeHere] - // CHECK:STDERR: union U; - // CHECK:STDERR: ^ + // CHECK:STDERR: ./decl_value_return_type.h:4:1: note: return type declared here [IncompleteReturnTypeHere] + // CHECK:STDERR: auto foo() -> U; + // CHECK:STDERR: ^ // CHECK:STDERR: Cpp.foo(); } diff --git a/toolchain/check/testdata/return/fail_returned_var_form.carbon b/toolchain/check/testdata/return/fail_returned_var_form.carbon new file mode 100644 index 000000000000..5348cb31853b --- /dev/null +++ b/toolchain/check/testdata/return/fail_returned_var_form.carbon @@ -0,0 +1,24 @@ +// 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-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon +// EXTRA-ARGS: --dump-sem-ir-ranges=only +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/return/fail_returned_var_form.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/return/fail_returned_var_form.carbon + +fn Mismatch() -> ref () { + // CHECK:STDERR: fail_returned_var_form.carbon:[[@LINE+7]]:3: error: `returned var` declaration in function with non-initializing return form [ReturnedVarNotInit] + // CHECK:STDERR: returned var v: () = (); + // CHECK:STDERR: ^~~~~~~~ + // CHECK:STDERR: fail_returned_var_form.carbon:[[@LINE-4]]:18: note: return form declared here [ReturnFormHereNote] + // CHECK:STDERR: fn Mismatch() -> ref () { + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: + returned var v: () = (); + return var; +} diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index d79fadb30ea2..dd0b08311fd4 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -469,10 +469,12 @@ CARBON_DIAGNOSTIC_KIND(TupleIndexOutOfBounds) CARBON_DIAGNOSTIC_KIND(TupleInitElementCountMismatch) CARBON_DIAGNOSTIC_KIND(PartialOnFinal) CARBON_DIAGNOSTIC_KIND(ReturnedVarHere) +CARBON_DIAGNOSTIC_KIND(ReturnedVarNotInit) CARBON_DIAGNOSTIC_KIND(ReturnedVarShadowed) CARBON_DIAGNOSTIC_KIND(ReturnedVarWithNoReturnType) CARBON_DIAGNOSTIC_KIND(ReturnedVarWrongType) CARBON_DIAGNOSTIC_KIND(ReturnExprWithReturnedVar) +CARBON_DIAGNOSTIC_KIND(ReturnFormHereNote) CARBON_DIAGNOSTIC_KIND(ReturnVarWithNoReturnedVar) CARBON_DIAGNOSTIC_KIND(ReturnStatementDisallowExpr) CARBON_DIAGNOSTIC_KIND(ReturnTypeHereNote) diff --git a/toolchain/sem_ir/expr_info.cpp b/toolchain/sem_ir/expr_info.cpp index d17257fb23b1..4b3db5831167 100644 --- a/toolchain/sem_ir/expr_info.cpp +++ b/toolchain/sem_ir/expr_info.cpp @@ -174,15 +174,39 @@ auto FindReturnSlotArgForInitializer(const File& sem_ir, InstId init_id) return init.dest_id; } case CARBON_KIND(Call call): { - if (!ReturnTypeInfo::ForCallee(sem_ir, call.callee_id) - .has_return_slot()) { + auto callee_function = GetCalleeAsFunction(sem_ir, call.callee_id); + const auto& function = + sem_ir.functions().Get(callee_function.function_id); + if (!function.return_form_inst_id.has_value()) { return InstId::None; } - if (!call.args_id.has_value()) { - // Argument initialization failed, so we have no return slot. - return InstId::None; + auto return_form_constant_id = GetConstantValueInSpecific( + sem_ir, callee_function.resolved_specific_id, + function.return_form_inst_id); + auto return_form = sem_ir.insts().Get( + sem_ir.constant_values().GetInstId(return_form_constant_id)); + CARBON_KIND_SWITCH(return_form) { + case CARBON_KIND(InitForm init_form): { + auto type_id = sem_ir.types().GetTypeIdForTypeInstId( + init_form.type_component_inst_id); + if (!InitRepr::ForType(sem_ir, type_id).MightBeInPlace()) { + return InstId::None; + } + + if (!call.args_id.has_value()) { + // Argument initialization failed, so we have no return slot. + return InstId::None; + } + + return sem_ir.inst_blocks().Get( + call.args_id)[init_form.index.index]; + } + case CARBON_KIND(RefForm _): { + return InstId::None; + } + default: + CARBON_FATAL("Unexpected inst kind: {0}", return_form); } - return sem_ir.inst_blocks().Get(call.args_id).back(); } case CARBON_KIND(ErrorInst _): { return InstId::None;