vyperlang / vyper

Pythonic Smart Contract Language for the EVM
https://vyperlang.org
Other
4.81k stars 788 forks source link

Unintended error message for bytestring convert #4111

Open Leminkay opened 2 months ago

Leminkay commented 2 months ago

Version Information

Issue description and/or POC

@external
def foo(a: Bytes[10]) -> Bytes[11]:
   return convert(a, Bytes[11])

The compilation of the example above will throw vyper.exceptions.InvalidType: Value and target type are both 'Bytes[11]' instead of vyper.exceptions.TypeMismatch: Can't convert Bytes[10] to Bytes[11]

The first condition in the _cast_bytestring function can't be reached

def _cast_bytestring(expr, arg, out_typ):
    # ban converting Bytes[20] to Bytes[21]
    if isinstance(arg.typ, out_typ.__class__) and arg.typ.maxlen <= out_typ.maxlen:
        _FAIL(arg.typ, out_typ, expr)
        # ^- should raise TypeMismatch(f"Can't convert {ityp} to {otyp}", source_expr)

Due to the check in the Convert::infer_arg_types:

if target_type.compare_type(value_type):
    raise InvalidType(f"Value and target type are both '{target_type}'", node)

Which is triggered by the _BytestringT comparison conditions.

if self._length:
    if not other._length:
        other.set_length(max(self._length, other._min_length))
    return self._length >= other._length

How can it be fixed?

charles-cooper commented 2 months ago

as a note, the root of this issue is that _BytestringT.compare_type is a mutating function. there have been a couple attempts to fix this (most recently, https://github.com/vyperlang/vyper/pull/3379 and https://github.com/vyperlang/vyper/pull/3765) but they are somewhat deep changes and also affect language semantics, so i deferred the work to 0.4.1 or later