mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-11-10 12:28:25 +00:00
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:
1 parent
f0b030f2c7
commit
3af25cc1ba
14 files changed
+595
-49
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
@@ -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]";
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user