Browse Source

Apply enclosing specifics to symbolic arguments in generic calls (#5597)

When deduce determines the type of its parameters, it converts each
argument to the correct type, generating an error if the argument can't
convert. It is at this point that enclosing specifics are applied to
arguments as well, as the enclosing specifics are stored in
`substitutions_`. However we were only doing this step for concrete
argument values. If the argument is a symbolic value, like a facet value
that has an enclosing specific, we failed to apply that enclosing
specific. Then the unconverted argument (without the enclosing specific
applied) would end up failing to convert to the parameter type even
though deduce found that it should.
Dana Jansens 11 months ago
parent
commit
315c79ea33

+ 1 - 2
toolchain/check/deduce.cpp

@@ -523,9 +523,8 @@ auto DeductionContext::CheckDeductionIsComplete() -> bool {
     // `DeductionInconsistent` diagnostic. If we defer that check until after
     // all conversions are done (after the code below) then we won't diagnose
     // that incorrectly.
-    auto arg_type_id = context().insts().Get(deduced_arg_id).type_id();
     auto binding_type_id = context().insts().Get(binding_id).type_id();
-    if (arg_type_id.is_concrete() && binding_type_id.is_symbolic()) {
+    if (binding_type_id.is_symbolic()) {
       auto param_type_const_id =
           SubstConstant(context(), SemIR::LocId(binding_id),
                         binding_type_id.AsConstantId(), substitutions_);

+ 82 - 0
toolchain/check/testdata/deduce/min_prelude/symbolic_facets.carbon

@@ -0,0 +1,82 @@
+// 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
+//
+// EXTRA-ARGS: --dump-sem-ir-ranges=only
+// INCLUDE-FILE: toolchain/testing/min_prelude/facet_types.carbon
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/deduce/min_prelude/symbolic_facets.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/deduce/min_prelude/symbolic_facets.carbon
+
+// By placing each interface inside a generic class, a facet type refering to
+// the interface becomes symbolic. Normally they would be concrete. This can
+// affect decisions in deduce which unwraps symbolic values, but still needs to
+// ensure they convert correctly.
+
+// --- fail_missing_interface.carbon
+library "[[@TEST_NAME]]";
+
+class C(CC:! type) {
+  interface A {}
+  fn F(T:! A) {}
+}
+
+class D(DD:! type) {
+  interface B {}
+  fn G(T:! B) {
+    // T only implements D(DD).B, so should not convert to a facet value
+    // implementing C(()).A.
+    //
+    // CHECK:STDERR: fail_missing_interface.carbon:[[@LINE+7]]:5: error: cannot convert type `T` that implements `B` into type implementing `A` [ConversionFailureFacetToFacet]
+    // CHECK:STDERR:     C(()).F(T);
+    // CHECK:STDERR:     ^~~~~~~~~~
+    // CHECK:STDERR: fail_missing_interface.carbon:[[@LINE-12]]:3: note: while deducing parameters of generic declared here [DeductionGenericHere]
+    // CHECK:STDERR:   fn F(T:! A) {}
+    // CHECK:STDERR:   ^~~~~~~~~~~~~
+    // CHECK:STDERR:
+    C(()).F(T);
+  }
+}
+
+// --- fail_interface_wrong_generic_param.carbon
+library "[[@TEST_NAME]]";
+
+class C(CC:! type) {
+  interface A {}
+  fn F(T:! A) {}
+}
+
+class D(DD:! type) {
+  interface B {}
+  fn G(T:! B & C({}).A) {
+    // T implements C({}).A and D(DD).B, so should not convert to a facet value
+    // implementing C(()).A.
+    //
+    // CHECK:STDERR: fail_interface_wrong_generic_param.carbon:[[@LINE+7]]:5: error: cannot convert type `T` that implements `A & B` into type implementing `A` [ConversionFailureFacetToFacet]
+    // CHECK:STDERR:     C(()).F(T);
+    // CHECK:STDERR:     ^~~~~~~~~~
+    // CHECK:STDERR: fail_interface_wrong_generic_param.carbon:[[@LINE-12]]:3: note: while deducing parameters of generic declared here [DeductionGenericHere]
+    // CHECK:STDERR:   fn F(T:! A) {}
+    // CHECK:STDERR:   ^~~~~~~~~~~~~
+    // CHECK:STDERR:
+    C(()).F(T);
+  }
+}
+
+// --- compatible_deduce.carbon
+library "[[@TEST_NAME]]";
+
+class C(CC:! type) {
+  interface A {}
+  fn F(T:! A) {}
+}
+
+class D(DD:! type) {
+  interface B {}
+  fn G(T:! B & C(()).A) {
+    C(()).F(T);
+  }
+}