diff --git a/toolchain/check/call.cpp b/toolchain/check/call.cpp index dbee03542cbd..16380add798c 100644 --- a/toolchain/check/call.cpp +++ b/toolchain/check/call.cpp @@ -249,6 +249,30 @@ auto PerformCallToFunction(Context& context, SemIR::LocId loc_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()) { diff --git a/toolchain/check/control_flow.cpp b/toolchain/check/control_flow.cpp index 2c95e4e937c8..c45fb11b4b07 100644 --- a/toolchain/check/control_flow.cpp +++ b/toolchain/check/control_flow.cpp @@ -204,9 +204,10 @@ auto AddBranchWithCleanups(Context& context, SemIR::LocId loc_id, } auto AddReturnInstWithCleanups(Context& context, - SemIR::LocIdAndInst loc_id_and_inst) -> void { + SemIR::LocIdAndInst loc_id_and_inst) + -> SemIR::InstId { AddCleanups(context, context.scope_stack().function_cleanup_scope_depth()); - AddInst(context, loc_id_and_inst); + return AddInst(context, loc_id_and_inst); } } // namespace Carbon::Check diff --git a/toolchain/check/control_flow.h b/toolchain/check/control_flow.h index 66aa5b2c468b..b67b67d783d4 100644 --- a/toolchain/check/control_flow.h +++ b/toolchain/check/control_flow.h @@ -110,18 +110,20 @@ auto AddBranchWithCleanups(Context& context, SemIR::LocId loc_id, // Adds a return instruction, along with cleanups for all live variables or // temporaries in the current function. If no instruction is passed, it -// defaults to `SemIR::Return{}`. +// defaults to `SemIR::Return{}`. Returns the return instruction. auto AddReturnInstWithCleanups(Context& context, - SemIR::LocIdAndInst loc_id_and_inst) -> void; + SemIR::LocIdAndInst loc_id_and_inst) + -> SemIR::InstId; template -auto AddReturnInstWithCleanups(Context& context, LocT loc) -> void { - AddReturnInstWithCleanups(context, SemIR::LocIdAndInst(loc, SemIR::Return{})); +auto AddReturnInstWithCleanups(Context& context, LocT loc) -> SemIR::InstId { + return AddReturnInstWithCleanups(context, + SemIR::LocIdAndInst(loc, SemIR::Return{})); } template auto AddReturnInstWithCleanups(Context& context, LocT loc, - SemIR::ReturnExpr inst) -> void { - AddReturnInstWithCleanups(context, SemIR::LocIdAndInst(loc, inst)); + SemIR::ReturnExpr inst) -> SemIR::InstId { + return AddReturnInstWithCleanups(context, SemIR::LocIdAndInst(loc, inst)); } } // namespace Carbon::Check diff --git a/toolchain/check/cpp/export.cpp b/toolchain/check/cpp/export.cpp index 7fd8a6391f0c..5173aa7f268d 100644 --- a/toolchain/check/cpp/export.cpp +++ b/toolchain/check/cpp/export.cpp @@ -1539,6 +1539,13 @@ static auto ExportGenericFunctionToCpp(Context& context, SemIR::LocId loc_id, static auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_id, SemIR::FunctionId callee_function_id) -> clang::NamedDecl* { + if (const auto& callee = context.functions().Get(callee_function_id); + callee.has_pending_deduced_return_type() || + (callee.has_deduced_return_type && callee.generic_id.has_value())) { + context.TODO(loc_id, "export of function with deduced return type"); + return nullptr; + } + auto target = BuildFunctionInfo(context, loc_id, callee_function_id); if (!target) { return nullptr; diff --git a/toolchain/check/function.cpp b/toolchain/check/function.cpp index da292f4561fc..e5594c2e8e02 100644 --- a/toolchain/check/function.cpp +++ b/toolchain/check/function.cpp @@ -59,6 +59,23 @@ auto AddReturnPattern(Context& context, SemIR::LocId loc_id, .type_inst_id = form_expr.type_component_inst_id}); } +auto PopReturnSpecifier(Context& context) -> ReturnSpecifierInfo { + auto [node_id, pattern_id] = + context.node_stack() + .PopWithNodeIdIf(); + if (!pattern_id) { + return {}; + } + if (*pattern_id == SemIR::AutoType::TypeInstId) { + // For a deduced return type, `HandleReturnSpecifier` pushes `auto` in place + // of a return pattern, and doesn't push a return form. + return {.node_id = node_id, .is_deduced = true}; + } + return {.node_id = node_id, + .pattern_id = *pattern_id, + .form = context.PopReturnForm()}; +} + auto IsValidBuiltinDeclaration(Context& context, const SemIR::Function& function, SemIR::BuiltinFunctionKind builtin_kind) @@ -359,6 +376,25 @@ auto CheckFunctionReturnPatternType(Context& context, SemIR::LocId loc_id, return arg_type_id; } +// Checks that the return type of a function definition is suitable for a +// definition. `return_call_param` is the `Call` parameter for the return, if +// any. +static auto CheckDefinitionReturnType(Context& context, + SemIR::InstId return_pattern_id, + SemIR::InstId return_call_param) -> void { + CheckFunctionReturnPatternType(context, SemIR::LocId(return_pattern_id), + 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().Get(return_call_param).type_id(), + SemIR::LocId(return_call_param)); + } +} + auto CheckFunctionDefinitionSignature(Context& context, SemIR::FunctionId function_id) -> void { auto& function = context.functions().Get(function_id); @@ -394,19 +430,78 @@ auto CheckFunctionDefinitionSignature(Context& context, // 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); + CheckDefinitionReturnType(context, function.return_pattern_id, + return_call_param); + } +} - // `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().Get(return_call_param).type_id(), - SemIR::LocId(return_call_param)); +auto SetDeducedReturnType(Context& context, SemIR::FunctionId function_id, + SemIR::LocId loc_id, SemIR::TypeId type_id) -> void { + auto& function = context.functions().Get(function_id); + CARBON_CHECK(function.has_pending_deduced_return_type()); + + // A function with a deduced return type can't be redeclared, so there is only + // one declaration to complete. + auto decl_id = function.latest_decl_id(); + auto decl = context.insts().GetAs(decl_id); + + // Reopen the function's declaration and pattern blocks to finish building the + // signature. Add the return form and return pattern, as + // `HandleReturnSpecifier` would for an explicitly declared return type, + // followed by the callee pattern-match IR for the return, as + // `PopNameComponent` would. + context.inst_block_stack().Push( + SemIR::InstBlockId::None, + context.inst_blocks().GetOrEmpty(decl.decl_block_id)); + context.pattern_block_stack().Push( + SemIR::InstBlockId::None, + context.inst_blocks().GetOrEmpty(function.pattern_block_id)); + auto form = Context::FormExpr::Error; + if (type_id != SemIR::ErrorInst::TypeId) { + // Represent the return type as a type literal at the location it was + // deduced from. This is a new instruction, rather than the canonical + // instruction for the type, so that in a generic it is attached to the + // current region, and can depend on anything declared before this point. + auto type_inst_id = AddTypeInst( + context, loc_id, + {.type_id = SemIR::TypeType::TypeId, + .value_id = context.types().GetTypeInstId(type_id)}); + auto form_inst_id = AddInst( + context, SemIR::LocIdAndInst::RuntimeVerified( + context.sem_ir(), loc_id, + SemIR::InitForm{.type_id = SemIR::FormType::TypeId, + .type_component_inst_id = type_inst_id})); + form = {.form_inst_id = form_inst_id, + .type_component_inst_id = type_inst_id, + .type_component_id = + context.types().GetTypeIdForTypeInstId(type_inst_id)}; + } + auto return_pattern_id = AddReturnPattern(context, loc_id, form); + auto results = CalleeReturnPatternMatch( + context, + {.call_param_patterns_id = function.call_param_patterns_id, + .call_params_id = function.call_params_id, + .param_ranges = function.call_param_ranges}, + return_pattern_id); + function.pattern_block_id = context.pattern_block_stack().Pop(); + decl.decl_block_id = context.inst_block_stack().Pop(); + ReplaceInstPreservingConstantValue(context, decl_id, decl); + + function.call_param_patterns_id = results.call_param_patterns_id; + function.call_params_id = results.call_params_id; + function.call_param_ranges = results.param_ranges; + function.return_type_inst_id = form.type_component_inst_id; + function.return_form_inst_id = form.form_inst_id; + function.return_pattern_id = return_pattern_id; + CARBON_CHECK(!function.has_pending_deduced_return_type()); + + if (type_id != SemIR::ErrorInst::TypeId) { + auto return_call_param = SemIR::InstId::None; + if (results.param_ranges.return_size() == 1) { + return_call_param = context.inst_blocks().Get( + results.call_params_id)[results.param_ranges.return_begin().index]; } + CheckDefinitionReturnType(context, return_pattern_id, return_call_param); } } @@ -635,6 +730,14 @@ auto StartFunctionDefinition(Context& context, SemIR::InstId decl_id, auto CheckFunctionReturnOnFinish(Context& context, Parse::NodeId node_id, SemIR::FunctionId function_id) -> void { + // If the return type is deduced and we've not seen a `returned var`, the + // return type is determined by the `return` statements in the body. + if (context.functions().Get(function_id).has_pending_deduced_return_type() && + !DeduceReturnTypeAtEndOfBody(context, function_id)) { + // There are no `return` statements, which has already been diagnosed. + return; + } + // If the `}` of the function is reachable, reject if we need a return value // and otherwise add an implicit `return;`. if (IsCurrentPositionReachable(context)) { diff --git a/toolchain/check/function.h b/toolchain/check/function.h index 0f12130afe98..20f755f01c9d 100644 --- a/toolchain/check/function.h +++ b/toolchain/check/function.h @@ -29,6 +29,38 @@ auto FindSelfPattern(Context& context, auto AddReturnPattern(Context& context, SemIR::LocId loc_id, Context::FormExpr form_expr) -> SemIR::InstId; +// Information about the return specifier of a function, as popped by +// `PopReturnSpecifier`. +struct ReturnSpecifierInfo { + // The return specifier, or `None` if there is none. + Parse::NodeId node_id = Parse::NoneNodeId(); + // Whether the return type is deduced from the body, as in `-> auto`. + bool is_deduced = false; + // The declared return pattern. `None` if there is no return specifier or + // the return type is deduced. + SemIR::InstId pattern_id = SemIR::InstId::None; + // The declared return form. `None` if there is no return specifier or the + // return type is deduced. + Context::FormExpr form = Context::FormExpr::None; + + auto has_return_specifier() const -> bool { return node_id.has_value(); } +}; + +// Pops the return specifier of a function declaration or function expression +// from the node stack, if it has one. +auto PopReturnSpecifier(Context& context) -> ReturnSpecifierInfo; + +// Sets the return type of a function whose return type is deduced, once that +// type is known. `type_id` is the deduced type, or the error type if deduction +// failed, and `loc_id` is the location it was deduced from. +// +// This creates the function's return form, return pattern and return `Call` +// parameter, just as if the return type had been declared explicitly. Within a +// generic function's definition, the return type is computed in the +// definition's eval block. +auto SetDeducedReturnType(Context& context, SemIR::FunctionId function_id, + SemIR::LocId loc_id, SemIR::TypeId type_id) -> void; + // Returns whether `function` is a valid declaration of `builtin_kind`. auto IsValidBuiltinDeclaration(Context& context, const SemIR::Function& function, @@ -136,7 +168,9 @@ auto StartFunctionDefinition(Context& context, SemIR::InstId decl_id, SemIR::FunctionId function_id) -> void; // Checks that a function definition has a `return` at the end if required, or -// adds an implicit `return;` if reachable. +// adds an implicit `return;` if reachable. If the function's return type is +// deduced and hasn't been deduced yet, deduces it from the function's `return` +// statements first. auto CheckFunctionReturnOnFinish(Context& context, Parse::NodeId node_id, SemIR::FunctionId function_id) -> void; diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 4b4acc393bba..24f462de369c 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -23,6 +23,7 @@ #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/check/unused.h" +#include "toolchain/diagnostics/format_providers.h" #include "toolchain/lex/token_kind.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/builtin_function_kind.h" @@ -50,24 +51,30 @@ auto HandleParseNode(Context& context, Parse::FunctionIntroducerId node_id) return true; } -// Handles a `->` or `->?` return declaration. -static auto HandleReturnDecl(Context& context, Parse::AnyReturnDeclId node_id) - -> bool { +// Handles a `->` or `->?` return specifier. +static auto HandleReturnSpecifier(Context& context, + Parse::AnyReturnSpecifierId node_id) -> bool { auto [expr_node_id, expr_inst_id] = context.node_stack().PopExprWithNodeId(); - Context::FormExpr form_expr = [&] { - // `-> T` provides a type expression, whereas `-> val T`, `-> ref T`, - // `-> var T`, and `->? F` provide a form. - auto expr_kind = context.parse_tree().node_kind(expr_node_id); - bool is_primitive_form = expr_kind == Parse::ValPrimitiveFormId::Kind || - expr_kind == Parse::RefPrimitiveFormId::Kind || - expr_kind == Parse::VarPrimitiveFormId::Kind; - if (context.parse_tree().node_kind(node_id) == Parse::ReturnTypeId::Kind && - !is_primitive_form) { - return ReturnExprAsForm(context, expr_node_id, expr_inst_id); - } else { - return FormExprAsForm(context, expr_node_id, expr_inst_id); - } - }(); + // `-> T` provides a type expression, whereas `-> val T`, `-> ref T`, + // `-> var T`, and `->? F` provide a form. + auto expr_kind = context.parse_tree().node_kind(expr_node_id); + bool is_primitive_form = expr_kind == Parse::ValPrimitiveFormId::Kind || + expr_kind == Parse::RefPrimitiveFormId::Kind || + expr_kind == Parse::VarPrimitiveFormId::Kind; + bool is_return_type = + context.parse_tree().node_kind(node_id) == Parse::ReturnTypeId::Kind && + !is_primitive_form; + if (is_return_type && context.parse_tree().node_kind(expr_node_id) == + Parse::NodeKind::AutoTypeLiteral) { + // `-> auto`: the return type is deduced from the function body, so there is + // no return form or pattern yet. Push a marker in place of the pattern; see + // `PopReturnSpecifier`. + context.node_stack().Push(node_id, SemIR::AutoType::TypeInstId); + return true; + } + Context::FormExpr form_expr = + is_return_type ? ReturnExprAsForm(context, expr_node_id, expr_inst_id) + : FormExprAsForm(context, expr_node_id, expr_inst_id); context.PushReturnForm(form_expr); context.node_stack().Push(node_id, AddReturnPattern(context, node_id, form_expr)); @@ -75,11 +82,11 @@ static auto HandleReturnDecl(Context& context, Parse::AnyReturnDeclId node_id) } auto HandleParseNode(Context& context, Parse::ReturnTypeId node_id) -> bool { - return HandleReturnDecl(context, node_id); + return HandleReturnSpecifier(context, node_id); } auto HandleParseNode(Context& context, Parse::ReturnFormId node_id) -> bool { - return HandleReturnDecl(context, node_id); + return HandleReturnSpecifier(context, node_id); } // Diagnoses issues with the modifiers, removing modifiers that shouldn't be @@ -367,6 +374,67 @@ static auto RequestVtableIfVirtual( context.vtable_stack().AddInstId(decl_id); } +// Diagnoses if a deduced return type is not permitted for the given function +// declaration, and returns whether it is permitted. If not, clears any virtual +// modifier, because a virtual function needs a known return type. +static auto CheckDeducedReturnTypeAllowed( + Context& context, Parse::AnyFunctionDeclId node_id, + Parse::NodeId return_node_id, bool is_definition, bool is_extern, + SemIR::Function::VirtualModifier& virtual_modifier, + const std::optional& parent_scope_inst) -> bool { + enum class Reason : int8_t { Builtin, Extern, InterfaceMember, Virtual }; + std::optional reason; + if (context.parse_tree().node_kind(node_id) == + Parse::NodeKind::BuiltinFunctionDefinitionStart) { + // A builtin function has no body to deduce from. + reason = Reason::Builtin; + } else if (is_extern) { + // An `extern` function is declared elsewhere. + reason = Reason::Extern; + } else if (parent_scope_inst && + parent_scope_inst->Is()) { + // An associated function's return type is part of the interface's contract + // with its impls, so shouldn't depend on a `default` definition. + // TODO: A `final` function can't be overridden, so deducing its return + // type from its definition would be reasonable. Consider allowing that + // once calls to generic functions with deduced return types are supported. + reason = Reason::InterfaceMember; + } else if (virtual_modifier != SemIR::Function::VirtualModifier::None) { + // The vtable is built before the function body is checked. + reason = Reason::Virtual; + virtual_modifier = SemIR::Function::VirtualModifier::None; + } + if (reason) { + CARBON_DIAGNOSTIC(DeducedReturnTypeNotAllowed, Error, + "{0:=0:builtin function|=1:`extern` function|=2:" + "interface member|=3:virtual function} with deduced " + "return type", + Diagnostics::IntAsSelect); + context.emitter().Emit(return_node_id, DeducedReturnTypeNotAllowed, + static_cast(*reason)); + return false; + } + + // The return type is deduced from the function body, so only a definition + // can declare one. As a consequence, the function can't be redeclared. + if (!is_definition) { + CARBON_DIAGNOSTIC(DeducedReturnTypeNotDefinition, Error, + "forward declaration of function with deduced return " + "type"); + context.emitter().Emit(return_node_id, DeducedReturnTypeNotDefinition); + return false; + } + + if (parent_scope_inst && parent_scope_inst->Is()) { + // TODO: The impl witness and any thunks are built before the function body + // is checked. + context.TODO(return_node_id, "deduced return type for `impl` member"); + return false; + } + + return true; +} + // Build a FunctionDecl describing the signature of a function. This // handles the common logic shared by function declaration syntax and function // definition syntax. @@ -374,20 +442,10 @@ static auto BuildFunctionDecl(Context& context, Parse::AnyFunctionDeclId node_id, bool is_definition) -> std::pair { - auto return_pattern_id = SemIR::InstId::None; - auto return_type_inst_id = SemIR::TypeInstId::None; - auto return_form_inst_id = SemIR::InstId::None; - if (auto [return_node, maybe_return_pattern_id] = - context.node_stack() - .PopWithNodeIdIf(); - maybe_return_pattern_id) { - return_pattern_id = *maybe_return_pattern_id; - auto return_form = context.PopReturnForm(); - return_type_inst_id = return_form.type_component_inst_id; - return_form_inst_id = return_form.form_inst_id; - } + auto return_specifier = PopReturnSpecifier(context); - auto name = PopNameComponent(context, return_pattern_id); + auto name = PopNameComponent(context, return_specifier.pattern_id, + return_specifier.has_return_specifier()); auto name_context = context.decl_name_stack().FinishName(name); context.node_stack() @@ -422,6 +480,14 @@ static auto BuildFunctionDecl(Context& context, auto evaluation_mode = GetEvaluationMode(introducer.modifier_set); auto interface_modifier = GetInterfaceModifier(introducer.modifier_set); + // If the return type is deduced in a context where that's not allowed, we + // will treat the function as having an invalid return type. + bool is_invalid_deduced_return = + return_specifier.is_deduced && + !CheckDeducedReturnTypeAllowed(context, node_id, return_specifier.node_id, + is_definition, is_extern, virtual_modifier, + parent_scope_inst); + // Add the function declaration. SemIR::FunctionDecl function_decl = {SemIR::TypeId::None, SemIR::FunctionId::None, @@ -430,21 +496,22 @@ static auto BuildFunctionDecl(Context& context, // Build the function entity. This will be merged into an existing function if // there is one, or otherwise added to the function store. - auto function_info = - SemIR::Function{name_context.MakeEntityWithParamsBase( - name, decl_id, is_extern, introducer.extern_library), - { - .call_param_patterns_id = name.call_param_patterns_id, - .call_params_id = name.call_params_id, - .call_param_ranges = name.param_ranges, - .return_type_inst_id = return_type_inst_id, - .return_form_inst_id = return_form_inst_id, - .return_pattern_id = return_pattern_id, - .virtual_modifier = virtual_modifier, - .evaluation_mode = evaluation_mode, - .interface_modifier = interface_modifier, - .self_param_id = self_param_id, - }}; + auto function_info = SemIR::Function{ + name_context.MakeEntityWithParamsBase(name, decl_id, is_extern, + introducer.extern_library), + { + .call_param_patterns_id = name.call_param_patterns_id, + .call_params_id = name.call_params_id, + .call_param_ranges = name.param_ranges, + .return_type_inst_id = return_specifier.form.type_component_inst_id, + .return_form_inst_id = return_specifier.form.form_inst_id, + .return_pattern_id = return_specifier.pattern_id, + .virtual_modifier = virtual_modifier, + .evaluation_mode = evaluation_mode, + .interface_modifier = interface_modifier, + .has_deduced_return_type = return_specifier.is_deduced, + .self_param_id = self_param_id, + }}; if (is_definition) { function_info.definition_id = decl_id; } @@ -480,6 +547,13 @@ static auto BuildFunctionDecl(Context& context, // Write the function ID into the FunctionDecl. ReplaceInstBeforeConstantUse(context, decl_id, function_decl); + // If we diagnosed the deduced return type, recover by giving the function an + // invalid return type. + if (is_invalid_deduced_return) { + SetDeducedReturnType(context, function_decl.function_id, + return_specifier.node_id, SemIR::ErrorInst::TypeId); + } + // Diagnose 'definition of `abstract` function' using the canonical Function's // modifiers. if (is_definition && @@ -495,8 +569,12 @@ static auto BuildFunctionDecl(Context& context, MaybeAddToNameLookup(context, name_context, introducer.modifier_set, name_context.parent_scope_id, decl_id); - ValidateForEntryPoint(context, node_id, function_decl.function_id, - function_info); + // If the return type is deduced, we validate the entry point at the end of + // the definition, once the return type is known. + if (!return_specifier.is_deduced) { + ValidateForEntryPoint(context, node_id, function_decl.function_id, + function_info); + } if (!is_definition && context.sem_ir().is_impl() && !is_extern) { context.definitions_required_by_decl().push_back(decl_id); @@ -621,11 +699,19 @@ auto HandleParseNode(Context& context, Parse::FunctionDefinitionStartId node_id) auto HandleParseNode(Context& context, Parse::FunctionDefinitionId node_id) -> bool { - SemIR::FunctionId function_id = - context.node_stack().Pop(); + auto [start_node_id, function_id] = + context.node_stack() + .PopWithNodeId(); CheckFunctionReturnOnFinish(context, node_id, function_id); FinishFunctionDefinition(context, function_id); + + // Now that the return type has been deduced, we can validate the entry point. + const auto& function = context.functions().Get(function_id); + if (function.has_deduced_return_type) { + ValidateForEntryPoint(context, start_node_id, function_id, function); + } + context.decl_name_stack().PopScope(/*check_unused=*/true); return true; diff --git a/toolchain/check/handle_lambda.cpp b/toolchain/check/handle_lambda.cpp index b4dd6abf7a3d..a5ef85290449 100644 --- a/toolchain/check/handle_lambda.cpp +++ b/toolchain/check/handle_lambda.cpp @@ -27,18 +27,7 @@ auto HandleParseNode(Context& context, Parse::LambdaIntroducerId node_id) auto HandleParseNode(Context& context, Parse::LambdaDefinitionStartId node_id) -> bool { - auto return_pattern_id = SemIR::InstId::None; - auto return_type_inst_id = SemIR::TypeInstId::None; - auto return_form_inst_id = SemIR::InstId::None; - if (auto [return_node, maybe_return_pattern_id] = - context.node_stack() - .PopWithNodeIdIf(); - maybe_return_pattern_id) { - return_pattern_id = *maybe_return_pattern_id; - auto return_form = context.PopReturnForm(); - return_type_inst_id = return_form.type_component_inst_id; - return_form_inst_id = return_form.form_inst_id; - } + auto return_specifier = PopReturnSpecifier(context); Parse::NodeId first_param_node_id = Parse::NoneNodeId(); Parse::NodeId last_param_node_id = Parse::NoneNodeId(); @@ -73,12 +62,12 @@ auto HandleParseNode(Context& context, Parse::LambdaDefinitionStartId node_id) auto match_results = CalleePatternMatch(context, *implicit_param_patterns_id, - *param_patterns_id, return_pattern_id); + *param_patterns_id, return_specifier.pattern_id); context.full_pattern_stack().PopFullPattern(); auto pattern_block_id = context.pattern_block_stack().Pop(); if (!param_patterns_id->has_value() && !implicit_param_patterns_id->has_value() && - !return_pattern_id.has_value()) { + !return_specifier.has_return_specifier()) { pattern_block_id = SemIR::InstBlockId::None; } auto decl_block_id = context.inst_block_stack().Pop(); @@ -119,9 +108,11 @@ auto HandleParseNode(Context& context, Parse::LambdaDefinitionStartId node_id) .call_param_patterns_id = match_results.call_param_patterns_id, .call_params_id = match_results.call_params_id, .call_param_ranges = match_results.param_ranges, - .return_type_inst_id = return_type_inst_id, - .return_form_inst_id = return_form_inst_id, - .return_pattern_id = return_pattern_id, + .return_type_inst_id = + return_specifier.form.type_component_inst_id, + .return_form_inst_id = return_specifier.form.form_inst_id, + .return_pattern_id = return_specifier.pattern_id, + .has_deduced_return_type = return_specifier.is_deduced, }}); context.inst_block_stack().AddInstId(decl_id); diff --git a/toolchain/check/handle_literal.cpp b/toolchain/check/handle_literal.cpp index ca1110cc865b..add26d412ded 100644 --- a/toolchain/check/handle_literal.cpp +++ b/toolchain/check/handle_literal.cpp @@ -148,6 +148,19 @@ auto HandleParseNode(Context& context, Parse::TypeTypeLiteralId node_id) auto HandleParseNode(Context& context, Parse::AutoTypeLiteralId node_id) -> bool { + // `auto` is currently only supported as the entire return type of a + // function, as in `fn F() -> auto`. In that case, `auto` is the only child of + // a `ReturnType` node, so is immediately followed by it in postorder. There + // is always a following node, because the tree ends with `FileEnd`. + // + // `HandleReturnSpecifier` recognizes the `auto` by its parse node, so the + // value we push here is just a placeholder. + Parse::NodeId next_node_id(node_id.index + 1); + if (context.parse_tree().node_kind(next_node_id) == + Parse::NodeKind::ReturnType) { + context.node_stack().Push(node_id, SemIR::AutoType::TypeInstId); + return true; + } return context.TODO(node_id, "HandleAutoTypeLiteral"); } diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index 845693533c62..783c71232c7d 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -2765,6 +2765,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver, return_form_const_id); } new_function.return_pattern_id = AddLoadedImportRef(resolver, return_pattern); + new_function.has_deduced_return_type = + import_function.has_deduced_return_type; if (import_function.definition_id.has_value()) { new_function.definition_id = new_function.first_owning_decl_id; } diff --git a/toolchain/check/merge.cpp b/toolchain/check/merge.cpp index 0eb14319999b..93e73c6b95c7 100644 --- a/toolchain/check/merge.cpp +++ b/toolchain/check/merge.cpp @@ -829,6 +829,20 @@ auto TryMergeRedecl(Context& context, return false; } } else if constexpr (IsFunction) { + // A function with a deduced return type can only be declared once, by its + // definition. Check this before comparing return types, because the return + // type of the previous declaration might not be deduced yet. + if (entity_info.new_entity.has_deduced_return_type || + prev_entity.has_deduced_return_type) { + CARBON_DIAGNOSTIC(DeducedReturnTypeRedecl, Error, + "redeclaration of function with deduced return type"); + context.emitter() + .Build(entity_info.new_entity.latest_decl_id(), + DeducedReturnTypeRedecl) + .Note(prev_entity.latest_decl_id(), RedeclPrevDecl) + .Emit(); + return false; + } if (!CheckFunctionTypeMatches(context, entity_info.new_entity, prev_entity)) { // Mismatch is diagnosed already if found. diff --git a/toolchain/check/name_component.cpp b/toolchain/check/name_component.cpp index e9f1f795583a..04cbc30c189b 100644 --- a/toolchain/check/name_component.cpp +++ b/toolchain/check/name_component.cpp @@ -9,8 +9,9 @@ namespace Carbon::Check { -auto PopNameComponent(Context& context, SemIR::InstId return_pattern_id) - -> NameComponent { +auto PopNameComponent(Context& context, SemIR::InstId return_pattern_id, + bool has_return_specifier) -> NameComponent { + CARBON_CHECK(has_return_specifier || !return_pattern_id.has_value()); Parse::NodeId first_param_node_id = Parse::NoneNodeId(); Parse::NodeId last_param_node_id = Parse::NoneNodeId(); @@ -49,8 +50,7 @@ auto PopNameComponent(Context& context, SemIR::InstId return_pattern_id) auto param_ranges = SemIR::Function::CallParamIndexRanges::Empty; auto pattern_block_id = SemIR::InstBlockId::None; if (param_patterns_id->has_value() || - implicit_param_patterns_id->has_value() || - return_pattern_id.has_value()) { + implicit_param_patterns_id->has_value() || has_return_specifier) { auto results = CalleePatternMatch(context, *implicit_param_patterns_id, *param_patterns_id, return_pattern_id); call_param_patterns_id = results.call_param_patterns_id; diff --git a/toolchain/check/name_component.h b/toolchain/check/name_component.h index c822e957ffdb..e333dfda7d5f 100644 --- a/toolchain/check/name_component.h +++ b/toolchain/check/name_component.h @@ -47,10 +47,13 @@ struct NameComponent { }; // Pops a name component from the node stack (and pattern block stack, if it has -// parameters). +// parameters or a return specifier). `return_pattern_id` is the pattern +// declared by the return specifier, if any. `has_return_specifier` indicates +// whether there is a return specifier; this can be true even if there is no +// return pattern, if the return type is deduced. auto PopNameComponent(Context& context, - SemIR::InstId return_pattern_id = SemIR::InstId::None) - -> NameComponent; + SemIR::InstId return_pattern_id = SemIR::InstId::None, + bool has_return_specifier = false) -> NameComponent; // Equivalent to PopNameComponent, but also diagnoses if the name component has // parameters. Sets `*diagnosed_params` to true when diagnosing parameters if diff --git a/toolchain/check/node_stack.h b/toolchain/check/node_stack.h index 0888c51426ec..619942bb7e2f 100644 --- a/toolchain/check/node_stack.h +++ b/toolchain/check/node_stack.h @@ -404,7 +404,7 @@ class NodeStack { Parse::NodeCategory::Statement | Parse::NodeCategory::Modifier, Id::Kind::None); - set_id_if_category_is(Parse::NodeCategory::ReturnDecl, + set_id_if_category_is(Parse::NodeCategory::ReturnSpecifier, Id::KindFor()); return result; } diff --git a/toolchain/check/pattern_match.cpp b/toolchain/check/pattern_match.cpp index 115f4c24037d..bf567c33b2a0 100644 --- a/toolchain/check/pattern_match.cpp +++ b/toolchain/check/pattern_match.cpp @@ -1133,6 +1133,39 @@ auto CalleePatternMatch(Context& context, .param_ranges = {implicit_end, explicit_end, return_end}}; } +auto CalleeReturnPatternMatch(Context& context, + const CalleePatternMatchResults& param_results, + SemIR::InstId return_pattern_id) + -> CalleePatternMatchResults { + CARBON_CHECK(param_results.param_ranges.return_size() == 0); + auto call_param_patterns = + context.inst_blocks().GetOrEmpty(param_results.call_param_patterns_id); + auto call_params = + context.inst_blocks().GetOrEmpty(param_results.call_params_id); + CARBON_CHECK(call_params.size() == call_param_patterns.size()); + auto explicit_end = param_results.param_ranges.explicit_end(); + CARBON_CHECK(static_cast(explicit_end.index) == call_params.size()); + + CalleeState state = { + .index = IndexSource(explicit_end), + .call_params = llvm::SmallVector(call_params.begin(), + call_params.end()), + .call_param_patterns = llvm::SmallVector( + call_param_patterns.begin(), call_param_patterns.end())}; + MatchContext match(context); + match.Match(&state, {.pattern_id = return_pattern_id, + .work = MatchContext::PreWork{.scrutinee_id = + SemIR::InstId::None}}); + auto return_end = SemIR::CallParamIndex(state.call_params.size()); + CARBON_CHECK(state.call_params.size() == state.call_param_patterns.size()); + + return {.call_param_patterns_id = + context.inst_blocks().Add(state.call_param_patterns), + .call_params_id = context.inst_blocks().Add(state.call_params), + .param_ranges = {param_results.param_ranges.implicit_end(), + explicit_end, return_end}}; +} + auto ThunkPatternMatch(Context& context, llvm::ArrayRef param_pattern_ids, llvm::ArrayRef outer_call_args) diff --git a/toolchain/check/pattern_match.h b/toolchain/check/pattern_match.h index 393f67fea8af..a6b6feb4e3ef 100644 --- a/toolchain/check/pattern_match.h +++ b/toolchain/check/pattern_match.h @@ -46,6 +46,19 @@ auto CalleePatternMatch(Context& context, SemIR::InstId return_pattern_id) -> CalleePatternMatchResults; +// Emits the callee pattern-match IR for `return_pattern_id`, for an entity +// whose parameters were already matched by `CalleePatternMatch` without a +// return pattern, producing `param_results`. This is used when the return +// pattern is only formed after the parameters, as for a deduced return type. +// Returns `param_results` extended with the return's `Call` parameters. +// +// As with `CalleePatternMatch`, the pattern block containing the return pattern +// should be on top of `context.pattern_block_stack()`. +auto CalleeReturnPatternMatch(Context& context, + const CalleePatternMatchResults& param_results, + SemIR::InstId return_pattern_id) + -> CalleePatternMatchResults; + // Return type for ThunkPatternMatch. struct ThunkPatternMatchResults { // The syntactic argument list. If there is a self parameter, the first diff --git a/toolchain/check/return.cpp b/toolchain/check/return.cpp index 339da22835ef..2010166eadca 100644 --- a/toolchain/check/return.cpp +++ b/toolchain/check/return.cpp @@ -8,20 +8,25 @@ #include "toolchain/check/context.h" #include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" +#include "toolchain/check/function.h" #include "toolchain/check/inst.h" #include "toolchain/sem_ir/expr_info.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { -// Gets the function that lexically encloses the current location. -auto GetCurrentFunctionForReturn(Context& context) -> SemIR::Function& { +// Gets the ID of the function that lexically encloses the current location. +static auto GetCurrentFunctionIdForReturn(Context& context) + -> SemIR::FunctionId { CARBON_CHECK(context.scope_stack().IsInFunctionScope(), "Handling return but not in a function"); auto decl_id = context.scope_stack().GetReturnScopeDeclId(); - auto function_id = - context.insts().GetAs(decl_id).function_id; - return context.functions().Get(function_id); + return context.insts().GetAs(decl_id).function_id; +} + +// Gets the function that lexically encloses the current location. +auto GetCurrentFunctionForReturn(Context& context) -> SemIR::Function& { + return context.functions().Get(GetCurrentFunctionIdForReturn(context)); } auto GetReturnedVarParam(Context& context, const SemIR::Function& function) @@ -68,6 +73,15 @@ static auto NoteReturnType(DiagnosticBuilder& diag, function.return_type_inst_id); } +// Produces a note that the return type of the given function is deduced, for a +// function whose return type has not been deduced yet. +static auto NoteReturnTypeIsDeduced(DiagnosticBuilder& diag, + const SemIR::Function& function) { + CARBON_DIAGNOSTIC(ReturnTypeIsDeducedNote, Note, + "return type of function is deduced"); + diag.Note(function.latest_decl_id(), ReturnTypeIsDeducedNote); +} + // Produces a note pointing at the currently in scope `returned var`. static auto NoteReturnedVar(DiagnosticBuilder& diag, SemIR::InstId returned_var_id) { @@ -75,10 +89,187 @@ static auto NoteReturnedVar(DiagnosticBuilder& diag, diag.Note(returned_var_id, ReturnedVarHere); } +namespace { +// The result of converting the operand of a `return` statement to the return +// form of the function. +struct ConvertedReturnExpr { + // The converted expression. + SemIR::InstId expr_id; + // The return slot that the converted expression initializes in place, if + // any. + SemIR::InstId dest_id = SemIR::InstId::None; +}; +} // namespace + +// Converts the operand of a `return` statement to the return form of +// `function`, which is required to have been declared or deduced already. +static auto ConvertReturnExpr(Context& context, SemIR::LocId loc_id, + const SemIR::Function& function, + SemIR::InstId expr_id) -> ConvertedReturnExpr { + auto return_type_id = + context.types().GetTypeIdForTypeInstId(function.return_type_inst_id); + auto return_form_id = function.GetDeclaredReturnForm(context.sem_ir()); + auto return_form = context.insts().Get(return_form_id); + CARBON_KIND_SWITCH(return_form) { + case CARBON_KIND(SemIR::InitForm _): { + if (!SemIR::InitRepr::ForType(context.sem_ir(), return_type_id) + .is_valid() || + return_type_id == SemIR::ErrorInst::TypeId) { + // We already diagnosed that the return type is invalid. + // Don't try to convert to it. + return {.expr_id = SemIR::ErrorInst::InstId}; + } + if (function.call_param_ranges.return_size() == 0) { + return {.expr_id = expr_id}; + } + CARBON_CHECK(function.call_param_ranges.return_size() == 1); + auto call_params = context.inst_blocks().Get(function.call_params_id); + auto out_param_id = + call_params[function.call_param_ranges.return_begin().index]; + CARBON_CHECK(out_param_id.has_value()); + expr_id = InitializeExisting(context, loc_id, out_param_id, expr_id, + /*for_return=*/true); + if (!SemIR::InitRepr::ForType(context.sem_ir(), return_type_id) + .MightBeInPlace()) { + out_param_id = SemIR::InstId::None; + } + return {.expr_id = expr_id, .dest_id = out_param_id}; + } + case CARBON_KIND(SemIR::RefForm ref_form): { + return {.expr_id = + Convert(context, loc_id, expr_id, + ConversionTarget{ + .kind = ConversionTarget::DurableRef, + .type_id = context.types().GetTypeIdForTypeInstId( + ref_form.type_component_inst_id)})}; + } + case CARBON_KIND(SemIR::ValueForm value_form): { + return {.expr_id = + Convert(context, loc_id, expr_id, + ConversionTarget{ + .kind = ConversionTarget::Value, + .type_id = context.types().GetTypeIdForTypeInstId( + value_form.type_component_inst_id)})}; + } + case CARBON_KIND(SemIR::ErrorInst _): { + return {.expr_id = SemIR::ErrorInst::InstId}; + } + case CARBON_KIND(SemIR::SymbolicBinding _): { + auto expr_form_info = SemIR::GetFormInfo(context.sem_ir(), expr_id); + if (expr_form_info.kind != SemIR::FormInfo::Dependent) { + context.TODO(loc_id, + "support nontrivial conversions between symbolic forms"); + return {.expr_id = SemIR::ErrorInst::InstId}; + } + auto expr_form_const_id = SemIR::GetConstantValueInSpecific( + context.sem_ir(), SemIR::SpecificId::None, + expr_form_info.form_inst_id); + auto declared_form_const_id = SemIR::GetConstantValueInSpecific( + context.sem_ir(), SemIR::SpecificId::None, return_form_id); + if (expr_form_const_id != declared_form_const_id) { + context.TODO(loc_id, + "support nontrivial conversions between symbolic forms"); + return {.expr_id = SemIR::ErrorInst::InstId}; + } + // expr_id's form is identical to the expected form, so we can use it + // directly. + return {.expr_id = expr_id}; + } + default: + CARBON_FATAL("Unexpected inst kind: {0}", return_form); + } +} + +// Builds a `return` statement in a function whose return type has not been +// deduced yet. Immediately builds control flow and cleanups, but defers the +// conversion of the returned expression to the return type until the return +// type is known; see `CompletePendingReturns`. +static auto BuildPendingReturn(Context& context, SemIR::LocId loc_id, + SemIR::InstId expr_id) -> void { + // The conversion will be spliced in here. Until then, this placeholder is an + // identity conversion. Because it precedes the cleanups, the return value is + // initialized before any local variables are destroyed. + auto splice_id = AddPlaceholderInst( + context, loc_id, + SemIR::SpliceBlock{.type_id = context.insts().Get(expr_id).type_id(), + .block_id = SemIR::InstBlockId::Empty, + .result_id = expr_id}); + auto return_id = AddReturnInstWithCleanups( + context, loc_id, + SemIR::ReturnExpr{.expr_id = splice_id, .dest_id = SemIR::InstId::None}); + context.scope_stack().AddPendingReturn({.expr_id = expr_id, + .splice_id = splice_id, + .return_inst_id = return_id}); +} + +// Completes the `return` statements built by `BuildPendingReturn` in the +// current function, once its return type has been deduced. +static auto CompletePendingReturns(Context& context, + SemIR::FunctionId function_id) -> void { + auto pending_returns = context.scope_stack().TakePendingReturns(); + if (pending_returns.empty()) { + return; + } + + const auto& function = context.functions().Get(function_id); + + // Any problems converting to the return type are probably due to the choice + // of return type, so point out where it came from. + Diagnostics::AnnotationScope annotate_diagnostics( + &context.emitter(), [&](DiagnosticBuilder& builder) { + // TODO: Track the instruction the return type was deduced for and use + // TypeOfInstId to preserve sugar. + CARBON_DIAGNOSTIC(ReturnTypeDeducedAs, Note, + "return type deduced as {0}", InstIdAsType); + builder.Note(function.return_type_inst_id, ReturnTypeDeducedAs, + function.return_type_inst_id); + }); + + for (const auto& pending_return : pending_returns) { + auto loc_id = SemIR::LocId(pending_return.return_inst_id); + + // Convert in a separate block that we splice in place of the placeholder, + // with its own cleanup scope for any temporaries. + context.inst_block_stack().Push(); + context.scope_stack().PushForSameRegion( + ScopeStack::CleanupScopeKind::Owned); + auto converted = + ConvertReturnExpr(context, loc_id, function, pending_return.expr_id); + AddAndDiscardScopeCleanups(context); + context.scope_stack().Pop(); + auto block_id = context.inst_block_stack().Pop(); + + ReplaceInstBeforeConstantUse( + context, pending_return.splice_id, + SemIR::SpliceBlock{ + .type_id = context.insts().Get(converted.expr_id).type_id(), + .block_id = block_id, + .result_id = converted.expr_id}); + if (converted.dest_id.has_value()) { + // The `return` statement has no type or constant value, and nothing + // refers to it, so it's safe to replace it. + auto return_inst = context.insts().GetAs( + pending_return.return_inst_id); + return_inst.dest_id = converted.dest_id; + ReplaceInstBeforeConstantUse(context, pending_return.return_inst_id, + return_inst); + } + } +} + auto RegisterReturnedVar(Context& context, Parse::NodeId returned_node, Parse::NodeId type_node, SemIR::TypeId type_id, SemIR::InstId bind_id, SemIR::NameId name_id) -> void { - auto& function = GetCurrentFunctionForReturn(context); + auto function_id = GetCurrentFunctionIdForReturn(context); + auto& function = context.functions().Get(function_id); + if (function.has_pending_deduced_return_type()) { + // In a function with a deduced return type, the first `returned var` + // determines the return type, and any earlier `return`s are converted to + // it. + SetDeducedReturnType(context, function_id, type_node, type_id); + CompletePendingReturns(context, function_id); + } + auto return_type_id = function.GetDeclaredReturnType(context.sem_ir()); // A `returned var` requires an explicit return type. @@ -129,11 +320,20 @@ auto RegisterReturnedVar(Context& context, Parse::NodeId returned_node, auto BuildReturnWithNoExpr(Context& context, SemIR::LocId loc_id) -> void { const auto& function = GetCurrentFunctionForReturn(context); - auto return_type_id = function.GetDeclaredReturnType(context.sem_ir()); + CARBON_DIAGNOSTIC(ReturnStatementMissingExpr, Error, "missing return value"); - if (return_type_id.has_value()) { - CARBON_DIAGNOSTIC(ReturnStatementMissingExpr, Error, - "missing return value"); + // `-> auto` requires a returned value, just like an explicit return type. + if (function.has_pending_deduced_return_type()) { + auto diag = context.emitter().Build(loc_id, ReturnStatementMissingExpr); + NoteReturnTypeIsDeduced(diag, function); + diag.Emit(); + // Treat this as returning an erroneous value, which doesn't participate in + // return type deduction. + BuildPendingReturn(context, loc_id, SemIR::ErrorInst::InstId); + return; + } + + if (function.GetDeclaredReturnType(context.sem_ir()).has_value()) { auto diag = context.emitter().Build(loc_id, ReturnStatementMissingExpr); NoteReturnType(diag, function); diag.Emit(); @@ -146,13 +346,20 @@ auto BuildReturnWithExpr(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id) -> void { const auto& function = GetCurrentFunctionForReturn(context); auto returned_var_id = GetCurrentReturnedVar(context); - auto out_param_id = SemIR::InstId::None; + + if (function.has_pending_deduced_return_type()) { + // Declaring a `returned var` would have deduced the return type. + CARBON_CHECK(!returned_var_id.has_value()); + BuildPendingReturn(context, loc_id, expr_id); + return; + } auto return_type_id = SemIR::TypeId::None; if (function.return_type_inst_id.has_value()) { return_type_id = context.types().GetTypeIdForTypeInstId(function.return_type_inst_id); } + auto converted = ConvertedReturnExpr{.expr_id = SemIR::ErrorInst::InstId}; if (!return_type_id.has_value()) { CARBON_DIAGNOSTIC( ReturnStatementDisallowExpr, Error, @@ -160,7 +367,6 @@ auto BuildReturnWithExpr(Context& context, SemIR::LocId loc_id, auto diag = context.emitter().Build(loc_id, ReturnStatementDisallowExpr); NoteNoReturnTypeProvided(diag, function); diag.Emit(); - expr_id = SemIR::ErrorInst::InstId; } else if (returned_var_id.has_value()) { CARBON_DIAGNOSTIC( ReturnExprWithReturnedVar, Error, @@ -168,86 +374,12 @@ auto BuildReturnWithExpr(Context& context, SemIR::LocId loc_id, auto diag = context.emitter().Build(loc_id, ReturnExprWithReturnedVar); NoteReturnedVar(diag, returned_var_id); diag.Emit(); - expr_id = SemIR::ErrorInst::InstId; } else { - auto return_form_id = function.GetDeclaredReturnForm(context.sem_ir()); - auto return_form = context.insts().Get(return_form_id); - CARBON_KIND_SWITCH(return_form) { - case CARBON_KIND(SemIR::InitForm _): { - if (!SemIR::InitRepr::ForType(context.sem_ir(), return_type_id) - .is_valid() || - return_type_id == SemIR::ErrorInst::TypeId) { - // We already diagnosed that the return type is invalid. - // Don't try to convert to it. - expr_id = SemIR::ErrorInst::InstId; - break; - } - auto call_params = context.inst_blocks().Get(function.call_params_id); - if (function.call_param_ranges.return_size() == 0) { - out_param_id = SemIR::InstId::None; - break; - } - CARBON_CHECK(function.call_param_ranges.return_size() == 1); - out_param_id = - call_params[function.call_param_ranges.return_begin().index]; - CARBON_CHECK(out_param_id.has_value()); - expr_id = InitializeExisting(context, loc_id, out_param_id, expr_id, - /*for_return=*/true); - if (!SemIR::InitRepr::ForType(context.sem_ir(), return_type_id) - .MightBeInPlace()) { - out_param_id = SemIR::InstId::None; - } - break; - } - case CARBON_KIND(SemIR::RefForm ref_form): { - expr_id = Convert( - context, loc_id, expr_id, - ConversionTarget{.kind = ConversionTarget::DurableRef, - .type_id = context.types().GetTypeIdForTypeInstId( - ref_form.type_component_inst_id)}); - break; - } - case CARBON_KIND(SemIR::ValueForm value_form): { - expr_id = Convert( - context, loc_id, expr_id, - ConversionTarget{.kind = ConversionTarget::Value, - .type_id = context.types().GetTypeIdForTypeInstId( - value_form.type_component_inst_id)}); - break; - } - case CARBON_KIND(SemIR::ErrorInst _): { - expr_id = SemIR::ErrorInst::InstId; - break; - } - case CARBON_KIND(SemIR::SymbolicBinding _): { - auto expr_form_info = SemIR::GetFormInfo(context.sem_ir(), expr_id); - if (expr_form_info.kind != SemIR::FormInfo::Dependent) { - context.TODO(loc_id, - "support nontrivial conversions between symbolic forms"); - expr_id = SemIR::ErrorInst::InstId; - break; - } - auto expr_form_const_id = SemIR::GetConstantValueInSpecific( - context.sem_ir(), SemIR::SpecificId::None, - expr_form_info.form_inst_id); - auto declared_form_const_id = SemIR::GetConstantValueInSpecific( - context.sem_ir(), SemIR::SpecificId::None, return_form_id); - if (expr_form_const_id != declared_form_const_id) { - context.TODO(loc_id, - "support nontrivial conversions between symbolic forms"); - expr_id = SemIR::ErrorInst::InstId; - break; - } - // expr_id's form is identical to the expected form, so we can use it - // directly. - break; - } - default: - CARBON_FATAL("Unexpected inst kind: {0}", return_form); - } + converted = ConvertReturnExpr(context, loc_id, function, expr_id); } - AddReturnInstWithCleanups(context, loc_id, - {.expr_id = expr_id, .dest_id = out_param_id}); + AddReturnInstWithCleanups( + context, loc_id, + {.expr_id = converted.expr_id, .dest_id = converted.dest_id}); } auto BuildReturnVar(Context& context, Parse::ReturnStatementId node_id) @@ -262,6 +394,13 @@ auto BuildReturnVar(Context& context, Parse::ReturnStatementId node_id) returned_var_id = SemIR::ErrorInst::InstId; } + if (function.has_pending_deduced_return_type()) { + // Declaring a `returned var` would have deduced the return type, so we + // diagnosed above, and `returned_var_id` is `ErrorInst::InstId`. + BuildPendingReturn(context, node_id, returned_var_id); + return; + } + auto return_param_id = GetReturnedVarParam(context, function); // Convert to a value expression in case the return logic needs a value, and @@ -273,4 +412,58 @@ auto BuildReturnVar(Context& context, Parse::ReturnStatementId node_id) {.expr_id = returned_var_id, .dest_id = return_param_id}); } +auto DeduceReturnTypeAtEndOfBody(Context& context, + SemIR::FunctionId function_id) -> bool { + const auto& function = context.functions().Get(function_id); + CARBON_CHECK(function.has_pending_deduced_return_type()); + auto pending_returns = context.scope_stack().PeekPendingReturns(); + auto decl_loc_id = SemIR::LocId(function.latest_decl_id()); + + if (pending_returns.empty()) { + CARBON_DIAGNOSTIC(DeducedReturnTypeWithoutReturn, Error, + "no `return` in function with deduced return type"); + context.emitter().Emit(decl_loc_id, DeducedReturnTypeWithoutReturn); + SetDeducedReturnType(context, function_id, decl_loc_id, + SemIR::ErrorInst::TypeId); + return false; + } + + // The return type is the type of the returned expressions. Erroneous returned + // expressions are ignored, to avoid follow-on diagnostics. + // TODO: Use the common type of the returned expressions. + auto first_expr_id = SemIR::InstId::None; + auto return_type_id = SemIR::ErrorInst::TypeId; + for (const auto& pending : pending_returns) { + auto type_id = context.insts().Get(pending.expr_id).type_id(); + if (type_id == SemIR::ErrorInst::TypeId) { + continue; + } + if (!first_expr_id.has_value()) { + first_expr_id = pending.expr_id; + return_type_id = type_id; + } else if (type_id != return_type_id) { + CARBON_DIAGNOSTIC( + DeducedReturnTypeMismatch, Error, + "deduced return type {0} does not match return type deduced earlier", + TypeOfInstId); + CARBON_DIAGNOSTIC(DeducedReturnTypeMismatchPrevious, Note, + "return type previously deduced as {0} here", + TypeOfInstId); + context.emitter() + .Build(pending.expr_id, DeducedReturnTypeMismatch, pending.expr_id) + .Note(first_expr_id, DeducedReturnTypeMismatchPrevious, first_expr_id) + .Emit(); + return_type_id = SemIR::ErrorInst::TypeId; + break; + } + } + + SetDeducedReturnType( + context, function_id, + first_expr_id.has_value() ? SemIR::LocId(first_expr_id) : decl_loc_id, + return_type_id); + CompletePendingReturns(context, function_id); + return true; +} + } // namespace Carbon::Check diff --git a/toolchain/check/return.h b/toolchain/check/return.h index fc82cf00ba45..dd0d077c4d85 100644 --- a/toolchain/check/return.h +++ b/toolchain/check/return.h @@ -36,6 +36,14 @@ auto BuildReturnWithExpr(Context& context, SemIR::LocId loc_id, // Checks and builds SemIR for a `return var;` statement. auto BuildReturnVar(Context& context, Parse::ReturnStatementId node_id) -> void; +// Deduces the return type of the current function, which must have an +// undeduced return type, from the `return` statements in its body, and +// completes the initialization of their return values. Called at the end of the +// function body. Returns false if there are no `return` statements to deduce +// from, after diagnosing that. +auto DeduceReturnTypeAtEndOfBody(Context& context, + SemIR::FunctionId function_id) -> bool; + } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_RETURN_H_ diff --git a/toolchain/check/scope_stack.cpp b/toolchain/check/scope_stack.cpp index a7d392bcb42d..23bbe80d746d 100644 --- a/toolchain/check/scope_stack.cpp +++ b/toolchain/check/scope_stack.cpp @@ -26,6 +26,8 @@ auto ScopeStack::sem_ir() const -> const SemIR::File& { auto ScopeStack::VerifyOnFinish() const -> void { CARBON_CHECK(return_scope_stack_.empty(), "{0}", return_scope_stack_.size()); + CARBON_CHECK(pending_return_stack_.empty(), "{0}", + pending_return_stack_.all_values_size()); CARBON_CHECK(break_continue_stack_.empty(), "{0}", break_continue_stack_.size()); CARBON_CHECK(scope_stack_.empty(), "{0}", scope_stack_.size()); @@ -136,6 +138,7 @@ auto ScopeStack::PushForFunctionBody(SemIR::InstId scope_inst_id) -> void { return_scope_stack_.push_back( {.decl_id = scope_inst_id, .cleanup_scope_depth = cleanup_scope_depth()}); + pending_return_stack_.PushArray(); } auto ScopeStack::Pop(bool check_unused) -> void { @@ -172,6 +175,9 @@ auto ScopeStack::Pop(bool check_unused) -> void { if (return_scope_stack_.back().decl_id == scope.scope_inst_id) { // Leaving the function scope. + CARBON_CHECK(pending_return_stack_.PeekArray().empty(), + "Leaving function with pending `return`s"); + pending_return_stack_.PopArray(); return_scope_stack_.pop_back(); } else { if (return_scope_stack_.back().nested_scope_index == scope.index) { diff --git a/toolchain/check/scope_stack.h b/toolchain/check/scope_stack.h index f80145136d9d..95ca74016983 100644 --- a/toolchain/check/scope_stack.h +++ b/toolchain/check/scope_stack.h @@ -191,6 +191,44 @@ class ScopeStack { return return_scope_stack_.back().decl_id; } + // A `return` statement in a function whose return type is being deduced. + // Initialization of the return value is deferred until the return type is + // known. + struct PendingReturn { + // The returned expression, before conversion to the return type. + SemIR::InstId expr_id; + // A placeholder `SpliceBlock` producing `expr_id`, to be replaced by the + // conversion of `expr_id` to the return type. + SemIR::InstId splice_id; + // The `ReturnExpr` instruction. Its destination is set once the return + // type is known. + SemIR::InstId return_inst_id; + }; + + // Adds a `return` statement in the current function whose initialization is + // waiting for the return type to be deduced. + auto AddPendingReturn(PendingReturn pending_return) -> void { + CARBON_CHECK(IsInFunctionScope(), "Handling return but not in a function"); + pending_return_stack_.AppendToTop(pending_return); + } + + // Returns the `return` statements in the current function whose + // initialization is waiting for the return type to be deduced. + auto PeekPendingReturns() const -> llvm::ArrayRef { + CARBON_CHECK(IsInFunctionScope(), "Handling return but not in a function"); + return pending_return_stack_.PeekArray(); + } + + // Removes and returns the `return` statements in the current function whose + // initialization is waiting for the return type to be deduced. + auto TakePendingReturns() -> llvm::SmallVector { + auto pending_returns = llvm::to_vector(PeekPendingReturns()); + // Leave an empty array for the current function. + pending_return_stack_.PopArray(); + pending_return_stack_.PushArray(); + return pending_returns; + } + // Looks up the name `name_id` in the current scope and enclosing scopes, but // do not look past `scope_index`. Returns the existing lookup result, if any. // If `use_loc_id` is specified, the name is marked as used at that location. @@ -432,6 +470,10 @@ class ScopeStack { // A stack of scopes from which we can `return`. llvm::SmallVector return_scope_stack_; + // For each entry in `return_scope_stack_`, the `return` statements whose + // initialization is waiting for the function's return type to be deduced. + ArrayStack pending_return_stack_; + // A stack of `break` and `continue` targets. llvm::SmallVector break_continue_stack_; diff --git a/toolchain/check/testdata/function/deduced_return/basic.carbon b/toolchain/check/testdata/function/deduced_return/basic.carbon new file mode 100644 index 000000000000..4e24521eb59e --- /dev/null +++ b/toolchain/check/testdata/function/deduced_return/basic.carbon @@ -0,0 +1,434 @@ +// 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/primitives.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/deduced_return/basic.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/deduced_return/basic.carbon + +// --- single_return.carbon +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +fn F() -> auto { + return 0 as i32; +} + +fn G() -> i32 { + return F(); +} +//@dump-sem-ir-end + +// --- multiple_returns.carbon +library "[[@TEST_NAME]]"; + +fn F(b: bool) -> auto { + if (b) { + return 1 as i32; + } + return 2 as i32; +} + +fn G() -> i32 { + return F(true); +} + +// --- int_literal.carbon +library "[[@TEST_NAME]]"; + +// Literal types are not decayed. +fn F() -> auto { + return 0; +} + +fn G() -> Core.IntLiteral { + return F(); +} + +// --- in_place.carbon +library "[[@TEST_NAME]]"; + +class C { + var a: i32; + var b: i32; +} + +//@dump-sem-ir-begin +fn F() -> auto { + return {.a = 1, .b = 2} as C; +} + +fn G() -> C { + return F(); +} +//@dump-sem-ir-end + +// --- type.carbon +library "[[@TEST_NAME]]"; + +eval fn F() -> auto { + return i32; +} + +var n: F() = 0; + +// --- nested.carbon +library "[[@TEST_NAME]]"; + +fn F() -> auto { + fn G() -> auto { + return 1 as i32; + } + return G(); +} + +fn H() -> i32 { + return F(); +} + +// --- lambda.carbon +library "[[@TEST_NAME]]"; + +fn F() -> i32 { + //@dump-sem-ir-begin + alias lambda = fn (x: i32) -> auto { return x; }; + return lambda(1); + //@dump-sem-ir-end +} + +// --- lambda_no_params.carbon +library "[[@TEST_NAME]]"; + +fn F() -> i32 { + alias lambda = fn () -> auto { return 1 as i32; }; + return lambda(); +} + +// --- member.carbon +library "[[@TEST_NAME]]"; + +class C { + fn F() -> auto { return 1 as i32; } + fn G(self) -> auto { return self.n; } + // The body of `F` is checked before the body of `H`. + fn H() -> i32 { return F(); } + var n: i32; +} + +fn Use(c: C) -> i32 { + return c.G(); +} + +// --- unreachable_end.carbon +library "[[@TEST_NAME]]"; + +fn F(b: bool) -> auto { + if (b) { + return 1 as i32; + } else { + return 2 as i32; + } +} + +fn G() -> i32 { + return F(false); +} + +// CHECK:STDOUT: --- single_return.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: type: type = facet_type [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %As.type.b93: type = facet_type <@As, @As(%i32)> [concrete] +// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.type.396: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%To) [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.84a: %Core.IntLiteral.as.As.impl.Convert.type.396 = struct_value () [symbolic] +// CHECK:STDOUT: %As.impl_witness.e09: = impl_witness imports.%As.impl_witness_table.1c2, @Core.IntLiteral.as.As.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.type.2b0: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.94f: %Core.IntLiteral.as.As.impl.Convert.type.2b0 = struct_value () [concrete] +// CHECK:STDOUT: %As.facet: %As.type.b93 = facet_value Core.IntLiteral, (%As.impl_witness.e09) [concrete] +// CHECK:STDOUT: %As.WithSelf.Convert.type.2be: type = fn_type @As.WithSelf.Convert, @As.WithSelf(%i32, %As.facet) [concrete] +// CHECK:STDOUT: %.979: type = fn_type_with_self_type %As.WithSelf.Convert.type.2be, %As.facet [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.bound: = bound_method %int_0.5c6, %Core.IntLiteral.as.As.impl.Convert.94f [concrete] +// CHECK:STDOUT: %.0b3: Core.Form = init_form %i32 [concrete] +// CHECK:STDOUT: %pattern_type.d94: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: %return.param_patt.299: %pattern_type.d94 = out_param_pattern [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.As.impl.Convert.94f, @Core.IntLiteral.as.As.impl.Convert(%int_32) [concrete] +// CHECK:STDOUT: %bound_method.e88: = bound_method %int_0.5c6, %Core.IntLiteral.as.As.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_0.753: %i32 = int_value 0 [concrete] +// CHECK:STDOUT: %return.patt.6fa: %pattern_type.d94 = return_slot_pattern %return.param_patt.299, %i32 [concrete] +// CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.a1d: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.3c1: %Int.as.Copy.impl.Op.type.a1d = struct_value () [symbolic] +// CHECK:STDOUT: %Copy.impl_witness.8af: = impl_witness imports.%Copy.impl_witness_table.f44, @Int.as.Copy.impl(%int_32) [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.648: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.cae: %Int.as.Copy.impl.Op.type.648 = struct_value () [concrete] +// CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.8af) [concrete] +// CHECK:STDOUT: %Copy.WithSelf.Op.type.f76: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] +// CHECK:STDOUT: %.2b9: type = fn_type_with_self_type %Copy.WithSelf.Op.type.f76, %Copy.facet [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.bound: = bound_method %int_0.753, %Int.as.Copy.impl.Op.cae [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.cae, @Int.as.Copy.impl.Op(%int_32) [concrete] +// CHECK:STDOUT: %bound_method.149: = bound_method %int_0.753, %Int.as.Copy.impl.Op.specific_fn [concrete] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core.import_ref.9e0: @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert.type (%Core.IntLiteral.as.As.impl.Convert.type.396) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert (constants.%Core.IntLiteral.as.As.impl.Convert.84a)] +// CHECK:STDOUT: %As.impl_witness_table.1c2 = impl_witness_table (%Core.import_ref.9e0), @Core.IntLiteral.as.As.impl [concrete] +// CHECK:STDOUT: %Core.import_ref.72f: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.a1d) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.3c1)] +// CHECK:STDOUT: %Copy.impl_witness_table.f44 = impl_witness_table (%Core.import_ref.72f), @Int.as.Copy.impl [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete = constants.%return.param_patt.299] +// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32.loc5_12 [concrete = constants.%return.patt.6fa] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %i32.loc5_12: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc5_12.3: Core.Form = init_form %i32.loc5_12 [concrete = constants.%.0b3] +// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 +// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { +// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete = constants.%return.param_patt.299] +// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32 [concrete = constants.%return.patt.6fa] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc8: Core.Form = init_form %i32 [concrete = constants.%.0b3] +// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 +// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() -> out %return.param: %i32 [deduced] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] +// CHECK:STDOUT: %i32.loc5_15: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %impl.elem0.loc5_12.1: %.979 = impl_witness_access constants.%As.impl_witness.e09, element0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.94f] +// CHECK:STDOUT: %bound_method.loc5_12.1: = bound_method %int_0, %impl.elem0.loc5_12.1 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound] +// CHECK:STDOUT: %specific_fn.loc5_12.1: = specific_function %impl.elem0.loc5_12.1, @Core.IntLiteral.as.As.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc5_12.2: = bound_method %int_0, %specific_fn.loc5_12.1 [concrete = constants.%bound_method.e88] +// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.call: init %i32 = call %bound_method.loc5_12.2(%int_0) [concrete = constants.%int_0.753] +// CHECK:STDOUT: %.loc5_12.1: %i32 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_0.753] +// CHECK:STDOUT: %.loc5_12.2: %i32 = converted %int_0, %.loc5_12.1 [concrete = constants.%int_0.753] +// CHECK:STDOUT: %.loc5_18: init %i32 = splice_block %Int.as.Copy.impl.Op.call [concrete = constants.%int_0.753] { +// CHECK:STDOUT: %impl.elem0.loc5_12.2: %.2b9 = impl_witness_access constants.%Copy.impl_witness.8af, element0 [concrete = constants.%Int.as.Copy.impl.Op.cae] +// CHECK:STDOUT: %bound_method.loc5_12.3: = bound_method %.loc5_12.2, %impl.elem0.loc5_12.2 [concrete = constants.%Int.as.Copy.impl.Op.bound] +// CHECK:STDOUT: %specific_fn.loc5_12.2: = specific_function %impl.elem0.loc5_12.2, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] +// CHECK:STDOUT: %bound_method.loc5_12.4: = bound_method %.loc5_12.2, %specific_fn.loc5_12.2 [concrete = constants.%bound_method.149] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc5_12.4(%.loc5_12.2) [concrete = constants.%int_0.753] +// CHECK:STDOUT: } +// CHECK:STDOUT: return %.loc5_18 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() -> out %return.param: %i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %F.call: init %i32 = call %F.ref() +// CHECK:STDOUT: return %F.call +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- in_place.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: type: type = facet_type [concrete] +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] +// CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] +// CHECK:STDOUT: %struct_type.a.b.cfd: type = struct_type {.a: Core.IntLiteral, .b: Core.IntLiteral} [concrete] +// CHECK:STDOUT: %struct: %struct_type.a.b.cfd = struct_value (%int_1.5b8, %int_2.ecc) [concrete] +// CHECK:STDOUT: %ImplicitAs.type.6b0: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] +// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6 = struct_value () [symbolic] +// CHECK:STDOUT: %ImplicitAs.impl_witness.d73: = impl_witness imports.%ImplicitAs.impl_witness_table.f53, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766 = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.6b0 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.d73) [concrete] +// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.58f: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] +// CHECK:STDOUT: %.33a: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.58f, %ImplicitAs.facet [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.282: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] +// CHECK:STDOUT: %bound_method.3bd: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_1.68a: %i32 = int_value 1 [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.8a6: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete] +// CHECK:STDOUT: %bound_method.a55: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_2.2e0: %i32 = int_value 2 [concrete] +// CHECK:STDOUT: %C.val: %C = struct_value (%int_1.68a, %int_2.2e0) [concrete] +// CHECK:STDOUT: %.a44: Core.Form = init_form %C [concrete] +// CHECK:STDOUT: %pattern_type.98b: type = pattern_type %C [concrete] +// CHECK:STDOUT: %return.param_patt.89a: %pattern_type.98b = out_param_pattern [concrete] +// CHECK:STDOUT: %return.patt.e4a: %pattern_type.98b = return_slot_pattern %return.param_patt.89a, %C [concrete] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core.import_ref.e47: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2)] +// CHECK:STDOUT: %ImplicitAs.impl_witness_table.f53 = impl_witness_table (%Core.import_ref.e47), @Core.IntLiteral.as.ImplicitAs.impl [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %return.param_patt: %pattern_type.98b = out_param_pattern [concrete = constants.%return.param_patt.89a] +// CHECK:STDOUT: %return.patt: %pattern_type.98b = return_slot_pattern %return.param_patt, %.loc10_27.3 [concrete = constants.%return.patt.e4a] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.loc10_27.3: type = type_literal constants.%C [concrete = constants.%C] +// CHECK:STDOUT: %.loc10_27.4: Core.Form = init_form %.loc10_27.3 [concrete = constants.%.a44] +// CHECK:STDOUT: %return.param: ref %C = out_param call_param0 +// CHECK:STDOUT: %return: ref %C = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { +// CHECK:STDOUT: %return.param_patt: %pattern_type.98b = out_param_pattern [concrete = constants.%return.param_patt.89a] +// CHECK:STDOUT: %return.patt: %pattern_type.98b = return_slot_pattern %return.param_patt, %C.ref [concrete = constants.%return.patt.e4a] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %.loc13_11.2: Core.Form = init_form %C.ref [concrete = constants.%.a44] +// CHECK:STDOUT: %return.param: ref %C = out_param call_param0 +// CHECK:STDOUT: %return: ref %C = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() -> out %return.param: %C [deduced] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] +// CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] +// CHECK:STDOUT: %.loc10_25.1: %struct_type.a.b.cfd = struct_literal (%int_1, %int_2) [concrete = constants.%struct] +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %impl.elem0.loc10_25.1: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60] +// CHECK:STDOUT: %bound_method.loc10_25.1: = bound_method %int_1, %impl.elem0.loc10_25.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.282] +// CHECK:STDOUT: %specific_fn.loc10_25.1: = specific_function %impl.elem0.loc10_25.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc10_25.2: = bound_method %int_1, %specific_fn.loc10_25.1 [concrete = constants.%bound_method.3bd] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_25.1: init %i32 = call %bound_method.loc10_25.2(%int_1) [concrete = constants.%int_1.68a] +// CHECK:STDOUT: %.loc10_25.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_25.1 [concrete = constants.%int_1.68a] +// CHECK:STDOUT: %.loc10_27.1: ref %C = splice_block %return.param {} +// CHECK:STDOUT: %.loc10_25.3: ref %i32 = class_element_access %.loc10_27.1, element0 +// CHECK:STDOUT: %.loc10_25.4: init %i32 to %.loc10_25.3 = in_place_init %.loc10_25.2 [concrete = constants.%int_1.68a] +// CHECK:STDOUT: %impl.elem0.loc10_25.2: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60] +// CHECK:STDOUT: %bound_method.loc10_25.3: = bound_method %int_2, %impl.elem0.loc10_25.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.8a6] +// CHECK:STDOUT: %specific_fn.loc10_25.2: = specific_function %impl.elem0.loc10_25.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc10_25.4: = bound_method %int_2, %specific_fn.loc10_25.2 [concrete = constants.%bound_method.a55] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_25.2: init %i32 = call %bound_method.loc10_25.4(%int_2) [concrete = constants.%int_2.2e0] +// CHECK:STDOUT: %.loc10_25.5: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_25.2 [concrete = constants.%int_2.2e0] +// CHECK:STDOUT: %.loc10_25.6: ref %i32 = class_element_access %.loc10_27.1, element1 +// CHECK:STDOUT: %.loc10_25.7: init %i32 to %.loc10_25.6 = in_place_init %.loc10_25.5 [concrete = constants.%int_2.2e0] +// CHECK:STDOUT: %.loc10_25.8: init %C to %.loc10_27.1 = class_init (%.loc10_25.4, %.loc10_25.7) [concrete = constants.%C.val] +// CHECK:STDOUT: %.loc10_27.2: init %C = converted %.loc10_25.1, %.loc10_25.8 [concrete = constants.%C.val] +// CHECK:STDOUT: %.loc10_31: init %C = splice_block %.loc10_27.2 [concrete = constants.%C.val] {} +// CHECK:STDOUT: return %.loc10_31 to %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() -> out %return.param: %C { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %.loc13_11.1: ref %C = splice_block %return.param {} +// CHECK:STDOUT: %F.call: init %C to %.loc13_11.1 = call %F.ref() +// CHECK:STDOUT: return %F.call to %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- lambda.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: type: type = facet_type [concrete] +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %.0b3: Core.Form = init_form %i32 [concrete] +// CHECK:STDOUT: %pattern_type.d94: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: %return.param_patt.299: %pattern_type.d94 = out_param_pattern [concrete] +// CHECK:STDOUT: %return.patt.6fa: %pattern_type.d94 = return_slot_pattern %return.param_patt.299, %i32 [concrete] +// CHECK:STDOUT: %x.param_patt: %pattern_type.d94 = value_param_pattern [concrete] +// CHECK:STDOUT: %x.patt: %pattern_type.d94 = wrapper_binding_pattern x, %x.param_patt [concrete] +// CHECK:STDOUT: %fn.type: type = fn_type @fn [concrete] +// CHECK:STDOUT: %fn: %fn.type = struct_value () [concrete] +// CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.a1d: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.3c1: %Int.as.Copy.impl.Op.type.a1d = struct_value () [symbolic] +// CHECK:STDOUT: %Copy.impl_witness.8af: = impl_witness imports.%Copy.impl_witness_table.f44, @Int.as.Copy.impl(%int_32) [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.648: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.cae: %Int.as.Copy.impl.Op.type.648 = struct_value () [concrete] +// CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.8af) [concrete] +// CHECK:STDOUT: %Copy.WithSelf.Op.type.f76: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] +// CHECK:STDOUT: %.2b9: type = fn_type_with_self_type %Copy.WithSelf.Op.type.f76, %Copy.facet [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.cae, @Int.as.Copy.impl.Op(%int_32) [concrete] +// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] +// CHECK:STDOUT: %ImplicitAs.type.6b0: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] +// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6 = struct_value () [symbolic] +// CHECK:STDOUT: %ImplicitAs.impl_witness.d73: = impl_witness imports.%ImplicitAs.impl_witness_table.f53, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766 = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.6b0 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.d73) [concrete] +// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.58f: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] +// CHECK:STDOUT: %.33a: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.58f, %ImplicitAs.facet [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] +// CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_1.68a: %i32 = int_value 1 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core.import_ref.72f: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.a1d) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.3c1)] +// CHECK:STDOUT: %Copy.impl_witness_table.f44 = impl_witness_table (%Core.import_ref.72f), @Int.as.Copy.impl [concrete] +// CHECK:STDOUT: %Core.import_ref.e47: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2)] +// CHECK:STDOUT: %ImplicitAs.impl_witness_table.f53 = impl_witness_table (%Core.import_ref.e47), @Core.IntLiteral.as.ImplicitAs.impl [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() -> out %return.param: %i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %fn.decl: %fn.type = fn_decl @fn [concrete = constants.%fn] { +// CHECK:STDOUT: %x.param_patt: %pattern_type.d94 = value_param_pattern [concrete = constants.%x.param_patt] +// CHECK:STDOUT: %x.patt: %pattern_type.d94 = wrapper_binding_pattern x, %x.param_patt [concrete = constants.%x.patt] +// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete = constants.%return.param_patt.299] +// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32.loc5_47 [concrete = constants.%return.patt.6fa] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %x.param: %i32 = value_param call_param0 +// CHECK:STDOUT: %i32.loc5_25: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %x: %i32 = wrapper_binding x, %x.param +// CHECK:STDOUT: %i32.loc5_47: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc5_47: Core.Form = init_form %i32.loc5_47 [concrete = constants.%.0b3] +// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 +// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %lambda: %fn.type = alias_binding lambda, %fn.decl [concrete = constants.%fn] +// CHECK:STDOUT: %lambda.ref: %fn.type = name_ref lambda, %lambda [concrete = constants.%fn] +// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] +// CHECK:STDOUT: %impl.elem0: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60] +// CHECK:STDOUT: %bound_method.loc6_17.1: = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] +// CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc6_17.2: = bound_method %int_1, %specific_fn [concrete = constants.%bound_method] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc6_17.2(%int_1) [concrete = constants.%int_1.68a] +// CHECK:STDOUT: %.loc6_17.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.68a] +// CHECK:STDOUT: %.loc6_17.2: %i32 = converted %int_1, %.loc6_17.1 [concrete = constants.%int_1.68a] +// CHECK:STDOUT: %fn.call: init %i32 = call %lambda.ref(%.loc6_17.2) +// CHECK:STDOUT: return %fn.call +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @fn(%x.param: %i32) -> out %return.param: %i32 [deduced] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %x.ref: %i32 = name_ref x, %x +// CHECK:STDOUT: %.loc5_48: init %i32 = splice_block %Int.as.Copy.impl.Op.call { +// CHECK:STDOUT: %impl.elem0: %.2b9 = impl_witness_access constants.%Copy.impl_witness.8af, element0 [concrete = constants.%Int.as.Copy.impl.Op.cae] +// CHECK:STDOUT: %bound_method.loc5_47.1: = bound_method %x.ref, %impl.elem0 +// CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] +// CHECK:STDOUT: %bound_method.loc5_47.2: = bound_method %x.ref, %specific_fn +// CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc5_47.2(%x.ref) +// CHECK:STDOUT: } +// CHECK:STDOUT: return %.loc5_48 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/deduced_return/fail_member.carbon b/toolchain/check/testdata/function/deduced_return/fail_member.carbon new file mode 100644 index 000000000000..e9e7888e6b9e --- /dev/null +++ b/toolchain/check/testdata/function/deduced_return/fail_member.carbon @@ -0,0 +1,88 @@ +// 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/primitives.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/deduced_return/fail_member.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/deduced_return/fail_member.carbon + +// --- fail_interface.carbon +library "[[@TEST_NAME]]"; + +interface I { + // CHECK:STDERR: fail_interface.carbon:[[@LINE+4]]:10: error: interface member with deduced return type [DeducedReturnTypeNotAllowed] + // CHECK:STDERR: fn F() -> auto; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + fn F() -> auto; +} + +// --- fail_interface_default.carbon +library "[[@TEST_NAME]]"; + +interface I { + // CHECK:STDERR: fail_interface_default.carbon:[[@LINE+4]]:18: error: interface member with deduced return type [DeducedReturnTypeNotAllowed] + // CHECK:STDERR: default fn F() -> auto { return 0 as i32; } + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + default fn F() -> auto { return 0 as i32; } +} + +// --- fail_virtual.carbon +library "[[@TEST_NAME]]"; + +base class B { + // CHECK:STDERR: fail_virtual.carbon:[[@LINE+4]]:29: error: virtual function with deduced return type [DeducedReturnTypeNotAllowed] + // CHECK:STDERR: virtual fn F(unused self) -> auto { return 0 as i32; } + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + virtual fn F(unused self) -> auto { return 0 as i32; } +} + +// --- fail_abstract.carbon +library "[[@TEST_NAME]]"; + +abstract class A { + // CHECK:STDERR: fail_abstract.carbon:[[@LINE+4]]:23: error: virtual function with deduced return type [DeducedReturnTypeNotAllowed] + // CHECK:STDERR: abstract fn F(self) -> auto; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + abstract fn F(self) -> auto; +} + +// --- fail_override.carbon +library "[[@TEST_NAME]]"; + +base class B { + virtual fn F(unused self) -> i32 { return 0; } +} + +class D { + extend base: B; + // CHECK:STDERR: fail_override.carbon:[[@LINE+4]]:30: error: virtual function with deduced return type [DeducedReturnTypeNotAllowed] + // CHECK:STDERR: override fn F(unused self) -> auto { return 0 as i32; } + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + override fn F(unused self) -> auto { return 0 as i32; } +} + +// --- fail_todo_impl.carbon +library "[[@TEST_NAME]]"; + +interface I { + fn F() -> i32; +} + +class C {} + +impl C as I { + // CHECK:STDERR: fail_todo_impl.carbon:[[@LINE+4]]:10: error: semantics TODO: `deduced return type for `impl` member` [SemanticsTodo] + // CHECK:STDERR: fn F() -> auto { return 0 as i32; } + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + fn F() -> auto { return 0 as i32; } +} diff --git a/toolchain/check/testdata/function/deduced_return/fail_redecl.carbon b/toolchain/check/testdata/function/deduced_return/fail_redecl.carbon new file mode 100644 index 000000000000..d7eda329e3c2 --- /dev/null +++ b/toolchain/check/testdata/function/deduced_return/fail_redecl.carbon @@ -0,0 +1,158 @@ +// 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/primitives.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/deduced_return/fail_redecl.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/deduced_return/fail_redecl.carbon + +// --- fail_forward_decl.carbon +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_forward_decl.carbon:[[@LINE+4]]:8: error: forward declaration of function with deduced return type [DeducedReturnTypeNotDefinition] +// CHECK:STDERR: fn F() -> auto; +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: +fn F() -> auto; + +// --- fail_forward_decl_then_def.carbon +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_forward_decl_then_def.carbon:[[@LINE+4]]:8: error: forward declaration of function with deduced return type [DeducedReturnTypeNotDefinition] +// CHECK:STDERR: fn F() -> auto; +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: +fn F() -> auto; +// CHECK:STDERR: fail_forward_decl_then_def.carbon:[[@LINE+7]]:1: error: redeclaration of function with deduced return type [DeducedReturnTypeRedecl] +// CHECK:STDERR: fn F() -> auto { return 0 as i32; } +// CHECK:STDERR: ^~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_forward_decl_then_def.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl] +// CHECK:STDERR: fn F() -> auto; +// CHECK:STDERR: ^~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn F() -> auto { return 0 as i32; } + +// --- fail_decl_then_auto_def.carbon +library "[[@TEST_NAME]]"; + +fn F() -> i32; +// CHECK:STDERR: fail_decl_then_auto_def.carbon:[[@LINE+7]]:1: error: redeclaration of function with deduced return type [DeducedReturnTypeRedecl] +// CHECK:STDERR: fn F() -> auto { return 0 as i32; } +// CHECK:STDERR: ^~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_decl_then_auto_def.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl] +// CHECK:STDERR: fn F() -> i32; +// CHECK:STDERR: ^~~~~~~~~~~~~~ +// CHECK:STDERR: +fn F() -> auto { return 0 as i32; } + +// --- fail_auto_def_then_decl.carbon +library "[[@TEST_NAME]]"; + +fn F() -> auto { return 0 as i32; } +// CHECK:STDERR: fail_auto_def_then_decl.carbon:[[@LINE+7]]:1: error: redeclaration of function with deduced return type [DeducedReturnTypeRedecl] +// CHECK:STDERR: fn F() -> i32; +// CHECK:STDERR: ^~~~~~~~~~~~~~ +// CHECK:STDERR: fail_auto_def_then_decl.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl] +// CHECK:STDERR: fn F() -> auto { return 0 as i32; } +// CHECK:STDERR: ^~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn F() -> i32; + +// --- fail_redefinition.carbon +library "[[@TEST_NAME]]"; + +fn F() -> auto { return 0 as i32; } +// CHECK:STDERR: fail_redefinition.carbon:[[@LINE+7]]:1: error: redeclaration of function with deduced return type [DeducedReturnTypeRedecl] +// CHECK:STDERR: fn F() -> auto { return 0 as i32; } +// CHECK:STDERR: ^~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_redefinition.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl] +// CHECK:STDERR: fn F() -> auto { return 0 as i32; } +// CHECK:STDERR: ^~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn F() -> auto { return 0 as i32; } + +// --- fail_member_out_of_line.carbon +library "[[@TEST_NAME]]"; + +class C { + // CHECK:STDERR: fail_member_out_of_line.carbon:[[@LINE+4]]:10: error: forward declaration of function with deduced return type [DeducedReturnTypeNotDefinition] + // CHECK:STDERR: fn F() -> auto; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + fn F() -> auto; +} + +// CHECK:STDERR: fail_member_out_of_line.carbon:[[@LINE+7]]:1: error: redeclaration of function with deduced return type [DeducedReturnTypeRedecl] +// CHECK:STDERR: fn C.F() -> auto { return 0 as i32; } +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_member_out_of_line.carbon:[[@LINE-6]]:3: note: previously declared here [RedeclPrevDecl] +// CHECK:STDERR: fn F() -> auto; +// CHECK:STDERR: ^~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn C.F() -> auto { return 0 as i32; } + +// --- fail_builtin.carbon +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_builtin.carbon:[[@LINE+8]]:26: error: builtin function with deduced return type [DeducedReturnTypeNotAllowed] +// CHECK:STDERR: fn F(n: Core.IntLiteral) -> auto = "int.make_type_signed"; +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: +// CHECK:STDERR: fail_builtin.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.make_type_signed" [InvalidBuiltinSignature] +// CHECK:STDERR: fn F(n: Core.IntLiteral) -> auto = "int.make_type_signed"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn F(n: Core.IntLiteral) -> auto = "int.make_type_signed"; + +// --- fail_extern.carbon +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_extern.carbon:[[@LINE+4]]:15: error: `extern` function with deduced return type [DeducedReturnTypeNotAllowed] +// CHECK:STDERR: extern fn F() -> auto { return 0 as i32; } +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: +extern fn F() -> auto { return 0 as i32; } + +// --- api_def.carbon +library "[[@TEST_NAME]]"; + +fn F() -> auto { return 0 as i32; } + +// --- fail_api_def.impl.carbon +impl library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_api_def.impl.carbon:[[@LINE+12]]:1: error: redeclaration of function with deduced return type [DeducedReturnTypeRedecl] +// CHECK:STDERR: fn F() -> i32; +// CHECK:STDERR: ^~~~~~~~~~~~~~ +// CHECK:STDERR: fail_api_def.impl.carbon:[[@LINE-5]]:1: in import [InImport] +// CHECK:STDERR: api_def.carbon:3:1: note: previously declared here [RedeclPrevDecl] +// CHECK:STDERR: fn F() -> auto { return 0 as i32; } +// CHECK:STDERR: ^~~~~~~~~~~~~~~~ +// CHECK:STDERR: +// CHECK:STDERR: fail_api_def.impl.carbon:[[@LINE+4]]:1: error: no definition found for declaration in impl file [MissingDefinitionInImpl] +// CHECK:STDERR: fn F() -> i32; +// CHECK:STDERR: ^~~~~~~~~~~~~~ +// CHECK:STDERR: +fn F() -> i32; + +// --- api_decl.carbon +library "[[@TEST_NAME]]"; + +fn F() -> i32; + +// --- fail_api_decl.impl.carbon +impl library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_api_decl.impl.carbon:[[@LINE+8]]:1: error: redeclaration of function with deduced return type [DeducedReturnTypeRedecl] +// CHECK:STDERR: fn F() -> auto { return 0 as i32; } +// CHECK:STDERR: ^~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_api_decl.impl.carbon:[[@LINE-5]]:1: in import [InImport] +// CHECK:STDERR: api_decl.carbon:3:1: note: previously declared here [RedeclPrevDecl] +// CHECK:STDERR: fn F() -> i32; +// CHECK:STDERR: ^~~~~~~~~~~~~~ +// CHECK:STDERR: +fn F() -> auto { return 0 as i32; } diff --git a/toolchain/check/testdata/function/deduced_return/fail_return.carbon b/toolchain/check/testdata/function/deduced_return/fail_return.carbon new file mode 100644 index 000000000000..ed23409f2756 --- /dev/null +++ b/toolchain/check/testdata/function/deduced_return/fail_return.carbon @@ -0,0 +1,147 @@ +// 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/primitives.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/deduced_return/fail_return.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/deduced_return/fail_return.carbon + +// --- fail_mismatch.carbon +library "[[@TEST_NAME]]"; + +fn F(b: bool) -> auto { + if (b) { + return 1 as i32; + } + // CHECK:STDERR: fail_mismatch.carbon:[[@LINE+7]]:10: error: deduced return type `bool` does not match return type deduced earlier [DeducedReturnTypeMismatch] + // CHECK:STDERR: return true; + // CHECK:STDERR: ^~~~ + // CHECK:STDERR: fail_mismatch.carbon:[[@LINE-5]]:12: note: return type previously deduced as `i32` here [DeducedReturnTypeMismatchPrevious] + // CHECK:STDERR: return 1 as i32; + // CHECK:STDERR: ^~~~~~~~ + // CHECK:STDERR: + return true; +} + +// --- fail_todo_mismatch_int_vs_int_literal.carbon +library "[[@TEST_NAME]]"; + +fn F(b: bool) -> auto { + if (b) { + return 1 as i32; + } + // TODO: This should work once we use CommonType in return type deduction. + // CHECK:STDERR: fail_todo_mismatch_int_vs_int_literal.carbon:[[@LINE+7]]:10: error: deduced return type `Core.IntLiteral` does not match return type deduced earlier [DeducedReturnTypeMismatch] + // CHECK:STDERR: return 0; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_todo_mismatch_int_vs_int_literal.carbon:[[@LINE-6]]:12: note: return type previously deduced as `i32` here [DeducedReturnTypeMismatchPrevious] + // CHECK:STDERR: return 1 as i32; + // CHECK:STDERR: ^~~~~~~~ + // CHECK:STDERR: + return 0; +} + +// --- fail_error_return_ignored.carbon +library "[[@TEST_NAME]]"; + +// The erroneous `return` doesn't affect the deduced return type. +fn F(b: bool) -> auto { + if (b) { + // CHECK:STDERR: fail_error_return_ignored.carbon:[[@LINE+4]]:12: error: name `Undeclared` not found [NameNotFound] + // CHECK:STDERR: return Undeclared; + // CHECK:STDERR: ^~~~~~~~~~ + // CHECK:STDERR: + return Undeclared; + } + return 1 as i32; +} + +fn G() -> i32 { + return F(true); +} + +// --- fail_return_no_value.carbon +library "[[@TEST_NAME]]"; + +fn F(b: bool) -> auto { + if (b) { + // CHECK:STDERR: fail_return_no_value.carbon:[[@LINE+7]]:5: error: missing return value [ReturnStatementMissingExpr] + // CHECK:STDERR: return; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_return_no_value.carbon:[[@LINE-5]]:1: note: return type of function is deduced [ReturnTypeIsDeducedNote] + // CHECK:STDERR: fn F(b: bool) -> auto { + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + return; + } + return 1 as i32; +} + +// --- fail_return_no_value_only.carbon +library "[[@TEST_NAME]]"; + +fn F() -> auto { + // CHECK:STDERR: fail_return_no_value_only.carbon:[[@LINE+7]]:3: error: missing return value [ReturnStatementMissingExpr] + // CHECK:STDERR: return; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_return_no_value_only.carbon:[[@LINE-4]]:1: note: return type of function is deduced [ReturnTypeIsDeducedNote] + // CHECK:STDERR: fn F() -> auto { + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: + return; +} + +// --- fail_reachable_end.carbon +library "[[@TEST_NAME]]"; + +fn F(b: bool) -> auto { + if (b) { + return 1 as i32; + } +// CHECK:STDERR: fail_reachable_end.carbon:[[@LINE+4]]:1: error: missing `return` at end of function with declared return type [MissingReturnStatement] +// CHECK:STDERR: } +// CHECK:STDERR: ^ +// CHECK:STDERR: +} + +// --- fail_no_return.carbon +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_no_return.carbon:[[@LINE+4]]:1: error: no `return` in function with deduced return type [DeducedReturnTypeWithoutReturn] +// CHECK:STDERR: fn F() -> auto {} +// CHECK:STDERR: ^~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn F() -> auto {} + +// --- fail_return_var_no_returned_var.carbon +library "[[@TEST_NAME]]"; + +fn F() -> auto { + // CHECK:STDERR: fail_return_var_no_returned_var.carbon:[[@LINE+4]]:3: error: `return var;` with no `returned var` in scope [ReturnVarWithNoReturnedVar] + // CHECK:STDERR: return var; + // CHECK:STDERR: ^~~~~~~~~~~ + // CHECK:STDERR: + return var; +} + +// --- fail_incomplete.carbon +library "[[@TEST_NAME]]"; + +class C; + +fn Make() -> C*; + +fn F() -> auto { + // CHECK:STDERR: fail_incomplete.carbon:[[@LINE+7]]:10: error: function returns incomplete type `C` [IncompleteTypeInFunctionReturnType] + // CHECK:STDERR: return *Make(); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_incomplete.carbon:[[@LINE-8]]:1: note: class was forward declared here [ClassForwardDeclaredHere] + // CHECK:STDERR: class C; + // CHECK:STDERR: ^~~~~~~~ + // CHECK:STDERR: + return *Make(); +} diff --git a/toolchain/check/testdata/function/deduced_return/generic.carbon b/toolchain/check/testdata/function/deduced_return/generic.carbon new file mode 100644 index 000000000000..b9a1714c315b --- /dev/null +++ b/toolchain/check/testdata/function/deduced_return/generic.carbon @@ -0,0 +1,138 @@ +// 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/primitives.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/deduced_return/generic.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/deduced_return/generic.carbon + +// --- generic_definition.carbon +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +fn F[T: Core.Copy](x: T) -> auto { return x; } +//@dump-sem-ir-end + +// --- fail_todo_call_generic.carbon +library "[[@TEST_NAME]]"; + +fn F[T: Core.Copy](x: T) -> auto { return x; } + +// CHECK:STDERR: fail_todo_call_generic.carbon:[[@LINE+4]]:24: error: semantics TODO: `call to generic function with deduced return type` [SemanticsTodo] +// CHECK:STDERR: fn G() -> i32 { return F(0 as i32); } +// CHECK:STDERR: ^~~~~~~~~~~ +// CHECK:STDERR: +fn G() -> i32 { return F(0 as i32); } + +// CHECK:STDOUT: --- generic_definition.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: type: type = facet_type [concrete] +// CHECK:STDOUT: %.Self.frozen: type = symbolic_binding .Self [symbolic_self] +// CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] +// CHECK:STDOUT: %pattern_type.e81: type = pattern_type %Copy.type [concrete] +// CHECK:STDOUT: %T.patt.fe6: %pattern_type.e81 = symbolic_binding_pattern T, 0 [symbolic] +// CHECK:STDOUT: %T.f84: %Copy.type = symbolic_binding T, 0 [symbolic] +// CHECK:STDOUT: %T.as_type: type = facet_access_type %T.f84 [symbolic] +// CHECK:STDOUT: %pattern_type.2c66b4.2: type = pattern_type %T.as_type [symbolic] +// CHECK:STDOUT: %x.param_patt: %pattern_type.2c66b4.2 = value_param_pattern [symbolic] +// CHECK:STDOUT: %x.patt: %pattern_type.2c66b4.2 = wrapper_binding_pattern x, %x.param_patt [symbolic] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %require_complete.d83: = require_complete_type %T.as_type [symbolic] +// CHECK:STDOUT: %.1ce700.2: Core.Form = init_form %T.as_type [symbolic] +// CHECK:STDOUT: %return.param_patt.bf4475.2: %pattern_type.2c66b4.2 = out_param_pattern [symbolic] +// CHECK:STDOUT: %return.patt.f44: %pattern_type.2c66b4.2 = return_slot_pattern %return.param_patt.bf4475.2, %T.as_type [symbolic] +// CHECK:STDOUT: %Copy.lookup_impl_witness.d9c: = lookup_impl_witness %T.f84, @Copy [symbolic] +// CHECK:STDOUT: %Copy.WithSelf.Op.type.d2f9c3.2: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.f84) [symbolic] +// CHECK:STDOUT: %.8bf: type = fn_type_with_self_type %Copy.WithSelf.Op.type.d2f9c3.2, %T.f84 [symbolic] +// CHECK:STDOUT: %impl.elem0.bb5: %.8bf = impl_witness_access %Copy.lookup_impl_witness.d9c, element0 [symbolic] +// CHECK:STDOUT: %specific_impl_fn.2ac: = specific_impl_function %impl.elem0.bb5, @Copy.WithSelf.Op(%T.f84) [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { +// CHECK:STDOUT: .Copy = %Core.Copy +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %T.patt.loc4_7.1: %pattern_type.e81 = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc4_7.2 (constants.%T.patt.fe6)] +// CHECK:STDOUT: %x.param_patt.loc4_21.1: @F.%pattern_type (%pattern_type.2c66b4.2) = value_param_pattern [symbolic = %x.param_patt.loc4_21.2 (constants.%x.param_patt)] +// CHECK:STDOUT: %x.patt.loc4_21.1: @F.%pattern_type (%pattern_type.2c66b4.2) = wrapper_binding_pattern x, %x.param_patt.loc4_21.1 [symbolic = %x.patt.loc4_21.2 (constants.%x.patt)] +// CHECK:STDOUT: %return.param_patt.loc4_43.1: @F.%pattern_type (%pattern_type.2c66b4.2) = out_param_pattern [symbolic = %return.param_patt.loc4_43.2 (constants.%return.param_patt.bf4475.2)] +// CHECK:STDOUT: %return.patt.loc4_43.1: @F.%pattern_type (%pattern_type.2c66b4.2) = return_slot_pattern %return.param_patt.loc4_43.1, %.loc4_43.6 [symbolic = %return.patt.loc4_43.2 (constants.%return.patt.f44)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.loc4_13: type = splice_block %Copy.ref [concrete = constants.%Copy.type] { +// CHECK:STDOUT: %.Self.frozen: type = symbolic_binding .Self [symbolic_self = constants.%.Self.frozen] +// CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] +// CHECK:STDOUT: %Copy.ref: type = name_ref Copy, imports.%Core.Copy [concrete = constants.%Copy.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %T.loc4_7.2: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc4_7.1 (constants.%T.f84)] +// CHECK:STDOUT: %x.param: @F.%T.as_type.loc4_23.1 (%T.as_type) = value_param call_param0 +// CHECK:STDOUT: %.loc4_23.1: type = splice_block %.loc4_23.2 [symbolic = %T.as_type.loc4_23.1 (constants.%T.as_type)] { +// CHECK:STDOUT: %T.ref: %Copy.type = name_ref T, %T.loc4_7.2 [symbolic = %T.loc4_7.1 (constants.%T.f84)] +// CHECK:STDOUT: %T.as_type.loc4_23.2: type = facet_access_type %T.ref [symbolic = %T.as_type.loc4_23.1 (constants.%T.as_type)] +// CHECK:STDOUT: %.loc4_23.2: type = converted %T.ref, %T.as_type.loc4_23.2 [symbolic = %T.as_type.loc4_23.1 (constants.%T.as_type)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x: @F.%T.as_type.loc4_23.1 (%T.as_type) = wrapper_binding x, %x.param +// CHECK:STDOUT: %.loc4_43.6: type = type_literal constants.%T.as_type [symbolic = %T.as_type.loc4_23.1 (constants.%T.as_type)] +// CHECK:STDOUT: %.loc4_43.7: Core.Form = init_form %.loc4_43.6 [symbolic = %.loc4_43.4 (constants.%.1ce700.2)] +// CHECK:STDOUT: %return.param: ref @F.%T.as_type.loc4_23.1 (%T.as_type) = out_param call_param1 +// CHECK:STDOUT: %return: ref @F.%T.as_type.loc4_23.1 (%T.as_type) = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @F(%T.loc4_7.2: %Copy.type) { +// CHECK:STDOUT: %T.patt.loc4_7.2: %pattern_type.e81 = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc4_7.2 (constants.%T.patt.fe6)] +// CHECK:STDOUT: %T.loc4_7.1: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc4_7.1 (constants.%T.f84)] +// CHECK:STDOUT: %T.as_type.loc4_23.1: type = facet_access_type %T.loc4_7.1 [symbolic = %T.as_type.loc4_23.1 (constants.%T.as_type)] +// CHECK:STDOUT: %pattern_type: type = pattern_type %T.as_type.loc4_23.1 [symbolic = %pattern_type (constants.%pattern_type.2c66b4.2)] +// CHECK:STDOUT: %x.param_patt.loc4_21.2: @F.%pattern_type (%pattern_type.2c66b4.2) = value_param_pattern [symbolic = %x.param_patt.loc4_21.2 (constants.%x.param_patt)] +// CHECK:STDOUT: %x.patt.loc4_21.2: @F.%pattern_type (%pattern_type.2c66b4.2) = wrapper_binding_pattern x, %x.param_patt.loc4_21.2 [symbolic = %x.patt.loc4_21.2 (constants.%x.patt)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %require_complete: = require_complete_type %T.as_type.loc4_23.1 [symbolic = %require_complete (constants.%require_complete.d83)] +// CHECK:STDOUT: %.loc4_43.4: Core.Form = init_form %T.as_type.loc4_23.1 [symbolic = %.loc4_43.4 (constants.%.1ce700.2)] +// CHECK:STDOUT: %return.param_patt.loc4_43.2: @F.%pattern_type (%pattern_type.2c66b4.2) = out_param_pattern [symbolic = %return.param_patt.loc4_43.2 (constants.%return.param_patt.bf4475.2)] +// CHECK:STDOUT: %return.patt.loc4_43.2: @F.%pattern_type (%pattern_type.2c66b4.2) = return_slot_pattern %return.param_patt.loc4_43.2, %T.as_type.loc4_23.1 [symbolic = %return.patt.loc4_43.2 (constants.%return.patt.f44)] +// CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.loc4_7.1) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.d2f9c3.2)] +// CHECK:STDOUT: %.loc4_43.5: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %T.loc4_7.1 [symbolic = %.loc4_43.5 (constants.%.8bf)] +// CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %T.loc4_7.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.d9c)] +// CHECK:STDOUT: %impl.elem0.loc4_43.2: @F.%.loc4_43.5 (%.8bf) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc4_43.2 (constants.%impl.elem0.bb5)] +// CHECK:STDOUT: %specific_impl_fn.loc4_43.2: = specific_impl_function %impl.elem0.loc4_43.2, @Copy.WithSelf.Op(%T.loc4_7.1) [symbolic = %specific_impl_fn.loc4_43.2 (constants.%specific_impl_fn.2ac)] +// CHECK:STDOUT: +// CHECK:STDOUT: fn(%x.param: @F.%T.as_type.loc4_23.1 (%T.as_type)) -> out %return.param: @F.%T.as_type.loc4_23.1 (%T.as_type) [deduced] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %x.ref: @F.%T.as_type.loc4_23.1 (%T.as_type) = name_ref x, %x +// CHECK:STDOUT: %.loc4_44: init @F.%T.as_type.loc4_23.1 (%T.as_type) = splice_block %Copy.WithSelf.Op.call { +// CHECK:STDOUT: %impl.elem0.loc4_43.1: @F.%.loc4_43.5 (%.8bf) = impl_witness_access constants.%Copy.lookup_impl_witness.d9c, element0 [symbolic = %impl.elem0.loc4_43.2 (constants.%impl.elem0.bb5)] +// CHECK:STDOUT: %bound_method.loc4_43.1: = bound_method %x.ref, %impl.elem0.loc4_43.1 +// CHECK:STDOUT: %.loc4_43.1: %Copy.type = converted constants.%T.as_type, constants.%T.f84 [symbolic = %T.loc4_7.1 (constants.%T.f84)] +// CHECK:STDOUT: %.loc4_43.2: %Copy.type = converted constants.%T.as_type, constants.%T.f84 [symbolic = %T.loc4_7.1 (constants.%T.f84)] +// CHECK:STDOUT: %specific_impl_fn.loc4_43.1: = specific_impl_function %impl.elem0.loc4_43.1, @Copy.WithSelf.Op(constants.%T.f84) [symbolic = %specific_impl_fn.loc4_43.2 (constants.%specific_impl_fn.2ac)] +// CHECK:STDOUT: %bound_method.loc4_43.2: = bound_method %x.ref, %specific_impl_fn.loc4_43.1 +// CHECK:STDOUT: %.loc4_43.3: ref @F.%T.as_type.loc4_23.1 (%T.as_type) = splice_block %return.param {} +// CHECK:STDOUT: %Copy.WithSelf.Op.call: init @F.%T.as_type.loc4_23.1 (%T.as_type) to %.loc4_43.3 = call %bound_method.loc4_43.2(%x.ref) +// CHECK:STDOUT: } +// CHECK:STDOUT: return %.loc4_44 to %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%T.f84) { +// CHECK:STDOUT: %T.patt.loc4_7.2 => constants.%T.patt.fe6 +// CHECK:STDOUT: %T.loc4_7.1 => constants.%T.f84 +// CHECK:STDOUT: %T.as_type.loc4_23.1 => constants.%T.as_type +// CHECK:STDOUT: %pattern_type => constants.%pattern_type.2c66b4.2 +// CHECK:STDOUT: %x.param_patt.loc4_21.2 => constants.%x.param_patt +// CHECK:STDOUT: %x.patt.loc4_21.2 => constants.%x.patt +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/deduced_return/import.carbon b/toolchain/check/testdata/function/deduced_return/import.carbon new file mode 100644 index 000000000000..41a7a292be59 --- /dev/null +++ b/toolchain/check/testdata/function/deduced_return/import.carbon @@ -0,0 +1,99 @@ +// 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/primitives.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/deduced_return/import.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/deduced_return/import.carbon + +// --- lib.carbon +library "[[@TEST_NAME]]"; + +fn F() -> auto { return 0 as i32; } + +class C { + fn G() -> auto { return 1 as i32; } +} + +// --- lib.impl.carbon +impl library "[[@TEST_NAME]]"; + +fn H() -> i32 { return F(); } + +// --- use.carbon +library "[[@TEST_NAME]]"; + +import library "lib"; + +//@dump-sem-ir-begin +fn I() -> i32 { return F(); } +fn J() -> i32 { return C.G(); } +//@dump-sem-ir-end + +// CHECK:STDOUT: --- use.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: type: type = facet_type [concrete] +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %.0b3: Core.Form = init_form %i32 [concrete] +// CHECK:STDOUT: %pattern_type.d94: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete] +// CHECK:STDOUT: %return.patt.6fa: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32 [concrete] +// CHECK:STDOUT: %I.type: type = fn_type @I [concrete] +// CHECK:STDOUT: %I: %I.type = struct_value () [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %J.type: type = fn_type @J [concrete] +// CHECK:STDOUT: %J: %J.type = struct_value () [concrete] +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %C.G.type: type = fn_type @C.G [concrete] +// CHECK:STDOUT: %C.G: %C.G.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Main.F: %F.type = import_ref Main//lib, F, loaded [concrete = constants.%F] +// CHECK:STDOUT: %Main.C: type = import_ref Main//lib, C, loaded [concrete = constants.%C] +// CHECK:STDOUT: %Main.import_ref.341: %C.G.type = import_ref Main//lib, loc6_18, loaded [concrete = constants.%C.G] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %I.decl: %I.type = fn_decl @I [concrete = constants.%I] { +// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete = constants.%return.param_patt] +// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32 [concrete = constants.%return.patt.6fa] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc6: Core.Form = init_form %i32 [concrete = constants.%.0b3] +// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 +// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %J.decl: %J.type = fn_decl @J [concrete = constants.%J] { +// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete = constants.%return.param_patt] +// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32 [concrete = constants.%return.patt.6fa] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc7: Core.Form = init_form %i32 [concrete = constants.%.0b3] +// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 +// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @I() -> out %return.param: %i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %F.ref: %F.type = name_ref F, imports.%Main.F [concrete = constants.%F] +// CHECK:STDOUT: %F.call: init %i32 = call %F.ref() +// CHECK:STDOUT: return %F.call +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @J() -> out %return.param: %i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] +// CHECK:STDOUT: %G.ref: %C.G.type = name_ref G, imports.%Main.import_ref.341 [concrete = constants.%C.G] +// CHECK:STDOUT: %C.G.call: init %i32 = call %G.ref() +// CHECK:STDOUT: return %C.G.call +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/deduced_return/returned_var.carbon b/toolchain/check/testdata/function/deduced_return/returned_var.carbon new file mode 100644 index 000000000000..009bc00e0d6c --- /dev/null +++ b/toolchain/check/testdata/function/deduced_return/returned_var.carbon @@ -0,0 +1,450 @@ +// 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/primitives.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/deduced_return/returned_var.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/deduced_return/returned_var.carbon + +// --- returned_var.carbon +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +fn F() -> auto { + returned var r: i32 = 0; + return var; +} +//@dump-sem-ir-end + +fn G() -> i32 { + return F(); +} + +// --- returned_var_after_return.carbon +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +fn F(b: bool) -> auto { + if (b) { + // This is converted to the type of the `returned var`. + return 1; + } + returned var r: i32 = 0; + return var; +} +//@dump-sem-ir-end + +fn G() -> i32 { + return F(true); +} + +// --- return_after_returned_var.carbon +library "[[@TEST_NAME]]"; + +fn F(b: bool) -> auto { + if (b) { + returned var r: i32 = 0; + return var; + } + // This is converted to the type of the `returned var`. + return 1; +} + +fn G() -> i32 { + return F(true); +} + +// --- returned_var_in_place.carbon +library "[[@TEST_NAME]]"; + +class C { + var a: i32; + var b: i32; +} + +//@dump-sem-ir-begin +fn F() -> auto { + // The `returned var` is initialized in the return slot. + returned var c: C = {.a = 1, .b = 2}; + return var; +} +//@dump-sem-ir-end + +// --- fail_return_before_returned_var.carbon +library "[[@TEST_NAME]]"; + +fn F(b: bool) -> auto { + if (b) { + // CHECK:STDERR: fail_return_before_returned_var.carbon:[[@LINE+6]]:5: error: cannot implicitly convert expression of type `bool` to `i32` [ConversionFailure] + // CHECK:STDERR: return true; + // CHECK:STDERR: ^~~~~~~~~~~~ + // CHECK:STDERR: fail_return_before_returned_var.carbon:[[@LINE+3]]:5: note: type `bool` does not implement interface `Core.ImplicitAs(i32)` [MissingImplInMemberAccessInContext] + // CHECK:STDERR: return true; + // CHECK:STDERR: ^~~~~~~~~~~~ + return true; + } + // CHECK:STDERR: fail_return_before_returned_var.carbon:[[@LINE+4]]:19: note: return type deduced as `i32` [ReturnTypeDeducedAs] + // CHECK:STDERR: returned var r: i32 = 0; + // CHECK:STDERR: ^~~ + // CHECK:STDERR: + returned var r: i32 = 0; + return var; +} + +// --- fail_returned_var_mismatch.carbon +library "[[@TEST_NAME]]"; + +fn F(b: bool) -> auto { + if (b) { + returned var r: i32 = 0; + return var; + } + // CHECK:STDERR: fail_returned_var_mismatch.carbon:[[@LINE+7]]:19: error: type `bool` of `returned var` does not match return type of enclosing function [ReturnedVarWrongType] + // CHECK:STDERR: returned var s: bool = true; + // CHECK:STDERR: ^~~~ + // CHECK:STDERR: fail_returned_var_mismatch.carbon:[[@LINE-6]]:21: note: return type of function is `i32` [ReturnTypeHereNote] + // CHECK:STDERR: returned var r: i32 = 0; + // CHECK:STDERR: ^~~ + // CHECK:STDERR: + returned var s: bool = true; + return var; +} + +// CHECK:STDOUT: --- returned_var.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: type: type = facet_type [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %pattern_type.d94: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: %r.patt: %pattern_type.d94 = ref_binding_pattern r [concrete] +// CHECK:STDOUT: %.0b3: Core.Form = init_form %i32 [concrete] +// CHECK:STDOUT: %return.param_patt.299: %pattern_type.d94 = out_param_pattern [concrete] +// CHECK:STDOUT: %return.patt.6fa: %pattern_type.d94 = return_slot_pattern %return.param_patt.299, %i32 [concrete] +// CHECK:STDOUT: %r.var_patt: %pattern_type.d94 = var_pattern %r.patt [concrete] +// CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] +// CHECK:STDOUT: %ImplicitAs.type.6b0: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] +// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6 = struct_value () [symbolic] +// CHECK:STDOUT: %ImplicitAs.impl_witness.d73: = impl_witness imports.%ImplicitAs.impl_witness_table.f53, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766 = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.6b0 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.d73) [concrete] +// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.58f: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] +// CHECK:STDOUT: %.33a: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.58f, %ImplicitAs.facet [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] +// CHECK:STDOUT: %bound_method: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_0.753: %i32 = int_value 0 [concrete] +// CHECK:STDOUT: %self.param_patt.0c5: %pattern_type.d94 = ref_param_pattern [concrete] +// CHECK:STDOUT: %self.patt.749: %pattern_type.d94 = wrapper_binding_pattern self, %self.param_patt.0c5 [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfc: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc5 [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01: %Destroy.WithSelf.SubobjectDestroy.type.dfc = struct_value () [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef0: type = fn_type @Destroy.WithSelf.Op.loc5 [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.Op.403: %Destroy.WithSelf.Op.type.ef0 = struct_value () [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbc: type = fn_type @Destroy.WithSelf.SelfDestruct.loc5 [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3: %Destroy.WithSelf.SelfDestruct.type.fbc = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core.import_ref.e47: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2)] +// CHECK:STDOUT: %ImplicitAs.impl_witness_table.f53 = impl_witness_table (%Core.import_ref.e47), @Core.IntLiteral.as.ImplicitAs.impl [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generated { +// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl: %Destroy.WithSelf.SubobjectDestroy.type.dfc = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc5 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01] { +// CHECK:STDOUT: %self.param_patt: %pattern_type.d94 = ref_param_pattern [concrete = constants.%self.param_patt.0c5] +// CHECK:STDOUT: %self.patt: %pattern_type.d94 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.749] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: ref %i32 = ref_param call_param0 +// CHECK:STDOUT: %self: ref %i32 = wrapper_binding self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %Destroy.WithSelf.Op.decl: %Destroy.WithSelf.Op.type.ef0 = fn_decl @Destroy.WithSelf.Op.loc5 [concrete = constants.%Destroy.WithSelf.Op.403] { +// CHECK:STDOUT: %self.param_patt: %pattern_type.d94 = ref_param_pattern [concrete = constants.%self.param_patt.0c5] +// CHECK:STDOUT: %self.patt: %pattern_type.d94 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.749] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: ref %i32 = ref_param call_param0 +// CHECK:STDOUT: %self: ref %i32 = wrapper_binding self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete = constants.%return.param_patt.299] +// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32.loc5_19.2 [concrete = constants.%return.patt.6fa] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %i32.loc5_19.2: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc5_19: Core.Form = init_form %i32.loc5_19.2 [concrete = constants.%.0b3] +// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 +// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() -> out %return.param: %i32 [deduced] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %r.var: ref %i32 = var_storage %r.var_patt +// CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] +// CHECK:STDOUT: %impl.elem0: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60] +// CHECK:STDOUT: %bound_method.loc5_12.1: = bound_method %int_0, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] +// CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc5_12.2: = bound_method %int_0, %specific_fn [concrete = constants.%bound_method] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc5_12.2(%int_0) [concrete = constants.%int_0.753] +// CHECK:STDOUT: %.loc5_12: init %i32 = converted %int_0, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.753] +// CHECK:STDOUT: assign %r.var, %.loc5_12 +// CHECK:STDOUT: %i32.loc5_19.1: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %r: ref %i32 = wrapper_binding r, %r.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %r.patt: %pattern_type.d94 = ref_binding_pattern r [concrete = constants.%r.patt] +// CHECK:STDOUT: %r.var_patt: %pattern_type.d94 = var_pattern %r.patt [concrete = constants.%r.var_patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc5_17: %i32 = acquire_value %r +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: = bound_method %r.var, constants.%Destroy.WithSelf.SelfDestruct.db3 +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound(%r.var) +// CHECK:STDOUT: return %.loc5_17 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc5(%self.param: ref %i32) = "no_op"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc5(%self.param: ref %i32) = "no_op"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc5(%self.param: ref %i32) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl(%self.param) +// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl(%self.param) +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- returned_var_after_return.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: type: type = facet_type [concrete] +// CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] +// CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern [concrete] +// CHECK:STDOUT: %b.patt: %pattern_type.831 = wrapper_binding_pattern b, %b.param_patt [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %pattern_type.d94: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: %r.patt: %pattern_type.d94 = ref_binding_pattern r [concrete] +// CHECK:STDOUT: %.0b3: Core.Form = init_form %i32 [concrete] +// CHECK:STDOUT: %return.param_patt.299: %pattern_type.d94 = out_param_pattern [concrete] +// CHECK:STDOUT: %return.patt.6fa: %pattern_type.d94 = return_slot_pattern %return.param_patt.299, %i32 [concrete] +// CHECK:STDOUT: %ImplicitAs.type.6b0: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] +// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6 = struct_value () [symbolic] +// CHECK:STDOUT: %ImplicitAs.impl_witness.d73: = impl_witness imports.%ImplicitAs.impl_witness_table.f53, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766 = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.6b0 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.d73) [concrete] +// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.58f: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] +// CHECK:STDOUT: %.33a: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.58f, %ImplicitAs.facet [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.282: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] +// CHECK:STDOUT: %bound_method.3bd: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_1.68a: %i32 = int_value 1 [concrete] +// CHECK:STDOUT: %r.var_patt: %pattern_type.d94 = var_pattern %r.patt [concrete] +// CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.762: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete] +// CHECK:STDOUT: %bound_method.036: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_0.753: %i32 = int_value 0 [concrete] +// CHECK:STDOUT: %self.param_patt.0c5: %pattern_type.d94 = ref_param_pattern [concrete] +// CHECK:STDOUT: %self.patt.749: %pattern_type.d94 = wrapper_binding_pattern self, %self.param_patt.0c5 [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfc: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc9 [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01: %Destroy.WithSelf.SubobjectDestroy.type.dfc = struct_value () [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef0: type = fn_type @Destroy.WithSelf.Op.loc9 [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.Op.403: %Destroy.WithSelf.Op.type.ef0 = struct_value () [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbc: type = fn_type @Destroy.WithSelf.SelfDestruct.loc9 [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3: %Destroy.WithSelf.SelfDestruct.type.fbc = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core.import_ref.e47: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2)] +// CHECK:STDOUT: %ImplicitAs.impl_witness_table.f53 = impl_witness_table (%Core.import_ref.e47), @Core.IntLiteral.as.ImplicitAs.impl [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generated { +// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl: %Destroy.WithSelf.SubobjectDestroy.type.dfc = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc9 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01] { +// CHECK:STDOUT: %self.param_patt: %pattern_type.d94 = ref_param_pattern [concrete = constants.%self.param_patt.0c5] +// CHECK:STDOUT: %self.patt: %pattern_type.d94 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.749] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: ref %i32 = ref_param call_param0 +// CHECK:STDOUT: %self: ref %i32 = wrapper_binding self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %Destroy.WithSelf.Op.decl: %Destroy.WithSelf.Op.type.ef0 = fn_decl @Destroy.WithSelf.Op.loc9 [concrete = constants.%Destroy.WithSelf.Op.403] { +// CHECK:STDOUT: %self.param_patt: %pattern_type.d94 = ref_param_pattern [concrete = constants.%self.param_patt.0c5] +// CHECK:STDOUT: %self.patt: %pattern_type.d94 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.749] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: ref %i32 = ref_param call_param0 +// CHECK:STDOUT: %self: ref %i32 = wrapper_binding self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern [concrete = constants.%b.param_patt] +// CHECK:STDOUT: %b.patt: %pattern_type.831 = wrapper_binding_pattern b, %b.param_patt [concrete = constants.%b.patt] +// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete = constants.%return.param_patt.299] +// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32.loc9_19.2 [concrete = constants.%return.patt.6fa] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %b.param: bool = value_param call_param0 +// CHECK:STDOUT: %.loc4: type = type_literal bool [concrete = bool] +// CHECK:STDOUT: %b: bool = wrapper_binding b, %b.param +// CHECK:STDOUT: %i32.loc9_19.2: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc9_19: Core.Form = init_form %i32.loc9_19.2 [concrete = constants.%.0b3] +// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 +// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%b.param: bool) -> out %return.param: %i32 [deduced] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %b.ref: bool = name_ref b, %b +// CHECK:STDOUT: if %b.ref br !if.then else br !if.else +// CHECK:STDOUT: +// CHECK:STDOUT: !if.then: +// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] +// CHECK:STDOUT: %.loc7_13.1: init %i32 = splice_block %.loc7_13.2 [concrete = constants.%int_1.68a] { +// CHECK:STDOUT: %impl.elem0.loc7: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60] +// CHECK:STDOUT: %bound_method.loc7_13.1: = bound_method %int_1, %impl.elem0.loc7 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.282] +// CHECK:STDOUT: %specific_fn.loc7: = specific_function %impl.elem0.loc7, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc7_13.2: = bound_method %int_1, %specific_fn.loc7 [concrete = constants.%bound_method.3bd] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7: init %i32 = call %bound_method.loc7_13.2(%int_1) [concrete = constants.%int_1.68a] +// CHECK:STDOUT: %.loc7_13.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7 [concrete = constants.%int_1.68a] +// CHECK:STDOUT: } +// CHECK:STDOUT: return %.loc7_13.1 +// CHECK:STDOUT: +// CHECK:STDOUT: !if.else: +// CHECK:STDOUT: %r.var: ref %i32 = var_storage %r.var_patt +// CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] +// CHECK:STDOUT: %impl.elem0.loc9: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60] +// CHECK:STDOUT: %bound_method.loc9_12.1: = bound_method %int_0, %impl.elem0.loc9 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.762] +// CHECK:STDOUT: %specific_fn.loc9: = specific_function %impl.elem0.loc9, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc9_12.2: = bound_method %int_0, %specific_fn.loc9 [concrete = constants.%bound_method.036] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9: init %i32 = call %bound_method.loc9_12.2(%int_0) [concrete = constants.%int_0.753] +// CHECK:STDOUT: %.loc9_12: init %i32 = converted %int_0, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9 [concrete = constants.%int_0.753] +// CHECK:STDOUT: assign %r.var, %.loc9_12 +// CHECK:STDOUT: %i32.loc9_19.1: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %r: ref %i32 = wrapper_binding r, %r.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %r.patt: %pattern_type.d94 = ref_binding_pattern r [concrete = constants.%r.patt] +// CHECK:STDOUT: %r.var_patt: %pattern_type.d94 = var_pattern %r.patt [concrete = constants.%r.var_patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc9_17: %i32 = acquire_value %r +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: = bound_method %r.var, constants.%Destroy.WithSelf.SelfDestruct.db3 +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound(%r.var) +// CHECK:STDOUT: return %.loc9_17 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc9(%self.param: ref %i32) = "no_op"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc9(%self.param: ref %i32) = "no_op"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc9(%self.param: ref %i32) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl(%self.param) +// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl(%self.param) +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- returned_var_in_place.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: type: type = facet_type [concrete] +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %pattern_type.98b: type = pattern_type %C [concrete] +// CHECK:STDOUT: %c.patt: %pattern_type.98b = ref_binding_pattern c [concrete] +// CHECK:STDOUT: %.a44: Core.Form = init_form %C [concrete] +// CHECK:STDOUT: %return.param_patt.89a: %pattern_type.98b = out_param_pattern [concrete] +// CHECK:STDOUT: %return.patt.e4a: %pattern_type.98b = return_slot_pattern %return.param_patt.89a, %C [concrete] +// CHECK:STDOUT: %c.var_patt: %pattern_type.98b = var_pattern %c.patt [concrete] +// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] +// CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] +// CHECK:STDOUT: %struct_type.a.b.cfd: type = struct_type {.a: Core.IntLiteral, .b: Core.IntLiteral} [concrete] +// CHECK:STDOUT: %struct: %struct_type.a.b.cfd = struct_value (%int_1.5b8, %int_2.ecc) [concrete] +// CHECK:STDOUT: %ImplicitAs.type.6b0: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] +// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6 = struct_value () [symbolic] +// CHECK:STDOUT: %ImplicitAs.impl_witness.d73: = impl_witness imports.%ImplicitAs.impl_witness_table.f53, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766 = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.6b0 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.d73) [concrete] +// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.58f: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] +// CHECK:STDOUT: %.33a: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.58f, %ImplicitAs.facet [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.282: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] +// CHECK:STDOUT: %bound_method.3bd: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_1.68a: %i32 = int_value 1 [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.8a6: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete] +// CHECK:STDOUT: %bound_method.a55: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_2.2e0: %i32 = int_value 2 [concrete] +// CHECK:STDOUT: %C.val: %C = struct_value (%int_1.68a, %int_2.2e0) [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core.import_ref.e47: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2)] +// CHECK:STDOUT: %ImplicitAs.impl_witness_table.f53 = impl_witness_table (%Core.import_ref.e47), @Core.IntLiteral.as.ImplicitAs.impl [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %return.param_patt: %pattern_type.98b = out_param_pattern [concrete = constants.%return.param_patt.89a] +// CHECK:STDOUT: %return.patt: %pattern_type.98b = return_slot_pattern %return.param_patt, %.loc11_19.1 [concrete = constants.%return.patt.e4a] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.loc11_19.1: type = type_literal constants.%C [concrete = constants.%C] +// CHECK:STDOUT: %.loc11_19.2: Core.Form = init_form %.loc11_19.1 [concrete = constants.%.a44] +// CHECK:STDOUT: %return.param: ref %C = out_param call_param0 +// CHECK:STDOUT: %return: ref %C = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() -> out %return.param: %C [deduced] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] +// CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] +// CHECK:STDOUT: %.loc11_38.1: %struct_type.a.b.cfd = struct_literal (%int_1, %int_2) [concrete = constants.%struct] +// CHECK:STDOUT: %impl.elem0.loc11_38.1: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60] +// CHECK:STDOUT: %bound_method.loc11_38.1: = bound_method %int_1, %impl.elem0.loc11_38.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.282] +// CHECK:STDOUT: %specific_fn.loc11_38.1: = specific_function %impl.elem0.loc11_38.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc11_38.2: = bound_method %int_1, %specific_fn.loc11_38.1 [concrete = constants.%bound_method.3bd] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_38.1: init %i32 = call %bound_method.loc11_38.2(%int_1) [concrete = constants.%int_1.68a] +// CHECK:STDOUT: %.loc11_38.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_38.1 [concrete = constants.%int_1.68a] +// CHECK:STDOUT: %.loc11_38.3: ref %i32 = class_element_access %return.param, element0 +// CHECK:STDOUT: %.loc11_38.4: init %i32 to %.loc11_38.3 = in_place_init %.loc11_38.2 [concrete = constants.%int_1.68a] +// CHECK:STDOUT: %impl.elem0.loc11_38.2: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60] +// CHECK:STDOUT: %bound_method.loc11_38.3: = bound_method %int_2, %impl.elem0.loc11_38.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.8a6] +// CHECK:STDOUT: %specific_fn.loc11_38.2: = specific_function %impl.elem0.loc11_38.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc11_38.4: = bound_method %int_2, %specific_fn.loc11_38.2 [concrete = constants.%bound_method.a55] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_38.2: init %i32 = call %bound_method.loc11_38.4(%int_2) [concrete = constants.%int_2.2e0] +// CHECK:STDOUT: %.loc11_38.5: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_38.2 [concrete = constants.%int_2.2e0] +// CHECK:STDOUT: %.loc11_38.6: ref %i32 = class_element_access %return.param, element1 +// CHECK:STDOUT: %.loc11_38.7: init %i32 to %.loc11_38.6 = in_place_init %.loc11_38.5 [concrete = constants.%int_2.2e0] +// CHECK:STDOUT: %.loc11_38.8: init %C to %return.param = class_init (%.loc11_38.4, %.loc11_38.7) [concrete = constants.%C.val] +// CHECK:STDOUT: %.loc11_12: init %C = converted %.loc11_38.1, %.loc11_38.8 [concrete = constants.%C.val] +// CHECK:STDOUT: assign %return.param, %.loc11_12 +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %c: ref %C = wrapper_binding c, %return.param +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c.patt: %pattern_type.98b = ref_binding_pattern c [concrete = constants.%c.patt] +// CHECK:STDOUT: %c.var_patt: %pattern_type.98b = var_pattern %c.patt [concrete = constants.%c.var_patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc11_17: %C = acquire_value %c +// CHECK:STDOUT: return %.loc11_17 to %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/deduced_return/use_before_deduced.carbon b/toolchain/check/testdata/function/deduced_return/use_before_deduced.carbon new file mode 100644 index 000000000000..31afb0876cca --- /dev/null +++ b/toolchain/check/testdata/function/deduced_return/use_before_deduced.carbon @@ -0,0 +1,55 @@ +// 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/primitives.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/deduced_return/use_before_deduced.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/deduced_return/use_before_deduced.carbon + +// --- fail_recursion.carbon +library "[[@TEST_NAME]]"; + +fn F(b: bool) -> auto { + if (b) { + // CHECK:STDERR: fail_recursion.carbon:[[@LINE+7]]:12: error: call to `F` before its return type is deduced [CallBeforeReturnTypeDeduced] + // CHECK:STDERR: return F(false); + // CHECK:STDERR: ^~~~~~~~ + // CHECK:STDERR: fail_recursion.carbon:[[@LINE-5]]:1: note: return type of `F` is deduced from its definition [ReturnTypeDeducedFromDefinition] + // CHECK:STDERR: fn F(b: bool) -> auto { + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + return F(false); + } + return 0 as i32; +} + +// --- fail_member_order.carbon +library "[[@TEST_NAME]]"; + +class C { + // The body of `F` is checked after the body of `G`. + // CHECK:STDERR: fail_member_order.carbon:[[@LINE+3]]:26: error: call to `F` before its return type is deduced [CallBeforeReturnTypeDeduced] + // CHECK:STDERR: fn G() -> i32 { return F(); } + // CHECK:STDERR: ^~~ + fn G() -> i32 { return F(); } + // CHECK:STDERR: fail_member_order.carbon:[[@LINE+4]]:3: note: return type of `F` is deduced from its definition [ReturnTypeDeducedFromDefinition] + // CHECK:STDERR: fn F() -> auto { return 0 as i32; } + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: + fn F() -> auto { return 0 as i32; } +} + +// --- recursion_after_returned_var.carbon +library "[[@TEST_NAME]]"; + +fn F(b: bool) -> auto { + returned var r: i32 = 0; + if (b) { + let unused n: i32 = F(false); + } + return var; +} diff --git a/toolchain/check/testdata/impl/lookup/specialization_poison.carbon b/toolchain/check/testdata/impl/lookup/specialization_poison.carbon index d0d1806f0987..04a308ad59b6 100644 --- a/toolchain/check/testdata/impl/lookup/specialization_poison.carbon +++ b/toolchain/check/testdata/impl/lookup/specialization_poison.carbon @@ -169,23 +169,30 @@ final impl forall [V: Core.Copy] V* as I where .T = V { fn F(self) -> V { return *self; } } -// --- fail_todo_poisoned_concrete_lookup_from_eval_fn.carbon +// --- fail_poisoned_concrete_lookup_from_eval_fn.carbon library "[[@TEST_NAME]]"; +interface Z {} + fn F() { class D {} - // CHECK:STDERR: fail_todo_poisoned_concrete_lookup_from_eval_fn.carbon:[[@LINE+4]]:22: error: semantics TODO: `HandleAutoTypeLiteral` [SemanticsTodo] - // CHECK:STDERR: musteval fn G() -> auto { - // CHECK:STDERR: ^~~~ - // CHECK:STDERR: musteval fn G() -> auto { class C(T: type) {} impl forall [T: type] C(T) as Z {} + // CHECK:STDERR: fail_poisoned_concrete_lookup_from_eval_fn.carbon:[[@LINE+3]]:5: error: found `impl` that would change the result of an earlier use of `C(D) as Z` [PoisonedImplLookupConcreteResult] + // CHECK:STDERR: C(D) as Z; + // CHECK:STDERR: ^~~~~~~~~ C(D) as Z; return C(D); } - // TODO: error: found `impl` that would change the result of an earlier use of `C(D) as Z`. + // CHECK:STDERR: fail_poisoned_concrete_lookup_from_eval_fn.carbon:[[@LINE+7]]:3: note: the use would select the `impl` here but it was not found yet [PoisonedImplLookupConcreteResultNoteBadImpl] + // CHECK:STDERR: impl G() as Z {} + // CHECK:STDERR: ^~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_poisoned_concrete_lookup_from_eval_fn.carbon:[[@LINE-11]]:5: note: the use had selected the `impl` here [PoisonedImplLookupConcreteResultNotePreviousImpl] + // CHECK:STDERR: impl forall [T: type] C(T) as Z {} + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: impl G() as Z {} } diff --git a/toolchain/check/testdata/impl/redeclaration.carbon b/toolchain/check/testdata/impl/redeclaration.carbon index 217725a74eeb..c240b03e4833 100644 --- a/toolchain/check/testdata/impl/redeclaration.carbon +++ b/toolchain/check/testdata/impl/redeclaration.carbon @@ -2,7 +2,7 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: @@ -176,23 +176,25 @@ class C { // CHECK:STDERR: impl C as Z {} -// --- fail_todo_redeclaration_in_block_scope_is_separate_impl.carbon +// --- fail_redeclaration_in_block_scope_is_separate_impl.carbon library "[[@TEST_NAME]]"; interface Z {} fn F() { - // CHECK:STDERR: fail_todo_redeclaration_in_block_scope_is_separate_impl.carbon:[[@LINE+4]]:22: error: semantics TODO: `HandleAutoTypeLiteral` [SemanticsTodo] - // CHECK:STDERR: musteval fn G() -> auto { - // CHECK:STDERR: ^~~~ - // CHECK:STDERR: musteval fn G() -> auto { class C {} impl C as Z {} return C; } - // TODO: This is not a redeclaration, since it's in a different scope. - // TODO: found non-final `impl` with the same type structure as another non-final `impl`. + // This is not a redeclaration, since it's in a different scope. + // CHECK:STDERR: fail_redeclaration_in_block_scope_is_separate_impl.carbon:[[@LINE+7]]:3: error: found non-final `impl` with the same type structure as another non-final `impl` [ImplNonFinalSameTypeStructure] + // CHECK:STDERR: impl G() as Z {} + // CHECK:STDERR: ^~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_redeclaration_in_block_scope_is_separate_impl.carbon:[[@LINE-7]]:5: note: other `impl` here [ImplNonFinalSameTypeStructureNote] + // CHECK:STDERR: impl C as Z {} + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: impl G() as Z {} } diff --git a/toolchain/check/testdata/main_run/fail_mismatch_return_auto.carbon b/toolchain/check/testdata/main_run/fail_mismatch_return_auto.carbon new file mode 100644 index 000000000000..1e2e9b107f3e --- /dev/null +++ b/toolchain/check/testdata/main_run/fail_mismatch_return_auto.carbon @@ -0,0 +1,19 @@ +// 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/primitives.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/main_run/fail_mismatch_return_auto.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/main_run/fail_mismatch_return_auto.carbon + +// CHECK:STDERR: fail_mismatch_return_auto.carbon:[[@LINE+4]]:1: error: invalid return type for `Main.Run` function; expected `fn (...)` or `fn (...) -> i32` [InvalidMainRunReturnType] +// CHECK:STDERR: fn Run() -> auto { +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn Run() -> auto { + return 0.0; +} diff --git a/toolchain/check/testdata/main_run/return_auto.carbon b/toolchain/check/testdata/main_run/return_auto.carbon new file mode 100644 index 000000000000..d971604f1f92 --- /dev/null +++ b/toolchain/check/testdata/main_run/return_auto.carbon @@ -0,0 +1,15 @@ +// 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/primitives.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/main_run/return_auto.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/main_run/return_auto.carbon + +fn Run() -> auto { + return 0 as i32; +} diff --git a/toolchain/diagnostics/kind.def b/toolchain/diagnostics/kind.def index cb24c664ea87..42d923ba838a 100644 --- a/toolchain/diagnostics/kind.def +++ b/toolchain/diagnostics/kind.def @@ -285,6 +285,7 @@ CARBON_DIAGNOSTIC_KIND(NamespaceDeclNotAtTopLevel) // Function call checking. CARBON_DIAGNOSTIC_KIND(CallArgCountMismatch) +CARBON_DIAGNOSTIC_KIND(CallBeforeReturnTypeDeduced) CARBON_DIAGNOSTIC_KIND(CallToNonCallable) CARBON_DIAGNOSTIC_KIND(CppCallArgTypeNotSupported) CARBON_DIAGNOSTIC_KIND(CppCallFieldNameNotSupported) @@ -296,10 +297,14 @@ CARBON_DIAGNOSTIC_KIND(InCallToEntity) CARBON_DIAGNOSTIC_KIND(InCallToFunctionParam) CARBON_DIAGNOSTIC_KIND(RefParamNoRefTag) CARBON_DIAGNOSTIC_KIND(RefTagNoRefParam) +CARBON_DIAGNOSTIC_KIND(ReturnTypeDeducedFromDefinition) CARBON_DIAGNOSTIC_KIND(SelfParameterNotAllowed) CARBON_DIAGNOSTIC_KIND(ValueForRefParam) // Function declaration checking. +CARBON_DIAGNOSTIC_KIND(DeducedReturnTypeNotAllowed) +CARBON_DIAGNOSTIC_KIND(DeducedReturnTypeNotDefinition) +CARBON_DIAGNOSTIC_KIND(DeducedReturnTypeRedecl) CARBON_DIAGNOSTIC_KIND(DefinedAbstractFunction) CARBON_DIAGNOSTIC_KIND(FunctionRedeclReturnTypeDiffers) CARBON_DIAGNOSTIC_KIND(FunctionRedeclReturnTypeDiffersNoReturn) @@ -560,6 +565,11 @@ CARBON_DIAGNOSTIC_KIND(ReturnStatementDisallowExpr) CARBON_DIAGNOSTIC_KIND(ReturnTypeHereNote) CARBON_DIAGNOSTIC_KIND(ReturnTypeOmittedNote) CARBON_DIAGNOSTIC_KIND(ReturnStatementMissingExpr) +CARBON_DIAGNOSTIC_KIND(DeducedReturnTypeMismatch) +CARBON_DIAGNOSTIC_KIND(DeducedReturnTypeMismatchPrevious) +CARBON_DIAGNOSTIC_KIND(DeducedReturnTypeWithoutReturn) +CARBON_DIAGNOSTIC_KIND(ReturnTypeDeducedAs) +CARBON_DIAGNOSTIC_KIND(ReturnTypeIsDeducedNote) CARBON_DIAGNOSTIC_KIND(ConversionFailure) CARBON_DIAGNOSTIC_KIND(ConversionFailureNonTypeToFacet) CARBON_DIAGNOSTIC_KIND(ConversionFailureFacetToFacet) diff --git a/toolchain/lower/testdata/function/definition/deduced_return.carbon b/toolchain/lower/testdata/function/definition/deduced_return.carbon new file mode 100644 index 000000000000..fbec2481c0f4 --- /dev/null +++ b/toolchain/lower/testdata/function/definition/deduced_return.carbon @@ -0,0 +1,188 @@ +// 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/primitives.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/function/definition/deduced_return.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/function/definition/deduced_return.carbon + +class C { + var a: i32; + var b: i32; +} + +fn Copy() -> auto { + return 1 as i32; +} + +fn InPlace() -> auto { + return {.a = 1, .b = 2} as C; +} + +fn ReturnedVar() -> auto { + returned var c: C = {.a = 3, .b = 4}; + return var; +} + +fn Use() -> i32 { + let unused c: C = InPlace(); + let unused d: C = ReturnedVar(); + return Copy(); +} + +// CHECK:STDOUT: ; --- +// CHECK:STDOUT: ; ModuleID = 'deduced_return.carbon' +// CHECK:STDOUT: source_filename = "deduced_return.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @C.val.32e.loc23_31 = internal constant { i32, i32 } { i32 1, i32 2 } +// CHECK:STDOUT: @C.val.7af.loc27_12 = internal constant { i32, i32 } { i32 3, i32 4 } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i32 @_CCopy.Main() #0 !dbg !46 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret i32 1, !dbg !48 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CInPlace.Main(ptr sret({ i32, i32 }) %return) #0 !dbg !49 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc23_25.3.a = getelementptr inbounds nuw { i32, i32 }, ptr %return, i32 0, i32 0, !dbg !50 +// CHECK:STDOUT: %.loc23_25.6.b = getelementptr inbounds nuw { i32, i32 }, ptr %return, i32 0, i32 1, !dbg !50 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @C.val.32e.loc23_31, i64 8, i1 false), !dbg !51 +// CHECK:STDOUT: ret void, !dbg !51 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CReturnedVar.Main(ptr sret({ i32, i32 }) %return) #0 !dbg !52 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc27_38.3.a = getelementptr inbounds nuw { i32, i32 }, ptr %return, i32 0, i32 0, !dbg !54 +// CHECK:STDOUT: %.loc27_38.6.b = getelementptr inbounds nuw { i32, i32 }, ptr %return, i32 0, i32 1, !dbg !54 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @C.val.7af.loc27_12, i64 8, i1 false), !dbg !55 +// CHECK:STDOUT: ret void, !dbg !58 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i32 @_CUse.Main() #0 !dbg !59 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc32_29.1.temp = alloca { i32, i32 }, align 4, !dbg !62 +// CHECK:STDOUT: %.loc33_33.1.temp = alloca { i32, i32 }, align 4, !dbg !65 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc32_29.1.temp), !dbg !62 +// CHECK:STDOUT: call void @_CInPlace.Main(ptr %.loc32_29.1.temp), !dbg !62 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc33_33.1.temp), !dbg !65 +// CHECK:STDOUT: call void @_CReturnedVar.Main(ptr %.loc33_33.1.temp), !dbg !65 +// CHECK:STDOUT: %Copy.call = call i32 @_CCopy.Main(), !dbg !68 +// CHECK:STDOUT: call void @"_CSelfDestruct.08d9e38b928ba575:core.Destroy.Core"(ptr %.loc33_33.1.temp), !dbg !65 +// CHECK:STDOUT: call void @"_CSelfDestruct.08d9e38b928ba575:core.Destroy.Core"(ptr %.loc32_29.1.temp), !dbg !62 +// CHECK:STDOUT: ret i32 %Copy.call, !dbg !69 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_CSelfDestruct.72395be3dbbe8946:core.Destroy.Core"(ptr %self) #0 !dbg !70 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !72 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_CSubobjectDestroy.2508ed710019eba8:core.Destroy.Core"(ptr %self) #0 !dbg !74 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc33_33.1.b = getelementptr inbounds nuw { i32, i32 }, ptr %self, i32 0, i32 1, !dbg !76 +// CHECK:STDOUT: call void @"_CSelfDestruct.72395be3dbbe8946:core.Destroy.Core"(ptr %.loc33_33.1.b), !dbg !76 +// CHECK:STDOUT: %.loc33_33.2.a = getelementptr inbounds nuw { i32, i32 }, ptr %self, i32 0, i32 0, !dbg !76 +// CHECK:STDOUT: call void @"_CSelfDestruct.72395be3dbbe8946:core.Destroy.Core"(ptr %.loc33_33.2.a), !dbg !76 +// CHECK:STDOUT: ret void, !dbg !76 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_CSelfDestruct.2508ed710019eba8:core.Destroy.Core"(ptr %self) #0 !dbg !78 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @"_CSubobjectDestroy.2508ed710019eba8:core.Destroy.Core"(ptr %self), !dbg !80 +// CHECK:STDOUT: ret void, !dbg !80 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_CSubobjectDestroy.08d9e38b928ba575:core.Destroy.Core"(ptr %self) #0 !dbg !82 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc33_33.1.b = getelementptr inbounds nuw { i32, i32 }, ptr %self, i32 0, i32 1, !dbg !84 +// CHECK:STDOUT: call void @"_CSelfDestruct.72395be3dbbe8946:core.Destroy.Core"(ptr %.loc33_33.1.b), !dbg !84 +// CHECK:STDOUT: %.loc33_33.2.a = getelementptr inbounds nuw { i32, i32 }, ptr %self, i32 0, i32 0, !dbg !84 +// CHECK:STDOUT: call void @"_CSelfDestruct.72395be3dbbe8946:core.Destroy.Core"(ptr %.loc33_33.2.a), !dbg !84 +// CHECK:STDOUT: ret void, !dbg !84 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_CSelfDestruct.08d9e38b928ba575:core.Destroy.Core"(ptr %self) #0 !dbg !86 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @"_CSubobjectDestroy.08d9e38b928ba575:core.Destroy.Core"(ptr %self), !dbg !88 +// CHECK:STDOUT: ret void, !dbg !88 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @"_CSelfDestruct.08d9e38b928ba575:core.Destroy.Core", { 1, 0 } +// CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 } +// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.dbg.cu = !{!35} +// CHECK:STDOUT: !llvm.module.flags = !{!2, !3} +// CHECK:STDOUT: +// CHECK:STDOUT: !2 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !3 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !34 = !DIFile(filename: "deduced_return.carbon", directory: "") +// CHECK:STDOUT: !35 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !34, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !36 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +// CHECK:STDOUT: !37 = !{!36} +// CHECK:STDOUT: !38 = !DISubroutineType(types: !37) +// CHECK:STDOUT: !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !40 = !{!39} +// CHECK:STDOUT: !41 = !DISubroutineType(types: !40) +// CHECK:STDOUT: !42 = !{null, !36} +// CHECK:STDOUT: !43 = !DISubroutineType(types: !42) +// CHECK:STDOUT: !44 = !{null, !39} +// CHECK:STDOUT: !45 = !DISubroutineType(types: !44) +// CHECK:STDOUT: !46 = distinct !DISubprogram(name: "Copy", linkageName: "_CCopy.Main", scope: null, file: !34, line: 18, type: !38, spFlags: DISPFlagDefinition, unit: !35) +// CHECK:STDOUT: !48 = !DILocation(line: 19, column: 3, scope: !46) +// CHECK:STDOUT: !49 = distinct !DISubprogram(name: "InPlace", linkageName: "_CInPlace.Main", scope: null, file: !34, line: 22, type: !41, spFlags: DISPFlagDefinition, unit: !35) +// CHECK:STDOUT: !50 = !DILocation(line: 23, column: 10, scope: !49) +// CHECK:STDOUT: !51 = !DILocation(line: 23, column: 3, scope: !49) +// CHECK:STDOUT: !52 = distinct !DISubprogram(name: "ReturnedVar", linkageName: "_CReturnedVar.Main", scope: null, file: !34, line: 26, type: !41, spFlags: DISPFlagDefinition, unit: !35) +// CHECK:STDOUT: !54 = !DILocation(line: 27, column: 23, scope: !52) +// CHECK:STDOUT: !55 = !DILocation(line: 27, column: 12, scope: !52) +// CHECK:STDOUT: !58 = !DILocation(line: 28, column: 3, scope: !52) +// CHECK:STDOUT: !59 = distinct !DISubprogram(name: "Use", linkageName: "_CUse.Main", scope: null, file: !34, line: 31, type: !38, spFlags: DISPFlagDefinition, unit: !35) +// CHECK:STDOUT: !62 = !DILocation(line: 32, column: 21, scope: !59) +// CHECK:STDOUT: !65 = !DILocation(line: 33, column: 21, scope: !59) +// CHECK:STDOUT: !68 = !DILocation(line: 34, column: 10, scope: !59) +// CHECK:STDOUT: !69 = !DILocation(line: 34, column: 3, scope: !59) +// CHECK:STDOUT: !70 = distinct !DISubprogram(name: "SelfDestruct", linkageName: "_CSelfDestruct.72395be3dbbe8946:core.Destroy.Core", scope: null, file: !34, line: 33, type: !43, spFlags: DISPFlagDefinition, unit: !35, retainedNodes: !73) +// CHECK:STDOUT: !71 = !DILocalVariable(arg: 1, scope: !70, type: !36) +// CHECK:STDOUT: !72 = !DILocation(line: 33, column: 21, scope: !70) +// CHECK:STDOUT: !73 = !{!71} +// CHECK:STDOUT: !74 = distinct !DISubprogram(name: "SubobjectDestroy", linkageName: "_CSubobjectDestroy.2508ed710019eba8:core.Destroy.Core", scope: null, file: !34, line: 33, type: !45, spFlags: DISPFlagDefinition, unit: !35, retainedNodes: !77) +// CHECK:STDOUT: !75 = !DILocalVariable(arg: 1, scope: !74, type: !39) +// CHECK:STDOUT: !76 = !DILocation(line: 33, column: 21, scope: !74) +// CHECK:STDOUT: !77 = !{!75} +// CHECK:STDOUT: !78 = distinct !DISubprogram(name: "SelfDestruct", linkageName: "_CSelfDestruct.2508ed710019eba8:core.Destroy.Core", scope: null, file: !34, line: 33, type: !45, spFlags: DISPFlagDefinition, unit: !35, retainedNodes: !81) +// CHECK:STDOUT: !79 = !DILocalVariable(arg: 1, scope: !78, type: !39) +// CHECK:STDOUT: !80 = !DILocation(line: 33, column: 21, scope: !78) +// CHECK:STDOUT: !81 = !{!79} +// CHECK:STDOUT: !82 = distinct !DISubprogram(name: "SubobjectDestroy", linkageName: "_CSubobjectDestroy.08d9e38b928ba575:core.Destroy.Core", scope: null, file: !34, line: 33, type: !45, spFlags: DISPFlagDefinition, unit: !35, retainedNodes: !85) +// CHECK:STDOUT: !83 = !DILocalVariable(arg: 1, scope: !82, type: !39) +// CHECK:STDOUT: !84 = !DILocation(line: 33, column: 21, scope: !82) +// CHECK:STDOUT: !85 = !{!83} +// CHECK:STDOUT: !86 = distinct !DISubprogram(name: "SelfDestruct", linkageName: "_CSelfDestruct.08d9e38b928ba575:core.Destroy.Core", scope: null, file: !34, line: 33, type: !45, spFlags: DISPFlagDefinition, unit: !35, retainedNodes: !89) +// CHECK:STDOUT: !87 = !DILocalVariable(arg: 1, scope: !86, type: !39) +// CHECK:STDOUT: !88 = !DILocation(line: 33, column: 21, scope: !86) +// CHECK:STDOUT: !89 = !{!87} +// CHECK:STDOUT: diff --git a/toolchain/parse/node_category.h b/toolchain/parse/node_category.h index 87bb568a96bd..22f38590803b 100644 --- a/toolchain/parse/node_category.h +++ b/toolchain/parse/node_category.h @@ -32,7 +32,7 @@ namespace Carbon::Parse { /* `require impls` or just `require impls` */ \ X(RequireImpls) \ X(Requirement) \ - X(ReturnDecl) \ + X(ReturnSpecifier) \ X(Statement) // We expect this to grow, so are using a bigger size than needed. diff --git a/toolchain/parse/node_ids.h b/toolchain/parse/node_ids.h index 70140a3abbe7..3c944ee5bd60 100644 --- a/toolchain/parse/node_ids.h +++ b/toolchain/parse/node_ids.h @@ -117,7 +117,7 @@ using AnyObserveOperandId = NodeIdInCategory; using AnyNonExprNameId = NodeIdInCategory; using AnyPackageNameId = NodeIdInCategory; -using AnyReturnDeclId = NodeIdInCategory; +using AnyReturnSpecifierId = NodeIdInCategory; namespace Internal { template diff --git a/toolchain/parse/typed_nodes.h b/toolchain/parse/typed_nodes.h index ffef3015426c..9ac8adb48082 100644 --- a/toolchain/parse/typed_nodes.h +++ b/toolchain/parse/typed_nodes.h @@ -128,7 +128,7 @@ using EmptyDecl = LeafNode; // A name that may be immediately followed by a signature (i.e. parameter lists -// and/or a return declaration). There may be false positives, because we make +// and/or a return specifier). There may be false positives, because we make // this determination based on the context and a single token of lookahead. using IdentifierNameMaybeBeforeSignature = LeafNode; // A name that is known not to be immediately followed by a signature (i.e. -// parameter lists and/or a return declaration). +// parameter lists and/or a return specifier). using IdentifierNameNotBeforeSignature = LeafNode i32`. struct ReturnType { static constexpr auto Kind = NodeKind::ReturnType.Define( - {.category = NodeCategory::ReturnDecl, .child_count = 1}); + {.category = NodeCategory::ReturnSpecifier, .child_count = 1}); Lex::MinusGreaterTokenIndex token; AnyExprId type; @@ -583,7 +583,7 @@ struct ReturnType { // A return form: `->? form(var i32)` struct ReturnForm { static constexpr auto Kind = NodeKind::ReturnForm.Define( - {.category = NodeCategory::ReturnDecl, .child_count = 1}); + {.category = NodeCategory::ReturnSpecifier, .child_count = 1}); Lex::MinusGreaterQuestionTokenIndex token; AnyExprId type; @@ -599,7 +599,7 @@ struct FunctionSignature { FunctionIntroducerId introducer; llvm::SmallVector modifiers; DeclName name; - std::optional return_type; + std::optional return_type; TokenKind token; }; @@ -795,7 +795,7 @@ struct LambdaDefinitionStart { LambdaIntroducerId introducer; std::optional implicit_params; std::optional explicit_params; - std::optional return_type; + std::optional return_type; Lex::TokenIndex token; }; diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index ae8587c995e5..dd29df861c0c 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -571,9 +571,15 @@ auto Formatter::FormatFunction(FunctionId id, const Function& fn) -> void { llvm::SaveAndRestore function_scope(scope_, inst_namer_.GetScopeFor(id)); + // The return type might not have been deduced if checking stopped early. FormatFunctionSignature(fn.call_params_id, fn.call_param_ranges.return_begin(), - fn.GetDeclaredReturnForm(*sem_ir_)); + fn.has_pending_deduced_return_type() + ? InstId::None + : fn.GetDeclaredReturnForm(*sem_ir_)); + if (fn.has_deduced_return_type) { + out() << " [deduced]"; + } if (auto builtin_kind = fn.GetBuiltinFunctionKind(*sem_ir_); builtin_kind != BuiltinFunctionKind::None) { diff --git a/toolchain/sem_ir/function.cpp b/toolchain/sem_ir/function.cpp index 69fe62d6046d..e7382d93c3b5 100644 --- a/toolchain/sem_ir/function.cpp +++ b/toolchain/sem_ir/function.cpp @@ -176,6 +176,8 @@ auto Function::GetBuiltinFunctionKind(const File& file) const auto Function::GetDeclaredReturnType(const File& file, SpecificId specific_id) const -> TypeId { + CARBON_CHECK(!has_pending_deduced_return_type(), + "Querying return type of function before it is deduced"); if (!return_type_inst_id.has_value()) { return TypeId::None; } @@ -185,6 +187,8 @@ auto Function::GetDeclaredReturnType(const File& file, auto Function::GetDeclaredReturnForm(const File& file, SpecificId specific_id) const -> InstId { + CARBON_CHECK(!has_pending_deduced_return_type(), + "Querying return form of function before it is deduced"); if (!return_form_inst_id.has_value()) { // Treat as equivalent to `-> ()`. return InstId::None; diff --git a/toolchain/sem_ir/function.h b/toolchain/sem_ir/function.h index b9335867c66a..a446328f57fb 100644 --- a/toolchain/sem_ir/function.h +++ b/toolchain/sem_ir/function.h @@ -67,7 +67,10 @@ struct FunctionFields { enum class InterfaceModifier : uint8_t { None, Default, Final }; // The following members always have values, and do not change throughout the - // lifetime of the function. + // lifetime of the function. The exception is a function with a deduced return + // type: its `Call` parameters, parameter pattern block and return + // information are extended once, when the return type is deduced. See + // `has_pending_deduced_return_type`. // This block consists of references to the `*ParamPattern` insts that // represent the function's `Call` parameters. The "`Call` parameters" are the @@ -149,8 +152,8 @@ struct FunctionFields { InstId return_form_inst_id; // The parameter pattern inst that is declared by the function's return - // declaration. This will be a ReturnSlotPattern, or None if the function - // doesn't have a return declaration. It may or may not be used, depending on + // specifier. This will be a ReturnSlotPattern, or None if the function + // doesn't have a return specifier. It may or may not be used, depending on // whether the type has an in-place initializing representation. // // TODO: Extend this to support composite return forms. @@ -180,6 +183,11 @@ struct FunctionFields { // Which, if any, interface modifier is applied to this function. InterfaceModifier interface_modifier = InterfaceModifier::None; + // Whether the function's return type is deduced from its body, as in + // `-> auto`. Until the return type is deduced, the function has no return + // information; see `has_pending_deduced_return_type`. + bool has_deduced_return_type = false; + // The `self` parameter pattern, if any. This is the first pattern in // `param_patterns_id` (from EntityWithParamsBase). InstId self_param_id = InstId::None; @@ -264,6 +272,9 @@ struct Function : public EntityWithParamsBase, if (interface_modifier != InterfaceModifier::None) { out << ", interface_modifier: " << interface_modifier; } + if (has_deduced_return_type) { + out << ", has_deduced_return_type: true"; + } if (!body_block_ids.empty()) { out << llvm::formatv( ", body: [{0}]", @@ -319,10 +330,20 @@ struct Function : public EntityWithParamsBase, // to both Builtin and Generated special functions. auto GetBuiltinFunctionKind(const File& file) const -> BuiltinFunctionKind; + // Returns whether this function's return type is to be deduced from its + // body, but has not been deduced yet. This is the case only between the + // function's declaration and the end of its body, or its first `returned + // var`. In this state, the function has no return information, and + // querying its return type or form is an error. + auto has_pending_deduced_return_type() const -> bool { + return has_deduced_return_type && !return_form_inst_id.has_value(); + } + // Gets the declared return type for a specific version of this function, or // the canonical return type for the original declaration no specific is // specified. Returns `None` if no return type was specified, in which - // case the effective return type is an empty tuple. + // case the effective return type is an empty tuple. The function must not + // have an undeduced return type. auto GetDeclaredReturnType(const File& file, SpecificId specific_id = SpecificId::None) const -> TypeId; @@ -331,6 +352,7 @@ struct Function : public EntityWithParamsBase, // function, or for the original declaration if no specific is specified. // Returns `None` if the function was declared without a return form, in which // case the effective return form is an empty tuple initializing expression. + // The function must not have an undeduced return type. auto GetDeclaredReturnForm(const File& file, SpecificId specific_id = SpecificId::None) const -> InstId; diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index a4e8ecbdb53d..39a7598993de 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -1571,7 +1571,7 @@ using NamespaceType = SingletonTypeInst">; // `form_id`. See `AnyFormParamAction` for member documentation. struct OutFormParamPatternAction { static constexpr auto Kind = - // TODO: Use Parse::AnyReturnDeclId once we support passing node + // TODO: Use Parse::AnyReturnSpecifierId once we support passing node // categories to Define. InstKind::OutFormParamPatternAction .Define>(