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

Don't require a thunk for parameter name differences. (#5404)

When checking whether we can use the function in an impl directly to
satisfy a signature in an interface, allow the parameter names to differ
between the two declarations.
Richard Smith 1 год назад
Родитель
Сommit
cab91d0590
2 измененных файлов с 26 добавлено и 65 удалено
  1. 8 6
      toolchain/check/merge.cpp
  2. 18 59
      toolchain/check/testdata/impl/no_prelude/impl_thunk.carbon

+ 8 - 6
toolchain/check/merge.cpp

@@ -206,7 +206,7 @@ static auto CheckRedeclParam(Context& context, bool is_implicit_param,
                              SemIR::InstId new_param_pattern_id,
                              SemIR::InstId prev_param_pattern_id,
                              SemIR::SpecificId prev_specific_id, bool diagnose,
-                             bool check_self) -> bool {
+                             bool check_syntax, bool check_self) -> bool {
   auto orig_new_param_pattern_id = new_param_pattern_id;
   auto orig_prev_param_pattern_id = prev_param_pattern_id;
 
@@ -300,7 +300,7 @@ static auto CheckRedeclParam(Context& context, bool is_implicit_param,
     return false;
   }
 
-  if (new_name_id != prev_name_id) {
+  if (check_syntax && new_name_id != prev_name_id) {
     emit_diagnostic();
     return false;
   }
@@ -315,7 +315,7 @@ static auto CheckRedeclParams(Context& context, SemIR::LocId new_decl_loc_id,
                               SemIR::InstBlockId prev_param_patterns_id,
                               bool is_implicit_param,
                               SemIR::SpecificId prev_specific_id, bool diagnose,
-                              bool check_self) -> bool {
+                              bool check_syntax, bool check_self) -> bool {
   // This will often occur for empty params.
   if (new_param_patterns_id == prev_param_patterns_id) {
     return true;
@@ -373,7 +373,8 @@ static auto CheckRedeclParams(Context& context, SemIR::LocId new_decl_loc_id,
        llvm::enumerate(new_param_pattern_ids, prev_param_pattern_ids)) {
     if (!CheckRedeclParam(context, is_implicit_param, index,
                           new_param_pattern_id, prev_param_pattern_id,
-                          prev_specific_id, diagnose, check_self)) {
+                          prev_specific_id, diagnose, check_syntax,
+                          check_self)) {
       return false;
     }
   }
@@ -493,7 +494,8 @@ auto CheckRedeclParamsMatch(Context& context, const DeclParams& new_entity,
   if (!CheckRedeclParams(
           context, new_entity.loc_id, new_entity.implicit_param_patterns_id,
           prev_entity.loc_id, prev_entity.implicit_param_patterns_id,
-          /*is_implicit_param=*/true, prev_specific_id, diagnose, check_self)) {
+          /*is_implicit_param=*/true, prev_specific_id, diagnose, check_syntax,
+          check_self)) {
     return false;
   }
   // Don't forward `check_self` here because it's extra cost, and `self` is only
@@ -502,7 +504,7 @@ auto CheckRedeclParamsMatch(Context& context, const DeclParams& new_entity,
                          new_entity.param_patterns_id, prev_entity.loc_id,
                          prev_entity.param_patterns_id,
                          /*is_implicit_param=*/false, prev_specific_id,
-                         diagnose, /*check_self=*/true)) {
+                         diagnose, check_syntax, /*check_self=*/true)) {
     return false;
   }
   if (check_syntax &&

+ 18 - 59
toolchain/check/testdata/impl/no_prelude/impl_thunk.carbon

@@ -8,39 +8,15 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/no_prelude/impl_thunk.carbon
 
-// --- fail_todo_param_name_differs.carbon
+// --- no_thunk_param_name_differs.carbon
 
 library "[[@TEST_NAME]]";
 
-// CHECK:STDERR: fail_todo_param_name_differs.carbon:[[@LINE+3]]:20: error: parameter has incomplete type `C` in function definition [IncompleteTypeInFunctionParam]
-// CHECK:STDERR: interface I { fn F(x: Self); }
-// CHECK:STDERR:                    ^~~~~~~
 interface I { fn F(x: Self); }
 
-// TODO: This currently uses a thunk due to the parameter name mismatch, but shouldn't!
-// TODO: This then fails because we attempt to build the thunk before `C` is complete.
-// CHECK:STDERR: fail_todo_param_name_differs.carbon:[[@LINE+3]]:1: note: class is incomplete within its definition [ClassIncompleteWithinDefinition]
-// CHECK:STDERR: class C {
-// CHECK:STDERR: ^~~~~~~~~
+// This is OK, and does not require a thunk despite the parameter name differing.
 class C {
   impl as I {
-    // CHECK:STDERR: fail_todo_param_name_differs.carbon:[[@LINE+17]]:5: note: while building thunk calling this function [ThunkCallee]
-    // CHECK:STDERR:     fn F(not_x: C);
-    // CHECK:STDERR:     ^~~~~~~~~~~~~~~
-    // CHECK:STDERR:
-    // CHECK:STDERR: fail_todo_param_name_differs.carbon:[[@LINE-13]]:20: error: forming value of incomplete type `C` [IncompleteTypeInValueConversion]
-    // CHECK:STDERR: interface I { fn F(x: Self); }
-    // CHECK:STDERR:                    ^~~~~~~
-    // CHECK:STDERR: fail_todo_param_name_differs.carbon:[[@LINE-9]]:1: note: class is incomplete within its definition [ClassIncompleteWithinDefinition]
-    // CHECK:STDERR: class C {
-    // CHECK:STDERR: ^~~~~~~~~
-    // CHECK:STDERR: fail_todo_param_name_differs.carbon:[[@LINE+7]]:10: note: initializing function parameter [InCallToFunctionParam]
-    // CHECK:STDERR:     fn F(not_x: C);
-    // CHECK:STDERR:          ^~~~~~~~
-    // CHECK:STDERR: fail_todo_param_name_differs.carbon:[[@LINE-22]]:15: note: while building thunk to match the signature of this function [ThunkSignature]
-    // CHECK:STDERR: interface I { fn F(x: Self); }
-    // CHECK:STDERR:               ^~~~~~~~~~~~~~
-    // CHECK:STDERR:
     fn F(not_x: C);
   }
 }
@@ -227,7 +203,7 @@ impl C as I {
   fn NoReturn() -> C;
 }
 
-// CHECK:STDOUT: --- fail_todo_param_name_differs.carbon
+// CHECK:STDOUT: --- no_thunk_param_name_differs.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %I.type: type = facet_type <@I> [concrete]
@@ -235,18 +211,15 @@ impl C as I {
 // CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self [symbolic]
 // CHECK:STDOUT:   %pattern_type.6de: type = pattern_type %Self.as_type [symbolic]
 // CHECK:STDOUT:   %F.type.cf0: type = fn_type @F.1 [concrete]
-// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %F.bc6: %F.type.cf0 = struct_value () [concrete]
 // CHECK:STDOUT:   %I.assoc_type: type = assoc_entity_type @I [concrete]
 // CHECK:STDOUT:   %assoc0: %I.assoc_type = assoc_entity element0, @I.%F.decl [concrete]
 // CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %I.impl_witness: <witness> = impl_witness @C.%I.impl_witness_table [concrete]
 // CHECK:STDOUT:   %pattern_type.c48: type = pattern_type %C [concrete]
-// CHECK:STDOUT:   %F.type.f3605c.1: type = fn_type @F.2 [concrete]
-// CHECK:STDOUT:   %F.4c32b3.1: %F.type.f3605c.1 = struct_value () [concrete]
+// CHECK:STDOUT:   %F.type.f36: type = fn_type @F.2 [concrete]
+// CHECK:STDOUT:   %F.4c3: %F.type.f36 = struct_value () [concrete]
 // CHECK:STDOUT:   %I.facet: %I.type = facet_value %C, (%I.impl_witness) [concrete]
-// CHECK:STDOUT:   %F.type.f3605c.2: type = fn_type @F.3 [concrete]
-// CHECK:STDOUT:   %F.4c32b3.2: %F.type.f3605c.2 = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
 // CHECK:STDOUT: }
@@ -266,13 +239,13 @@ impl C as I {
 // CHECK:STDOUT:     %x.patt: @F.1.%pattern_type (%pattern_type.6de) = binding_pattern x [concrete]
 // CHECK:STDOUT:     %x.param_patt: @F.1.%pattern_type (%pattern_type.6de) = value_param_pattern %x.patt, call_param0 [concrete]
 // CHECK:STDOUT:   } {
-// CHECK:STDOUT:     %x.param: @F.1.%Self.as_type.loc7_23.1 (%Self.as_type) = value_param call_param0
-// CHECK:STDOUT:     %.loc7_23.1: type = splice_block %.loc7_23.2 [symbolic = %Self.as_type.loc7_23.1 (constants.%Self.as_type)] {
+// CHECK:STDOUT:     %x.param: @F.1.%Self.as_type.loc4_23.1 (%Self.as_type) = value_param call_param0
+// CHECK:STDOUT:     %.loc4_23.1: type = splice_block %.loc4_23.2 [symbolic = %Self.as_type.loc4_23.1 (constants.%Self.as_type)] {
 // CHECK:STDOUT:       %Self.ref: %I.type = name_ref Self, @I.%Self [symbolic = %Self (constants.%Self)]
-// CHECK:STDOUT:       %Self.as_type.loc7_23.2: type = facet_access_type %Self.ref [symbolic = %Self.as_type.loc7_23.1 (constants.%Self.as_type)]
-// CHECK:STDOUT:       %.loc7_23.2: type = converted %Self.ref, %Self.as_type.loc7_23.2 [symbolic = %Self.as_type.loc7_23.1 (constants.%Self.as_type)]
+// CHECK:STDOUT:       %Self.as_type.loc4_23.2: type = facet_access_type %Self.ref [symbolic = %Self.as_type.loc4_23.1 (constants.%Self.as_type)]
+// CHECK:STDOUT:       %.loc4_23.2: type = converted %Self.ref, %Self.as_type.loc4_23.2 [symbolic = %Self.as_type.loc4_23.1 (constants.%Self.as_type)]
 // CHECK:STDOUT:     }
-// CHECK:STDOUT:     %x: @F.1.%Self.as_type.loc7_23.1 (%Self.as_type) = bind_name x, %x.param
+// CHECK:STDOUT:     %x: @F.1.%Self.as_type.loc4_23.1 (%Self.as_type) = bind_name x, %x.param
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %assoc0: %I.assoc_type = assoc_entity element0, %F.decl [concrete = constants.%assoc0]
 // CHECK:STDOUT:
@@ -283,7 +256,7 @@ impl C as I {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: impl @impl: %Self.ref as %I.ref {
-// CHECK:STDOUT:   %F.decl.loc33_19.1: %F.type.f3605c.1 = fn_decl @F.2 [concrete = constants.%F.4c32b3.1] {
+// CHECK:STDOUT:   %F.decl: %F.type.f36 = fn_decl @F.2 [concrete = constants.%F.4c3] {
 // CHECK:STDOUT:     %not_x.patt: %pattern_type.c48 = binding_pattern not_x [concrete]
 // CHECK:STDOUT:     %not_x.param_patt: %pattern_type.c48 = value_param_pattern %not_x.patt, call_param0 [concrete]
 // CHECK:STDOUT:   } {
@@ -291,17 +264,10 @@ impl C as I {
 // CHECK:STDOUT:     %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C]
 // CHECK:STDOUT:     %not_x: %C = bind_name not_x, %not_x.param
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %F.decl.loc33_19.2: %F.type.f3605c.2 = fn_decl @F.3 [concrete = constants.%F.4c32b3.2] {
-// CHECK:STDOUT:     %x.patt: %pattern_type.c48 = binding_pattern x [concrete]
-// CHECK:STDOUT:     %x.param_patt: %pattern_type.c48 = value_param_pattern %x.patt, call_param0 [concrete]
-// CHECK:STDOUT:   } {
-// CHECK:STDOUT:     %x.param: %C = value_param call_param0
-// CHECK:STDOUT:     %x: %C = bind_name x, %x.param
-// CHECK:STDOUT:   }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .C = <poisoned>
-// CHECK:STDOUT:   .F = %F.decl.loc33_19.1
+// CHECK:STDOUT:   .F = %F.decl
 // CHECK:STDOUT:   witness = @C.%I.impl_witness
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -310,7 +276,7 @@ impl C as I {
 // CHECK:STDOUT:     %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C]
 // CHECK:STDOUT:     %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %I.impl_witness_table = impl_witness_table (@impl.%F.decl.loc33_19.2), @impl [concrete]
+// CHECK:STDOUT:   %I.impl_witness_table = impl_witness_table (@impl.%F.decl), @impl [concrete]
 // CHECK:STDOUT:   %I.impl_witness: <witness> = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
@@ -324,30 +290,23 @@ impl C as I {
 // CHECK:STDOUT:
 // CHECK:STDOUT: generic fn @F.1(@I.%Self: %I.type) {
 // CHECK:STDOUT:   %Self: %I.type = bind_symbolic_name Self, 0 [symbolic = %Self (constants.%Self)]
-// CHECK:STDOUT:   %Self.as_type.loc7_23.1: type = facet_access_type %Self [symbolic = %Self.as_type.loc7_23.1 (constants.%Self.as_type)]
-// CHECK:STDOUT:   %pattern_type: type = pattern_type %Self.as_type.loc7_23.1 [symbolic = %pattern_type (constants.%pattern_type.6de)]
+// CHECK:STDOUT:   %Self.as_type.loc4_23.1: type = facet_access_type %Self [symbolic = %Self.as_type.loc4_23.1 (constants.%Self.as_type)]
+// CHECK:STDOUT:   %pattern_type: type = pattern_type %Self.as_type.loc4_23.1 [symbolic = %pattern_type (constants.%pattern_type.6de)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn(%x.param: @F.1.%Self.as_type.loc7_23.1 (%Self.as_type));
+// CHECK:STDOUT:   fn(%x.param: @F.1.%Self.as_type.loc4_23.1 (%Self.as_type));
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.2(%not_x.param: %C);
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @F.3(%x.param: %C) {
-// CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %x.ref: %C = name_ref x, %x.param
-// CHECK:STDOUT:   %F.call: init %empty_tuple.type = call @impl.%F.decl.loc33_19.1(<error>)
-// CHECK:STDOUT:   return
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: specific @F.1(constants.%Self) {
 // CHECK:STDOUT:   %Self => constants.%Self
-// CHECK:STDOUT:   %Self.as_type.loc7_23.1 => constants.%Self.as_type
+// CHECK:STDOUT:   %Self.as_type.loc4_23.1 => constants.%Self.as_type
 // CHECK:STDOUT:   %pattern_type => constants.%pattern_type.6de
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @F.1(constants.%I.facet) {
 // CHECK:STDOUT:   %Self => constants.%I.facet
-// CHECK:STDOUT:   %Self.as_type.loc7_23.1 => constants.%C
+// CHECK:STDOUT:   %Self.as_type.loc4_23.1 => constants.%C
 // CHECK:STDOUT:   %pattern_type => constants.%pattern_type.c48
 // CHECK:STDOUT: }
 // CHECK:STDOUT: