Преглед изворни кода

Use a "type structure" of each impl to choose the best match (#5124)

The type structure is built for the impl lookup query for the
combination of the self type and the interface being queried. Then it is
built for each `impl` definition that is a potential candidate.

The type structures are compared to ensure they have a compatible
structure, and the `impl` declaration is not considered if they do not.

Finally, the type structures are used as a sorting key for the candidate
`impl` declarations, with the most-specified type structures (the ones
with the furthest distance to the first symbolic value) coming first in
the ordering.

See
https://docs.carbon-lang.dev/docs/design/generics/overview.html#parameterized-impl-declarations
for the design of the type structure and related ordering.

Most of the commits in this PR landed in #5158 (11ae0e27ab5c121) by
mistake, but this includes the final changes since that PR was written.

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
Dana Jansens пре 1 година
родитељ
комит
6041e9aa9d

+ 8 - 16
toolchain/check/testdata/generic/template/unimplemented.carbon

@@ -15,10 +15,6 @@ library "[[@TEST_NAME]]";
 // Check that we get a reasonable diagnostic for an unimplemented operation on a
 // template dependent expression.
 fn F[template T:! type](x: T) -> i32 {
-  // CHECK:STDERR: fail_todo_unimplemented_operator.carbon:[[@LINE+8]]:10: error: semantics TODO: `Impl lookup on template-dependent type value` [SemanticsTodo]
-  // CHECK:STDERR:   return x.n * 3;
-  // CHECK:STDERR:          ^~~
-  // CHECK:STDERR:
   // CHECK:STDERR: fail_todo_unimplemented_operator.carbon:[[@LINE+4]]:10: error: cannot access member of interface `Core.Mul` in type `<dependent type>` that does not implement that interface [MissingImplInMemberAccess]
   // CHECK:STDERR:   return x.n * 3;
   // CHECK:STDERR:          ^~~~~~~
@@ -38,10 +34,6 @@ class C {
 // template dependent value where the type is concrete but determined through
 // the template dependent value.
 fn F(template c:! C) -> i32 {
-  // CHECK:STDERR: fail_todo_unimplemented_value.carbon:[[@LINE+8]]:10: error: semantics TODO: `Impl lookup on template-dependent type value` [SemanticsTodo]
-  // CHECK:STDERR:   return c.n * 3;
-  // CHECK:STDERR:          ^~~
-  // CHECK:STDERR:
   // CHECK:STDERR: fail_todo_unimplemented_value.carbon:[[@LINE+4]]:10: error: cannot access member of interface `Core.Mul` in type `<dependent type>` that does not implement that interface [MissingImplInMemberAccess]
   // CHECK:STDERR:   return c.n * 3;
   // CHECK:STDERR:          ^~~~~~~
@@ -126,15 +118,15 @@ fn F[template T:! type](x: T) {
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT:   %require_complete: <witness> = require_complete_type @F.%T.loc6_15.2 (%T) [template = %require_complete (constants.%require_complete.4ae)]
-// CHECK:STDOUT:   %.loc15_11.3: <instruction> = refine_type_action %x.ref, @F.%T.loc6_15.2 (%T) [template]
-// CHECK:STDOUT:   %.loc15_11.4: <instruction> = access_member_action %.loc15_11.1, n [template]
-// CHECK:STDOUT:   %.loc15_11.5: type = type_of_inst %.loc15_11.4 [template]
+// CHECK:STDOUT:   %.loc11_11.3: <instruction> = refine_type_action %x.ref, @F.%T.loc6_15.2 (%T) [template]
+// CHECK:STDOUT:   %.loc11_11.4: <instruction> = access_member_action %.loc11_11.1, n [template]
+// CHECK:STDOUT:   %.loc11_11.5: type = type_of_inst %.loc11_11.4 [template]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   fn[%T.patt.loc6_15.1: type](%x.param_patt: @F.%T.loc6_15.2 (%T)) -> %i32 {
 // CHECK:STDOUT:   !entry:
 // CHECK:STDOUT:     %x.ref: @F.%T.loc6_15.2 (%T) = name_ref x, %x
-// CHECK:STDOUT:     %.loc15_11.1: @F.%T.loc6_15.2 (%T) = splice_inst %.loc15_11.3
-// CHECK:STDOUT:     %.loc15_11.2: @F.%.loc15_11.5 (@F.%.loc15_11.5) = splice_inst %.loc15_11.4
+// CHECK:STDOUT:     %.loc11_11.1: @F.%T.loc6_15.2 (%T) = splice_inst %.loc11_11.3
+// CHECK:STDOUT:     %.loc11_11.2: @F.%.loc11_11.5 (@F.%.loc11_11.5) = splice_inst %.loc11_11.4
 // CHECK:STDOUT:     %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3]
 // CHECK:STDOUT:     return <error>
 // CHECK:STDOUT:   }
@@ -211,13 +203,13 @@ fn F[template T:! type](x: T) {
 // CHECK:STDOUT:   %c.patt.loc11_15.2: %C = symbolic_binding_pattern c, 0, template [template = %c.patt.loc11_15.2 (constants.%c.patt)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
-// CHECK:STDOUT:   %.loc20_11.2: <instruction> = access_member_action %c.ref, n [template]
-// CHECK:STDOUT:   %.loc20_11.3: type = type_of_inst %.loc20_11.2 [template]
+// CHECK:STDOUT:   %.loc16_11.2: <instruction> = access_member_action %c.ref, n [template]
+// CHECK:STDOUT:   %.loc16_11.3: type = type_of_inst %.loc16_11.2 [template]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   fn(%c.patt.loc11_15.1: %C) -> %i32 {
 // CHECK:STDOUT:   !entry:
 // CHECK:STDOUT:     %c.ref: %C = name_ref c, %c.loc11_15.1 [template = %c.loc11_15.2 (constants.%c)]
-// CHECK:STDOUT:     %.loc20_11.1: @F.%.loc20_11.3 (@F.%.loc20_11.3) = splice_inst %.loc20_11.2
+// CHECK:STDOUT:     %.loc16_11.1: @F.%.loc16_11.3 (@F.%.loc16_11.3) = splice_inst %.loc16_11.2
 // CHECK:STDOUT:     %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3]
 // CHECK:STDOUT:     return <error>
 // CHECK:STDOUT:   }

+ 9 - 30
toolchain/check/type_structure.cpp

@@ -185,6 +185,15 @@ class TypeStructureBuilder {
           Push(SymbolicType());
           break;
         }
+        case SemIR::TypeOfInst::Kind: {
+          // TODO: For a template value with a fixed type, such as `template n:!
+          // i32`, we could look at the type of the value to see if it's
+          // template-dependent (which it's not here) and add that type to the
+          // type structure?
+          // https://github.com/carbon-language/carbon-lang/pull/5124#discussion_r2006617038
+          Push(SymbolicType());
+          break;
+        }
 
           // ==== Concrete types ====
 
@@ -287,36 +296,6 @@ class TypeStructureBuilder {
           }
           break;
         }
-        case CARBON_KIND(SemIR::TypeOfInst type_of): {
-          (void)type_of;
-          auto const_id = context_.constant_values().Get(inst_id);
-          // TODO: TypeOfInst should not be encountered in impl lookup with a
-          // template-dependent value, since impl lookup on such values should
-          // be deferred to type-checking the specific. So this should become a
-          // CARBON_FATAL code path. For now it is possible, though, to reach
-          // here and it results in a diagnostic.
-          //
-          // However, TypeOfInst can be determined to be a concrete value for a
-          // template value with a non-template dependent type. This currently
-          // does not happen though, the TypeOfInst is always template
-          // dependent. If it does in the future then we will want to use the
-          // concrete constant value instruction of `inst_id` in the type
-          // structure:
-          //   if (const_id.is_concrete()) {
-          //     PushInstId(context_.constant_values().GetInstId(const_id));
-          //   }
-          //
-          // If TypeOfInst was used for more general metaprogramming then we may
-          // need to handle both concrete and perhaps symbolic (non-template)
-          // values of TypeOfInst.
-          CARBON_CHECK(const_id.is_symbolic());
-          auto sym = context_.constant_values().GetSymbolicConstant(const_id);
-          CARBON_CHECK(sym.dependence == SemIR::ConstantDependence::Template,
-                       "TypeOfInst with non-template symbolic value?");
-          context_.TODO(context_.insts().GetLocId(inst_id),
-                        "Impl lookup on template-dependent type value");
-          break;
-        }
         default:
           CARBON_FATAL("Unhandled type instruction {0}", inst_id);
       }