From 3192cfc776a0f7df5300e04402d5ae06f10f1630 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 31 Oct 2024 13:13:05 -0700 Subject: [PATCH] Add import support for `specific_function` constants. (#4465) These can currently only be imported as part of the eval block for a generic, because they only show up as the callee of a call instruction. --- toolchain/check/import_ref.cpp | 18 ++ .../generic/no_prelude/import_specific.carbon | 225 ++++++++++++++++++ 2 files changed, 243 insertions(+) create mode 100644 toolchain/check/testdata/function/generic/no_prelude/import_specific.carbon diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index 296e1288a67d..5608cbeeea47 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -1223,6 +1223,9 @@ class ImportRefResolver { case CARBON_KIND(SemIR::PointerType inst): { return TryResolveTypedInst(inst); } + case CARBON_KIND(SemIR::SpecificFunction inst): { + return TryResolveTypedInst(inst); + } case CARBON_KIND(SemIR::SymbolicBindingPattern inst): { return TryResolveTypedInst(inst); } @@ -2110,6 +2113,21 @@ class ImportRefResolver { {.type_id = SemIR::TypeId::TypeType, .pointee_id = pointee_type_id}); } + auto TryResolveTypedInst(SemIR::SpecificFunction inst) -> ResolveResult { + auto type_const_id = GetLocalConstantId(inst.type_id); + auto callee_id = GetLocalConstantInstId(inst.callee_id); + auto specific_data = GetLocalSpecificData(inst.specific_id); + if (HasNewWork()) { + return Retry(); + } + + auto type_id = context_.GetTypeIdForTypeConstant(type_const_id); + auto specific_id = GetOrAddLocalSpecific(inst.specific_id, specific_data); + return ResolveAs({.type_id = type_id, + .callee_id = callee_id, + .specific_id = specific_id}); + } + auto TryResolveTypedInst(SemIR::StructType inst, SemIR::InstId import_inst_id) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType); diff --git a/toolchain/check/testdata/function/generic/no_prelude/import_specific.carbon b/toolchain/check/testdata/function/generic/no_prelude/import_specific.carbon new file mode 100644 index 000000000000..990aa333a949 --- /dev/null +++ b/toolchain/check/testdata/function/generic/no_prelude/import_specific.carbon @@ -0,0 +1,225 @@ +// 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 +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/generic/no_prelude/import_specific.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/no_prelude/import_specific.carbon + +// --- library.carbon + +library "[[@TEST_NAME]]"; + +fn F(T:! type) { +} + +fn G(T:! type) { + F(T); +} + +// --- user.carbon + +library "[[@TEST_NAME]]"; + +import library "library"; + +class C {} + +fn H() { + // TODO: We currently crash when importing G if we don't force F to be imported first. + F(C); + + G(C); +} + +// CHECK:STDOUT: --- library.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %T.patt.1: type = symbolic_binding_pattern T, 0 [symbolic] +// CHECK:STDOUT: %F.type: type = fn_type @F [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %F: %F.type = struct_value () [template] +// CHECK:STDOUT: %T.patt.2: type = symbolic_binding_pattern T, 0 [symbolic] +// CHECK:STDOUT: %G.type: type = fn_type @G [template] +// CHECK:STDOUT: %G: %G.type = struct_value () [template] +// CHECK:STDOUT: %.2: = specific_function %F, @F(%T) [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: .G = %G.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [template = constants.%F] { +// CHECK:STDOUT: %T.patt.loc4_6.1: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc4_6.2 (constants.%T.patt.1)] +// CHECK:STDOUT: %T.param_patt: type = value_param_pattern %T.patt.loc4_6.1, runtime_param [symbolic = %T.patt.loc4_6.2 (constants.%T.patt.1)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %T.param: type = value_param runtime_param +// CHECK:STDOUT: %T.loc4_6.1: type = bind_symbolic_name T, 0, %T.param [symbolic = %T.loc4_6.2 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [template = constants.%G] { +// CHECK:STDOUT: %T.patt.loc7_6.1: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc7_6.2 (constants.%T.patt.2)] +// CHECK:STDOUT: %T.param_patt: type = value_param_pattern %T.patt.loc7_6.1, runtime_param [symbolic = %T.patt.loc7_6.2 (constants.%T.patt.2)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %T.param: type = value_param runtime_param +// CHECK:STDOUT: %T.loc7_6.1: type = bind_symbolic_name T, 0, %T.param [symbolic = %T.loc7_6.2 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @F(%T.loc4_6.1: type) { +// CHECK:STDOUT: %T.loc4_6.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_6.2 (constants.%T)] +// CHECK:STDOUT: %T.patt.loc4_6.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc4_6.2 (constants.%T.patt.1)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: +// CHECK:STDOUT: fn(%T.param_patt: type) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @G(%T.loc7_6.1: type) { +// CHECK:STDOUT: %T.loc7_6.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc7_6.2 (constants.%T)] +// CHECK:STDOUT: %T.patt.loc7_6.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc7_6.2 (constants.%T.patt.2)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %.loc8_3.2: = specific_function constants.%F, @F(%T.loc7_6.2) [symbolic = %.loc8_3.2 (constants.%.2)] +// CHECK:STDOUT: +// CHECK:STDOUT: fn(%T.param_patt: type) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F] +// CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc7_6.1 [symbolic = %T.loc7_6.2 (constants.%T)] +// CHECK:STDOUT: %.loc8_3.1: = specific_function %F.ref, @F(constants.%T) [symbolic = %.loc8_3.2 (constants.%.2)] +// CHECK:STDOUT: %F.call: init %.1 = call %.loc8_3.1() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%T) { +// CHECK:STDOUT: %T.loc4_6.2 => constants.%T +// CHECK:STDOUT: %T.patt.loc4_6.2 => constants.%T +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @G(constants.%T) { +// CHECK:STDOUT: %T.loc7_6.2 => constants.%T +// CHECK:STDOUT: %T.patt.loc7_6.2 => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(@G.%T.loc7_6.2) { +// CHECK:STDOUT: %T.loc4_6.2 => constants.%T +// CHECK:STDOUT: %T.patt.loc4_6.2 => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- user.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %.2: = complete_type_witness %.1 [template] +// CHECK:STDOUT: %H.type: type = fn_type @H [template] +// CHECK:STDOUT: %.3: type = tuple_type () [template] +// CHECK:STDOUT: %H: %H.type = struct_value () [template] +// CHECK:STDOUT: %F.type: type = fn_type @F [template] +// CHECK:STDOUT: %F: %F.type = struct_value () [template] +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %T.patt: type = symbolic_binding_pattern T, 0 [symbolic] +// CHECK:STDOUT: %.4: = specific_function %F, @F(%C) [template] +// CHECK:STDOUT: %G.type: type = fn_type @G [template] +// CHECK:STDOUT: %G: %G.type = struct_value () [template] +// CHECK:STDOUT: %.5: = specific_function %F, @F(%T) [symbolic] +// CHECK:STDOUT: %.6: = specific_function %G, @G(%C) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %import_ref.1: %F.type = import_ref Main//library, inst+7, loaded [template = constants.%F] +// CHECK:STDOUT: %import_ref.2: %G.type = import_ref Main//library, inst+19, loaded [template = constants.%G] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .F = imports.%import_ref.1 +// CHECK:STDOUT: .G = imports.%import_ref.2 +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .H = %H.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %default.import = import +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} {} +// CHECK:STDOUT: %H.decl: %H.type = fn_decl @H [template = constants.%H] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %.loc6: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @H() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %F.ref: %F.type = name_ref F, imports.%import_ref.1 [template = constants.%F] +// CHECK:STDOUT: %C.ref.loc10: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: %.loc10: = specific_function %F.ref, @F(constants.%C) [template = constants.%.4] +// CHECK:STDOUT: %F.call: init %.3 = call %.loc10() +// CHECK:STDOUT: %G.ref: %G.type = name_ref G, imports.%import_ref.2 [template = constants.%G] +// CHECK:STDOUT: %C.ref.loc12: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: %.loc12: = specific_function %G.ref, @G(constants.%C) [template = constants.%.6] +// CHECK:STDOUT: %G.call: init %.3 = call %.loc12() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @F(constants.%T: type) { +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: %T.patt: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt (constants.%T.patt)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: +// CHECK:STDOUT: fn(%T.param_patt: type); +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @G(constants.%T: type) { +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: %T.patt: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt (constants.%T.patt)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %.1: = specific_function constants.%F, @F(%T) [symbolic = %.1 (constants.%.5)] +// CHECK:STDOUT: +// CHECK:STDOUT: fn(%T.param_patt: type); +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%T) { +// CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: %T.patt => constants.%T +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%C) { +// CHECK:STDOUT: %T => constants.%C +// CHECK:STDOUT: %T.patt => constants.%C +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @G(constants.%T) { +// CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: %T.patt => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(@G.%T) { +// CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: %T.patt => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @G(constants.%C) { +// CHECK:STDOUT: %T => constants.%C +// CHECK:STDOUT: %T.patt => constants.%C +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %.1 => constants.%.4 +// CHECK:STDOUT: } +// CHECK:STDOUT: