Przeglądaj źródła

Add array size deduction from tuple(fix#1590) (#2825)

Allow array size deduction from tuples and arrays on array declaration

Closes #1590
kshokhin 2 lat temu
rodzic
commit
40b3518d37

+ 5 - 2
common/fuzzing/proto_to_carbon.cpp

@@ -383,8 +383,11 @@ static auto ExpressionToCarbon(const Fuzzing::Expression& expression,
           expression.array_type_literal();
       out << "[";
       ExpressionToCarbon(array_literal.element_type(), out);
-      out << "; ";
-      ExpressionToCarbon(array_literal.size(), out);
+      out << ";";
+      if (array_literal.has_size()) {
+        out << " ";
+        ExpressionToCarbon(array_literal.size(), out);
+      }
       out << "]";
       break;
     }

+ 5 - 2
explorer/ast/expression.cpp

@@ -287,8 +287,11 @@ void Expression::Print(llvm::raw_ostream& out) const {
     }
     case ExpressionKind::ArrayTypeLiteral: {
       const auto& array_literal = cast<ArrayTypeLiteral>(*this);
-      out << "[" << array_literal.element_type_expression() << "; "
-          << array_literal.size_expression() << "]";
+      out << "[" << array_literal.element_type_expression() << ";";
+      if (array_literal.has_size_expression()) {
+        out << " " << array_literal.size_expression();
+      }
+      out << "]";
       break;
     }
     case ExpressionKind::IdentifierExpression:

+ 14 - 6
explorer/ast/expression.h

@@ -1217,9 +1217,9 @@ class ArrayTypeLiteral : public ConstantValueLiteral {
  public:
   // Constructs an array type literal which uses the given expressions to
   // represent the element type and size.
-  explicit ArrayTypeLiteral(SourceLocation source_loc,
-                            Nonnull<Expression*> element_type_expression,
-                            Nonnull<Expression*> size_expression)
+  explicit ArrayTypeLiteral(
+      SourceLocation source_loc, Nonnull<Expression*> element_type_expression,
+      std::optional<Nonnull<Expression*>> size_expression = std::nullopt)
       : ConstantValueLiteral(AstNodeKind::ArrayTypeLiteral, source_loc),
         element_type_expression_(element_type_expression),
         size_expression_(size_expression) {}
@@ -1241,14 +1241,22 @@ class ArrayTypeLiteral : public ConstantValueLiteral {
     return *element_type_expression_;
   }
 
+  auto has_size_expression() const -> bool {
+    return size_expression_.has_value();
+  }
+
   auto size_expression() const -> const Expression& {
-    return *size_expression_;
+    CARBON_CHECK(size_expression_.has_value());
+    return **size_expression_;
+  }
+  auto size_expression() -> Expression& {
+    CARBON_CHECK(size_expression_.has_value());
+    return **size_expression_;
   }
-  auto size_expression() -> Expression& { return *size_expression_; }
 
  private:
   Nonnull<Expression*> element_type_expression_;
-  Nonnull<Expression*> size_expression_;
+  std::optional<Nonnull<Expression*>> size_expression_;
 };
 
 // Converts paren_contents to an Expression, interpreting the parentheses as

+ 5 - 2
explorer/ast/value.cpp

@@ -703,8 +703,11 @@ void Value::Print(llvm::raw_ostream& out) const {
     }
     case Value::Kind::StaticArrayType: {
       const auto& array_type = cast<StaticArrayType>(*this);
-      out << "[" << array_type.element_type() << "; " << array_type.size()
-          << "]";
+      out << "[" << array_type.element_type() << ";";
+      if (array_type.has_size()) {
+        out << " " << array_type.size();
+      }
+      out << "]";
       break;
     }
   }

+ 8 - 3
explorer/ast/value.h

@@ -1578,7 +1578,8 @@ class StaticArrayType : public Value {
  public:
   // Constructs a statically-sized array type with the given element type and
   // size.
-  StaticArrayType(Nonnull<const Value*> element_type, size_t size)
+  StaticArrayType(Nonnull<const Value*> element_type,
+                  std::optional<size_t> size)
       : Value(Kind::StaticArrayType),
         element_type_(element_type),
         size_(size) {}
@@ -1593,11 +1594,15 @@ class StaticArrayType : public Value {
   }
 
   auto element_type() const -> const Value& { return *element_type_; }
-  auto size() const -> size_t { return size_; }
+  auto size() const -> size_t {
+    CARBON_CHECK(has_size());
+    return *size_;
+  }
+  auto has_size() const -> bool { return size_.has_value(); }
 
  private:
   Nonnull<const Value*> element_type_;
-  size_t size_;
+  std::optional<size_t> size_;
 };
 
 template <typename R, typename F>

+ 4 - 2
explorer/fuzzing/ast_to_proto.cpp

@@ -354,8 +354,10 @@ static auto ExpressionToProto(const Expression& expression)
           expression_proto.mutable_array_type_literal();
       *array_literal_proto->mutable_element_type() =
           ExpressionToProto(array_literal.element_type_expression());
-      *array_literal_proto->mutable_size() =
-          ExpressionToProto(array_literal.size_expression());
+      if (array_literal.has_size_expression()) {
+        *array_literal_proto->mutable_size() =
+            ExpressionToProto(array_literal.size_expression());
+      }
       break;
     }
   }

+ 17 - 0
explorer/interpreter/interpreter.cpp

@@ -412,6 +412,20 @@ auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
       // `auto` matches any type, without binding any new names. We rely
       // on the typechecker to ensure that `v` is a type.
       return true;
+    case Value::Kind::StaticArrayType: {
+      switch (v->kind()) {
+        case Value::Kind::TupleType:
+        case Value::Kind::TupleValue: {
+          return true;
+        }
+        case Value::Kind::StaticArrayType: {
+          const auto& v_arr = cast<StaticArrayType>(*v);
+          return v_arr.has_size();
+        }
+        default:
+          return false;
+      }
+    }
     default:
       return ValueEqual(p, v, std::nullopt);
   }
@@ -838,6 +852,7 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
           break;
         case Value::Kind::StaticArrayType: {
           const auto& array_type = cast<StaticArrayType>(*destination_type);
+          CARBON_CHECK(array_type.has_size());
           destination_element_types.resize(array_type.size(),
                                            &array_type.element_type());
           break;
@@ -2121,6 +2136,7 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
               v, Convert(act.results()[0], dest_type, stmt.source_loc()));
         } else if (dest_type->kind() == Value::Kind::StaticArrayType) {
           const auto& array = cast<StaticArrayType>(dest_type);
+          CARBON_CHECK(array->has_size());
           const auto& element_type = array->element_type();
           const auto size = array->size();
 
@@ -2289,6 +2305,7 @@ auto Interpreter::StepDeclaration() -> ErrorOr<Success> {
         }
       } else if (var_type->kind() == Value::Kind::StaticArrayType) {
         const auto& array = cast<StaticArrayType>(var_type);
+        CARBON_CHECK(array->has_size());
         const auto& element_type = array->element_type();
         const auto size = array->size();
 

+ 4 - 2
explorer/interpreter/resolve_names.cpp

@@ -412,8 +412,10 @@ auto NameResolver::ResolveNames(Expression& expression,
       auto& array_literal = cast<ArrayTypeLiteral>(expression);
       CARBON_RETURN_IF_ERROR(ResolveNames(
           array_literal.element_type_expression(), enclosing_scope));
-      CARBON_RETURN_IF_ERROR(
-          ResolveNames(array_literal.size_expression(), enclosing_scope));
+      if (array_literal.has_size_expression()) {
+        CARBON_RETURN_IF_ERROR(
+            ResolveNames(array_literal.size_expression(), enclosing_scope));
+      }
       break;
     }
     case ExpressionKind::BoolTypeLiteral:

+ 119 - 34
explorer/interpreter/type_checker.cpp

@@ -320,7 +320,7 @@ static auto ExpectConcreteType(SourceLocation source_loc,
 
 // Returns whether *value represents the type of a Carbon value, as
 // opposed to a type pattern or a non-type value.
-static auto TypeContainsAuto(Nonnull<const Value*> type) -> bool {
+static auto TypeIsDeduceable(Nonnull<const Value*> type) -> bool {
   CARBON_CHECK(IsType(type)) << "expected a type, but found " << *type;
 
   switch (type->kind()) {
@@ -375,13 +375,15 @@ static auto TypeContainsAuto(Nonnull<const Value*> type) -> bool {
       return llvm::any_of(
           llvm::map_range(cast<StructType>(type)->fields(),
                           [](const NamedValue& v) { return v.value; }),
-          TypeContainsAuto);
+          TypeIsDeduceable);
     case Value::Kind::TupleType:
-      return llvm::any_of(cast<TupleType>(type)->elements(), TypeContainsAuto);
+      return llvm::any_of(cast<TupleType>(type)->elements(), TypeIsDeduceable);
     case Value::Kind::PointerType:
-      return TypeContainsAuto(&cast<PointerType>(type)->pointee_type());
+      return TypeIsDeduceable(&cast<PointerType>(type)->pointee_type());
     case Value::Kind::StaticArrayType:
-      return TypeContainsAuto(&cast<StaticArrayType>(type)->element_type());
+      const auto* array_type = cast<StaticArrayType>(type);
+      return !array_type->has_size() ||
+             TypeIsDeduceable(&array_type->element_type());
   }
 }
 
@@ -395,8 +397,60 @@ static auto IsPlaceholderType(Nonnull<const Value*> type) -> bool {
 
 // 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`.
-static auto IsConcreteType(Nonnull<const Value*> value) -> bool {
-  return IsType(value) && !TypeContainsAuto(value);
+static auto IsNonDeduceableType(Nonnull<const Value*> value) -> bool {
+  return IsType(value) && !TypeIsDeduceable(value);
+}
+
+static auto ExpectResolvedBindingType(const BindingPattern& binding,
+                                      Nonnull<const Value*> type)
+    -> ErrorOr<Success> {
+  switch (type->kind()) {
+    case Value::Kind::AutoType: {
+      auto error = ProgramError(binding.source_loc());
+      error << "cannot deduce `auto` type for ";
+      if (type != &binding.type().value()) {
+        error << *type << " in ";
+      }
+      return error << binding;
+    }
+    case Value::Kind::StructType: {
+      const auto fields = cast<StructType>(type)->fields();
+      for (const auto& field : fields) {
+        if (auto result = ExpectResolvedBindingType(binding, field.value);
+            !result.ok()) {
+          return result;
+        }
+      }
+      return Success();
+    }
+    case Value::Kind::TupleType: {
+      const auto elems = cast<TupleType>(type)->elements();
+      for (const auto* elem : elems) {
+        if (auto result = ExpectResolvedBindingType(binding, elem);
+            !result.ok()) {
+          return result;
+        }
+      }
+      return Success();
+    }
+    case Value::Kind::PointerType:
+      return ExpectResolvedBindingType(
+          binding, &cast<PointerType>(type)->pointee_type());
+    case Value::Kind::StaticArrayType: {
+      const auto* array_type = cast<StaticArrayType>(type);
+      if (!array_type->has_size()) {
+        auto error = ProgramError(binding.source_loc());
+        error << "cannot deduce size for ";
+        if (type != &binding.type().value()) {
+          error << *array_type << " in ";
+        }
+        return error << binding;
+      }
+      return ExpectResolvedBindingType(binding, &array_type->element_type());
+    }
+    default:
+      return Success();
+  }
 }
 
 // Returns whether the given value is template-dependent, that is, if it
@@ -535,8 +589,8 @@ auto TypeChecker::IsImplicitlyConvertible(
   // Check for an exact match or for an implicit conversion.
   // TODO: `impl` definitions of `ImplicitAs` should be provided to cover these
   // conversions.
-  CARBON_CHECK(IsConcreteType(source));
-  CARBON_CHECK(IsConcreteType(destination));
+  CARBON_CHECK(IsNonDeduceableType(source));
+  CARBON_CHECK(IsNonDeduceableType(destination));
   if (IsSameType(source, destination, impl_scope)) {
     return true;
   }
@@ -3983,22 +4037,26 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
           Nonnull<const Value*> element_type,
           TypeCheckTypeExp(&array_literal.element_type_expression(),
                            impl_scope));
-      CARBON_RETURN_IF_ERROR(
-          TypeCheckExp(&array_literal.size_expression(), impl_scope));
-      CARBON_RETURN_IF_ERROR(ExpectExactType(
-          array_literal.size_expression().source_loc(), "array size",
-          arena_->New<IntType>(),
-          &array_literal.size_expression().static_type(), impl_scope));
-      CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> size_value,
-                              InterpExp(&array_literal.size_expression()));
-      if (cast<IntValue>(size_value)->value() < 0) {
-        return ProgramError(array_literal.size_expression().source_loc())
-               << "Array size cannot be negative";
+      std::optional<size_t> array_size;
+      if (array_literal.has_size_expression()) {
+        CARBON_RETURN_IF_ERROR(
+            TypeCheckExp(&array_literal.size_expression(), impl_scope));
+        CARBON_RETURN_IF_ERROR(ExpectExactType(
+            array_literal.size_expression().source_loc(), "array size",
+            arena_->New<IntType>(),
+            &array_literal.size_expression().static_type(), impl_scope));
+        CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> size_value,
+                                InterpExp(&array_literal.size_expression()));
+        if (cast<IntValue>(size_value)->value() < 0) {
+          return ProgramError(array_literal.size_expression().source_loc())
+                 << "Array size cannot be negative";
+        }
+        array_size = cast<IntValue>(size_value)->value();
       }
       array_literal.set_static_type(arena_->New<TypeType>());
       array_literal.set_expression_category(ExpressionCategory::Value);
-      array_literal.set_constant_value(arena_->New<StaticArrayType>(
-          element_type, cast<IntValue>(size_value)->value()));
+      array_literal.set_constant_value(
+          arena_->New<StaticArrayType>(element_type, array_size));
       return Success();
     }
   }
@@ -4066,11 +4124,11 @@ auto TypeChecker::TypeCheckTypeExp(Nonnull<Expression*> type_expression,
   CARBON_CHECK(IsType(type))
       << "type expression did not produce a type, got " << *type;
   if (concrete) {
-    if (TypeContainsAuto(type)) {
+    if (TypeIsDeduceable(type)) {
       return ProgramError(type_expression->source_loc())
              << "`auto` is not permitted in this context";
     }
-    CARBON_CHECK(IsConcreteType(type))
+    CARBON_CHECK(IsNonDeduceableType(type))
         << "unknown kind of non-concrete type " << *type;
   }
   CARBON_CHECK(!IsPlaceholderType(type))
@@ -4130,6 +4188,24 @@ auto TypeChecker::TypeCheckWhereClause(Nonnull<WhereClause*> clause,
   }
 }
 
+// Returns the list size for type deduction.
+static auto GetSize(Nonnull<const Value*> from) -> size_t {
+  switch (from->kind()) {
+    case Value::Kind::TupleType:
+    case Value::Kind::TupleValue: {
+      const auto& from_tup = cast<TupleValueBase>(*from);
+      return from_tup.elements().size();
+    }
+    case Value::Kind::StaticArrayType: {
+      const auto& from_arr = cast<StaticArrayType>(*from);
+      CARBON_CHECK(from_arr.has_size());
+      return from_arr.size();
+    }
+    default:
+      return 0;
+  }
+}
+
 auto TypeChecker::TypeCheckPattern(
     Nonnull<Pattern*> p, PatternRequirements requirements,
     std::optional<Nonnull<const Value*>> expected, ImplScope& impl_scope,
@@ -4192,7 +4268,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 (!IsConcreteType(type)) {
+        if (!IsNonDeduceableType(type)) {
           BindingMap generic_args;
           if (!PatternMatch(type, *expected, binding.type().source_loc(),
                             std::nullopt, generic_args, trace_stream_,
@@ -4201,14 +4277,23 @@ auto TypeChecker::TypeCheckPattern(
                    << "type pattern '" << *type
                    << "' does not match actual type '" << **expected << "'";
           }
-          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;
+          }
         }
-      } else if (TypeContainsAuto(type)) {
-        return ProgramError(binding.source_loc())
-               << "cannot deduce `auto` type for " << binding;
+      } else {
+        CARBON_RETURN_IF_ERROR(ExpectResolvedBindingType(binding, type));
       }
-      CARBON_CHECK(IsConcreteType(type)) << "did not resolve " << binding
-                                         << " to concrete type, got " << *type;
+
+      CARBON_CHECK(IsNonDeduceableType(type))
+          << "did not resolve " << binding << " to concrete type, got "
+          << *type;
       CARBON_CHECK(!IsPlaceholderType(type))
           << "should be no way to write a placeholder type";
       binding.set_static_type(type);
@@ -4638,8 +4723,8 @@ auto TypeChecker::TypeCheckStmt(Nonnull<Statement*> s,
         return_term.set_static_type(&ret.value_node().static_type());
       } else {
         // TODO: Consider using `ExpectExactType` here.
-        CARBON_CHECK(IsConcreteType(&return_term.static_type()));
-        CARBON_CHECK(IsConcreteType(&ret.value_node().static_type()));
+        CARBON_CHECK(IsNonDeduceableType(&return_term.static_type()));
+        CARBON_CHECK(IsNonDeduceableType(&ret.value_node().static_type()));
         if (!IsSameType(&return_term.static_type(),
                         &ret.value_node().static_type(), impl_scope)) {
           return ProgramError(ret.value_node().base().source_loc())
@@ -4825,7 +4910,7 @@ auto TypeChecker::DeclareCallableDeclaration(Nonnull<CallableDeclaration*> f,
           ExpectReturnOnAllPaths(f->body(), f->source_loc()));
     }
   }
-  CARBON_CHECK(IsConcreteType(&f->return_term().static_type()));
+  CARBON_CHECK(IsNonDeduceableType(&f->return_term().static_type()));
 
   f->set_static_type(arena_->New<FunctionType>(
       &f->param_pattern().static_type(), std::move(generic_parameters),

+ 5 - 0
explorer/syntax/parser.ypp

@@ -421,6 +421,11 @@ primary_expression:
       $$ = arena->New<ArrayTypeLiteral>(context.source_loc(),
                                         $[type_expression], $[size_expression]);
     }
+| LEFT_SQUARE_BRACKET expression[type_expression] SEMICOLON RIGHT_SQUARE_BRACKET
+    {
+      $$ = arena->New<ArrayTypeLiteral>(context.source_loc(),
+                                        $[type_expression]);
+    }
 ;
 postfix_expression:
   primary_expression

+ 16 - 0
explorer/testdata/array/deduce_size_from_array.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
+// CHECK:STDOUT: result: 0
+
+package ExplorerTest api;
+
+fn Main() -> i32 {
+  var arr: [i32; 2] = (0, 1);
+  var x: [i32;] = arr;
+  var index: i32 = 1;
+  x[index] = 0;
+  return x[0] + x[1];
+}

+ 16 - 0
explorer/testdata/array/deduce_size_from_array_destructured.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
+// CHECK:STDOUT: result: 0
+
+package ExplorerTest api;
+
+fn Main() -> i32 {
+  var arr: [i32; 2] = (0, 1);
+  var (x: auto, y: [i32;]) = (1, arr);
+  var index: i32 = 1;
+  y[index] = 0;
+  return y[0] + y[1];
+}

+ 15 - 0
explorer/testdata/array/deduce_size_from_tuple.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
+// CHECK:STDOUT: result: 0
+
+package ExplorerTest api;
+
+fn Main() -> i32 {
+  var x: [i32;] = (0, 1);
+  var index: i32 = 1;
+  x[index] = 0;
+  return x[0] + x[1];
+}

+ 15 - 0
explorer/testdata/array/deduce_size_from_tuple_destructured.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
+// CHECK:STDOUT: result: 0
+
+package ExplorerTest api;
+
+fn Main() -> i32 {
+  var (x: auto, y: [i32;]) = (42, (0, 1));
+  var index: i32 = 1;
+  y[index] = 0;
+  return y[0] + y[1];
+}

+ 14 - 0
explorer/testdata/array/deduce_zero_size_from_array.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
+// CHECK:STDOUT: result: 0
+
+package ExplorerTest api;
+
+fn Main() -> i32 {
+  var a: [i32;0] = ();
+  var b: [i32;] = a;
+  return 0;
+}

+ 13 - 0
explorer/testdata/array/deduce_zero_size_from_empty_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
+// CHECK:STDOUT: result: 0
+
+package ExplorerTest api;
+
+fn Main() -> i32 {
+  var a: [i32;] = ();
+  return 0;
+}

+ 14 - 0
explorer/testdata/array/fail_index_deduced_size_from_array.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 arr: [i32; 2] = (0, 1);
+  var x: [i32;] = arr;
+  // CHECK:STDERR: RUNTIME ERROR: fail_index_deduced_size_from_array.carbon:[[@LINE+1]]: index 2 out of range in (0, 1)
+  return x[2];
+}

+ 13 - 0
explorer/testdata/array/fail_index_deduced_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;] = (0, 1);
+  // CHECK:STDERR: RUNTIME ERROR: fail_index_deduced_size_from_tuple.carbon:[[@LINE+1]]: index 2 out of range in (0, 1)
+  return x[2];
+}

+ 13 - 0
explorer/testdata/array/fail_to_deduce_nested_array_size_in_tuple_no_expression_to_deduce_from.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_in_tuple_no_expression_to_deduce_from.carbon:[[@LINE+1]]: cannot deduce size for [i32;] in x: (i32, [i32;])
+  var x: (i32, [i32;]);
+  return x[0];
+}

+ 13 - 0
explorer/testdata/array/fail_to_deduce_size_from_int.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_int.carbon:[[@LINE+1]]: type pattern '[i32;]' does not match actual type 'i32'
+  var x: [i32;] = 42;
+  return x[0];
+}

+ 13 - 0
explorer/testdata/array/fail_to_deduce_size_no_expression_to_deduce_from.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_no_expression_to_deduce_from.carbon:[[@LINE+1]]: cannot deduce size for x: [i32;]
+  var x: [i32;];
+  return x[0];
+}