vyper --version): 0.1.0-beta16 (current master)As reported by @michwill on Gitter, passing a struct between functions, where the struct contains an array, results in malformed data. WIth the following contract:
struct A:
many: uint256[4]
one: uint256
@private
@constant
def _foo(_many: uint256[4], _one: uint256) -> A:
return A({many: _many, one: _one})
@public
@constant
def foo() -> A:
return self._foo([1, 2, 3, 4], 5)
@public
@constant
def bar() -> (uint256[4], uint256):
out: A = self._foo([1, 2, 3, 4], 5)
return out.many, out.one
Here is the output of each public function:
>>> contract.foo()
((1, 2, 5, 0), 0)
>>> contract.bar()
((0, 0, 5, 4), 3)
Observations:
@constant modifier has no effect.It looks like an off-by-one (or 2?) error to me, but I'm unsure how to fix it.
Do you get the same answers if you reverse the calling order?
The answers seems different if the order is changed in the declaration of struct
I get differently incorrect answers. Placing the non-array before the array does NOT result in the non-array being correctly located so I think the issue is with the struct itself, not just how the array is handled inside the struct... if that makes sense.
Yes, that's exactly what I am observing, too
Any hints of why could that be happening?
Been looking at this some more. The issue isn't only with structs, it also happens when passing a tuple that contains an array.
Looking at the IR it seems that there's an extra MSTORE which overwrites one of the return values. The section of the code where the instruction is generated is: vyper/parser/self_call.py#L227
I think this is a lingering issue from #1838 - @charles-cooper any chance you could take a look?
Good call, seems like we can't draft a new release until we figure out this issue
@iamdefinitelyahuman thanks - I will take a look later today or tomorrow.
Any luck?
hey @michwill - looking into this now
probably declared victory on https://github.com/vyperlang/vyper/issues/1839 and #1409 a little too soon
Been looking at this some more. The issue isn't only with structs, it also happens when passing a tuple that contains an array.
Looking at the IR it seems that there's an extra
MSTOREwhich overwrites one of the return values. The section of the code where the instruction is generated is: vyper/parser/self_call.py#L227I think this is a lingering issue from #1838 - @charles-cooper any chance you could take a look?
@iamdefinitelyahuman which extra mstore do you see?
i see the issue - call-to-self code assumes that tuple/struct members will always be 1 word
So - I could fix the unpacking code in self_call.py but I think what I want to do is change our internal calling convention to use unpacked data instead of ABI-encoded data. It will simplify the compiler and reduce code size.
Most helpful comment
So - I could fix the unpacking code in
self_call.pybut I think what I want to do is change our internal calling convention to use unpacked data instead of ABI-encoded data. It will simplify the compiler and reduce code size.