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

Add tests for poisoning specializations and final associated constants (#5191)

- A concrete query should poison any further specializations of an impl
that are found in the same file.
- A symbolic query should poison any final specializations of an impl
that are found in the same file.
- A final generic specialization should allow generic code to use the
concrete type in an associated constant.

The last one was discussed in open discussion:
https://docs.google.com/document/d/1Iut5f2TQBrtBNIduF4vJYOKfw7MbS8xH_J01_Q4e6Rk/edit?resourcekey=0-mc_vh5UzrzXfU4kO-3tOjA&tab=t.0#heading=h.g7v3y38ydkc7

We decided to take this approach for now, as it reduces possible states
that we have to deal with in the toolchain. And we can revisit if it's
causing problems for ordering impls in carbon code.
Dana Jansens 1 год назад
Родитель
Сommit
e65866d8c3

+ 33 - 0
toolchain/check/testdata/impl/lookup/min_prelude/specialization.carbon

@@ -447,3 +447,36 @@ impl C as Z(C) where .V = {} {
 fn F() {
   let x: {} = G(C);
 }
+
+// --- fail_todo_specialization_of_type_constant_in_generic_context_with_final_impl.carbon
+library "[[@TEST_NAME]]";
+
+interface I {
+  let T:! type;
+  fn F[self: Self]() -> T;
+}
+
+impl forall [U:! type] U as I where .T = () {
+  fn F[self: Self]() -> () { return (); }
+}
+final impl forall [V:! type] V* as I where .T = V {
+  fn F[self: Self]() -> V { return *self; }
+}
+
+fn H[W:! type](v: W) -> W.(I.T) {
+  return v.(I.F)();
+}
+
+// The return of `H` is `(X*).(I.T)` which has a final impl making it `X`.
+// While this function is still a generic context, it should see the concrete
+// `X` type and the return of `H(p)` should convert (a no-op) to `X`.
+fn G[X:! type](p: X*) -> X {
+  // CHECK:STDERR: fail_todo_specialization_of_type_constant_in_generic_context_with_final_impl.carbon:[[@LINE+7]]:3: error: cannot implicitly convert value of type `<symbolic>` to `X` [ConversionFailure]
+  // CHECK:STDERR:   return H(p);
+  // CHECK:STDERR:   ^~~~~~~~~~~~
+  // CHECK:STDERR: fail_todo_specialization_of_type_constant_in_generic_context_with_final_impl.carbon:[[@LINE+4]]:3: note: type `<symbolic>` does not implement interface `Core.ImplicitAs(X)` [MissingImplInMemberAccessNote]
+  // CHECK:STDERR:   return H(p);
+  // CHECK:STDERR:   ^~~~~~~~~~~~
+  // CHECK:STDERR:
+  return H(p);
+}

+ 116 - 0
toolchain/check/testdata/impl/lookup/min_prelude/specialization_poison.carbon

@@ -0,0 +1,116 @@
+// 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
+//
+// INCLUDE-FILE: toolchain/testing/min_prelude/facet_types.carbon
+// EXTRA-ARGS: --no-dump-sem-ir --custom-core
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/lookup/min_prelude/specialization_poison.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/lookup/min_prelude/specialization_poison.carbon
+
+// --- todo_fail_final_poisoned_concrete_query.carbon
+library "[[@TEST_NAME]]";
+
+interface I {
+  let T:! type;
+  fn F[self: Self]() -> T;
+}
+
+impl forall [U:! type] U as I where .T = () {
+  fn F[self: Self]() -> () { return (); }
+}
+
+fn H[W:! type](v: W) -> W.(I.T) {
+  return v.(I.F)();
+}
+
+class C { adapt (); }
+
+fn G(p: C*) -> () {
+  // This concrete impl lookup query poisons any further specializations.
+  return H(p);
+}
+
+// TODO: Diagnose this as a poisoned specialization.
+impl C* as I where .T = C {
+  fn F[self: Self]() -> C { return *self; }
+}
+
+// --- todo_fail_final_poisoned_concrete_query_nested_type_in_self.carbon
+library "[[@TEST_NAME]]";
+
+interface I {
+  let T:! type;
+  fn F[self: Self]() -> T;
+}
+
+class C(U:! type) {}
+
+impl forall [U:! type] C(U) as I where .T = () {
+  fn F[self: Self]() -> () { return (); }
+}
+
+fn G(c: C(())) -> () {
+  // This concrete impl lookup query poisons any further specializations.
+  return c.(I.F)();
+}
+
+// TODO: Diagnose this as a poisoned specialization.
+impl C(()) as I where .T = {} {
+  fn F[self: Self]() -> {} { return {}; }
+}
+
+// --- todo_fail_final_poisoned_concrete_query_nested_type_in_interface.carbon
+library "[[@TEST_NAME]]";
+
+interface I(U:! type) {
+  let T:! type;
+  fn F[self: Self]() -> T;
+}
+
+impl forall [U:! type] U as I(U) where .T = () {
+  fn F[self: Self]() -> () { return (); }
+}
+
+class C {}
+
+fn G(c: C) -> () {
+  // This concrete impl lookup query poisons any further specializations.
+  return c.(I(C).F)();
+}
+
+// TODO: Diagnose this as a poisoned specialization.
+impl C as I(C) where .T = {} {
+  fn F[self: Self]() -> {} { return {}; }
+}
+
+// --- todo_fail_final_poisoned_by_generic_query.carbon
+library "[[@TEST_NAME]]";
+
+interface I {
+  let T:! type;
+  fn F[self: Self]() -> T;
+}
+
+impl forall [U:! type] U as I where .T = () {
+  fn F[self: Self]() -> () { return (); }
+}
+
+fn H[W:! type](v: W) -> W.(I.T) {
+  return v.(I.F)();
+}
+
+// This function could return a concrete `X` if it saw the `final` impl below.
+fn G[X:! type](p: X*) -> (X*).(I.T) {
+  return H(p);
+}
+
+// TODO: This should be diagnosed as poisoned, as H() uses an associated
+// constant from the `impl` which was treated as a symbolic, but this would
+// change it to be a concrete type.
+final impl forall [V:! type] V* as I where .T = V {
+  fn F[self: Self]() -> V { return *self; }
+}