vyper icon indicating copy to clipboard operation
vyper copied to clipboard

Folding Not Supported in Certain Locations

Open ritzdorf opened this issue 1 year ago • 2 comments

Version Information

  • vyper Version (output of vyper --version OR linkable commit hash vyperlang/vyper@): vyperlang/vyper@b43fface148b45cbd2c90b5d24f77398a63745b5

Issue Description

Several Vyper constructs require or can be optimized if constant values are provided. To achieve this, AST nodes can be folded to constants by the compiler, however, the following locations do not support folding:

  • length in slice(x, start, length) when x is msg.data or address.code:

    • isinstance(parent.args[2], vy_ast.Int) in _validate_msg_data_attribute()
    • isinstance(parent.args[2], vy_ast.Int) in _validate_address_code()
  • length in slice(x, start, length):

    • if length_literal is not None condition in Slice.fetch_call_return()
  • index in variable[index] when the variable is an array.

    • isinstance(node, vy_ast.Int) in _SequenceT.validate_index_type()
  • index in variable[index] when the variable is a tuple.

    • not isinstance(node, vy_ast.Int) in TupleT.validate_index_type()
  • x or y in x ** y:

    • left, right = _get_lr() in NumericT.validate_numeric_op()
  • x in convert(x, T)

    • _convert._to_int(), _convert.to_decimal() and _convert._cast_bytestring() does not check if expr has a folded value.
  • topic in raw_log(topic, data) .

    • not isinstance(node.args[0], vy_ast.List) in RawLog.infer_arg_types()

POC

The following example demonstrates the different issues.

@external
def foo():
    x: Bytes[32] = slice(msg.data, 0, 31 + 1) # StructureException
@external
def foo(tuple: (uint256,uint256)) -> uint256:
    return tuple[0+1] # InvalidType
k: constant(Bytes[3]) = b'aaa'
@external
def foo():

    a: Bytes[2] = convert (k, Bytes[2]) # compiles instead of failing
    b: Bytes[2] = convert (b'aaa', Bytes[2]) # TypeMismatch
a: constant(uint256) = 12
@external
def foo():
    # The following check is inserted by the compiler although it is not necessary
    # [assert, [iszero, [shr, 128, 12 <12>]]],
    b: uint128 = convert (a, uint128)
topic: constant(bytes32) = 0x1212121212121210212801291212121212121210121212121212121212121212
@external
def foo():
    raw_log([[topic]][0], b'') # InvalidType

ritzdorf avatar Apr 25 '24 15:04 ritzdorf

One more location to add: default values.

Example:

@external
def foo(x: int256 = 2**255-1):

does not compile with:

TypeMismatch('Expected int256 but literal can only be cast as uint256.', vyper.ast.nodes.Int:

ritzdorf avatar May 11 '24 22:05 ritzdorf

One more location to add: default values.

Example:

@external
def foo(x: int256 = 2**255-1):

does not compile with:

TypeMismatch('Expected int256 but literal can only be cast as uint256.', vyper.ast.nodes.Int:

this one is expected behavior i think, since 2**255 is out of bounds of the range of int256, and compile-time safemath should match runtime safemath.

charles-cooper avatar Jul 19 '24 00:07 charles-cooper

The following have been fixed in https://github.com/vyperlang/vyper/commit/75fb0594ab3011491fd124abe534573dbd9ba052:

  • index in variable[index] when the variable is an array.
  • index in variable[index] when the variable is a tuple.

tserg avatar Nov 20 '24 03:11 tserg

The x or y case in x ** y have been fixed in https://github.com/vyperlang/vyper/commit/e1adb7b3344c1ac03facfa553830a94dd7def2e2.

tserg avatar Nov 20 '24 03:11 tserg