diff --git a/toolchain/check/call.cpp b/toolchain/check/call.cpp index b60192e3bd6d..892a97fdb8a8 100644 --- a/toolchain/check/call.cpp +++ b/toolchain/check/call.cpp @@ -268,7 +268,7 @@ auto PerformCallToFunction(Context& context, SemIR::LocId loc_id, .Emit(); return SemIR::ErrorInst::InstId; } - if (callee.has_deduced_return_type && callee_specific_id->has_value()) { + 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; diff --git a/toolchain/check/cpp/export.cpp b/toolchain/check/cpp/export.cpp index 8ac814b8d4f7..2c4853c2644f 100644 --- a/toolchain/check/cpp/export.cpp +++ b/toolchain/check/cpp/export.cpp @@ -1578,7 +1578,7 @@ static auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_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())) { + (callee.has_deduced_return_type() && callee.generic_id.has_value())) { context.TODO(loc_id, "export of function with deduced return type"); return nullptr; } diff --git a/toolchain/check/function.cpp b/toolchain/check/function.cpp index f0ed7cc4e4f8..c41f462a9976 100644 --- a/toolchain/check/function.cpp +++ b/toolchain/check/function.cpp @@ -66,10 +66,32 @@ auto PopReturnSpecifier(Context& context) -> ReturnSpecifierInfo { if (!pattern_id) { return {}; } + // For a deduced return type, `HandleReturnSpecifier` pushes a marker in place + // of a return pattern, and doesn't push a return form. The marker is either + // `auto`, for `-> auto`, or a primitive form whose type component is `auto`, + // for `-> ref auto` and similar. + using DeducedReturnKind = SemIR::Function::DeducedReturnKind; 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, .deduced_return_kind = DeducedReturnKind::Init}; + } + if (auto form = + context.insts().TryGetAs(*pattern_id)) { + CARBON_CHECK(form->type_component_id == SemIR::AutoType::TypeInstId); + auto kind = DeducedReturnKind::None; + switch (form->kind) { + case SemIR::InitForm::Kind: + kind = DeducedReturnKind::Init; + break; + case SemIR::RefForm::Kind: + kind = DeducedReturnKind::Ref; + break; + case SemIR::ValueForm::Kind: + kind = DeducedReturnKind::Value; + break; + default: + CARBON_FATAL("Unexpected primitive form kind: {0}", form->kind); + } + return {.node_id = node_id, .deduced_return_kind = kind}; } return {.node_id = node_id, .pattern_id = *pattern_id, @@ -441,6 +463,8 @@ auto SetDeducedReturnType(Context& context, SemIR::FunctionId function_id, auto& function = context.functions().Get(function_id); CARBON_CHECK(function.has_pending_deduced_return_type()); + loc_id = context.insts().GetLocIdForDesugaring(loc_id); + // 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(); @@ -467,11 +491,27 @@ auto SetDeducedReturnType(Context& context, SemIR::FunctionId function_id, context, loc_id, {.type_id = SemIR::TypeType::TypeId, .value_id = context.types().GetTypeInstId(type_id)}); + SemIR::InstKind form_kind = SemIR::InitForm::Kind; + switch (function.deduced_return_kind) { + case SemIR::Function::DeducedReturnKind::None: + CARBON_FATAL("Return type is not deduced"); + case SemIR::Function::DeducedReturnKind::Init: + form_kind = SemIR::InitForm::Kind; + break; + case SemIR::Function::DeducedReturnKind::Ref: + form_kind = SemIR::RefForm::Kind; + break; + case SemIR::Function::DeducedReturnKind::Value: + form_kind = SemIR::ValueForm::Kind; + break; + } 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})); + context, + SemIR::LocIdAndInst::RuntimeVerified( + context.sem_ir(), loc_id, + SemIR::AnyPrimitiveForm{.kind = form_kind, + .type_id = SemIR::FormType::TypeId, + .type_component_id = type_inst_id})); form = {.form_inst_id = form_inst_id, .type_component_inst_id = type_inst_id, .type_component_id = diff --git a/toolchain/check/function.h b/toolchain/check/function.h index 20f755f01c9d..a6c29ce3c1ca 100644 --- a/toolchain/check/function.h +++ b/toolchain/check/function.h @@ -34,8 +34,10 @@ auto AddReturnPattern(Context& context, SemIR::LocId loc_id, 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; + // Whether, and how, the return type is deduced from the body, as in + // `-> auto` or `-> ref auto`. + SemIR::Function::DeducedReturnKind deduced_return_kind = + SemIR::Function::DeducedReturnKind::None; // The declared return pattern. `None` if there is no return specifier or // the return type is deduced. SemIR::InstId pattern_id = SemIR::InstId::None; @@ -44,6 +46,9 @@ struct ReturnSpecifierInfo { Context::FormExpr form = Context::FormExpr::None; auto has_return_specifier() const -> bool { return node_id.has_value(); } + auto is_deduced() const -> bool { + return deduced_return_kind != SemIR::Function::DeducedReturnKind::None; + } }; // Pops the return specifier of a function declaration or function expression diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 24f462de369c..56905977f70c 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -64,14 +64,24 @@ static auto HandleReturnSpecifier(Context& context, 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) { + if (is_return_type && expr_kind == 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; } + if (is_primitive_form && + context.insts() + .GetAs(expr_inst_id) + .type_component_id == SemIR::AutoType::TypeInstId) { + // `-> ref auto`, `-> val auto`, or `-> var auto`: the category is known but + // the type is deduced from the function body. Push the primitive form, + // whose type component is `auto`, as a marker in place of the pattern; see + // `PopReturnSpecifier`. + context.node_stack().Push(node_id, expr_inst_id); + 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); @@ -483,7 +493,7 @@ static auto BuildFunctionDecl(Context& context, // 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 && + return_specifier.is_deduced() && !CheckDeducedReturnTypeAllowed(context, node_id, return_specifier.node_id, is_definition, is_extern, virtual_modifier, parent_scope_inst); @@ -509,7 +519,7 @@ static auto BuildFunctionDecl(Context& context, .virtual_modifier = virtual_modifier, .evaluation_mode = evaluation_mode, .interface_modifier = interface_modifier, - .has_deduced_return_type = return_specifier.is_deduced, + .deduced_return_kind = return_specifier.deduced_return_kind, .self_param_id = self_param_id, }}; if (is_definition) { @@ -571,7 +581,7 @@ static auto BuildFunctionDecl(Context& context, // 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) { + if (!return_specifier.is_deduced()) { ValidateForEntryPoint(context, node_id, function_decl.function_id, function_info); } @@ -708,7 +718,7 @@ auto HandleParseNode(Context& context, Parse::FunctionDefinitionId node_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) { + if (function.has_deduced_return_type()) { ValidateForEntryPoint(context, start_node_id, function_id, function); } diff --git a/toolchain/check/handle_lambda.cpp b/toolchain/check/handle_lambda.cpp index b1be626af776..fa2c4423aee7 100644 --- a/toolchain/check/handle_lambda.cpp +++ b/toolchain/check/handle_lambda.cpp @@ -112,7 +112,7 @@ auto HandleParseNode(Context& context, Parse::LambdaDefinitionStartId node_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, + .deduced_return_kind = return_specifier.deduced_return_kind, }}); context.inst_block_stack().AddInstId(decl_id); diff --git a/toolchain/check/handle_literal.cpp b/toolchain/check/handle_literal.cpp index add26d412ded..eb610e6e7a93 100644 --- a/toolchain/check/handle_literal.cpp +++ b/toolchain/check/handle_literal.cpp @@ -149,15 +149,28 @@ 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`. + // function, as in `fn F() -> auto`, or as the type in a primitive return + // form, as in `fn F() -> ref auto`. In those cases, `auto` is the only child + // of a `ReturnType` node or a primitive form 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) { + // `HandleReturnSpecifier` recognizes the `auto`, so the value we push here is + // just a placeholder. For a primitive form, the primitive form handler will + // use the placeholder as its type component. + // + // TODO: Using parse tree lookahead is a hack; stop doing this. Ideally we'd + // fix this as part of a change to accept and handle `auto` more broadly. + auto next_node_kind = + context.parse_tree().node_kind(Parse::NodeId(node_id.index + 1)); + bool is_return_type = next_node_kind == Parse::NodeKind::ReturnType; + bool is_return_primitive_form = + (next_node_kind == Parse::NodeKind::RefPrimitiveForm || + next_node_kind == Parse::NodeKind::ValPrimitiveForm || + next_node_kind == Parse::NodeKind::VarPrimitiveForm) && + context.parse_tree().node_kind(Parse::NodeId(node_id.index + 2)) == + Parse::NodeKind::ReturnType; + if (is_return_type || is_return_primitive_form) { context.node_stack().Push(node_id, SemIR::AutoType::TypeInstId); return true; } diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index 783c71232c7d..1d38c23ed84e 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -2765,8 +2765,7 @@ 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; + new_function.deduced_return_kind = import_function.deduced_return_kind; 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 93e0f958a4b6..31182d9c242e 100644 --- a/toolchain/check/merge.cpp +++ b/toolchain/check/merge.cpp @@ -833,8 +833,8 @@ auto TryMergeRedecl(Context& context, // 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) { + 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() diff --git a/toolchain/check/return.cpp b/toolchain/check/return.cpp index 64ae8700c783..6d1ae99748a8 100644 --- a/toolchain/check/return.cpp +++ b/toolchain/check/return.cpp @@ -304,7 +304,12 @@ auto RegisterReturnedVar(Context& context, Parse::NodeId returned_node, auto diag = context.emitter().Build(returned_node, ReturnedVarNotInit); CARBON_DIAGNOSTIC_LABEL(ReturnFormHereNote, Info, "return form declared here"); - diag.Attach(function.return_form_inst_id, ReturnFormHereNote); + // A deduced return form is located where its type was deduced from, which + // is this `returned var`, so point at the function declaration instead. + diag.Attach(function.has_deduced_return_type() + ? function.latest_decl_id() + : function.return_form_inst_id, + ReturnFormHereNote); diag.Emit(); } diff --git a/toolchain/check/testdata/function/deduced_return/form.carbon b/toolchain/check/testdata/function/deduced_return/form.carbon new file mode 100644 index 000000000000..f1c3f8dc6b90 --- /dev/null +++ b/toolchain/check/testdata/function/deduced_return/form.carbon @@ -0,0 +1,435 @@ +// 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/form.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/deduced_return/form.carbon + +// --- ref_auto.carbon +library "[[@TEST_NAME]]"; + +var g: i32 = 0; + +//@dump-sem-ir-begin +fn F() -> ref auto { + return g; +} + +fn G() -> ref i32 { + return F(); +} +//@dump-sem-ir-end + +// --- ref_auto_param.carbon +library "[[@TEST_NAME]]"; + +class C { + var a: i32; + var b: i32; +} + +fn F(ref c: C, select_a: bool) -> ref auto { + if (select_a) { + return c.a; + } + return c.b; +} + +fn G(ref c: C) -> ref i32 { + return F(ref c, true); +} + +// --- ref_auto_const.carbon +library "[[@TEST_NAME]]"; + +var g: const i32 = 0 as i32; + +// The deduced type retains `const`. +fn F() -> ref auto { + return g; +} + +fn G() -> ref const i32 { + return F(); +} + +// --- val_auto.carbon +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +fn F() -> val auto { + return 1 as i32; +} + +fn G() { + let unused x: i32 = F(); +} +//@dump-sem-ir-end + +// --- var_auto.carbon +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +fn F() -> var auto { + return 1 as i32; +} + +fn G() { + var unused x: i32 = F(); +} +//@dump-sem-ir-end + +// --- var_auto_returned_var.carbon +library "[[@TEST_NAME]]"; + +fn F() -> var auto { + returned var v: i32 = 1; + return var; +} + +// --- fail_ref_auto_non_ref.carbon +library "[[@TEST_NAME]]"; + +fn F() -> ref auto { + // CHECK:STDERR: fail_ref_auto_non_ref.carbon:[[@LINE+3]]:3-18: error: cannot bind durable reference to non-reference value of type `i32` [ConversionFailureNonRefToRef] + // CHECK:STDERR: fail_ref_auto_non_ref.carbon:[[@LINE+2]]:10-17: note: return type deduced as `i32` [ReturnTypeDeducedAs] + // CHECK:STDERR: + return 0 as i32; +} + +// --- fail_ref_auto_returned_var.carbon +library "[[@TEST_NAME]]"; + +fn F() -> ref auto { + // CHECK:STDERR: fail_ref_auto_returned_var.carbon:[[@LINE+3]]:3-10: error: `returned var` declaration in function with non-initializing return form [ReturnedVarNotInit] + // CHECK:STDERR: fail_ref_auto_returned_var.carbon:[[@LINE-2]]:1-20: note: return form declared here [ReturnFormHereNote] + // CHECK:STDERR: + returned var v: i32 = 1; + return var; +} + +// --- fail_val_auto_returned_var.carbon +library "[[@TEST_NAME]]"; + +fn F() -> val auto { + // CHECK:STDERR: fail_val_auto_returned_var.carbon:[[@LINE+3]]:3-10: error: `returned var` declaration in function with non-initializing return form [ReturnedVarNotInit] + // CHECK:STDERR: fail_val_auto_returned_var.carbon:[[@LINE-2]]:1-20: note: return form declared here [ReturnFormHereNote] + // CHECK:STDERR: + returned var v: i32 = 1; + return var; +} + +// --- fail_ref_auto_forward_decl.carbon +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_ref_auto_forward_decl.carbon:[[@LINE+2]]:8-18: error: forward declaration of function with deduced return type [DeducedReturnTypeNotDefinition] +// CHECK:STDERR: +fn F() -> ref auto; + +// --- fail_ref_auto_missing_return_value.carbon +library "[[@TEST_NAME]]"; + +fn F() -> ref auto { + // CHECK:STDERR: fail_ref_auto_missing_return_value.carbon:[[@LINE+3]]:3-9: error: missing return value [ReturnStatementMissingExpr] + // CHECK:STDERR: fail_ref_auto_missing_return_value.carbon:[[@LINE-2]]:1-20: note: return type of function is deduced [ReturnTypeIsDeducedNote] + // CHECK:STDERR: + return; +} + +// --- fail_todo_form_literal.carbon +library "[[@TEST_NAME]]"; + +var g: i32 = 0; + +// CHECK:STDERR: fail_todo_form_literal.carbon:[[@LINE+2]]:21-24: error: semantics TODO: `HandleAutoTypeLiteral` [SemanticsTodo] +// CHECK:STDERR: +fn F() ->? form(ref auto) { + return g; +} + +// CHECK:STDOUT: --- ref_auto.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: %pattern_type.d94: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: %.010: Core.Form = ref_form auto [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %.30e: Core.Form = ref_form %i32 [concrete] +// CHECK:STDOUT: %.b71: %pattern_type.d94 = ref_return_pattern [concrete] +// CHECK:STDOUT: %return.patt.2c5: %pattern_type.d94 = return_slot_pattern %.b71, %i32 [concrete] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %.loc7_10.1: %pattern_type.d94 = ref_return_pattern [concrete = constants.%.b71] +// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %.loc7_10.1, %i32 [concrete = constants.%return.patt.2c5] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.loc6: Core.Form = ref_form auto [concrete = constants.%.010] +// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc7_10.2: Core.Form = ref_form %i32 [concrete = constants.%.30e] +// CHECK:STDOUT: %.loc7_10.3: ref %i32 = ref_return +// CHECK:STDOUT: %return: ref %i32 = return_slot %.loc7_10.3 +// CHECK:STDOUT: } +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { +// CHECK:STDOUT: %.loc10_11.1: %pattern_type.d94 = ref_return_pattern [concrete = constants.%.b71] +// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %.loc10_11.1, %.loc10_11.3 [concrete = constants.%return.patt.2c5] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc10_11.2: Core.Form = ref_form %i32 [concrete = constants.%.30e] +// CHECK:STDOUT: %.loc10_11.3: type = type_component_of %.loc10_11.2 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc10_11.4: ref %i32 = ref_return +// CHECK:STDOUT: %return: ref %i32 = return_slot %.loc10_11.4 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() -> ref %i32 [deduced] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %g.ref: ref %i32 = name_ref g, file.%g [concrete = file.%g.var] +// CHECK:STDOUT: %.loc7_11: ref %i32 = splice_block %g.ref [concrete = file.%g.var] {} +// CHECK:STDOUT: return %.loc7_11 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() -> ref %i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %F.call: ref %i32 = call %F.ref() +// CHECK:STDOUT: return %F.call +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- val_auto.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: type: type = facet_type [concrete] +// CHECK:STDOUT: %.0c3: Core.Form = value_form auto [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_32: Core.IntLiteral = int_value 32 [concrete] +// 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_1.5b8, %Core.IntLiteral.as.As.impl.Convert.94f [concrete] +// CHECK:STDOUT: %pattern_type.d94: type = pattern_type %i32 [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: = bound_method %int_1.5b8, %Core.IntLiteral.as.As.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_1.68a: %i32 = int_value 1 [concrete] +// CHECK:STDOUT: %.acd: Core.Form = value_form %i32 [concrete] +// CHECK:STDOUT: %.45b: %pattern_type.d94 = value_return_pattern [concrete] +// CHECK:STDOUT: %return.patt.f8b: %pattern_type.d94 = return_slot_pattern %.45b, %i32 [concrete] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: %x.patt: %pattern_type.d94 = value_binding_pattern x [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: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %.loc5_12.1: %pattern_type.d94 = value_return_pattern [concrete = constants.%.45b] +// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %.loc5_12.1, %i32.loc5_12 [concrete = constants.%return.patt.f8b] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.loc4: Core.Form = value_form auto [concrete = constants.%.0c3] +// CHECK:STDOUT: %i32.loc5_12: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc5_12.4: Core.Form = value_form %i32.loc5_12 [concrete = constants.%.acd] +// CHECK:STDOUT: %.loc5_12.5: %i32 = value_return +// CHECK:STDOUT: %return: %i32 = return_slot %.loc5_12.5 +// CHECK:STDOUT: } +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() -> val %i32 [deduced] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] +// CHECK:STDOUT: %i32.loc5_15: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %impl.elem0: %.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_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound] +// CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @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_1, %specific_fn [concrete = constants.%bound_method] +// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.call: init %i32 = call %bound_method.loc5_12.2(%int_1) [concrete = constants.%int_1.68a] +// CHECK:STDOUT: %.loc5_12.2: %i32 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.68a] +// CHECK:STDOUT: %.loc5_12.3: %i32 = converted %int_1, %.loc5_12.2 [concrete = constants.%int_1.68a] +// CHECK:STDOUT: %.loc5_18: %i32 = splice_block %.loc5_12.3 [concrete = constants.%int_1.68a] {} +// CHECK:STDOUT: return %.loc5_18 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %F.call: %i32 = call %F.ref() +// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %x: %i32 = wrapper_binding x, %F.call +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %x.patt: %pattern_type.d94 = value_binding_pattern x [concrete = constants.%x.patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- var_auto.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: type: type = facet_type [concrete] +// CHECK:STDOUT: %.79e: Core.Form = init_form auto [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: %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_1.5b8, %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.5c4: = bound_method %int_1.5b8, %Core.IntLiteral.as.As.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_1.68a: %i32 = int_value 1 [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_1.68a, %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.a13: = bound_method %int_1.68a, %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: %x.patt: %pattern_type.d94 = ref_binding_pattern x [concrete] +// CHECK:STDOUT: %x.var_patt: %pattern_type.d94 = var_pattern %x.patt [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.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: 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: %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: %.loc4: Core.Form = init_form auto [concrete = constants.%.79e] +// 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: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() -> out %return.param: %i32 [deduced] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] +// 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_1, %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_1, %specific_fn.loc5_12.1 [concrete = constants.%bound_method.5c4] +// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.call: init %i32 = call %bound_method.loc5_12.2(%int_1) [concrete = constants.%int_1.68a] +// CHECK:STDOUT: %.loc5_12.1: %i32 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.68a] +// CHECK:STDOUT: %.loc5_12.2: %i32 = converted %int_1, %.loc5_12.1 [concrete = constants.%int_1.68a] +// CHECK:STDOUT: %.loc5_18: init %i32 = splice_block %Int.as.Copy.impl.Op.call [concrete = constants.%int_1.68a] { +// 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.a13] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc5_12.4(%.loc5_12.2) [concrete = constants.%int_1.68a] +// CHECK:STDOUT: } +// CHECK:STDOUT: return %.loc5_18 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %x.var: ref %i32 = var_storage %x.var_patt +// 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: assign %x.var, %F.call +// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %x: ref %i32 = wrapper_binding x, %x.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %x.patt: %pattern_type.d94 = ref_binding_pattern x [concrete = constants.%x.patt] +// CHECK:STDOUT: %x.var_patt: %pattern_type.d94 = var_pattern %x.patt [concrete = constants.%x.var_patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: = bound_method %x.var, constants.%Destroy.WithSelf.SelfDestruct.db3 +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound(%x.var) +// CHECK:STDOUT: return +// 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: diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index dd29df861c0c..34b80c399be4 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -577,7 +577,7 @@ auto Formatter::FormatFunction(FunctionId id, const Function& fn) -> void { fn.has_pending_deduced_return_type() ? InstId::None : fn.GetDeclaredReturnForm(*sem_ir_)); - if (fn.has_deduced_return_type) { + if (fn.has_deduced_return_type()) { out() << " [deduced]"; } diff --git a/toolchain/sem_ir/function.h b/toolchain/sem_ir/function.h index a446328f57fb..5399671fa41c 100644 --- a/toolchain/sem_ir/function.h +++ b/toolchain/sem_ir/function.h @@ -66,6 +66,20 @@ struct FunctionFields { // TODO: turn into a CARBON_ENUM for more convenient checks. enum class InterfaceModifier : uint8_t { None, Default, Final }; + // Kinds of deduced return. For a function whose return type is deduced from + // its body, this is the category of the return form, which is specified + // explicitly, as in `-> ref auto`. + enum class DeducedReturnKind : uint8_t { + // The return type is not deduced. + None, + // `-> auto` or `-> var auto`. + Init, + // `-> ref auto`. + Ref, + // `-> val auto`. + Value, + }; + // The following members always have values, and do not change throughout the // lifetime of the function. The exception is a function with a deduced return // type: its `Call` parameters, parameter pattern block and return @@ -183,10 +197,10 @@ 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 + // Whether, and how, 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; + DeducedReturnKind deduced_return_kind = DeducedReturnKind::None; // The `self` parameter pattern, if any. This is the first pattern in // `param_patterns_id` (from EntityWithParamsBase). @@ -218,6 +232,23 @@ struct FunctionFields { CARBON_FATAL("unhandled `ImplModifier` during printing"); } + + friend auto operator<<(llvm::raw_ostream& out, DeducedReturnKind kind) + -> llvm::raw_ostream& { + using enum DeducedReturnKind; + switch (kind) { + case None: + return out << "none"; + case Init: + return out << "init"; + case Ref: + return out << "ref"; + case Value: + return out << "value"; + } + + CARBON_FATAL("unhandled `DeducedReturnKind` during printing"); + } }; inline constexpr FunctionFields::CallParamIndexRanges @@ -272,8 +303,8 @@ 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 (deduced_return_kind != DeducedReturnKind::None) { + out << ", deduced_return_kind: " << deduced_return_kind; } if (!body_block_ids.empty()) { out << llvm::formatv( @@ -336,7 +367,12 @@ struct Function : public EntityWithParamsBase, // 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(); + return has_deduced_return_type() && !return_form_inst_id.has_value(); + } + + // Returns whether this function's return type is deduced from its body. + auto has_deduced_return_type() const -> bool { + return deduced_return_kind != DeducedReturnKind::None; } // Gets the declared return type for a specific version of this function, or diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 299c87529a72..b1ec8077ff3a 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -1680,13 +1680,15 @@ struct RefBindingPattern { EntityNameId entity_name_id; }; +// A primitive reference form. struct RefForm { - static constexpr auto Kind = - InstKind::RefForm.Define>( - {.ir_name = "ref_form", - .constant_kind = InstConstantKind::Always, - .is_lowered = false}); + // The node is usually a `RefPrimitiveForm` or `ref` prefix operator, but for + // a deduced return form, such as `-> ref auto`, it is the expression that the + // type was deduced from. + static constexpr auto Kind = InstKind::RefForm.Define( + {.ir_name = "ref_form", + .constant_kind = InstConstantKind::Always, + .is_lowered = false}); TypeId type_id; TypeInstId type_component_inst_id; @@ -2493,11 +2495,12 @@ struct ValueBindingPattern { // A primitive value form. struct ValueForm { - static constexpr auto Kind = - InstKind::ValueForm.Define( - {.ir_name = "value_form", - .constant_kind = InstConstantKind::Always, - .is_lowered = false}); + // The node is usually a `ValPrimitiveForm`, but for a deduced return form, + // such as `-> val auto`, it is the expression that the type was deduced from. + static constexpr auto Kind = InstKind::ValueForm.Define( + {.ir_name = "value_form", + .constant_kind = InstConstantKind::Always, + .is_lowered = false}); TypeId type_id; TypeInstId type_component_inst_id;