Repository: vyperlang/vyper

Issue:
Constant-size array of DynArrays
### Version Information

* vyper Version (output of `vyper --version`): 0.3.3
* OS: osx
* Python Version (output of `python --version`): 3.8.9

### What's your issue about?
Code:
```python3
# @version 0.3.3

a: DynArray[uint256, 2][2]
```
Error:
```shell
CompilerError: vyper returned the following errors:

DynArray must be defined with base type and max length, e.g. DynArray[bool, 5]
  contract "contracts/tmp.vy", line 3:3 
       2
  ---> 3 a: DynArray[uint256, 2][2]
```

### How can it be fixed?

May be `Array[DynArray[uint256, 2], 2]` in future versions will automatically fix this issue.



Discussion:

Resolving patch:
diff --git a/vyper/semantics/types/indexable/sequence.py b/vyper/semantics/types/indexable/sequence.py
--- a/vyper/semantics/types/indexable/sequence.py
+++ b/vyper/semantics/types/indexable/sequence.py
@@ -218,6 +218,7 @@ class DynamicArrayPrimitive(BasePrimitive):
     _id = "DynArray"
     _type = DynamicArrayDefinition
     _valid_literal = (vy_ast.List,)
+    _as_array = True
 
     @classmethod
     def from_annotation(
diff --git a/vyper/semantics/types/utils.py b/vyper/semantics/types/utils.py
--- a/vyper/semantics/types/utils.py
+++ b/vyper/semantics/types/utils.py
@@ -183,7 +183,11 @@ def get_type_from_annotation(
             f"No builtin or user-defined type named '{type_name}'. {suggestions_str}", node
         ) from None
 
-    if getattr(type_obj, "_as_array", False) and isinstance(node, vy_ast.Subscript):
+    if (
+        getattr(type_obj, "_as_array", False)
+        and isinstance(node, vy_ast.Subscript)
+        and node.value.get("id") != "DynArray"
+    ):
         # TODO: handle `is_immutable` for arrays
         # if type can be an array and node is a subscript, create an `ArrayDefinition`
         length = get_index_value(node.slice)


Test patch:
diff --git a/tests/functional/context/types/test_type_from_annotation.py b/tests/functional/context/types/test_type_from_annotation.py
--- a/tests/functional/context/types/test_type_from_annotation.py
+++ b/tests/functional/context/types/test_type_from_annotation.py
@@ -74,7 +74,9 @@ def test_base_types_as_multidimensional_arrays(build_node, namespace, type_str):
 @pytest.mark.parametrize("idx", ["0", "-1", "0x00", "'1'", "foo", "[1]", "(1,)"])
 def test_invalid_index(build_node, idx, type_str):
     node = build_node(f"{type_str}[{idx}]")
-    with pytest.raises((ArrayIndexException, InvalidType, UndeclaredDefinition)):
+    with pytest.raises(
+        (ArrayIndexException, InvalidType, StructureException, UndeclaredDefinition)
+    ):
         get_type_from_annotation(node, DataLocation.STORAGE)
 
 
diff --git a/tests/grammar/vyper.lark b/tests/grammar/vyper.lark
--- a/tests/grammar/vyper.lark
+++ b/tests/grammar/vyper.lark
@@ -80,7 +80,7 @@ enum_body: _NEWLINE _INDENT (enum_member _NEWLINE)+ _DEDENT
 enum_def: _ENUM_DECL NAME ":" enum_body
 
 // Types
-array_def: (NAME | array_def) "[" (DEC_NUMBER | NAME) "]"
+array_def: (NAME | array_def | dyn_array_def) "[" (DEC_NUMBER | NAME) "]"
 dyn_array_def: "DynArray" "[" (NAME | array_def | dyn_array_def) "," (DEC_NUMBER | NAME) "]"
 tuple_def: "(" ( NAME | array_def | dyn_array_def | tuple_def ) ( "," ( NAME | array_def | dyn_array_def | tuple_def ) )* [","] ")"
 // NOTE: Map takes a basic type and maps to another type (can be non-basic, including maps)
diff --git a/tests/parser/types/test_lists.py b/tests/parser/types/test_lists.py
--- a/tests/parser/types/test_lists.py
+++ b/tests/parser/types/test_lists.py
@@ -514,6 +514,46 @@ def foo() -> (uint256, uint256[3], uint256[2]):
     assert c.foo() == [666, [1, 2, 3], [88, 12]]
 
 
+def test_list_of_dynarray(get_contract):
+    code = """
+@external
+def bar(x: int128) -> DynArray[int128, 2][2]:
+    a: DynArray[int128, 2][2] = [[x, x * 2], [x * 3, x * 4]]
+    return a
+
+@external
+def foo(x: int128) -> int128:
+    a: DynArray[int128, 2][2] = [[x, x * 2], [x * 3, x * 4]]
+    return a[0][0] * a[1][1]
+    """
+    c = get_contract(code)
+    assert c.bar(7) == [[7, 14], [21, 28]]
+    assert c.foo(7) == 196
+
+
+def test_list_of_nested_dynarray(get_contract):
+    code = """
+@external
+def bar(x: int128) -> DynArray[int128, 2][2][2]:
+    a: DynArray[int128, 2][2][2] = [
+        [[x, x * 2], [x * 3, x * 4]],
+        [[x * 5, x * 6], [x * 7, x * 8]],
+    ]
+    return a
+
+@external
+def foo(x: int128) -> int128:
+    a: DynArray[int128, 2][2][2] = [
+        [[x, x * 2], [x * 3, x * 4]],
+        [[x * 5, x * 6], [x * 7, x * 8]],
+    ]
+    return a[0][0][0] * a[1][1][1]
+    """
+    c = get_contract(code)
+    assert c.bar(7) == [[[7, 14], [21, 28]], [[35, 42], [49, 56]]]
+    assert c.foo(7) == 392
+
+
 def test_list_of_structs_arg(get_contract):
     code = """
 struct Foo: