Просмотр исходного кода

Concrete impl takes precedence over a facet value (#5305)

If a concrete impl is found via lookup, its associated constants should
be used over the constants found through a facet value.
Dana Jansens 1 год назад
Родитель
Сommit
da83b65aa2

+ 16 - 8
toolchain/check/impl_lookup.cpp

@@ -441,15 +441,23 @@ auto LookupImplWitness(Context& context, SemIR::LocId loc_id,
   for (const auto& interface : interfaces) {
     // TODO: Since both `interfaces` and `query_self_const_id` are sorted lists,
     // do an O(N+M) merge instead of O(N*M) nested loops.
-    auto result_witness_id =
-        FindWitnessInFacet(context, loc_id, query_self_const_id, interface);
-    // TODO: If the impl lookup finds a final impl, it should take precedence
-    // over the witness from the facet value. See the test:
-    // fail_todo_final_impl_precidence_over_facet_value.carbon.
-    if (!result_witness_id.has_value()) {
-      result_witness_id = GetOrAddLookupImplWitness(
-          context, loc_id, query_self_const_id, interface);
+
+    auto result_witness_id = GetOrAddLookupImplWitness(
+        context, loc_id, query_self_const_id, interface);
+
+    if (!result_witness_id.has_value() ||
+        !context.insts().Is<SemIR::ImplWitness>(result_witness_id)) {
+      // If the witness is not concrete (the result is not final), then we
+      // prefer the witness from the query's facet value (if it is one), which
+      // may include rewrite rules of associated constants that can be relied
+      // on.
+      auto facet_value_witness_id =
+          FindWitnessInFacet(context, loc_id, query_self_const_id, interface);
+      if (facet_value_witness_id.has_value()) {
+        result_witness_id = facet_value_witness_id;
+      }
     }
+
     if (result_witness_id.has_value()) {
       result_witness_ids.push_back(result_witness_id);
     } else {

+ 3 - 15
toolchain/check/testdata/impl/lookup/min_prelude/find_in_final.carbon

@@ -62,7 +62,7 @@ fn F[T:! Z where .X = ()](z: T) {
   let a: z.Y = ();
 }
 
-// --- fail_todo_final_impl_makes_compatible_facet_values.carbon
+// --- final_impl_makes_compatible_facet_values.carbon
 library "[[@TEST_NAME]]";
 
 interface I {}
@@ -80,24 +80,12 @@ class D(T:! J) {
 
 fn F(T:! I & J) {
   // `I` found in the facet type; The facet value of `T` in `C(T)` holds a `FacetAccessWitness`.
-  // TODO: It should use the `final impl` and thus hold the same `ImplWitness`
   var x: C(T);
   // `D.c` used the final impl decl to make a facet value with an `ImplWitness`
   // for its `T` in `C(T)`.
   var y: D(T);
-  // This converts a pointer to a facet value (should be `ImplWitness` but
-  // isn't) to a pointer to a facet value (with `ImplWitness`).
-  //
-  // TODO: It should type check when they are both the same generic type
-  // `C(T:! I)` defined by the `final impl` of `I`.
-  //
-  // CHECK:STDERR: fail_todo_final_impl_makes_compatible_facet_values.carbon:[[@LINE+7]]:3: error: cannot implicitly convert expression of type `C(T as I)*` to `C(T as I)*` [ConversionFailure]
-  // CHECK:STDERR:   y.c = &x;
-  // CHECK:STDERR:   ^~~~~~~~
-  // CHECK:STDERR: fail_todo_final_impl_makes_compatible_facet_values.carbon:[[@LINE+4]]:3: note: type `C(T as I)*` does not implement interface `Core.ImplicitAs(C(T as I)*)` [MissingImplInMemberAccessNote]
-  // CHECK:STDERR:   y.c = &x;
-  // CHECK:STDERR:   ^~~~~~~~
-  // CHECK:STDERR:
+  // This converts a pointer to a type with with facet value (an `ImplWitness`)
+  // to a pointer to a type with a facet value (the same `ImplWitness`).
   y.c = &x;
 }