Repository: vyperlang/vyper

Issue:
`CompilerPanic` when writing to an array in calldata
### Version Information

* vyper Version (output of `vyper --version`): 0.3.8+commit.ae7d6d28
* OS: OSX
* Python Version (output of `python --version`): 3.8.0

### What's your issue about?

Note that this bug is not present in v0.3.7 but only in the latest commits of the master branch.

When trying to write to calldata by modifying an element of an array, the compiler panic where it would emit an `ImmutableViolation: Cannot write to calldata` in v0.3.7 and prior versions

```Vyper
@external
def test(a: uint256[4]):
    a[0] = 1
```
```Bash
Error compiling: tests/customs/code.vy
Traceback (most recent call last):
  File "/Users/trocher/Documents/thesis/vyper/venv/bin/vyper", line 11, in <module>
    load_entry_point('vyper==0.3.8', 'console_scripts', 'vyper')()
  File "/Users/trocher/Documents/thesis/vyper/vyper/cli/vyper_compile.py", line 57, in _parse_cli_args
    return _parse_args(sys.argv[1:])
  File "/Users/trocher/Documents/thesis/vyper/vyper/cli/vyper_compile.py", line 154, in _parse_args
    compiled = compile_files(
  File "/Users/trocher/Documents/thesis/vyper/vyper/cli/vyper_compile.py", line 294, in compile_files
    compiler_data = vyper.compile_codes(
  File "/Users/trocher/Documents/thesis/vyper/vyper/evm/opcodes.py", line 226, in _wrapper
    return fn(*args, **kwargs)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/__init__.py", line 141, in compile_codes
    exc_handler(contract_name, exc)
  File "/Users/trocher/Documents/thesis/vyper/vyper/cli/vyper_compile.py", line 189, in exc_handler
    raise exception
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/__init__.py", line 138, in compile_codes
    out[contract_name][output_format] = OUTPUT_FORMATS[output_format](compiler_data)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/output.py", line 248, in build_bytecode_output
    return f"0x{compiler_data.bytecode.hex()}"
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/functools.py", line 966, in __get__
    val = self.func(instance)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 150, in bytecode
    self.assembly, is_runtime=False, no_bytecode_metadata=self.no_bytecode_metadata
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/functools.py", line 966, in __get__
    val = self.func(instance)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 141, in assembly
    return generate_assembly(self.ir_nodes, self.no_optimize)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 126, in ir_nodes
    ir, ir_runtime, sigs = self._ir_output
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/functools.py", line 966, in __get__
    val = self.func(instance)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 122, in _ir_output
    return generate_ir_nodes(self.global_ctx, self.no_optimize)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 258, in generate_ir_nodes
    ir_nodes, ir_runtime, function_sigs = module.generate_ir_for_module(global_ctx)
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/module.py", line 165, in generate_ir_for_module
    runtime, internal_functions = _runtime_ir(runtime_functions, all_sigs, global_ctx)
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/module.py", line 103, in _runtime_ir
    func_ir = generate_ir_for_function(func_ast, all_sigs, global_ctx, skip_nonpayable_check)
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/function_definitions/common.py", line 62, in generate_ir_for_function
    o = generate_ir_for_external_function(code, sig, context, skip_nonpayable_check)
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/function_definitions/external_function.py", line 182, in generate_ir_for_external_function
    body += [parse_body(code.body, context, ensure_terminated=True)]
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/stmt.py", line 410, in parse_body
    ir = parse_stmt(stmt, context)
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/stmt.py", line 384, in parse_stmt
    return Stmt(stmt, context).ir_node
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/stmt.py", line 40, in __init__
    self.ir_node = fn()
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/stmt.py", line 78, in parse_Assign
    ir_node = make_setter(target, sub)
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/core.py", line 829, in make_setter
    return STORE(left, right)
  File "/Users/trocher/Documents/thesis/vyper/vyper/codegen/core.py", line 601, in STORE
    raise CompilerPanic(f"unreachable {ptr.location}")  # pragma: notest
vyper.exceptions.CompilerPanic: unreachable AddrSpace(name='calldata', word_scale=32, load_op='calldataload', store_op=None)

This is an unhandled internal compiler error. Please create an issue on Github to notify the developers.
https://github.com/vyperlang/vyper/issues/new?template=bug.md
```


Discussion:

Resolving patch:
diff --git a/vyper/semantics/analysis/utils.py b/vyper/semantics/analysis/utils.py
--- a/vyper/semantics/analysis/utils.py
+++ b/vyper/semantics/analysis/utils.py
@@ -96,6 +96,11 @@ def get_expr_info(self, node: vy_ast.VyperNode) -> ExprInfo:
 
             return ExprInfo(t, location=location, is_constant=is_constant)
 
+        # If it's a Subscript, propagate the subscriptable varinfo
+        if isinstance(node, vy_ast.Subscript):
+            info = self.get_expr_info(node.value)
+            return info.copy_with_type(t)
+
         return ExprInfo(t)
 
     def get_exact_type_from_node(self, node, include_type_exprs=False):


Test patch:
diff --git a/tests/parser/exceptions/test_constancy_exception.py b/tests/parser/exceptions/test_constancy_exception.py
--- a/tests/parser/exceptions/test_constancy_exception.py
+++ b/tests/parser/exceptions/test_constancy_exception.py
@@ -99,7 +99,27 @@ def test_statefulness_violations(bad_code):
         """
 @external
 def foo(x: int128):
-    x = 5"""
+    x = 5
+        """,
+        """
+@external
+def test(a: uint256[4]):
+    a[0] = 1
+        """,
+        """
+@external
+def test(a: uint256[4][4]):
+    a[0][1] = 1
+        """,
+        """
+struct Foo:
+    a: DynArray[DynArray[uint256, 2], 2]
+
+@external
+def foo(f: Foo) -> Foo:
+    f.a[1] = [0, 1]
+    return f
+        """,
     ],
 )
 def test_immutability_violations(bad_code):
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
@@ -1565,10 +1565,11 @@ def _foo2() -> Foo:
 
 @internal
 def _foo3(f: Foo) -> Foo:
-    f.b1[0][1][0].a1[0][0] = [0, 0]
-    f.b1[1][0][0].a1[0][1] = [0, 0]
-    f.b1[1][1][0].a1[1][1] = [0, 0]
-    return f
+    new_f: Foo = f
+    new_f.b1[0][1][0].a1[0][0] = [0, 0]
+    new_f.b1[1][0][0].a1[0][1] = [0, 0]
+    new_f.b1[1][1][0].a1[1][1] = [0, 0]
+    return new_f
 
 @external
 def bar() -> DynArray[DynArray[DynArray[uint256, 2], 2], 2]: