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

Add nested array size deduction from tuple (#2909)

As a follow up to #2825 this pr implements size deduction for nested
arrays.
E.g. `var x: [[i32;];] = ((1,2), (3,4));`.

Also updated the pattern matching logic for arrays, now it also checks
element types of the tuple size being deduced from. As a result code
like `var x: [i32;] = ("foo", "bar");`(note, that it tries to init an
array of i32 with a tuple of strings) fails to compile with a pattern
match error instead of an implicit cast failed error.

Moved some common type-related logic used in type_checker.cpp and
interpreter.cpp to the separate file.
kshokhin 2 лет назад
Родитель
Сommit
b6d660b4d9

+ 1 - 0
explorer/interpreter/BUILD

@@ -306,6 +306,7 @@ cc_library(
     ],
     deps = [
         ":action",
+        ":type_utils",
         "//explorer/ast",
         "//explorer/base:arena",
         "//explorer/base:nonnull",

+ 0 - 1
explorer/interpreter/interpreter.h

@@ -26,7 +26,6 @@ auto InterpExp(Nonnull<const Expression*> e, Nonnull<Arena*> arena,
                Nonnull<TraceStream*> trace_stream,
                Nonnull<llvm::raw_ostream*> print_stream)
     -> ErrorOr<Nonnull<const Value*>>;
-
 }  // namespace Carbon
 
 #endif  // CARBON_EXPLORER_INTERPRETER_INTERPRETER_H_

+ 32 - 2
explorer/interpreter/pattern_match.cpp

@@ -4,10 +4,13 @@
 
 #include "explorer/interpreter/pattern_match.h"
 
+#include <algorithm>
+
 #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<const Value*> 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<StaticArrayType>(*p);
       switch (v.value()->kind()) {
         case Value::Kind::TupleType:
         case Value::Kind::TupleValue: {
-          return true;
+          const auto& v_tup = cast<TupleValueBase>(*v.value());
+          if (v_tup.elements().empty()) {
+            return !TypeIsDeduceable(&p_arr.element_type());
+          }
+
+          std::vector<Nonnull<const Value*>> 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<StaticArrayType>(*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;

+ 5 - 11
explorer/interpreter/type_checker.cpp

@@ -4114,8 +4114,8 @@ auto TypeChecker::TypeCheckExpImpl(Nonnull<Expression*> e,
       auto& array_literal = cast<ArrayTypeLiteral>(*e);
       CARBON_ASSIGN_OR_RETURN(
           Nonnull<const Value*> element_type,
-          TypeCheckTypeExp(&array_literal.element_type_expression(),
-                           impl_scope));
+          TypeCheckTypeExp(&array_literal.element_type_expression(), impl_scope,
+                           false));
       std::optional<size_t> 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<StaticArrayType>(*type);
-            CARBON_CHECK(!arr.has_size());
-            const size_t size = GetSize(*expected);
-            type = arena_->New<StaticArrayType>(&arr.element_type(), size);
-          } else {
-            type = *expected;
-          }
+          type = DeducePatternType(type, *expected, arena_);
         }
+
       } else {
         CARBON_RETURN_IF_ERROR(ExpectResolvedBindingType(binding, type));
       }

+ 25 - 0
explorer/interpreter/type_utils.cpp

@@ -222,4 +222,29 @@ auto IsTypeOfType(Nonnull<const Value*> value) -> bool {
       return true;
   }
 }
+
+auto DeducePatternType(Nonnull<const Value*> type,
+                       Nonnull<const Value*> expected, Nonnull<Arena*> arena)
+    -> Nonnull<const Value*> {
+  if (type->kind() == Value::Kind::StaticArrayType) {
+    const auto& arr = cast<StaticArrayType>(*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<const Value*> expected_elem_type =
+          expected->kind() == Value::Kind::StaticArrayType
+              ? &cast<StaticArrayType>(expected)->element_type()
+              : cast<TupleType>(expected)->elements()[0];
+      return arena->New<StaticArrayType>(
+          DeducePatternType(&arr.element_type(), expected_elem_type, arena),
+          size);
+    } else {
+      return arena->New<StaticArrayType>(&arr.element_type(), size);
+    }
+  }
+
+  return expected;
+}
 }  // namespace Carbon

+ 6 - 0
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<const Value*> from) -> size_t;
 // Returns whether the value is a type whose values are themselves known to be
 // types.
 auto IsTypeOfType(Nonnull<const Value*> value) -> bool;
+
+// Deduces concrete type for 'type' based on 'expected'
+auto DeducePatternType(Nonnull<const Value*> type,
+                       Nonnull<const Value*> expected, Nonnull<Arena*> arena)
+    -> Nonnull<const Value*>;
 }  // namespace Carbon
 
 #endif  // CARBON_EXPLORER_INTERPRETER_TYPE_UTILS_H_

+ 14 - 0
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];
+}

+ 13 - 0
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];
+}

+ 13 - 0
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];
+}

+ 15 - 0
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];
+}

+ 13 - 0
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];
+}

+ 16 - 0
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

+ 18 - 0
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

+ 14 - 0
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

+ 16 - 0
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

+ 17 - 0
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

+ 16 - 0
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