ExprCategory for guaranteed-in-place initializing expressions (#6623)

The primary change in this PR is to split the `Initializing` expression
category into separate `ReprInitializing` and `InPlaceInitializing`
categories, depending on whether initialization uses the types
initializing representation, or is guaranteed to be in place. It also
rationalizes and documents the SemIR-level semantics of those categories
(including where #5545's "ephemeral entire reference" category will
fit), and introduces two new inst kinds to close gaps exposed in the
process.

Some additional secondary changes:
- Consistently format the storage arguments of initializers with `to`,
regardless of whether initialization is in-place, and document the `to`
notation.
- Rename some inst kinds and functions, and restructure some of the
code, for clarity and consistency with the new documentation.
- Resolve a TODO to handle more category conversions in
`CategoryConverter`, in order to make it easier to reason about category
conversions.

See #6588 and the review history of this PR for background.

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
Geoff Romer
2026-02-04 02:27:12 +00:00
committed by GitHub
co-authored by Richard Smith
parent 2980e6bcbb
commit e5b05a1fac
127 changed files with 1352 additions and 926 deletions
@@ -1651,13 +1651,13 @@ fn F() {
// CHECK:STDOUT: %.loc22_44.1: ref %i128 = temporary_storage
// CHECK:STDOUT: %addr.loc22_44.2: %ptr.974 = addr_of %.loc22_44.1
// CHECK:STDOUT: %foo__carbon_thunk.call.loc22: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc22_44.1, %addr.loc22_44.2)
// CHECK:STDOUT: %.loc22_44.2: init %i128 = in_place_init %foo__carbon_thunk.call.loc22, %.loc22_44.1
// CHECK:STDOUT: %.loc22_44.2: init %i128 to %.loc22_44.1 = mark_in_place_init %foo__carbon_thunk.call.loc22
// CHECK:STDOUT: %.loc22_10: type = splice_block %i128.loc22 [concrete = constants.%i128] {
// CHECK:STDOUT: %int_128.loc22: Core.IntLiteral = int_value 128 [concrete = constants.%int_128]
// CHECK:STDOUT: %i128.loc22: type = class_type @Int, @Int(constants.%int_128) [concrete = constants.%i128]
// CHECK:STDOUT: }
// CHECK:STDOUT: %.loc22_44.3: %i128 = value_of_initializer %.loc22_44.2
// CHECK:STDOUT: %.loc22_44.4: %i128 = converted %.loc22_44.2, %.loc22_44.3
// CHECK:STDOUT: %.loc22_44.3: ref %i128 = temporary %.loc22_44.1, %.loc22_44.2
// CHECK:STDOUT: %.loc22_44.4: %i128 = acquire_value %.loc22_44.3
// CHECK:STDOUT: %d: %i128 = value_binding d, %.loc22_44.4
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %e.patt: %pattern_type.57d = value_binding_pattern e [concrete]
@@ -1683,13 +1683,13 @@ fn F() {
// CHECK:STDOUT: %.loc25_45.1: ref %i128 = temporary_storage
// CHECK:STDOUT: %addr.loc25_45.2: %ptr.974 = addr_of %.loc25_45.1
// CHECK:STDOUT: %foo__carbon_thunk.call.loc25: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc25_45.1, %addr.loc25_45.2)
// CHECK:STDOUT: %.loc25_45.2: init %i128 = in_place_init %foo__carbon_thunk.call.loc25, %.loc25_45.1
// CHECK:STDOUT: %.loc25_45.2: init %i128 to %.loc25_45.1 = mark_in_place_init %foo__carbon_thunk.call.loc25
// CHECK:STDOUT: %.loc25_10: type = splice_block %i128.loc25 [concrete = constants.%i128] {
// CHECK:STDOUT: %int_128.loc25: Core.IntLiteral = int_value 128 [concrete = constants.%int_128]
// CHECK:STDOUT: %i128.loc25: type = class_type @Int, @Int(constants.%int_128) [concrete = constants.%i128]
// CHECK:STDOUT: }
// CHECK:STDOUT: %.loc25_45.3: %i128 = value_of_initializer %.loc25_45.2
// CHECK:STDOUT: %.loc25_45.4: %i128 = converted %.loc25_45.2, %.loc25_45.3
// CHECK:STDOUT: %.loc25_45.3: ref %i128 = temporary %.loc25_45.1, %.loc25_45.2
// CHECK:STDOUT: %.loc25_45.4: %i128 = acquire_value %.loc25_45.3
// CHECK:STDOUT: %e: %i128 = value_binding e, %.loc25_45.4
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %f.patt: %pattern_type.57d = value_binding_pattern f [concrete]
@@ -1715,13 +1715,13 @@ fn F() {
// CHECK:STDOUT: %.loc28_45.1: ref %i128 = temporary_storage
// CHECK:STDOUT: %addr.loc28_45.2: %ptr.974 = addr_of %.loc28_45.1
// CHECK:STDOUT: %foo__carbon_thunk.call.loc28: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc28_45.1, %addr.loc28_45.2)
// CHECK:STDOUT: %.loc28_45.2: init %i128 = in_place_init %foo__carbon_thunk.call.loc28, %.loc28_45.1
// CHECK:STDOUT: %.loc28_45.2: init %i128 to %.loc28_45.1 = mark_in_place_init %foo__carbon_thunk.call.loc28
// CHECK:STDOUT: %.loc28_10: type = splice_block %i128.loc28 [concrete = constants.%i128] {
// CHECK:STDOUT: %int_128.loc28: Core.IntLiteral = int_value 128 [concrete = constants.%int_128]
// CHECK:STDOUT: %i128.loc28: type = class_type @Int, @Int(constants.%int_128) [concrete = constants.%i128]
// CHECK:STDOUT: }
// CHECK:STDOUT: %.loc28_45.3: %i128 = value_of_initializer %.loc28_45.2
// CHECK:STDOUT: %.loc28_45.4: %i128 = converted %.loc28_45.2, %.loc28_45.3
// CHECK:STDOUT: %.loc28_45.3: ref %i128 = temporary %.loc28_45.1, %.loc28_45.2
// CHECK:STDOUT: %.loc28_45.4: %i128 = acquire_value %.loc28_45.3
// CHECK:STDOUT: %f: %i128 = value_binding f, %.loc28_45.4
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %g.patt: %pattern_type.57d = value_binding_pattern g [concrete]
@@ -1747,22 +1747,30 @@ fn F() {
// CHECK:STDOUT: %.loc31_64.1: ref %i128 = temporary_storage
// CHECK:STDOUT: %addr.loc31_64.2: %ptr.974 = addr_of %.loc31_64.1
// CHECK:STDOUT: %foo__carbon_thunk.call.loc31: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc31_64.1, %addr.loc31_64.2)
// CHECK:STDOUT: %.loc31_64.2: init %i128 = in_place_init %foo__carbon_thunk.call.loc31, %.loc31_64.1
// CHECK:STDOUT: %.loc31_64.2: init %i128 to %.loc31_64.1 = mark_in_place_init %foo__carbon_thunk.call.loc31
// CHECK:STDOUT: %.loc31_10: type = splice_block %i128.loc31 [concrete = constants.%i128] {
// CHECK:STDOUT: %int_128.loc31: Core.IntLiteral = int_value 128 [concrete = constants.%int_128]
// CHECK:STDOUT: %i128.loc31: type = class_type @Int, @Int(constants.%int_128) [concrete = constants.%i128]
// CHECK:STDOUT: }
// CHECK:STDOUT: %.loc31_64.3: %i128 = value_of_initializer %.loc31_64.2
// CHECK:STDOUT: %.loc31_64.4: %i128 = converted %.loc31_64.2, %.loc31_64.3
// CHECK:STDOUT: %.loc31_64.3: ref %i128 = temporary %.loc31_64.1, %.loc31_64.2
// CHECK:STDOUT: %.loc31_64.4: %i128 = acquire_value %.loc31_64.3
// CHECK:STDOUT: %g: %i128 = value_binding g, %.loc31_64.4
// CHECK:STDOUT: %DestroyOp.bound.loc31: <bound method> = bound_method %.loc31_25.4, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call.loc31: init %empty_tuple.type = call %DestroyOp.bound.loc31(%.loc31_25.4)
// CHECK:STDOUT: %DestroyOp.bound.loc28: <bound method> = bound_method %.loc28_25.4, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call.loc28: init %empty_tuple.type = call %DestroyOp.bound.loc28(%.loc28_25.4)
// CHECK:STDOUT: %DestroyOp.bound.loc25: <bound method> = bound_method %.loc25_25.4, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call.loc25: init %empty_tuple.type = call %DestroyOp.bound.loc25(%.loc25_25.4)
// CHECK:STDOUT: %DestroyOp.bound.loc22: <bound method> = bound_method %.loc22_25.4, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call.loc22: init %empty_tuple.type = call %DestroyOp.bound.loc22(%.loc22_25.4)
// CHECK:STDOUT: %DestroyOp.bound.loc31_64: <bound method> = bound_method %.loc31_64.3, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call.loc31_64: init %empty_tuple.type = call %DestroyOp.bound.loc31_64(%.loc31_64.3)
// CHECK:STDOUT: %DestroyOp.bound.loc31_25: <bound method> = bound_method %.loc31_25.4, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call.loc31_25: init %empty_tuple.type = call %DestroyOp.bound.loc31_25(%.loc31_25.4)
// CHECK:STDOUT: %DestroyOp.bound.loc28_45: <bound method> = bound_method %.loc28_45.3, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call.loc28_45: init %empty_tuple.type = call %DestroyOp.bound.loc28_45(%.loc28_45.3)
// CHECK:STDOUT: %DestroyOp.bound.loc28_25: <bound method> = bound_method %.loc28_25.4, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call.loc28_25: init %empty_tuple.type = call %DestroyOp.bound.loc28_25(%.loc28_25.4)
// CHECK:STDOUT: %DestroyOp.bound.loc25_45: <bound method> = bound_method %.loc25_45.3, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call.loc25_45: init %empty_tuple.type = call %DestroyOp.bound.loc25_45(%.loc25_45.3)
// CHECK:STDOUT: %DestroyOp.bound.loc25_25: <bound method> = bound_method %.loc25_25.4, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call.loc25_25: init %empty_tuple.type = call %DestroyOp.bound.loc25_25(%.loc25_25.4)
// CHECK:STDOUT: %DestroyOp.bound.loc22_44: <bound method> = bound_method %.loc22_44.3, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call.loc22_44: init %empty_tuple.type = call %DestroyOp.bound.loc22_44(%.loc22_44.3)
// CHECK:STDOUT: %DestroyOp.bound.loc22_25: <bound method> = bound_method %.loc22_25.4, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call.loc22_25: init %empty_tuple.type = call %DestroyOp.bound.loc22_25(%.loc22_25.4)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -2194,16 +2202,18 @@ fn F() {
// CHECK:STDOUT: %.loc7_27.1: ref %f64.d77 = temporary_storage
// CHECK:STDOUT: %addr.loc7_27.2: %ptr.bcc = addr_of %.loc7_27.1
// CHECK:STDOUT: %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc7_27.1, %addr.loc7_27.2)
// CHECK:STDOUT: %.loc7_27.2: init %f64.d77 = in_place_init %foo__carbon_thunk.call, %.loc7_27.1
// CHECK:STDOUT: %.loc7_27.2: init %f64.d77 to %.loc7_27.1 = mark_in_place_init %foo__carbon_thunk.call
// CHECK:STDOUT: %.loc7_10: type = splice_block %f64 [concrete = constants.%f64.d77] {
// CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete = constants.%int_64]
// CHECK:STDOUT: %f64: type = class_type @Float, @Float(constants.%int_64) [concrete = constants.%f64.d77]
// CHECK:STDOUT: }
// CHECK:STDOUT: %.loc7_27.3: %f64.d77 = value_of_initializer %.loc7_27.2
// CHECK:STDOUT: %.loc7_27.4: %f64.d77 = converted %.loc7_27.2, %.loc7_27.3
// CHECK:STDOUT: %.loc7_27.3: ref %f64.d77 = temporary %.loc7_27.1, %.loc7_27.2
// CHECK:STDOUT: %.loc7_27.4: %f64.d77 = acquire_value %.loc7_27.3
// CHECK:STDOUT: %d: %f64.d77 = value_binding d, %.loc7_27.4
// CHECK:STDOUT: %DestroyOp.bound: <bound method> = bound_method %.loc7_24.4, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call: init %empty_tuple.type = call %DestroyOp.bound(%.loc7_24.4)
// CHECK:STDOUT: %DestroyOp.bound.loc7_27: <bound method> = bound_method %.loc7_27.3, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call.loc7_27: init %empty_tuple.type = call %DestroyOp.bound.loc7_27(%.loc7_27.3)
// CHECK:STDOUT: %DestroyOp.bound.loc7_24: <bound method> = bound_method %.loc7_24.4, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call.loc7_24: init %empty_tuple.type = call %DestroyOp.bound.loc7_24(%.loc7_24.4)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -2340,9 +2350,12 @@ fn F() {
// CHECK:STDOUT: %.loc15_19.1: ref %f64.d77 = temporary_storage
// CHECK:STDOUT: %addr.loc15_19.2: %ptr.bcc = addr_of %.loc15_19.1
// CHECK:STDOUT: %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc15_19.1, %addr.loc15_19.2)
// CHECK:STDOUT: %.loc15_19.2: init %f64.d77 = in_place_init %foo__carbon_thunk.call, %.loc15_19.1
// CHECK:STDOUT: %DestroyOp.bound: <bound method> = bound_method %.loc15_11.4, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call: init %empty_tuple.type = call %DestroyOp.bound(%.loc15_11.4)
// CHECK:STDOUT: %.loc15_19.2: init %f64.d77 to %.loc15_19.1 = mark_in_place_init %foo__carbon_thunk.call
// CHECK:STDOUT: %.loc15_19.3: ref %f64.d77 = temporary %.loc15_19.1, %.loc15_19.2
// CHECK:STDOUT: %DestroyOp.bound.loc15_19: <bound method> = bound_method %.loc15_19.3, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call.loc15_19: init %empty_tuple.type = call %DestroyOp.bound.loc15_19(%.loc15_19.3)
// CHECK:STDOUT: %DestroyOp.bound.loc15_11: <bound method> = bound_method %.loc15_11.4, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call.loc15_11: init %empty_tuple.type = call %DestroyOp.bound.loc15_11(%.loc15_11.4)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT: