vyper icon indicating copy to clipboard operation
vyper copied to clipboard

Ternary operator in conditions does not compile

Open ritzdorf opened this issue 1 year ago • 2 comments

Version Information

  • vyper Version (output of vyper --version OR linkable commit hash vyperlang/vyper@commitish): 0.4.0rc5+commit.98370f50

Issue description

When a ternary operator is used inside conditions, e.g. if or assert compilation fails.

Typically the error looks something like:

vyper.exceptions.CompilerPanic: missing symbols: {'self.foo()4'}

PoC

Minimal with assert:

@internal
def foo() -> uint256:
    return 42

@external
def bar():
    assert 43 == (self.foo() if True else self.foo()), "test"

Minimal with if:

@internal
def foo() -> uint256:
    return 42

@external
def bar():
    if 43 == (self.foo() if True else self.foo()):
        pass

More extensive:

#@version 0.4.0.rc5

@internal
def foo() -> uint256:
    return 42

@internal
def baz() -> bool:
    return False

@external
def bar():
    b: bool = False
    # These compile
    assert 43 == (self.foo() if self.baz() else self.foo()), "test"
    u: uint256 = self.foo() if empty(bool) else self.foo()

    # These do not compile
    #assert 43 == (self.foo() if empty(bool) else self.foo()), "test"
    #assert (self.foo() if empty(bool) else self.foo()) == 43, "test"
    #assert (self.foo() if empty(bool) else self.foo()) == (self.foo() if empty(bool) else self.foo()), "test"

Credit: @pcaversaccio and @trocher

ritzdorf avatar May 23 '24 10:05 ritzdorf

whe i run this example without optimizations --no-optimize, the revert doesn't happen

@internal
def foo() -> uint256:
    return 42

@external
def bar():
    assert 43 == (self.foo() if True else self.foo()), "test"

cyberthirst avatar May 31 '24 13:05 cyberthirst

whe i run this example without optimizations --no-optimize, the revert doesn't happen

@internal
def foo() -> uint256:
    return 42

@external
def bar():
    assert 43 == (self.foo() if True else self.foo()), "test"

I'd expect such behaviour as I think the issue is due to the optimizer removing the if or else expression with the dead branch eliminator but the symbol check is not being deactivated since arithmetic operations optimizations have should_check_symbols set (and in those case the if/else is in an arithmetic op)

https://github.com/vyperlang/vyper/blob/24cfe0bcc10ec9418054c8def5cf4eeaa3ed0164/vyper/ir/optimizer.py#L448-L450

https://github.com/vyperlang/vyper/blob/24cfe0bcc10ec9418054c8def5cf4eeaa3ed0164/vyper/ir/optimizer.py#L498

trocher avatar Jun 10 '24 08:06 trocher