Ver Fonte

Properly check that a constraint appears after `is`. (#2304)

In particular, if we find the name of an interface after `is`, it should
be exapnded to a constraint, including any implied constraints such as
extended interfaces, rather than forming a constraint requiring only
that interface in isolation.
Richard Smith há 3 anos atrás
pai
commit
b4418e29ec

+ 10 - 19
explorer/interpreter/type_checker.cpp

@@ -3132,25 +3132,16 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
             CARBON_ASSIGN_OR_RETURN(
             CARBON_ASSIGN_OR_RETURN(
                 Nonnull<const Value*> constraint,
                 Nonnull<const Value*> constraint,
                 InterpExp(&is_clause.constraint(), arena_, trace_stream_));
                 InterpExp(&is_clause.constraint(), arena_, trace_stream_));
-            if (auto* interface = dyn_cast<InterfaceType>(constraint)) {
-              // `where X is Y` produces an `impl` constraint.
-              builder.AddImplConstraint({.type = type, .interface = interface});
-            } else if (auto* constraint_type =
-                           dyn_cast<ConstraintType>(constraint)) {
-              // Transform `where .B is (C where .D is E)` into
-              // `where .B is C and .B.D is E` then add all the resulting
-              // constraints.
-              CARBON_RETURN_IF_ERROR(
-                  builder.AddAndSubstitute(*this, constraint_type, type,
-                                           builder.GetSelfWitness(), Bindings(),
-                                           /*add_lookup_contexts=*/false));
-            } else {
-              return ProgramError(is_clause.constraint().source_loc())
-                     << "expression after `is` does not resolve to a "
-                        "constraint, found value "
-                     << *constraint << " of type "
-                     << is_clause.constraint().static_type();
-            }
+            CARBON_ASSIGN_OR_RETURN(
+                Nonnull<const ConstraintType*> constraint_type,
+                ConvertToConstraintType(is_clause.source_loc(),
+                                        "expression after `is`", constraint));
+            // Transform `where .B is (C where .D is E)` into `where .B is C
+            // and .B.D is E` then add all the resulting constraints.
+            CARBON_RETURN_IF_ERROR(
+                builder.AddAndSubstitute(*this, constraint_type, type,
+                                         builder.GetSelfWitness(), Bindings(),
+                                         /*add_lookup_contexts=*/false));
             break;
             break;
           }
           }
           case WhereClauseKind::EqualsWhereClause: {
           case WhereClauseKind::EqualsWhereClause: {

+ 1 - 1
explorer/testdata/constraint/fail_where_is_non_constraint.carbon

@@ -10,7 +10,7 @@ package ExplorerTest api;
 
 
 interface A {}
 interface A {}
 
 
-// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/fail_where_is_non_constraint.carbon:[[@LINE+1]]: expression after `is` does not resolve to a constraint, found value i32 of type Type
+// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/fail_where_is_non_constraint.carbon:[[@LINE+1]]: expected a constraint in expression after `is`, found i32
 alias B = A where i32 is i32;
 alias B = A where i32 is i32;
 
 
 fn Main() -> i32 { return 0; }
 fn Main() -> i32 { return 0; }

+ 33 - 0
explorer/testdata/constraint/where_is.carbon

@@ -0,0 +1,33 @@
+// 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
+//
+// AUTOUPDATE
+// RUN: %{explorer-run}
+// RUN: %{explorer-run-trace}
+// CHECK:STDOUT: i32.F
+// CHECK:STDOUT: result: 0
+
+package ExplorerTest api;
+
+interface Base {
+  fn F();
+}
+
+interface Extension {
+  extends Base;
+}
+
+fn F[T:! Type where .Self is Extension](x: T) {
+  x.(Extension.F)();
+}
+
+impl i32 as Extension {
+  fn F() { Print("i32.F"); }
+}
+
+fn Main() -> i32 {
+  var n: i32 = 0;
+  F(n);
+  return 0;
+}