瀏覽代碼

Handle insts that resolve to `type` on the RHS of `impls` (#5712)

In a facet type constraint, you can write `where .Self impls T` for any
facet type `T`, or the constant `type`. It is possible to write `type`
in different ways though, with a `NameRef` instruction appearing on the
RHS instead of `TypeType`. In this case, the canonical constant value's
instruction will still be `TypeType`, so make eval look at the canonical
instruction to see this.

Add a test with an `alias Type = type` which hits this case.

After this change, we only will accept and find one of the following on
the RHS of `impls`:
- `TypeType`
- A facet type
- An error, if the source code had something else there, which will
already be diagnosed. Tested by `fail_right_of_impls_non_type.carbon`
and `fail_right_of_impls_non_facet_type.carbon`.

So we handle these three cases, and drop the implicit handling of other
things which will never appear there.
Dana Jansens 10 月之前
父節點
當前提交
3585b31813
共有 2 個文件被更改,包括 159 次插入10 次删除
  1. 17 10
      toolchain/check/eval.cpp
  2. 142 0
      toolchain/check/testdata/where_expr/constraints.carbon

+ 17 - 10
toolchain/check/eval.cpp

@@ -1999,11 +1999,10 @@ auto TryEvalTypedInst<SemIR::BindSymbolicName>(EvalContext& eval_context,
   return MakeConstantResult(eval_context.context(), bind, phase);
 }
 
-// Returns whether `inst_id` is the same constant facet value as
+// Returns whether `const_id` is the same constant facet value as
 // `facet_value_inst_id`.
-static auto IsSameFacetValue(Context& context, SemIR::InstId inst_id,
+static auto IsSameFacetValue(Context& context, SemIR::ConstantId const_id,
                              SemIR::InstId facet_value_inst_id) -> bool {
-  auto const_id = context.constant_values().Get(inst_id);
   if (auto facet_access_type = context.insts().TryGetAs<SemIR::FacetAccessType>(
           context.constant_values().GetInstId(const_id))) {
     const_id =
@@ -2051,16 +2050,24 @@ auto TryEvalTypedInst<SemIR::WhereExpr>(EvalContext& eval_context,
       } else if (auto impls =
                      eval_context.insts().TryGetAs<SemIR::RequirementImpls>(
                          inst_id)) {
-        if (IsSameFacetValue(eval_context.context(), impls->lhs_id,
+        SemIR::ConstantId lhs_const_id =
+            eval_context.GetConstantValue(impls->lhs_id);
+        SemIR::ConstantId rhs_const_id =
+            eval_context.GetConstantValue(impls->rhs_id);
+        if (IsSameFacetValue(eval_context.context(), lhs_const_id,
                              typed_inst.period_self_id)) {
-          if (impls->rhs_id == SemIR::TypeType::TypeInstId) {
+          auto rhs_inst_id =
+              eval_context.constant_values().GetInstId(rhs_const_id);
+          if (rhs_inst_id == SemIR::ErrorInst::InstId) {
+            // `.Self impls <error>`.
+            return SemIR::ErrorInst::ConstantId;
+          } else if (rhs_inst_id == SemIR::TypeType::TypeInstId) {
             // `.Self impls type` -> nothing to do.
-          } else if (auto facet_type =
-                         eval_context.insts().TryGetAs<SemIR::FacetType>(
-                             RequireConstantValue(eval_context, impls->rhs_id,
-                                                  &phase))) {
+          } else {
+            auto facet_type = eval_context.insts().GetAs<SemIR::FacetType>(
+                RequireConstantValue(eval_context, impls->rhs_id, &phase));
             const auto& more_info =
-                eval_context.facet_types().Get(facet_type->facet_type_id);
+                eval_context.facet_types().Get(facet_type.facet_type_id);
             // The way to prevent lookup into the interface requirements of a
             // facet type is to put it to the right of a `.Self impls`, which we
             // accomplish by putting them into `self_impls_constraints`.

+ 142 - 0
toolchain/check/testdata/where_expr/constraints.carbon

@@ -10,6 +10,39 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/where_expr/constraints.carbon
 
+// --- self_impls_type.carbon
+
+library "[[@TEST_NAME]]";
+
+interface I {}
+
+//@dump-sem-ir-begin
+fn F(T:! I where .Self impls type);
+//@dump-sem-ir-end
+
+alias Type = type;
+
+//@dump-sem-ir-begin
+fn G(T:! I where .Self impls Type);
+//@dump-sem-ir-end
+
+// --- fail_self_impls_error.carbon
+
+library "[[@TEST_NAME]]";
+
+interface I {}
+
+//@dump-sem-ir-begin
+// Because there's an error inside the `where`, the constant value of the
+// `where` should be an error also.
+//
+// CHECK:STDERR: fail_self_impls_error.carbon:[[@LINE+4]]:30: error: name `J` not found [NameNotFound]
+// CHECK:STDERR: fn F(T:! I where .Self impls J);
+// CHECK:STDERR:                              ^
+// CHECK:STDERR:
+fn F(T:! I where .Self impls J);
+//@dump-sem-ir-end
+
 // --- state_constraints.carbon
 
 library "[[@TEST_NAME]]";
@@ -151,6 +184,115 @@ fn F() {
 //@dump-sem-ir-end
 }
 
+// CHECK:STDOUT: --- self_impls_type.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %I.type: type = facet_type <@I> [concrete]
+// CHECK:STDOUT:   %.Self: %I.type = bind_symbolic_name .Self [symbolic_self]
+// CHECK:STDOUT:   %.Self.as_type: type = facet_access_type %.Self [symbolic_self]
+// CHECK:STDOUT:   %T: %I.type = bind_symbolic_name T, 0 [symbolic]
+// CHECK:STDOUT:   %pattern_type: type = pattern_type %I.type [concrete]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
+// CHECK:STDOUT:   %G.type: type = fn_type @G [concrete]
+// CHECK:STDOUT:   %G: %G.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
+// CHECK:STDOUT:     %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %.loc7_12.1: type = splice_block %.loc7_12.2 [concrete = constants.%I.type] {
+// CHECK:STDOUT:       %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type]
+// CHECK:STDOUT:       <elided>
+// CHECK:STDOUT:       %.Self.ref: %I.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self]
+// CHECK:STDOUT:       %.Self.as_type: type = facet_access_type %.Self.ref [symbolic_self = constants.%.Self.as_type]
+// CHECK:STDOUT:       %.loc7_18: type = converted %.Self.ref, %.Self.as_type [symbolic_self = constants.%.Self.as_type]
+// CHECK:STDOUT:       %.loc7_12.2: type = where_expr %.Self [concrete = constants.%I.type] {
+// CHECK:STDOUT:         requirement_impls %.loc7_18, type
+// CHECK:STDOUT:       }
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %T.loc7_6.1: %I.type = bind_symbolic_name T, 0 [symbolic = %T.loc7_6.2 (constants.%T)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {
+// CHECK:STDOUT:     %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %.loc13_12.1: type = splice_block %.loc13_12.2 [concrete = constants.%I.type] {
+// CHECK:STDOUT:       %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type]
+// CHECK:STDOUT:       <elided>
+// CHECK:STDOUT:       %.Self.ref: %I.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self]
+// CHECK:STDOUT:       %Type.ref: type = name_ref Type, file.%Type [concrete = type]
+// CHECK:STDOUT:       %.Self.as_type: type = facet_access_type %.Self.ref [symbolic_self = constants.%.Self.as_type]
+// CHECK:STDOUT:       %.loc13_18: type = converted %.Self.ref, %.Self.as_type [symbolic_self = constants.%.Self.as_type]
+// CHECK:STDOUT:       %.loc13_12.2: type = where_expr %.Self [concrete = constants.%I.type] {
+// CHECK:STDOUT:         requirement_impls %.loc13_18, %Type.ref
+// CHECK:STDOUT:       }
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %T.loc13_6.1: %I.type = bind_symbolic_name T, 0 [symbolic = %T.loc13_6.2 (constants.%T)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @F(%T.loc7_6.1: %I.type) {
+// CHECK:STDOUT:   %T.loc7_6.2: %I.type = bind_symbolic_name T, 0 [symbolic = %T.loc7_6.2 (constants.%T)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn();
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @G(%T.loc13_6.1: %I.type) {
+// CHECK:STDOUT:   %T.loc13_6.2: %I.type = bind_symbolic_name T, 0 [symbolic = %T.loc13_6.2 (constants.%T)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn();
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F(constants.%T) {
+// CHECK:STDOUT:   %T.loc7_6.2 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @G(constants.%T) {
+// CHECK:STDOUT:   %T.loc13_6.2 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_self_impls_error.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %I.type: type = facet_type <@I> [concrete]
+// CHECK:STDOUT:   %.Self: %I.type = bind_symbolic_name .Self [symbolic_self]
+// CHECK:STDOUT:   %.Self.as_type: type = facet_access_type %.Self [symbolic_self]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
+// CHECK:STDOUT:     %T.patt: <error> = symbolic_binding_pattern T, 0 [concrete]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %.loc14_12.1: type = splice_block %.loc14_12.2 [concrete = <error>] {
+// CHECK:STDOUT:       %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type]
+// CHECK:STDOUT:       <elided>
+// CHECK:STDOUT:       %.Self.ref: %I.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self]
+// CHECK:STDOUT:       %J.ref: <error> = name_ref J, <error> [concrete = <error>]
+// CHECK:STDOUT:       %.Self.as_type: type = facet_access_type %.Self.ref [symbolic_self = constants.%.Self.as_type]
+// CHECK:STDOUT:       %.loc14_18: type = converted %.Self.ref, %.Self.as_type [symbolic_self = constants.%.Self.as_type]
+// CHECK:STDOUT:       %.loc14_12.2: type = where_expr %.Self [concrete = <error>] {
+// CHECK:STDOUT:         requirement_impls %.loc14_18, <error>
+// CHECK:STDOUT:       }
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %T: <error> = bind_symbolic_name T, 0 [concrete = <error>]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @F(%T: <error>) {
+// CHECK:STDOUT:   fn();
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F(<error>) {}
+// CHECK:STDOUT:
 // CHECK:STDOUT: --- state_constraints.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {