diff --git a/toolchain/check/action.cpp b/toolchain/check/action.cpp index e2e0b9a4d169..dc865fe27fd7 100644 --- a/toolchain/check/action.cpp +++ b/toolchain/check/action.cpp @@ -214,6 +214,24 @@ static auto RefineTypedOperand(Context& context, SemIR::LocId loc_id, return inst_id; } +template + requires SemIR::Internal::IsIdKindType && + std::derived_from +static auto RefineTypedOperand(Context& context, SemIR::LocId /*loc_id*/, + DerivedInstIdT inst_id) -> DerivedInstIdT { + // Refine an instruction that refers to a value within the current generic to + // refer to the corresponding value within the specific. This is analogous to + // the work we do to rebuild generic constants in the eval block, but is done + // as refinement rather than rebuilding since action instructions *only* live + // in the eval block. + auto result = GetOrAddInstWithSpecificConstantValue(context, inst_id); + if constexpr (requires { DerivedInstIdT(result); }) { + return DerivedInstIdT(result); + } else { + return DerivedInstIdT::UnsafeMake(result); + } +} + template static auto RefineTypedOperand(Context& context, SemIR::LocId loc_id, SemIR::BundleId bundle_id) diff --git a/toolchain/check/generic.cpp b/toolchain/check/generic.cpp index 7f4cfc755106..d951e0ee5fe1 100644 --- a/toolchain/check/generic.cpp +++ b/toolchain/check/generic.cpp @@ -228,12 +228,11 @@ static auto AddGenericTypeToEvalBlock(Context& context, SemIR::LocId loc_id, } // Adds instructions to compute the substituted value of `inst_id` in each -// specific into the eval block for the current generic region. Returns a -// symbolic constant instruction ID that refers to the substituted constant -// value in each specific. -static auto AddGenericConstantToEvalBlock(Context& context, - SemIR::InstId inst_id) - -> SemIR::ConstantId { +// specific into the eval block for the current generic region. Returns the +// instruction within the eval block that computes the substituted constant. +static auto AddGenericConstantInstToEvalBlock(Context& context, + SemIR::InstId inst_id) + -> SemIR::InstId { CARBON_CHECK(context.constant_values().Get(inst_id).is_symbolic(), "Adding generic constant {0} with non-symbolic value {1}", context.insts().Get(inst_id), @@ -248,9 +247,29 @@ static auto AddGenericConstantToEvalBlock(Context& context, CARBON_CHECK(new_inst_id != const_inst_id, "No substitutions performed for generic constant {0}", context.insts().Get(inst_id)); + return new_inst_id; +} + +// Adds instructions to compute the substituted value of `inst_id` in each +// specific into the eval block for the current generic region. Returns a +// symbolic constant instruction ID that refers to the substituted constant +// value in each specific. +static auto AddGenericConstantToEvalBlock(Context& context, + SemIR::InstId inst_id) + -> SemIR::ConstantId { + auto new_inst_id = AddGenericConstantInstToEvalBlock(context, inst_id); return context.constant_values().GetAttached(new_inst_id); } +auto GetOrAddInstWithSpecificConstantValue(Context& context, + SemIR::InstId inst_id) + -> SemIR::InstId { + if (!context.constant_values().Get(inst_id).is_symbolic()) { + return inst_id; + } + return AddGenericConstantInstToEvalBlock(context, inst_id); +} + // Adds an instruction that performs a template action to the eval block for the // generic. The instruction should not yet have been added to any block. The // instruction might refer to types and constants that need to be rewritten, so diff --git a/toolchain/check/generic.h b/toolchain/check/generic.h index 623e8a5fe866..0eebcef155ca 100644 --- a/toolchain/check/generic.h +++ b/toolchain/check/generic.h @@ -53,6 +53,15 @@ struct DependentInst { auto AttachDependentInstToCurrentGeneric(Context& context, DependentInst dependent_inst) -> void; +// Given an instruction that might have a generic constant value, returns an +// instruction that has the corresponding specific constant value in the current +// generic, if any. This is typically not necessary except when manually adding +// instructions directly to the eval block, for example when building an Action +// instruction. +auto GetOrAddInstWithSpecificConstantValue(Context& context, + SemIR::InstId inst_id) + -> SemIR::InstId; + // Discard the information about the current generic entity. This should be // called instead of `FinishGenericDecl` if the corresponding `Generic` object // would not actually be used, or when recovering from an error. diff --git a/toolchain/check/testdata/generic/template/convert.carbon b/toolchain/check/testdata/generic/template/convert.carbon index 9daff4626963..9bf64b7887f3 100644 --- a/toolchain/check/testdata/generic/template/convert.carbon +++ b/toolchain/check/testdata/generic/template/convert.carbon @@ -110,6 +110,55 @@ fn Test(d: D) { let _: i32 = AsI32(d); } +// --- as_type.carbon + +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +fn AsType[template T: type](x: T, template U: type) { + let _: U = x as U; +} +//@dump-sem-ir-end + +fn Test1(n: i32) { + AsType(n, i32); +} + +class C { + var n: i32; + impl as Core.As(i32) { + fn Convert(self) -> i32 { return self.n; } + } +} + +fn Test2(c: C) { + AsType(c, i32); +} + +// --- fail_cannot_as_type.carbon + +library "[[@TEST_NAME]]"; + +fn AsType[template T: type](x: T, template U: type) { + let _: U = x as U; +} + +class D {} + +fn Test(d: D) { + // CHECK:STDERR: fail_cannot_as_type.carbon:[[@LINE+10]]:3: error: unable to monomorphize specific `AsType(D, i32)` [ResolvingSpecificHere] + // CHECK:STDERR: AsType(d, i32); + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: fail_cannot_as_type.carbon:[[@LINE-9]]:14: note: cannot convert expression of type `D` to `i32` with `as` [ConversionFailure] + // CHECK:STDERR: let _: U = x as U; + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: fail_cannot_as_type.carbon:[[@LINE-12]]:14: note: type `D` does not implement interface `Core.As(i32)` [MissingImplInMemberAccessInContext] + // CHECK:STDERR: let _: U = x as U; + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: + AsType(d, i32); +} + // CHECK:STDOUT: --- convert.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -445,3 +494,173 @@ fn Test(d: D) { // CHECK:STDOUT: %.loc6_18.2 => constants.%inst.splice_block.132 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- as_type.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %type: type = facet_type [concrete] +// CHECK:STDOUT: %.Self.frozen: %type = symbolic_binding .Self [symbolic_self] +// CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] +// CHECK:STDOUT: %T.patt.d47011.1: %pattern_type.98f = symbolic_binding_pattern T, 0, template [template] +// CHECK:STDOUT: %T.67db0b.1: type = symbolic_binding T, 0, template [template] +// CHECK:STDOUT: %pattern_type.51d1c4.1: type = pattern_type %T.67db0b.1 [template] +// CHECK:STDOUT: %x.param_patt.91d: %pattern_type.51d1c4.1 = value_param_pattern [template] +// CHECK:STDOUT: %x.patt.260: %pattern_type.51d1c4.1 = wrapper_binding_pattern x, %x.param_patt.91d [template] +// CHECK:STDOUT: %U.patt.014: %pattern_type.98f = symbolic_binding_pattern U, 1, template [template] +// CHECK:STDOUT: %U.091: type = symbolic_binding U, 1, template [template] +// CHECK:STDOUT: %AsType.type: type = fn_type @AsType [concrete] +// CHECK:STDOUT: %AsType: %AsType.type = struct_value () [concrete] +// CHECK:STDOUT: %require_complete.944: = require_complete_type %T.67db0b.1 [template] +// CHECK:STDOUT: %require_complete.441: = require_complete_type %U.091 [template] +// CHECK:STDOUT: %pattern_type.946: type = pattern_type %U.091 [template] +// CHECK:STDOUT: %_.patt.972: %pattern_type.946 = value_binding_pattern _ [template] +// 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.6b6: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] +// CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] +// CHECK:STDOUT: %x.param_patt.cee: %pattern_type.6b6 = value_param_pattern [concrete] +// CHECK:STDOUT: %x.patt.e3b: %pattern_type.6b6 = wrapper_binding_pattern x, %x.param_patt.cee [concrete] +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %As.type.155: type = facet_type <@As, @As(%i32)> [concrete] +// CHECK:STDOUT: %As.impl_witness.80e: = impl_witness @C.as.As.impl.%As.impl_witness_table [concrete] +// CHECK:STDOUT: %pattern_type.98b: type = pattern_type %C [concrete] +// CHECK:STDOUT: %C.as.As.impl.Convert.type: type = fn_type @C.as.As.impl.Convert [concrete] +// CHECK:STDOUT: %C.as.As.impl.Convert: %C.as.As.impl.Convert.type = struct_value () [concrete] +// CHECK:STDOUT: %As.facet: %As.type.155 = facet_value %C, (%As.impl_witness.80e) [concrete] +// CHECK:STDOUT: %As.WithSelf.Convert.type.087: type = fn_type @As.WithSelf.Convert, @As.WithSelf(%i32, %As.facet) [concrete] +// CHECK:STDOUT: %struct_type.n: type = struct_type {.n: %i32} [concrete] +// CHECK:STDOUT: %complete_type.cdf: = complete_type_witness %struct_type.n [concrete] +// CHECK:STDOUT: %x.param_patt.0f9: %pattern_type.98b = value_param_pattern [concrete] +// CHECK:STDOUT: %x.patt.953: %pattern_type.98b = wrapper_binding_pattern x, %x.param_patt.0f9 [concrete] +// CHECK:STDOUT: %_.patt.bed: %pattern_type.6b6 = value_binding_pattern _ [concrete] +// CHECK:STDOUT: %inst.as_compatible.1ff: = inst_value [concrete] { +// CHECK:STDOUT: %.775: %i32 = as_compatible @AsType.%x.ref +// CHECK:STDOUT: } +// CHECK:STDOUT: %inst.splice_block.5e1: = inst_value [concrete] { +// CHECK:STDOUT: %.093: %i32 = splice_block %.775 {} +// CHECK:STDOUT: } +// CHECK:STDOUT: %inst.splice_block.520: = inst_value [concrete] { +// CHECK:STDOUT: %.b12: %i32 = splice_block %.093 {} +// CHECK:STDOUT: } +// CHECK:STDOUT: %inst.as_compatible.d98: = inst_value [concrete] { +// CHECK:STDOUT: %.f8c: %C = as_compatible @AsType.%x.ref +// CHECK:STDOUT: } +// CHECK:STDOUT: %.419: type = fn_type_with_self_type %As.WithSelf.Convert.type.087, %As.facet [concrete] +// CHECK:STDOUT: %inst.splice_block.550: = inst_value [concrete] { +// CHECK:STDOUT: %.24f: %i32 = splice_block %.0bd { +// CHECK:STDOUT: %impl.elem0.47b: %.419 = impl_witness_access %As.impl_witness.80e, element0 [concrete = %C.as.As.impl.Convert] +// CHECK:STDOUT: %bound_method: = bound_method %.f8c, %impl.elem0.47b +// CHECK:STDOUT: %C.as.As.impl.Convert.call: init %i32 = call %bound_method(%.f8c) +// CHECK:STDOUT: %.0bd: %i32 = value_of_initializer %C.as.As.impl.Convert.call +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: %inst.splice_block.022: = inst_value [concrete] { +// CHECK:STDOUT: %.77f: %i32 = splice_block %.24f {} +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %AsType.decl: %AsType.type = fn_decl @AsType [concrete = constants.%AsType] { +// CHECK:STDOUT: %T.patt.loc5_21.1: %pattern_type.98f = symbolic_binding_pattern T, 0, template [template = %T.patt.loc5_21.2 (constants.%T.patt.d47011.1)] +// CHECK:STDOUT: %x.param_patt.loc5_30.1: @AsType.%pattern_type.loc5 (%pattern_type.51d1c4.1) = value_param_pattern [template = %x.param_patt.loc5_30.2 (constants.%x.param_patt.91d)] +// CHECK:STDOUT: %x.patt.loc5_30.1: @AsType.%pattern_type.loc5 (%pattern_type.51d1c4.1) = wrapper_binding_pattern x, %x.param_patt.loc5_30.1 [template = %x.patt.loc5_30.2 (constants.%x.patt.260)] +// CHECK:STDOUT: %U.patt.loc5_45.1: %pattern_type.98f = symbolic_binding_pattern U, 1, template [template = %U.patt.loc5_45.2 (constants.%U.patt.014)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.loc5_23.1: type = splice_block %.loc5_23.2 [concrete = type] { +// CHECK:STDOUT: %.Self.frozen.loc5_21: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.frozen] +// CHECK:STDOUT: %.loc5_23.2: type = type_literal type [concrete = type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %T.loc5_21.2: type = symbolic_binding T, 0, template [template = %T.loc5_21.1 (constants.%T.67db0b.1)] +// CHECK:STDOUT: %x.param: @AsType.%T.loc5_21.1 (%T.67db0b.1) = value_param call_param0 +// CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc5_21.2 [template = %T.loc5_21.1 (constants.%T.67db0b.1)] +// CHECK:STDOUT: %x: @AsType.%T.loc5_21.1 (%T.67db0b.1) = wrapper_binding x, %x.param +// CHECK:STDOUT: %.loc5_47.1: type = splice_block %.loc5_47.2 [concrete = type] { +// CHECK:STDOUT: %.Self.frozen.loc5_45: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.frozen] +// CHECK:STDOUT: %.loc5_47.2: type = type_literal type [concrete = type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %U.loc5_45.2: type = symbolic_binding U, 1, template [template = %U.loc5_45.1 (constants.%U.091)] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @AsType(%T.loc5_21.2: type, %U.loc5_45.2: type) { +// CHECK:STDOUT: %T.patt.loc5_21.2: %pattern_type.98f = symbolic_binding_pattern T, 0, template [template = %T.patt.loc5_21.2 (constants.%T.patt.d47011.1)] +// CHECK:STDOUT: %T.loc5_21.1: type = symbolic_binding T, 0, template [template = %T.loc5_21.1 (constants.%T.67db0b.1)] +// CHECK:STDOUT: %pattern_type.loc5: type = pattern_type %T.loc5_21.1 [template = %pattern_type.loc5 (constants.%pattern_type.51d1c4.1)] +// CHECK:STDOUT: %x.param_patt.loc5_30.2: @AsType.%pattern_type.loc5 (%pattern_type.51d1c4.1) = value_param_pattern [template = %x.param_patt.loc5_30.2 (constants.%x.param_patt.91d)] +// CHECK:STDOUT: %x.patt.loc5_30.2: @AsType.%pattern_type.loc5 (%pattern_type.51d1c4.1) = wrapper_binding_pattern x, %x.param_patt.loc5_30.2 [template = %x.patt.loc5_30.2 (constants.%x.patt.260)] +// CHECK:STDOUT: %U.patt.loc5_45.2: %pattern_type.98f = symbolic_binding_pattern U, 1, template [template = %U.patt.loc5_45.2 (constants.%U.patt.014)] +// CHECK:STDOUT: %U.loc5_45.1: type = symbolic_binding U, 1, template [template = %U.loc5_45.1 (constants.%U.091)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %require_complete.loc5: = require_complete_type %T.loc5_21.1 [template = %require_complete.loc5 (constants.%require_complete.944)] +// CHECK:STDOUT: %require_complete.loc6: = require_complete_type %U.loc5_45.1 [template = %require_complete.loc6 (constants.%require_complete.441)] +// CHECK:STDOUT: %pattern_type.loc6: type = pattern_type %U.loc5_45.1 [template = %pattern_type.loc6 (constants.%pattern_type.946)] +// CHECK:STDOUT: %_.patt.loc6_8.2: @AsType.%pattern_type.loc6 (%pattern_type.946) = value_binding_pattern _ [template = %_.patt.loc6_8.2 (constants.%_.patt.972)] +// CHECK:STDOUT: %.loc6_16.4: = refine_type_action %x.ref, %T.loc5_21.1 [template] +// CHECK:STDOUT: %.loc6_16.5: = convert_action %.loc6_16.1, %U.loc5_45.1, element7 [template] +// CHECK:STDOUT: %.loc6_16.6: = convert_to_category_action %.loc6_16.2, element1 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: fn(%x.param: @AsType.%T.loc5_21.1 (%T.67db0b.1)) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %x.ref: @AsType.%T.loc5_21.1 (%T.67db0b.1) = name_ref x, %x +// CHECK:STDOUT: %U.ref.loc6_19: type = name_ref U, %U.loc5_45.2 [template = %U.loc5_45.1 (constants.%U.091)] +// CHECK:STDOUT: %.loc6_16.1: @AsType.%T.loc5_21.1 (%T.67db0b.1) = splice_inst %.loc6_16.4 +// CHECK:STDOUT: %.loc6_16.2: @AsType.%U.loc5_45.1 (%U.091) = splice_inst %.loc6_16.5 +// CHECK:STDOUT: %.loc6_16.3: @AsType.%U.loc5_45.1 (%U.091) = splice_inst %.loc6_16.6 +// CHECK:STDOUT: %U.ref.loc6_10: type = name_ref U, %U.loc5_45.2 [template = %U.loc5_45.1 (constants.%U.091)] +// CHECK:STDOUT: %_: @AsType.%U.loc5_45.1 (%U.091) = wrapper_binding _, %.loc6_16.3 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %_.patt.loc6_8.1: @AsType.%pattern_type.loc6 (%pattern_type.946) = value_binding_pattern _ [template = %_.patt.loc6_8.2 (constants.%_.patt.972)] +// CHECK:STDOUT: } +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @AsType(constants.%T.67db0b.1, constants.%U.091) { +// CHECK:STDOUT: %T.patt.loc5_21.2 => constants.%T.patt.d47011.1 +// CHECK:STDOUT: %T.loc5_21.1 => constants.%T.67db0b.1 +// CHECK:STDOUT: %pattern_type.loc5 => constants.%pattern_type.51d1c4.1 +// CHECK:STDOUT: %x.param_patt.loc5_30.2 => constants.%x.param_patt.91d +// CHECK:STDOUT: %x.patt.loc5_30.2 => constants.%x.patt.260 +// CHECK:STDOUT: %U.patt.loc5_45.2 => constants.%U.patt.014 +// CHECK:STDOUT: %U.loc5_45.1 => constants.%U.091 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @AsType(constants.%i32, constants.%i32) { +// CHECK:STDOUT: %T.patt.loc5_21.2 => constants.%T.patt.d47011.1 +// CHECK:STDOUT: %T.loc5_21.1 => constants.%i32 +// CHECK:STDOUT: %pattern_type.loc5 => constants.%pattern_type.6b6 +// CHECK:STDOUT: %x.param_patt.loc5_30.2 => constants.%x.param_patt.cee +// CHECK:STDOUT: %x.patt.loc5_30.2 => constants.%x.patt.e3b +// CHECK:STDOUT: %U.patt.loc5_45.2 => constants.%U.patt.014 +// CHECK:STDOUT: %U.loc5_45.1 => constants.%i32 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %require_complete.loc5 => constants.%complete_type.f8a +// CHECK:STDOUT: %require_complete.loc6 => constants.%complete_type.f8a +// CHECK:STDOUT: %pattern_type.loc6 => constants.%pattern_type.6b6 +// CHECK:STDOUT: %_.patt.loc6_8.2 => constants.%_.patt.bed +// CHECK:STDOUT: %.loc6_16.4 => constants.%inst.as_compatible.1ff +// CHECK:STDOUT: %.loc6_16.5 => constants.%inst.splice_block.5e1 +// CHECK:STDOUT: %.loc6_16.6 => constants.%inst.splice_block.520 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @AsType(constants.%C, constants.%i32) { +// CHECK:STDOUT: %T.patt.loc5_21.2 => constants.%T.patt.d47011.1 +// CHECK:STDOUT: %T.loc5_21.1 => constants.%C +// CHECK:STDOUT: %pattern_type.loc5 => constants.%pattern_type.98b +// CHECK:STDOUT: %x.param_patt.loc5_30.2 => constants.%x.param_patt.0f9 +// CHECK:STDOUT: %x.patt.loc5_30.2 => constants.%x.patt.953 +// CHECK:STDOUT: %U.patt.loc5_45.2 => constants.%U.patt.014 +// CHECK:STDOUT: %U.loc5_45.1 => constants.%i32 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %require_complete.loc5 => constants.%complete_type.cdf +// CHECK:STDOUT: %require_complete.loc6 => constants.%complete_type.f8a +// CHECK:STDOUT: %pattern_type.loc6 => constants.%pattern_type.6b6 +// CHECK:STDOUT: %_.patt.loc6_8.2 => constants.%_.patt.bed +// CHECK:STDOUT: %.loc6_16.4 => constants.%inst.as_compatible.d98 +// CHECK:STDOUT: %.loc6_16.5 => constants.%inst.splice_block.550 +// CHECK:STDOUT: %.loc6_16.6 => constants.%inst.splice_block.022 +// CHECK:STDOUT: } +// CHECK:STDOUT: