Import support for generics and specifics (#4179)

Import generics and specifics when they are referenced by imported
entities.

When importing a generic, we import the symbolic constants required by
its eval block, and then rebuild the eval block itself given the list of
constants it needs to compute. This is likely a bit less efficient than
directly importing the contents of the eval block, but avoids needing to
either extend the importer code to be able to import the instructions
that can appear in the eval block or extend the evaluator to cope with
instructions from a different `SemIR::File`.

Importing a symbolic constant is unaffected, and does not yet preserve
the associated generic and index within that generic, so uses of a
generic from an imported IR still don't pick up values from the
specific, but the improved functionality can be seen in the changes to
the SemIR in the testcases.
This commit is contained in:
Richard Smith
2024-07-31 23:53:14 +00:00
committed by GitHub
parent f67791cfee
commit 3c8fc714a8
37 changed files with 1156 additions and 336 deletions
@@ -99,21 +99,26 @@ impl C as AddWith(C) {
// CHECK:STDOUT: %.2: type = tuple_type () [template]
// CHECK:STDOUT: %AddWith: %AddWith.type = struct_value () [template]
// CHECK:STDOUT: %T: type = bind_symbolic_name T 0 [symbolic]
// CHECK:STDOUT: %.3: type = interface_type @AddWith, invalid(%T) [symbolic]
// CHECK:STDOUT: %.3: type = interface_type @AddWith, @AddWith(%T) [symbolic]
// CHECK:STDOUT: %Self: %.3 = bind_symbolic_name Self 1 [symbolic]
// CHECK:STDOUT: %.4: type = interface_type @AddWith, invalid(%C) [template]
// CHECK:STDOUT: %F.type.1: type = fn_type @F.1 [template]
// CHECK:STDOUT: %F.1: %F.type.1 = struct_value () [template]
// CHECK:STDOUT: %F.type.2: type = fn_type @F.2 [template]
// CHECK:STDOUT: %F.2: %F.type.2 = struct_value () [template]
// CHECK:STDOUT: %.5: <witness> = interface_witness (%F.1) [template]
// CHECK:STDOUT: %F.type.2: type = fn_type @F.1, @AddWith(%T) [symbolic]
// CHECK:STDOUT: %.4: type = assoc_entity_type @AddWith, %F.type.2 [symbolic]
// CHECK:STDOUT: %.5: %.4 = assoc_entity element0, imports.%import_ref.2 [symbolic]
// CHECK:STDOUT: %F.2: %F.type.2 = struct_value () [symbolic]
// CHECK:STDOUT: %.6: type = interface_type @AddWith, @AddWith(%C) [template]
// CHECK:STDOUT: %F.type.3: type = fn_type @F.2 [template]
// CHECK:STDOUT: %F.3: %F.type.3 = struct_value () [template]
// CHECK:STDOUT: %.7: <witness> = interface_witness (%F.3) [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %import_ref.1: %AddWith.type = import_ref Main//a, inst+4, loaded [template = constants.%AddWith]
// CHECK:STDOUT: %import_ref.2 = import_ref Main//a, inst+10, unloaded
// CHECK:STDOUT: %import_ref.3 = import_ref Main//a, inst+16, unloaded
// CHECK:STDOUT: %import_ref.4: %F.type.2 = import_ref Main//a, inst+12, loaded [template = constants.%F.2]
// CHECK:STDOUT: %import_ref.2 = import_ref Main//a, inst+12, unloaded
// CHECK:STDOUT: %import_ref.3 = import_ref Main//a, inst+10, unloaded
// CHECK:STDOUT: %import_ref.4 = import_ref Main//a, inst+16, unloaded
// CHECK:STDOUT: %import_ref.5: %F.type.2 = import_ref Main//a, inst+12, loaded [template = constants.%F.1]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
@@ -123,26 +128,38 @@ impl C as AddWith(C) {
// CHECK:STDOUT: }
// CHECK:STDOUT: %default.import = import <invalid>
// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {}
// CHECK:STDOUT: %.loc7_20.1: type = value_of_initializer %.loc7_18 [template = constants.%.4]
// CHECK:STDOUT: %.loc7_20.2: type = converted %.loc7_18, %.loc7_20.1 [template = constants.%.4]
// CHECK:STDOUT: %.loc7_20.1: type = value_of_initializer %.loc7_18 [template = constants.%.6]
// CHECK:STDOUT: %.loc7_20.2: type = converted %.loc7_18, %.loc7_20.1 [template = constants.%.6]
// CHECK:STDOUT: impl_decl @impl {
// CHECK:STDOUT: %C.ref.loc7_6: type = name_ref C, %C.decl [template = constants.%C]
// CHECK:STDOUT: %AddWith.ref: %AddWith.type = name_ref AddWith, imports.%import_ref.1 [template = constants.%AddWith]
// CHECK:STDOUT: %C.ref.loc7_19: type = name_ref C, %C.decl [template = constants.%C]
// CHECK:STDOUT: %.loc7_18: init type = call %AddWith.ref(%C.ref.loc7_19) [template = constants.%.4]
// CHECK:STDOUT: %.loc7_18: init type = call %AddWith.ref(%C.ref.loc7_19) [template = constants.%.6]
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @AddWith {
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = imports.%import_ref.2
// CHECK:STDOUT: .F = imports.%import_ref.3
// CHECK:STDOUT: witness = (imports.%import_ref.4)
// CHECK:STDOUT: generic interface @AddWith(constants.%T: type) {
// CHECK:STDOUT: %T: type = bind_symbolic_name T 0 [symbolic = %T (constants.%T)]
// CHECK:STDOUT:
// CHECK:STDOUT: !definition:
// CHECK:STDOUT: %.1: type = interface_type @AddWith, @AddWith(%T) [symbolic = %.1 (constants.%.3)]
// CHECK:STDOUT: %Self: %.3 = bind_symbolic_name Self 1 [symbolic = %Self (constants.%Self)]
// CHECK:STDOUT: %F.type: type = fn_type @F.1, @AddWith(%T) [symbolic = %F.type (constants.%F.type.2)]
// CHECK:STDOUT: %F: @AddWith.%F.type (%F.type.2) = struct_value () [symbolic = %F (constants.%F.2)]
// CHECK:STDOUT: %.2: type = assoc_entity_type @AddWith, @AddWith.%F.type (%F.type.2) [symbolic = %.2 (constants.%.4)]
// CHECK:STDOUT: %.3: @AddWith.%.2 (%.4) = assoc_entity element0, imports.%import_ref.2 [symbolic = %.3 (constants.%.5)]
// CHECK:STDOUT:
// CHECK:STDOUT: interface {
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = imports.%import_ref.3
// CHECK:STDOUT: .F = imports.%import_ref.4
// CHECK:STDOUT: witness = (imports.%import_ref.5)
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: impl @impl: %C as %.4 {
// CHECK:STDOUT: %F.decl: %F.type.1 = fn_decl @F.1 [template = constants.%F.1] {}
// CHECK:STDOUT: %.1: <witness> = interface_witness (%F.decl) [template = constants.%.5]
// CHECK:STDOUT: impl @impl: %C as %.6 {
// CHECK:STDOUT: %F.decl: %F.type.3 = fn_decl @F.2 [template = constants.%F.3] {}
// CHECK:STDOUT: %.1: <witness> = interface_witness (%F.decl) [template = constants.%.7]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .F = %F.decl
@@ -154,14 +171,25 @@ impl C as AddWith(C) {
// CHECK:STDOUT: .Self = constants.%C
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F.1() {
// CHECK:STDOUT: generic fn @F.1(constants.%T: type, constants.%Self: %.3) {
// CHECK:STDOUT:
// CHECK:STDOUT: fn();
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F.2() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F.2();
// CHECK:STDOUT: specific @AddWith(constants.%T) {
// CHECK:STDOUT: %T => constants.%T
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: specific invalid(constants.%T);
// CHECK:STDOUT: specific @AddWith(@AddWith.%T) {
// CHECK:STDOUT: %T => constants.%T
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: specific invalid(constants.%C);
// CHECK:STDOUT: specific @AddWith(constants.%C) {
// CHECK:STDOUT: %T => constants.%C
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -281,9 +281,15 @@ var f: ForwardDeclared* = &f_ref.f;
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F.1();
// CHECK:STDOUT: generic fn @F.1(constants.%Self.2: %.3) {
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F.2();
// CHECK:STDOUT: fn();
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generic fn @F.2(constants.%Self.3: %.4) {
// CHECK:STDOUT:
// CHECK:STDOUT: fn();
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
// CHECK:STDOUT: !entry: