Repository: vyperlang/vyper

Issue:
Map lookup as key fails
### What's your issue about?

When using one map lookup for another, it always returns 0.

Fails:
```python
test_map1: {
    a: int128,
    c: int128
}[int128]


test_map2: {
    b: int128,
    c: int128
}[int128]


@public
def set():
    self.test_map1[1].a = 333
    self.test_map2[333].b = 111
    self.test_map2[333].c = 111


@public
def get(inp: int128) -> int128:
    return self.test_map2[self.test_map1[inp].a].b
````

However the following works:
```python
# ... snip
@public
def get(inp: int128) -> int128:
    idx: int128 = self.test_map1[inp].a
    return self.test_map2[self.test_map1[inp].a].b
```

### How can it be fixed?

Fill this in if you know how to fix it.

#### Cute Animal Picture
^.^



Discussion:
`idx = ...` would load the value on to the stack, I wonder if that has something to do with it. Try creating a log with `self.test_map1[inp].a`, see if it does something similar.

Resolving patch:
diff --git a/vyper/compile_lll.py b/vyper/compile_lll.py
--- a/vyper/compile_lll.py
+++ b/vyper/compile_lll.py
@@ -225,20 +225,26 @@ def compile_to_assembly(code, withargs=None, break_dest=None, height=0):
     # SHA3 a single value
     elif code.value == 'sha3_32':
         o = compile_to_assembly(code.args[0], withargs, break_dest, height)
-        o.extend(['PUSH1', MemoryPositions.FREE_VAR_SPACE, 'MSTORE', 'PUSH1', 32, 'PUSH1', MemoryPositions.FREE_VAR_SPACE, 'SHA3'])
+        o.extend([
+            'PUSH1', MemoryPositions.FREE_VAR_SPACE,
+            'MSTORE',
+            'PUSH1', 32,
+            'PUSH1', MemoryPositions.FREE_VAR_SPACE,
+            'SHA3'
+        ])
         return o
     # SHA3 a 64 byte value
     elif code.value == 'sha3_64':
         o = compile_to_assembly(code.args[0], withargs, break_dest, height)
-        o.extend([
-            'PUSH1', MemoryPositions.FREE_VAR_SPACE,
-            'MSTORE'
-        ])
         o.extend(compile_to_assembly(code.args[1], withargs, break_dest, height))
         o.extend([
             'PUSH1', MemoryPositions.FREE_VAR_SPACE2,
             'MSTORE',
-            'PUSH1', 64, 'PUSH1', MemoryPositions.FREE_VAR_SPACE, 'SHA3'
+            'PUSH1', MemoryPositions.FREE_VAR_SPACE,
+            'MSTORE',
+            'PUSH1', 64,
+            'PUSH1', MemoryPositions.FREE_VAR_SPACE,
+            'SHA3'
         ])
         return o
     # <= operator
diff --git a/vyper/compiler.py b/vyper/compiler.py
--- a/vyper/compiler.py
+++ b/vyper/compiler.py
@@ -5,7 +5,20 @@
 
 def compile(code, *args, **kwargs):
     lll = optimizer.optimize(parser.parse_tree_to_lll(parser.parse(code), code, runtime_only=kwargs.get('bytecode_runtime', False)))
-    return compile_lll.assembly_to_evm(compile_lll.compile_to_assembly(lll))
+    asm = compile_lll.compile_to_assembly(lll)
+
+    def find_nested_opcode(asm_list, key):
+        if key in asm_list:
+            return True
+        else:
+            sublists = [sub for sub in asm_list if isinstance(sub, list)]
+            return any([find_nested_opcode(x, key) for x in sublists])
+
+    if find_nested_opcode(asm, 'DEBUG'):
+        print('Please not this code contains DEBUG opcode.')
+        print('This will only work in a support EVM. This FAIL on any other nodes.')
+
+    return compile_lll.assembly_to_evm(asm)
 
 
 def gas_estimate(origcode, *args, **kwargs):
diff --git a/vyper/opcodes.py b/vyper/opcodes.py
--- a/vyper/opcodes.py
+++ b/vyper/opcodes.py
@@ -69,6 +69,7 @@
     'STATICCALL': [0xfa, 6, 1, 40],
     'REVERT': [0xfd, 2, 0, 0],
     'INVALID': [0xfe, 0, 0, 0],
+    'DEBUG': [0xa5, 0, 0, 0]
 }
 
 pseudo_opcodes = {


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,8 +1,9 @@
+import eth_tester
+import logging
 import pytest
+import web3
 
 from functools import wraps
-import eth_tester
-import web3
 
 from eth_tester import (
     EthereumTester,
@@ -29,16 +30,42 @@
     optimizer,
 )
 
-
 ############
 # PATCHING #
 ############
 
-
 setattr(eth_tester.backends.pyevm.main, 'GENESIS_GAS_LIMIT', 10**9)
 setattr(eth_tester.backends.pyevm.main, 'GENESIS_DIFFICULTY', 1)
 
 
+def set_evm_verbose_logging():
+    logger = logging.getLogger('evm')
+    logger.setLevel('TRACE')
+
+
+def set_evm_opcode_debugger():
+    import evm
+    from evm.vm.opcode import as_opcode
+    from vyper.opcodes import opcodes as vyper_opcodes
+    from evm.vm.forks.byzantium.computation import ByzantiumComputation
+
+    def debug_opcode(computation):
+        from eth_utils import to_hex  # noqa
+        import ipdb; ipdb.set_trace()  # noqa
+
+    opcodes = ByzantiumComputation.opcodes.copy()
+    opcodes[vyper_opcodes['DEBUG'][0]] = as_opcode(
+        logic_fn=debug_opcode,
+        mnemonic="DEBUG",
+        gas_cost=0
+    )
+    setattr(evm.vm.forks.byzantium.computation.ByzantiumComputation, 'opcodes', opcodes)
+
+# Useful options to comment out whilst working:
+# set_evm_verbose_logging()
+# set_evm_opcode_debugger()
+
+
 @pytest.fixture(autouse=True)
 def patch_log_filter_remove(monkeypatch):
 
diff --git a/tests/parser/features/test_assignment.py b/tests/parser/features/test_assignment.py
--- a/tests/parser/features/test_assignment.py
+++ b/tests/parser/features/test_assignment.py
@@ -125,3 +125,45 @@ def foo2() -> uint256:
     return x
 """
     assert_compile_failed(lambda: get_contract_with_gas_estimation(code), ParserException)
+
+
+# See #838. Confirm that nested keys and structs work properly.
+def test_nested_map_key_works(get_contract_with_gas_estimation):
+    code = """
+test_map1: {a: int128, b: int128 }[int128]
+test_map2: {c: int128, d: int128}[int128]
+
+@public
+def set():
+    self.test_map1[1].a = 333
+    self.test_map2[333].c = 111
+
+
+@public
+def get(i: int128) -> int128:
+    idx: int128 = self.test_map1[i].a
+    return self.test_map2[idx].c
+    """
+    c = get_contract_with_gas_estimation(code)
+    assert c.set(transact={})
+    assert c.get(1) == 111
+
+
+def test_nested_map_key_problem(get_contract_with_gas_estimation):
+    code = """
+test_map1: {a: int128, b: int128 }[int128]
+test_map2: {c: int128, d: int128}[int128]
+
+@public
+def set():
+    self.test_map1[1].a = 333
+    self.test_map2[333].c = 111
+
+
+@public
+def get() -> int128:
+    return self.test_map2[self.test_map1[1].a].c
+    """
+    c = get_contract_with_gas_estimation(code)
+    assert c.set(transact={})
+    assert c.get() == 111