Repository: vyperlang/vyper

Issue:
External Call to Struct not working
### Version Information

* vyper Version (output of `vyper --version`): 0.1.0b8
* OS: osx
* Python Version (output of `python --version`): 3.7.1

### What's your issue about?

I am unable to interact with structs in 1 contract (let’s call it contract A) that I get as a return value from a function in another contract (let’s call it contract B).

For example, I’m defining the struct in both contract A and B as follows:

```
struct Model:
    x: timedelta
    y: uint256
```

The function (in contract B) that returns the struct is this:

```
@public
@constant
def getModel(_model: address) -> Model:
    return self.models[_model]
```

I can pull it into contract A like this and have it compile successfully:

```
contract B:
    def getModel(_model: address) -> Model: constant
```

But when I try to interact with the members of the struct (i.e. x and y), like this:

```
m: uint256 = B(self.b).getModel(self.model).y
```

I get a compilation error saying `Invalid output type: struct`. Any idea why this would be happening (why it would compile fine when declaring the contract at the top but failing in the function body)?



Discussion:
Seems to be an error in how the interface is imported. When the function is defined locally it compiles OK:
```python
struct Model:
    x: timedelta
    y: uint256

contract B:
    def getModel(_model: address) -> Model: constant

b: address
model: address

_models: map(address, Model)

@public
@constant
def getModelA(addr: address) -> Model :
    return self._models[addr]

@public
def foo() -> uint256 :
    # m: uint256 = B(self.b).getModel(self.model).y # <-- doesn't compile
    m: uint256 = self.getModelA(self.model).y
    return m
```

Resolving patch:
diff --git a/vyper/parser/external_call.py b/vyper/parser/external_call.py
--- a/vyper/parser/external_call.py
+++ b/vyper/parser/external_call.py
@@ -17,7 +17,7 @@
 from vyper.types import (
     BaseType,
     ByteArrayLike,
-    TupleType,
+    TupleLike,
     get_size_of_type,
 )
 
@@ -97,7 +97,7 @@ def get_external_contract_call_output(sig, context):
         returner = [0, output_placeholder]
     elif isinstance(sig.output_type, ByteArrayLike):
         returner = [0, output_placeholder + 32]
-    elif isinstance(sig.output_type, TupleType):
+    elif isinstance(sig.output_type, TupleLike):
         returner = [0, output_placeholder]
     else:
         raise TypeMismatchException("Invalid output type: %s" % sig.output_type)


Test patch:
diff --git a/tests/parser/features/external_contracts/test_external_contract_calls.py b/tests/parser/features/external_contracts/test_external_contract_calls.py
--- a/tests/parser/features/external_contracts/test_external_contract_calls.py
+++ b/tests/parser/features/external_contracts/test_external_contract_calls.py
@@ -777,3 +777,33 @@ def test(addr: address) -> (int128, address, bytes[10]):
 
     assert c1.out_literals() == [1, "0x0000000000000000000000000000000000000123", b"random"]
     assert c2.test(c1.address) == [1, "0x0000000000000000000000000000000000000123", b"random"]
+
+
+def test_struct_return_external_contract_call(get_contract_with_gas_estimation):
+    contract_1 = """
+struct X:
+    x: int128
+    y: address
+@public
+def out_literals() -> X:
+    return X({x: 1, y: 0x0000000000000000000000000000000000012345})
+    """
+
+    contract_2 = """
+struct X:
+    x: int128
+    y: address
+contract Test:
+    def out_literals() -> X : constant
+
+@public
+def test(addr: address) -> (int128, address):
+    ret: X = Test(addr).out_literals()
+    return ret.x, ret.y
+
+    """
+    c1 = get_contract_with_gas_estimation(contract_1)
+    c2 = get_contract_with_gas_estimation(contract_2)
+
+    assert c1.out_literals() == [1, "0x0000000000000000000000000000000000012345"]
+    assert c2.test(c1.address) == c1.out_literals()