Support for -> ref/var/val auto. (#7977)

Extend support for return type deduction to also handle top-level
category specifiers.

Assisted-by: Claude via Antigravity
This commit is contained in:
Richard Smith authored and GitHub committed 2026-10-15 21:13:38 +00:00
1 parent f0b030f2c7
commit 3af25cc1ba
14 files changed
+595 -49

No files matched your search

+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
}
+47 -7
View File
@@ -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<SemIR::AnyPrimitiveForm>(*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 =
+7 -2
View File
@@ -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
+16 -6
View File
@@ -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<SemIR::AnyPrimitiveForm>(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);
}
+1 -1
View File
@@ -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);
+21 -8
View File
@@ -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;
}
+1 -2
View File
@@ -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;
}
+2 -2
View File
@@ -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()
+6 -1
View File
@@ -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();
}
@@ -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 <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: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- val_auto.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <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: <witness> = 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> = 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> = specific_function %Core.IntLiteral.as.As.impl.Convert.94f, @Core.IntLiteral.as.As.impl.Convert(%int_32) [concrete]
// CHECK:STDOUT: %bound_method: <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> = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound]
// CHECK:STDOUT: %specific_fn: <specific function> = 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> = 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 <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: <witness> = 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> = 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> = 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> = 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: <witness> = 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> = bound_method %int_1.68a, %Int.as.Copy.impl.Op.cae [concrete]
// CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: <specific function> = specific_function %Int.as.Copy.impl.Op.cae, @Int.as.Copy.impl.Op(%int_32) [concrete]
// CHECK:STDOUT: %bound_method.a13: <bound method> = 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> = 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> = 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> = 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> = 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> = 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> = 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> = 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:
+1 -1
View File
@@ -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]";
}
+42 -6
View File
@@ -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
+14 -11
View File
@@ -1680,13 +1680,15 @@ struct RefBindingPattern {
EntityNameId entity_name_id;
};
// A primitive reference form.
struct RefForm {
static constexpr auto Kind =
InstKind::RefForm.Define<Parse::NodeIdOneOf<Parse::PrefixOperatorRefId,
Parse::RefPrimitiveFormId>>(
{.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<Parse::NodeId>(
{.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<Parse::ValPrimitiveFormId>(
{.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<Parse::NodeId>(
{.ir_name = "value_form",
.constant_kind = InstConstantKind::Always,
.is_lowered = false});
TypeId type_id;
TypeInstId type_component_inst_id;