Initial support for return forms (#6556)

The main changes here are:
- Introducing `InitForm` and `RefForm` to represent initializing and
reference forms (the two return forms currently supported by the
parser).
- Introducing the `FormType` singleton inst to represent their type
(i.e. `Core.Form`).
- Emitting an inst representing a function's declared return form as
part of handling the function signature.

The return form inst is currently ignored. Subsequent PRs will expose it
in `SemIR::Function` and use it to determine the form of call
expressions.
This commit is contained in:
Geoff Romer
2026-01-07 00:54:18 +00:00
committed by GitHub
parent 444c18dfa3
commit 505b1c86b9
225 changed files with 4164 additions and 2583 deletions
@@ -93,11 +93,13 @@ fn CallFAndGIncomplete() {
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %D: type = class_type @D [concrete]
// CHECK:STDOUT: %.64f: form = init_form %C, call_param0 [concrete]
// CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete]
// CHECK:STDOUT: %ReturnCUnused.type: type = fn_type @ReturnCUnused [concrete]
// CHECK:STDOUT: %ReturnCUnused: %ReturnCUnused.type = struct_value () [concrete]
// CHECK:STDOUT: %ReturnCUsed.type: type = fn_type @ReturnCUsed [concrete]
// CHECK:STDOUT: %ReturnCUsed: %ReturnCUsed.type = struct_value () [concrete]
// CHECK:STDOUT: %.101: form = init_form %D, call_param0 [concrete]
// CHECK:STDOUT: %pattern_type.9c8: type = pattern_type %D [concrete]
// CHECK:STDOUT: %ReturnDUnused.type: type = fn_type @ReturnDUnused [concrete]
// CHECK:STDOUT: %ReturnDUnused: %ReturnDUnused.type = struct_value () [concrete]
@@ -135,6 +137,7 @@ fn CallFAndGIncomplete() {
// CHECK:STDOUT: %return.param_patt: %pattern_type.7c7 = out_param_pattern %return.patt, call_param0 [concrete]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C]
// CHECK:STDOUT: %.loc7: form = init_form %C.ref, call_param0 [concrete = constants.%.64f]
// CHECK:STDOUT: %return.param: ref %C = out_param call_param0
// CHECK:STDOUT: %return: ref %C = return_slot %return.param
// CHECK:STDOUT: }
@@ -143,6 +146,7 @@ fn CallFAndGIncomplete() {
// CHECK:STDOUT: %return.param_patt: %pattern_type.7c7 = out_param_pattern %return.patt, call_param0 [concrete]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C]
// CHECK:STDOUT: %.loc8: form = init_form %C.ref, call_param0 [concrete = constants.%.64f]
// CHECK:STDOUT: %return.param: ref %C = out_param call_param0
// CHECK:STDOUT: %return: ref %C = return_slot %return.param
// CHECK:STDOUT: }
@@ -151,6 +155,7 @@ fn CallFAndGIncomplete() {
// CHECK:STDOUT: %return.param_patt: %pattern_type.9c8 = out_param_pattern %return.patt, call_param0 [concrete]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %D.ref: type = name_ref D, file.%D.decl.loc5 [concrete = constants.%D]
// CHECK:STDOUT: %.loc9: form = init_form %D.ref, call_param0 [concrete = constants.%.101]
// CHECK:STDOUT: %return.param: ref %D = out_param call_param0
// CHECK:STDOUT: %return: ref %D = return_slot %return.param
// CHECK:STDOUT: }
@@ -159,6 +164,7 @@ fn CallFAndGIncomplete() {
// CHECK:STDOUT: %return.param_patt: %pattern_type.9c8 = out_param_pattern %return.patt, call_param0 [concrete]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %D.ref: type = name_ref D, file.%D.decl.loc5 [concrete = constants.%D]
// CHECK:STDOUT: %.loc10: form = init_form %D.ref, call_param0 [concrete = constants.%.101]
// CHECK:STDOUT: %return.param: ref %D = out_param call_param0
// CHECK:STDOUT: %return: ref %D = return_slot %return.param
// CHECK:STDOUT: }
@@ -121,6 +121,7 @@ fn A {
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
// CHECK:STDOUT: %.62c: form = init_form %empty_tuple.type, call_param0 [concrete]
// CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete]
// CHECK:STDOUT: %A.type: type = fn_type @A [concrete]
// CHECK:STDOUT: %A: %A.type = struct_value () [concrete]
@@ -136,6 +137,7 @@ fn A {
// CHECK:STDOUT: } {
// CHECK:STDOUT: %.loc7_10.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple]
// CHECK:STDOUT: %.loc7_10.2: type = converted %.loc7_10.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
// CHECK:STDOUT: %.loc7_10.3: form = init_form %.loc7_10.2, call_param0 [concrete = constants.%.62c]
// CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0
// CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param
// CHECK:STDOUT: }
@@ -148,6 +150,7 @@ fn A {
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
// CHECK:STDOUT: %.62c: form = init_form %empty_tuple.type, call_param0 [concrete]
// CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete]
// CHECK:STDOUT: %A.type: type = fn_type @A [concrete]
// CHECK:STDOUT: %A: %A.type = struct_value () [concrete]
@@ -163,6 +166,7 @@ fn A {
// CHECK:STDOUT: } {
// CHECK:STDOUT: %.loc7_12.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple]
// CHECK:STDOUT: %.loc7_12.2: type = converted %.loc7_12.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
// CHECK:STDOUT: %.loc7_12.3: form = init_form %.loc7_12.2, call_param0 [concrete = constants.%.62c]
// CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0
// CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param
// CHECK:STDOUT: }
+20 -4
View File
@@ -270,6 +270,7 @@ import library "extern_api";
// CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete]
// CHECK:STDOUT: %.4d5: form = init_form %i32, call_param1 [concrete]
// CHECK:STDOUT: %B.type: type = fn_type @B [concrete]
// CHECK:STDOUT: %B: %B.type = struct_value () [concrete]
// CHECK:STDOUT: %tuple.type.85c: type = tuple_type (type) [concrete]
@@ -277,6 +278,7 @@ import library "extern_api";
// CHECK:STDOUT: %tuple.type.a1c: type = tuple_type (%i32) [concrete]
// CHECK:STDOUT: %pattern_type.b74: type = pattern_type %tuple.type.a1c [concrete]
// CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete]
// CHECK:STDOUT: %.d06: form = init_form %struct_type.c, call_param1 [concrete]
// CHECK:STDOUT: %pattern_type.688: type = pattern_type %struct_type.c [concrete]
// CHECK:STDOUT: %C.type: type = fn_type @C [concrete]
// CHECK:STDOUT: %C: %C.type = struct_value () [concrete]
@@ -314,8 +316,9 @@ import library "extern_api";
// CHECK:STDOUT: } {
// CHECK:STDOUT: %int_32.loc5_17: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
// CHECK:STDOUT: %i32.loc5_17: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
// CHECK:STDOUT: %.loc5_17: form = init_form %i32.loc5_17, call_param1 [concrete = constants.%.4d5]
// CHECK:STDOUT: %b.param: %i32 = value_param call_param0
// CHECK:STDOUT: %.loc5: type = splice_block %i32.loc5_9 [concrete = constants.%i32] {
// CHECK:STDOUT: %.loc5_9: type = splice_block %i32.loc5_9 [concrete = constants.%i32] {
// CHECK:STDOUT: %int_32.loc5_9: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
// CHECK:STDOUT: %i32.loc5_9: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
// CHECK:STDOUT: }
@@ -332,6 +335,7 @@ import library "extern_api";
// CHECK:STDOUT: %int_32.loc6_25: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
// CHECK:STDOUT: %i32.loc6_25: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
// CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete = constants.%struct_type.c]
// CHECK:STDOUT: %.loc6_28: form = init_form %struct_type.c, call_param1 [concrete = constants.%.d06]
// CHECK:STDOUT: %c.param: %tuple.type.a1c = value_param call_param0
// CHECK:STDOUT: %.loc6_14.1: type = splice_block %.loc6_14.3 [concrete = constants.%tuple.type.a1c] {
// CHECK:STDOUT: %int_32.loc6_10: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
@@ -370,6 +374,7 @@ import library "extern_api";
// CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete]
// CHECK:STDOUT: %.4d5: form = init_form %i32, call_param1 [concrete]
// CHECK:STDOUT: %B.type: type = fn_type @B [concrete]
// CHECK:STDOUT: %B: %B.type = struct_value () [concrete]
// CHECK:STDOUT: %tuple.type.85c: type = tuple_type (type) [concrete]
@@ -377,6 +382,7 @@ import library "extern_api";
// CHECK:STDOUT: %tuple.type.a1c: type = tuple_type (%i32) [concrete]
// CHECK:STDOUT: %pattern_type.b74: type = pattern_type %tuple.type.a1c [concrete]
// CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete]
// CHECK:STDOUT: %.d06: form = init_form %struct_type.c, call_param1 [concrete]
// CHECK:STDOUT: %pattern_type.688: type = pattern_type %struct_type.c [concrete]
// CHECK:STDOUT: %C.type: type = fn_type @C [concrete]
// CHECK:STDOUT: %C: %C.type = struct_value () [concrete]
@@ -414,8 +420,9 @@ import library "extern_api";
// CHECK:STDOUT: } {
// CHECK:STDOUT: %int_32.loc5_52: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
// CHECK:STDOUT: %i32.loc5_52: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
// CHECK:STDOUT: %.loc5_52: form = init_form %i32.loc5_52, call_param1 [concrete = constants.%.4d5]
// CHECK:STDOUT: %b.param: %i32 = value_param call_param0
// CHECK:STDOUT: %.loc5: type = splice_block %i32.loc5_44 [concrete = constants.%i32] {
// CHECK:STDOUT: %.loc5_44: type = splice_block %i32.loc5_44 [concrete = constants.%i32] {
// CHECK:STDOUT: %int_32.loc5_44: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
// CHECK:STDOUT: %i32.loc5_44: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
// CHECK:STDOUT: }
@@ -432,6 +439,7 @@ import library "extern_api";
// CHECK:STDOUT: %int_32.loc6_60: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
// CHECK:STDOUT: %i32.loc6_60: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
// CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete = constants.%struct_type.c]
// CHECK:STDOUT: %.loc6_63: form = init_form %struct_type.c, call_param1 [concrete = constants.%.d06]
// CHECK:STDOUT: %c.param: %tuple.type.a1c = value_param call_param0
// CHECK:STDOUT: %.loc6_49.1: type = splice_block %.loc6_49.3 [concrete = constants.%tuple.type.a1c] {
// CHECK:STDOUT: %int_32.loc6_45: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
@@ -658,12 +666,14 @@ import library "extern_api";
// CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete]
// CHECK:STDOUT: %.4d5: form = init_form %i32, call_param1 [concrete]
// CHECK:STDOUT: %B.type: type = fn_type @B [concrete]
// CHECK:STDOUT: %B: %B.type = struct_value () [concrete]
// CHECK:STDOUT: %tuple.type.85c: type = tuple_type (type) [concrete]
// CHECK:STDOUT: %tuple.896: %tuple.type.85c = tuple_value (%i32) [concrete]
// CHECK:STDOUT: %tuple.type.a1c: type = tuple_type (%i32) [concrete]
// CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete]
// CHECK:STDOUT: %.d06: form = init_form %struct_type.c, call_param1 [concrete]
// CHECK:STDOUT: %pattern_type.688: type = pattern_type %struct_type.c [concrete]
// CHECK:STDOUT: %C.type: type = fn_type @C [concrete]
// CHECK:STDOUT: %C: %C.type = struct_value () [concrete]
@@ -732,8 +742,9 @@ import library "extern_api";
// CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] {} {
// CHECK:STDOUT: %int_32.loc23_24: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
// CHECK:STDOUT: %i32.loc23_24: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
// CHECK:STDOUT: %.loc23_24: form = init_form %i32.loc23_24, call_param1 [concrete = constants.%.4d5]
// CHECK:STDOUT: %b.param: %i32 = value_param call_param0
// CHECK:STDOUT: %.loc23: type = splice_block %i32.loc23_16 [concrete = constants.%i32] {
// CHECK:STDOUT: %.loc23_16: type = splice_block %i32.loc23_16 [concrete = constants.%i32] {
// CHECK:STDOUT: %int_32.loc23_16: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
// CHECK:STDOUT: %i32.loc23_16: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
// CHECK:STDOUT: }
@@ -745,6 +756,7 @@ import library "extern_api";
// CHECK:STDOUT: %int_32.loc32_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
// CHECK:STDOUT: %i32.loc32_32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
// CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete = constants.%struct_type.c]
// CHECK:STDOUT: %.loc32_35: form = init_form %struct_type.c, call_param1 [concrete = constants.%.d06]
// CHECK:STDOUT: %c.param: %tuple.type.a1c = value_param call_param0
// CHECK:STDOUT: %.loc32_21.1: type = splice_block %.loc32_21.3 [concrete = constants.%tuple.type.a1c] {
// CHECK:STDOUT: %int_32.loc32_17: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
@@ -883,8 +895,10 @@ import library "extern_api";
// CHECK:STDOUT: %D: %D.type = struct_value () [concrete]
// CHECK:STDOUT: %E.type: type = fn_type @E [concrete]
// CHECK:STDOUT: %E: %E.type = struct_value () [concrete]
// CHECK:STDOUT: %.49e: form = init_form %i32, call_param1 [concrete]
// CHECK:STDOUT: %tuple.type.85c: type = tuple_type (type) [concrete]
// CHECK:STDOUT: %tuple.38e: %tuple.type.85c = tuple_value (%i32) [concrete]
// CHECK:STDOUT: %.407: form = init_form %struct_type.c, call_param1 [concrete]
// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
// CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete]
// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete]
@@ -946,8 +960,9 @@ import library "extern_api";
// CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] {} {
// CHECK:STDOUT: %int_32.loc7_24: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
// CHECK:STDOUT: %i32.loc7_24: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
// CHECK:STDOUT: %.loc7_24: form = init_form %i32.loc7_24, call_param1 [concrete = constants.%.49e]
// CHECK:STDOUT: %b.param: %i32 = value_param call_param0
// CHECK:STDOUT: %.loc7: type = splice_block %i32.loc7_16 [concrete = constants.%i32] {
// CHECK:STDOUT: %.loc7_16: type = splice_block %i32.loc7_16 [concrete = constants.%i32] {
// CHECK:STDOUT: %int_32.loc7_16: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
// CHECK:STDOUT: %i32.loc7_16: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
// CHECK:STDOUT: }
@@ -959,6 +974,7 @@ import library "extern_api";
// CHECK:STDOUT: %int_32.loc8_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
// CHECK:STDOUT: %i32.loc8_32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
// CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete = constants.%struct_type.c]
// CHECK:STDOUT: %.loc8_35: form = init_form %struct_type.c, call_param1 [concrete = constants.%.407]
// CHECK:STDOUT: %c.param: %tuple.type.dd4 = value_param call_param0
// CHECK:STDOUT: %.loc8_21.1: type = splice_block %.loc8_21.3 [concrete = constants.%tuple.type.dd4] {
// CHECK:STDOUT: %int_32.loc8_17: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
@@ -0,0 +1,24 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon
// EXTRA-ARGS: --dump-sem-ir-ranges=only
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/ref.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/ref.carbon
// --- fail_todo_ref_return.carbon
fn F() -> ref i32;
fn G() {
// CHECK:STDERR: fail_todo_ref_return.carbon:[[@LINE+4]]:20: error: cannot bind durable reference to non-reference value of type `i32` [ConversionFailureNonRefToRef]
// CHECK:STDERR: let ref x: i32 = F();
// CHECK:STDERR: ^~~
// CHECK:STDERR:
let ref x: i32 = F();
}