From b4418e29ecf15564bda9d0438fc7d4e3cfa26c29 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 18 Oct 2022 09:07:58 -0700 Subject: [PATCH] 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. --- explorer/interpreter/type_checker.cpp | 29 ++++++---------- .../fail_where_is_non_constraint.carbon | 2 +- explorer/testdata/constraint/where_is.carbon | 33 +++++++++++++++++++ 3 files changed, 44 insertions(+), 20 deletions(-) create mode 100644 explorer/testdata/constraint/where_is.carbon diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index 95eba1afa6de..a9f1ba8d1a8c 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -3132,25 +3132,16 @@ auto TypeChecker::TypeCheckExp(Nonnull e, CARBON_ASSIGN_OR_RETURN( Nonnull constraint, InterpExp(&is_clause.constraint(), arena_, trace_stream_)); - if (auto* interface = dyn_cast(constraint)) { - // `where X is Y` produces an `impl` constraint. - builder.AddImplConstraint({.type = type, .interface = interface}); - } else if (auto* constraint_type = - dyn_cast(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 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; } case WhereClauseKind::EqualsWhereClause: { diff --git a/explorer/testdata/constraint/fail_where_is_non_constraint.carbon b/explorer/testdata/constraint/fail_where_is_non_constraint.carbon index 13b36c32da61..e6953b575480 100644 --- a/explorer/testdata/constraint/fail_where_is_non_constraint.carbon +++ b/explorer/testdata/constraint/fail_where_is_non_constraint.carbon @@ -10,7 +10,7 @@ package ExplorerTest api; 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; fn Main() -> i32 { return 0; } diff --git a/explorer/testdata/constraint/where_is.carbon b/explorer/testdata/constraint/where_is.carbon new file mode 100644 index 000000000000..21a8220113e4 --- /dev/null +++ b/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; +}