Repository: vyperlang/vyper

Issue:
can't declare variable `public` but can define custom getter
### Version Information

* vyper Version:  0.3.4+commit.f31f0ec
* OS: osx
* Python Version: 3.8.9

### What's your issue about?

This code fails to compile:

```
slates : public(HashMap[bytes32, DynArray[address, 15]])
```

but I can define my own getter for that type just fine:

```
slates : HashMap[bytes32, DynArray[address, 15]]

@external
def slate(sid :bytes32) -> DynArray[address, 15]:
   return self.slates[sid]
```



Discussion:
I realize now it is ambiguous whether the getter should take 1 or 2 args (return a dynarray or address). Maybe this is intended behavior? 
not intended behavior. there is an open issue about the ambiguity, but this is a typechecker bug
for future reference -- this raises the following
```
vyper.exceptions.InvalidType: DynArray
  contract "repros/3016.vy", function "slates", line 1:33 
  ---> 1 slates : public(HashMap[bytes32, DynArray[address, 15]])
  ----------------------------------------^
       2
```
`git bisect` indicates this was introduced in be2c59a604070c75212dca90beb4c33fed908c8b

Resolving patch:
diff --git a/vyper/ast/expansion.py b/vyper/ast/expansion.py
--- a/vyper/ast/expansion.py
+++ b/vyper/ast/expansion.py
@@ -53,6 +53,9 @@ def generate_public_variable_getters(vyper_module: vy_ast.Module) -> None:
             if annotation.value.get("id") == "HashMap":  # type: ignore
                 # for a HashMap, split the key/value types and use the key type as the next arg
                 arg, annotation = annotation.slice.value.elements  # type: ignore
+            elif annotation.value.get("id") == "DynArray":
+                arg = vy_ast.Name(id=type_._id)
+                annotation = annotation.slice.value.elements[0]  # type: ignore
             else:
                 # for other types, build an input arg node from the expected type
                 # and remove the outer `Subscript` from the annotation
@@ -66,7 +69,7 @@ def generate_public_variable_getters(vyper_module: vy_ast.Module) -> None:
             )
 
         # after iterating the input types, the remaining annotation node is our return type
-        return_node = annotation
+        return_node = copy.copy(annotation)
 
         # join everything together as a new `FunctionDef` node, annotate it
         # with the type, and append it to the existing `Module` node


Test patch:
diff --git a/tests/ast/nodes/test_evaluate_compare.py b/tests/ast/nodes/test_evaluate_compare.py
--- a/tests/ast/nodes/test_evaluate_compare.py
+++ b/tests/ast/nodes/test_evaluate_compare.py
@@ -47,7 +47,7 @@ def foo(a: uint128, b: uint128) -> bool:
 
 
 @pytest.mark.fuzzing
-@settings(max_examples=20, deadline=500)
+@settings(max_examples=20, deadline=1000)
 @given(left=st.integers(), right=st.lists(st.integers(), min_size=1, max_size=16))
 def test_compare_in(left, right, get_contract):
     source = f"""
@@ -76,7 +76,7 @@ def bar(a: int128) -> bool:
 
 
 @pytest.mark.fuzzing
-@settings(max_examples=20, deadline=500)
+@settings(max_examples=20, deadline=1000)
 @given(left=st.integers(), right=st.lists(st.integers(), min_size=1, max_size=16))
 def test_compare_not_in(left, right, get_contract):
     source = f"""
diff --git a/tests/parser/types/test_dynamic_array.py b/tests/parser/types/test_dynamic_array.py
--- a/tests/parser/types/test_dynamic_array.py
+++ b/tests/parser/types/test_dynamic_array.py
@@ -1621,6 +1621,35 @@ def ix(i: uint256) -> decimal:
     assert_tx_failed(lambda: c.ix(len(some_good_primes) + 1))
 
 
+def test_public_dynarray(get_contract):
+    code = """
+my_list: public(DynArray[uint256, 5])
+@external
+def __init__():
+    self.my_list = [1,2,3]
+    """
+    c = get_contract(code)
+
+    for i, t in enumerate([1, 2, 3]):
+        assert c.my_list(i) == t
+
+
+def test_nested_public_dynarray(get_contract):
+    code = """
+my_list: public(DynArray[DynArray[uint256, 5], 5])
+@external
+def __init__():
+    self.my_list = [[1,2,3]]
+    """
+    c = get_contract(code)
+
+    for i, l in enumerate([[1, 2, 3]]):
+        for j, t in enumerate(l):
+            assert c.my_list(i, j) == t
+
+
+# TODO test negative public(DynArray) cases?
+
 # CMC 2022-08-04 these are blocked due to typechecker bug; leaving as
 # negative tests so we know if/when the typechecker is fixed.
 # (don't consider it a high priority to fix since membership in
@@ -1641,8 +1670,6 @@ def foo(x: uint256) -> bool:
     assert_compile_failed(lambda: get_contract(code))
 
 
-# TODO test loops
-
 # Would be nice to put this somewhere accessible, like in vyper.types or something
 integer_types = ["uint8", "int128", "int256", "uint256"]