diff --git a/explorer/interpreter/BUILD b/explorer/interpreter/BUILD index 1bc1be134f7a..86dee96224d9 100644 --- a/explorer/interpreter/BUILD +++ b/explorer/interpreter/BUILD @@ -306,6 +306,7 @@ cc_library( ], deps = [ ":action", + ":type_utils", "//explorer/ast", "//explorer/base:arena", "//explorer/base:nonnull", diff --git a/explorer/interpreter/interpreter.h b/explorer/interpreter/interpreter.h index 087a1f51aed0..65fc05c63ff5 100644 --- a/explorer/interpreter/interpreter.h +++ b/explorer/interpreter/interpreter.h @@ -26,7 +26,6 @@ auto InterpExp(Nonnull e, Nonnull arena, Nonnull trace_stream, Nonnull print_stream) -> ErrorOr>; - } // namespace Carbon #endif // CARBON_EXPLORER_INTERPRETER_INTERPRETER_H_ diff --git a/explorer/interpreter/pattern_match.cpp b/explorer/interpreter/pattern_match.cpp index 96c32c8e0933..7fb6979034b2 100644 --- a/explorer/interpreter/pattern_match.cpp +++ b/explorer/interpreter/pattern_match.cpp @@ -4,10 +4,13 @@ #include "explorer/interpreter/pattern_match.h" +#include + #include "explorer/ast/value.h" #include "explorer/base/arena.h" #include "explorer/base/trace_stream.h" #include "explorer/interpreter/action.h" +#include "explorer/interpreter/type_utils.h" #include "llvm/Support/Casting.h" using llvm::cast; @@ -202,14 +205,41 @@ auto PatternMatch(Nonnull p, ExpressionResult v, // on the typechecker to ensure that `v.value()` is a type. return true; case Value::Kind::StaticArrayType: { + const auto& p_arr = cast(*p); switch (v.value()->kind()) { case Value::Kind::TupleType: case Value::Kind::TupleValue: { - return true; + const auto& v_tup = cast(*v.value()); + if (v_tup.elements().empty()) { + return !TypeIsDeduceable(&p_arr.element_type()); + } + + std::vector> deduced_types; + deduced_types.reserve(v_tup.elements().size()); + for (const auto& tup_elem : v_tup.elements()) { + if (!PatternMatch(&p_arr.element_type(), make_expr_result(tup_elem), + source_loc, bindings, generic_args, trace_stream, + arena)) { + return false; + } + + deduced_types.emplace_back( + DeducePatternType(&p_arr.element_type(), tup_elem, arena)); + } // for + + return std::adjacent_find(deduced_types.begin(), deduced_types.end(), + [](const auto& lhs, const auto& rhs) { + return !TypeEqual(lhs, rhs, std::nullopt); + }) == deduced_types.end(); } case Value::Kind::StaticArrayType: { const auto& v_arr = cast(*v.value()); - return v_arr.has_size(); + if (!v_arr.has_size()) { + return false; + } + return PatternMatch( + &p_arr.element_type(), make_expr_result(&v_arr.element_type()), + source_loc, bindings, generic_args, trace_stream, arena); } default: return false; diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index 4a04829e1ee5..ef057e9b0898 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -4114,8 +4114,8 @@ auto TypeChecker::TypeCheckExpImpl(Nonnull e, auto& array_literal = cast(*e); CARBON_ASSIGN_OR_RETURN( Nonnull element_type, - TypeCheckTypeExp(&array_literal.element_type_expression(), - impl_scope)); + TypeCheckTypeExp(&array_literal.element_type_expression(), impl_scope, + false)); std::optional array_size; if (array_literal.has_size_expression()) { CARBON_RETURN_IF_ERROR( @@ -4331,7 +4331,7 @@ auto TypeChecker::TypeCheckPattern( if (expected) { // TODO: Per proposal #2188, we should be performing conversions at // this level rather than on the overall initializer. - if (!IsNonDeduceableType(type)) { + if (TypeIsDeduceable(type)) { BindingMap generic_args; if (!PatternMatch(type, ExpressionResult::Value(*expected), binding.type().source_loc(), std::nullopt, @@ -4341,15 +4341,9 @@ auto TypeChecker::TypeCheckPattern( << "' does not match actual type '" << **expected << "'"; } - if (type->kind() == Value::Kind::StaticArrayType) { - const auto& arr = cast(*type); - CARBON_CHECK(!arr.has_size()); - const size_t size = GetSize(*expected); - type = arena_->New(&arr.element_type(), size); - } else { - type = *expected; - } + type = DeducePatternType(type, *expected, arena_); } + } else { CARBON_RETURN_IF_ERROR(ExpectResolvedBindingType(binding, type)); } diff --git a/explorer/interpreter/type_utils.cpp b/explorer/interpreter/type_utils.cpp index 8b6fb981a5e3..2b7d731adc5c 100644 --- a/explorer/interpreter/type_utils.cpp +++ b/explorer/interpreter/type_utils.cpp @@ -222,4 +222,29 @@ auto IsTypeOfType(Nonnull value) -> bool { return true; } } + +auto DeducePatternType(Nonnull type, + Nonnull expected, Nonnull arena) + -> Nonnull { + if (type->kind() == Value::Kind::StaticArrayType) { + const auto& arr = cast(*type); + const size_t size = arr.has_size() ? arr.size() : GetSize(expected); + if (!IsNonDeduceableType(&arr.element_type())) { + CARBON_CHECK(expected->kind() == Value::Kind::StaticArrayType || + expected->kind() == Value::Kind::TupleType); + + Nonnull expected_elem_type = + expected->kind() == Value::Kind::StaticArrayType + ? &cast(expected)->element_type() + : cast(expected)->elements()[0]; + return arena->New( + DeducePatternType(&arr.element_type(), expected_elem_type, arena), + size); + } else { + return arena->New(&arr.element_type(), size); + } + } + + return expected; +} } // namespace Carbon diff --git a/explorer/interpreter/type_utils.h b/explorer/interpreter/type_utils.h index 37367d42a943..ca2bcd0129c6 100644 --- a/explorer/interpreter/type_utils.h +++ b/explorer/interpreter/type_utils.h @@ -10,6 +10,7 @@ namespace Carbon { class Value; +class Arena; // Returns whether `value` is a concrete type, which would be valid as the // static type of an expression. This is currently any type other than `auto`. @@ -30,6 +31,11 @@ auto GetSize(Nonnull from) -> size_t; // Returns whether the value is a type whose values are themselves known to be // types. auto IsTypeOfType(Nonnull value) -> bool; + +// Deduces concrete type for 'type' based on 'expected' +auto DeducePatternType(Nonnull type, + Nonnull expected, Nonnull arena) + -> Nonnull; } // namespace Carbon #endif // CARBON_EXPLORER_INTERPRETER_TYPE_UTILS_H_ diff --git a/explorer/testdata/array/fail_to_deduce_nested_array_size_from_tuple_different_subtuples_size.carbon b/explorer/testdata/array/fail_to_deduce_nested_array_size_from_tuple_different_subtuples_size.carbon new file mode 100644 index 000000000000..d6dba4311ff4 --- /dev/null +++ b/explorer/testdata/array/fail_to_deduce_nested_array_size_from_tuple_different_subtuples_size.carbon @@ -0,0 +1,14 @@ +// 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 + +package ExplorerTest api; + +fn Main() -> i32 { + // CHECK:STDERR: COMPILATION ERROR: fail_to_deduce_nested_array_size_from_tuple_different_subtuples_size.carbon:[[@LINE+1]]: type pattern '{{\[\[}}i32;];]' does not match actual type '((i32, i32), (i32, i32, i32))' + var x: [[i32;];] = ((1,2), (3,4,5)); + + return x[0][2]; +} diff --git a/explorer/testdata/array/fail_to_deduce_nested_array_size_from_tuple_of_different_types.carbon b/explorer/testdata/array/fail_to_deduce_nested_array_size_from_tuple_of_different_types.carbon new file mode 100644 index 000000000000..0720ebecf9ed --- /dev/null +++ b/explorer/testdata/array/fail_to_deduce_nested_array_size_from_tuple_of_different_types.carbon @@ -0,0 +1,13 @@ +// 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 + +package ExplorerTest api; + +fn Main() -> i32 { + // CHECK:STDERR: COMPILATION ERROR: fail_to_deduce_nested_array_size_from_tuple_of_different_types.carbon:[[@LINE+1]]: type pattern '{{\[\[}}i32;]; 2]' does not match actual type '((i32, i32), (String, String))' + var x: [[i32;];2] = ((1,2), ("foo", "bar")); + return x[0]; +} diff --git a/explorer/testdata/array/fail_to_deduce_size_from_tuple_of_different_types.carbon b/explorer/testdata/array/fail_to_deduce_size_from_tuple_of_different_types.carbon new file mode 100644 index 000000000000..ba7d1ccd7bb4 --- /dev/null +++ b/explorer/testdata/array/fail_to_deduce_size_from_tuple_of_different_types.carbon @@ -0,0 +1,13 @@ +// 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 + +package ExplorerTest api; + +fn Main() -> i32 { + // CHECK:STDERR: COMPILATION ERROR: fail_to_deduce_size_from_tuple_of_different_types.carbon:[[@LINE+1]]: type pattern '[i32;]' does not match actual type '(i32, String)' + var x: [i32;] = (42, "foo"); + return x[0]; +} diff --git a/explorer/testdata/array/fail_to_index_deduced_nested_size_from_array.carbon b/explorer/testdata/array/fail_to_index_deduced_nested_size_from_array.carbon new file mode 100644 index 000000000000..f7d235f53e43 --- /dev/null +++ b/explorer/testdata/array/fail_to_index_deduced_nested_size_from_array.carbon @@ -0,0 +1,15 @@ +// 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 + +package ExplorerTest api; + +fn Main() -> i32 { + var arr: [[i32;2];2] = ((1,2), (3,4)); + + var x : [[i32;];] = arr; + // CHECK:STDERR: RUNTIME ERROR: fail_to_index_deduced_nested_size_from_array.carbon:[[@LINE+1]]: index 2 out of range in (1, 2) + return x[0][2]; +} diff --git a/explorer/testdata/array/fail_to_index_deduced_nested_size_from_tuple.carbon b/explorer/testdata/array/fail_to_index_deduced_nested_size_from_tuple.carbon new file mode 100644 index 000000000000..18f13829811e --- /dev/null +++ b/explorer/testdata/array/fail_to_index_deduced_nested_size_from_tuple.carbon @@ -0,0 +1,13 @@ +// 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 + +package ExplorerTest api; + +fn Main() -> i32 { + var x: [[i32;];] = ((1,2), (3,4)); + // CHECK:STDERR: RUNTIME ERROR: fail_to_index_deduced_nested_size_from_tuple.carbon:[[@LINE+1]]: index 2 out of range in (1, 2) + return x[0][2]; +} diff --git a/explorer/testdata/array/nested_and_external_array_size_deduction_from_tuple.carbon b/explorer/testdata/array/nested_and_external_array_size_deduction_from_tuple.carbon new file mode 100644 index 000000000000..803473d80f7b --- /dev/null +++ b/explorer/testdata/array/nested_and_external_array_size_deduction_from_tuple.carbon @@ -0,0 +1,16 @@ +// 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 + +package ExplorerTest api; + +fn Main() -> i32 { + var x: [[i32;];] = ((1,2), (3,4)); + x[0][1] = 1; + x[1][0] = 1; + return x[0][0] + x[0][1] + x[1][0] + x[1][1]; +} + +// CHECK:STDOUT: result: 7 diff --git a/explorer/testdata/array/nested_array_size_deduction_from_array.carbon b/explorer/testdata/array/nested_array_size_deduction_from_array.carbon new file mode 100644 index 000000000000..7a55530a8ab7 --- /dev/null +++ b/explorer/testdata/array/nested_array_size_deduction_from_array.carbon @@ -0,0 +1,18 @@ +// 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 + +package ExplorerTest api; + +fn Main() -> i32 { + var arr: [[i32;2];2] = ((1,2), (3,4)); + + var x : [[i32;];] = arr; + x[0][1] = 1; + x[1][0] = 1; + return x[0][0] + x[0][1] + x[1][0] + x[1][1]; +} + +// CHECK:STDOUT: result: 7 diff --git a/explorer/testdata/array/nested_array_size_deduction_from_empty_tuple.carbon b/explorer/testdata/array/nested_array_size_deduction_from_empty_tuple.carbon new file mode 100644 index 000000000000..2f7eb962f87c --- /dev/null +++ b/explorer/testdata/array/nested_array_size_deduction_from_empty_tuple.carbon @@ -0,0 +1,14 @@ +// 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 + +package ExplorerTest api; + +fn Main() -> i32 { + var x: [[i32;];2] = ((), ()); + return 42; +} + +// CHECK:STDOUT: result: 42 diff --git a/explorer/testdata/array/nested_array_size_deduction_from_tuple.carbon b/explorer/testdata/array/nested_array_size_deduction_from_tuple.carbon new file mode 100644 index 000000000000..9ac60a6e4a45 --- /dev/null +++ b/explorer/testdata/array/nested_array_size_deduction_from_tuple.carbon @@ -0,0 +1,16 @@ +// 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 + +package ExplorerTest api; + +fn Main() -> i32 { + var x: [[i32;];2] = ((1,2), (3,4)); + x[0][1] = 1; + x[1][0] = 1; + return x[0][0] + x[0][1] + x[1][0] + x[1][1]; +} + +// CHECK:STDOUT: result: 7 diff --git a/explorer/testdata/array/nested_array_size_deduction_from_tuple_of_tuple_and_array.carbon b/explorer/testdata/array/nested_array_size_deduction_from_tuple_of_tuple_and_array.carbon new file mode 100644 index 000000000000..8cf10b900a1d --- /dev/null +++ b/explorer/testdata/array/nested_array_size_deduction_from_tuple_of_tuple_and_array.carbon @@ -0,0 +1,17 @@ +// 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 + +package ExplorerTest api; + +fn Main() -> i32 { + var x: [i32; 2] = (1, 2); + var y: [[i32;];] = (x, (3,4)); + y[0][1] = 1; + y[1][0] = 1; + return y[0][0] + y[0][1] + y[1][0] + y[1][1]; +} + +// CHECK:STDOUT: result: 7 diff --git a/explorer/testdata/array/three_dim_array_size_deduction_from_tuple.carbon b/explorer/testdata/array/three_dim_array_size_deduction_from_tuple.carbon new file mode 100644 index 000000000000..6d0182d9b2c3 --- /dev/null +++ b/explorer/testdata/array/three_dim_array_size_deduction_from_tuple.carbon @@ -0,0 +1,16 @@ +// 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 + +package ExplorerTest api; + +fn Main() -> i32 { + let v: i32 = 8; + var x: [[[i32;];];] = (((1,2), (3,4)), ((5,6), (7,v))); + x[1][1][1] -= v; + return x[1][1][1]; +} + +// CHECK:STDOUT: result: 0