def parse_UnaryOp(self): operand = Expr.parse_value_expr(self.expr.operand, self.context) if isinstance(self.expr.op, vy_ast.Not): if isinstance(operand.typ, BaseType) and operand.typ.typ == "bool": return IRnode.from_list(["iszero", operand], typ="bool") if isinstance(self.expr.op, vy_ast.Invert): if isinstance(operand.typ, EnumType): n_members = len(operand.typ.members) # use (xor 0b11..1 operand) to flip all the bits in # `operand`. `mask` could be a very large constant and # hurt codesize, but most user enums will likely have few # enough members that the mask will not be large. mask = (2**n_members) - 1 return IRnode.from_list(["xor", mask, operand], typ=operand.typ) if is_base_type(operand.typ, "uint256"): return IRnode.from_list(["not", operand], typ=operand.typ) # block `~` for all other integer types, since reasoning # about dirty bits is not entirely trivial. maybe revisit # this at a later date. raise UnimplementedException( f"~ is not supported for {operand.typ}", self.expr) if isinstance(self.expr.op, vy_ast.USub) and is_numeric_type( operand.typ): assert operand.typ._num_info.is_signed # Clamp on minimum signed integer value as we cannot negate that # value (all other integer values are fine) min_int_val, _ = operand.typ._num_info.bounds return IRnode.from_list( ["sub", 0, clamp("sgt", operand, min_int_val)], typ=operand.typ)
def parse_BinOp(self): left = Expr.parse_value_expr(self.expr.left, self.context) right = Expr.parse_value_expr(self.expr.right, self.context) if not is_numeric_type(left.typ) or not is_numeric_type(right.typ): return ltyp, rtyp = left.typ.typ, right.typ.typ # Sanity check - ensure that we aren't dealing with different types # This should be unreachable due to the type check pass assert ltyp == rtyp, f"unreachable, {ltyp}!={rtyp}, {self.expr}" if isinstance(self.expr.op, vy_ast.BitAnd): new_typ = left.typ return IRnode.from_list(["and", left, right], typ=new_typ) if isinstance(self.expr.op, vy_ast.BitOr): new_typ = left.typ return IRnode.from_list(["or", left, right], typ=new_typ) if isinstance(self.expr.op, vy_ast.BitXor): new_typ = left.typ return IRnode.from_list(["xor", left, right], typ=new_typ) out_typ = BaseType(ltyp) with left.cache_when_complex("x") as ( b1, x), right.cache_when_complex("y") as (b2, y): if isinstance(self.expr.op, vy_ast.Add): ret = arithmetic.safe_add(x, y) elif isinstance(self.expr.op, vy_ast.Sub): ret = arithmetic.safe_sub(x, y) elif isinstance(self.expr.op, vy_ast.Mult): ret = arithmetic.safe_mul(x, y) elif isinstance(self.expr.op, vy_ast.Div): ret = arithmetic.safe_div(x, y) elif isinstance(self.expr.op, vy_ast.Mod): ret = arithmetic.safe_mod(x, y) elif isinstance(self.expr.op, vy_ast.Pow): ret = arithmetic.safe_pow(x, y) else: return # raises return IRnode.from_list(b1.resolve(b2.resolve(ret)), typ=out_typ)
def parse_UnaryOp(self): operand = Expr.parse_value_expr(self.expr.operand, self.context) if isinstance(self.expr.op, vy_ast.Not): if isinstance(operand.typ, BaseType) and operand.typ.typ == "bool": return IRnode.from_list(["iszero", operand], typ="bool") elif isinstance(self.expr.op, vy_ast.USub) and is_numeric_type( operand.typ): assert operand.typ._num_info.is_signed # Clamp on minimum integer value as we cannot negate that value # (all other integer values are fine) min_int_val, _ = operand.typ._num_info.bounds return IRnode.from_list( ["sub", 0, ["clampgt", operand, min_int_val]], typ=operand.typ, )
def parse_UnaryOp(self): operand = Expr.parse_value_expr(self.expr.operand, self.context) if isinstance(self.expr.op, vy_ast.Not): if isinstance(operand.typ, BaseType) and operand.typ.typ == "bool": return LLLnode.from_list(["iszero", operand], typ="bool", pos=getpos(self.expr)) elif isinstance(self.expr.op, vy_ast.USub) and is_numeric_type( operand.typ): # Clamp on minimum integer value as we cannot negate that value # (all other integer values are fine) min_int_val = get_min_val_for_type(operand.typ.typ) return LLLnode.from_list( ["sub", 0, ["clampgt", operand, min_int_val]], typ=operand.typ, pos=getpos(self.expr), )
def parse_Compare(self): left = Expr.parse_value_expr(self.expr.left, self.context) right = Expr.parse_value_expr(self.expr.right, self.context) if right.value is None: return if isinstance(self.expr.op, (vy_ast.In, vy_ast.NotIn)): if isinstance(right.typ, ArrayLike): return self.build_in_comparator() return # pragma: notest if isinstance(self.expr.op, vy_ast.Gt): op = "sgt" elif isinstance(self.expr.op, vy_ast.GtE): op = "sge" elif isinstance(self.expr.op, vy_ast.LtE): op = "sle" elif isinstance(self.expr.op, vy_ast.Lt): op = "slt" elif isinstance(self.expr.op, vy_ast.Eq): op = "eq" elif isinstance(self.expr.op, vy_ast.NotEq): op = "ne" else: return # pragma: notest # Compare (limited to 32) byte arrays. if isinstance(left.typ, ByteArrayLike) and isinstance( right.typ, ByteArrayLike): left = Expr(self.expr.left, self.context).lll_node right = Expr(self.expr.right, self.context).lll_node length_mismatch = left.typ.maxlen != right.typ.maxlen left_over_32 = left.typ.maxlen > 32 right_over_32 = right.typ.maxlen > 32 if length_mismatch or left_over_32 or right_over_32: left_keccak = keccak256_helper(self.expr, left, self.context) right_keccak = keccak256_helper(self.expr, right, self.context) if op == "eq" or op == "ne": return LLLnode.from_list( [op, left_keccak, right_keccak], typ="bool", pos=getpos(self.expr), ) else: return else: def load_bytearray(side): if side.location == "storage": return ["sload", ["add", 1, side]] else: load = load_op(side.location) return [load, ["add", 32, side]] return LLLnode.from_list( [op, load_bytearray(left), load_bytearray(right)], typ="bool", pos=getpos(self.expr), ) # Compare other types. elif is_numeric_type(left.typ) and is_numeric_type(right.typ): if left.typ.typ == right.typ.typ == "uint256": # this works because we only have one unsigned integer type # in the future if others are added, this logic must be expanded op = self._signed_to_unsigned_comparision_op(op) elif isinstance(left.typ, BaseType) and isinstance( right.typ, BaseType): if op not in ("eq", "ne"): return else: # kludge to block behavior in #2638 # TODO actually implement equality for complex types raise TypeMismatch( "equality not yet supported for complex types, see issue #2638", self.expr) return LLLnode.from_list([op, left, right], typ="bool", pos=getpos(self.expr))
def parse_BinOp(self): left = Expr.parse_value_expr(self.expr.left, self.context) right = Expr.parse_value_expr(self.expr.right, self.context) if not is_numeric_type(left.typ) or not is_numeric_type(right.typ): return pos = getpos(self.expr) types = {left.typ.typ, right.typ.typ} literals = {left.typ.is_literal, right.typ.is_literal} # If one value of the operation is a literal, we recast it to match the non-literal type. # We know this is OK because types were already verified in the actual typechecking pass. # This is a temporary solution to not break codegen while we work toward removing types # altogether at this stage of complition. @iamdefinitelyahuman if literals == {True, False } and len(types) > 1 and "decimal" not in types: if left.typ.is_literal and SizeLimits.in_bounds( right.typ.typ, left.value): left = LLLnode.from_list( left.value, typ=BaseType(right.typ.typ, is_literal=True), pos=pos, ) elif right.typ.is_literal and SizeLimits.in_bounds( left.typ.typ, right.value): right = LLLnode.from_list( right.value, typ=BaseType(left.typ.typ, is_literal=True), pos=pos, ) ltyp, rtyp = left.typ.typ, right.typ.typ # Sanity check - ensure that we aren't dealing with different types # This should be unreachable due to the type check pass assert ltyp == rtyp, "unreachable" arith = None if isinstance(self.expr.op, (vy_ast.Add, vy_ast.Sub)): new_typ = BaseType(ltyp) if ltyp == "uint256": if isinstance(self.expr.op, vy_ast.Add): # safeadd arith = [ "seq", ["assert", ["ge", ["add", "l", "r"], "l"]], ["add", "l", "r"] ] elif isinstance(self.expr.op, vy_ast.Sub): # safesub arith = [ "seq", ["assert", ["ge", "l", "r"]], ["sub", "l", "r"] ] elif ltyp == "int256": if isinstance(self.expr.op, vy_ast.Add): op, comp1, comp2 = "add", "sge", "slt" else: op, comp1, comp2 = "sub", "sle", "sgt" if right.typ.is_literal: if right.value >= 0: arith = [ "seq", ["assert", [comp1, [op, "l", "r"], "l"]], [op, "l", "r"] ] else: arith = [ "seq", ["assert", [comp2, [op, "l", "r"], "l"]], [op, "l", "r"] ] else: arith = [ "with", "ans", [op, "l", "r"], [ "seq", [ "assert", [ "or", [ "and", ["sge", "r", 0], [comp1, "ans", "l"] ], [ "and", ["slt", "r", 0], [comp2, "ans", "l"] ], ], ], "ans", ], ] elif ltyp in ("decimal", "int128", "uint8"): op = "add" if isinstance(self.expr.op, vy_ast.Add) else "sub" arith = [op, "l", "r"] elif isinstance(self.expr.op, vy_ast.Mult): new_typ = BaseType(ltyp) if ltyp == "uint256": arith = [ "with", "ans", ["mul", "l", "r"], [ "seq", [ "assert", [ "or", ["eq", ["div", "ans", "l"], "r"], ["iszero", "l"] ] ], "ans", ], ] elif ltyp == "int256": if version_check(begin="constantinople"): upper_bound = ["shl", 255, 1] else: upper_bound = -(2**255) if not left.typ.is_literal and not right.typ.is_literal: bounds_check = [ "assert", [ "or", ["ne", "l", ["not", 0]], ["ne", "r", upper_bound] ], ] elif left.typ.is_literal and left.value == -1: bounds_check = ["assert", ["ne", "r", upper_bound]] elif right.typ.is_literal and right.value == -(2**255): bounds_check = ["assert", ["ne", "l", ["not", 0]]] else: bounds_check = "pass" arith = [ "with", "ans", ["mul", "l", "r"], [ "seq", bounds_check, [ "assert", [ "or", ["eq", ["sdiv", "ans", "l"], "r"], ["iszero", "l"] ] ], "ans", ], ] elif ltyp in ("int128", "uint8"): arith = ["mul", "l", "r"] elif ltyp == "decimal": arith = [ "with", "ans", ["mul", "l", "r"], [ "seq", [ "assert", [ "or", ["eq", ["sdiv", "ans", "l"], "r"], ["iszero", "l"] ] ], ["sdiv", "ans", DECIMAL_DIVISOR], ], ] elif isinstance(self.expr.op, vy_ast.Div): if right.typ.is_literal and right.value == 0: return new_typ = BaseType(ltyp) if right.typ.is_literal: divisor = "r" else: # only apply the non-zero clamp when r is not a constant divisor = ["clamp_nonzero", "r"] if ltyp in ("uint8", "uint256"): arith = ["div", "l", divisor] elif ltyp == "int256": if version_check(begin="constantinople"): upper_bound = ["shl", 255, 1] else: upper_bound = -(2**255) if not left.typ.is_literal and not right.typ.is_literal: bounds_check = [ "assert", [ "or", ["ne", "r", ["not", 0]], ["ne", "l", upper_bound] ], ] elif left.typ.is_literal and left.value == -(2**255): bounds_check = ["assert", ["ne", "r", ["not", 0]]] elif right.typ.is_literal and right.value == -1: bounds_check = ["assert", ["ne", "l", upper_bound]] else: bounds_check = "pass" arith = ["seq", bounds_check, ["sdiv", "l", divisor]] elif ltyp == "int128": arith = ["sdiv", "l", divisor] elif ltyp == "decimal": arith = [ "sdiv", ["mul", "l", DECIMAL_DIVISOR], divisor, ] elif isinstance(self.expr.op, vy_ast.Mod): if right.typ.is_literal and right.value == 0: return new_typ = BaseType(ltyp) if right.typ.is_literal: divisor = "r" else: # only apply the non-zero clamp when r is not a constant divisor = ["clamp_nonzero", "r"] if ltyp in ("uint8", "uint256"): arith = ["mod", "l", divisor] else: arith = ["smod", "l", divisor] elif isinstance(self.expr.op, vy_ast.Pow): new_typ = BaseType(ltyp) if self.expr.left.get("value") == 1: return LLLnode.from_list([1], typ=new_typ, pos=pos) if self.expr.left.get("value") == 0: return LLLnode.from_list(["iszero", right], typ=new_typ, pos=pos) if ltyp == "int128": is_signed = True num_bits = 128 elif ltyp == "int256": is_signed = True num_bits = 256 elif ltyp == "uint8": is_signed = False num_bits = 8 else: is_signed = False num_bits = 256 if isinstance(self.expr.left, vy_ast.Int): value = self.expr.left.value upper_bound = calculate_largest_power(value, num_bits, is_signed) + 1 # for signed integers, this also prevents negative values clamp = ["lt", right, upper_bound] return LLLnode.from_list( ["seq", ["assert", clamp], ["exp", left, right]], typ=new_typ, pos=pos, ) elif isinstance(self.expr.right, vy_ast.Int): value = self.expr.right.value upper_bound = calculate_largest_base(value, num_bits, is_signed) + 1 if is_signed: clamp = [ "and", ["slt", left, upper_bound], ["sgt", left, -upper_bound] ] else: clamp = ["lt", left, upper_bound] return LLLnode.from_list( ["seq", ["assert", clamp], ["exp", left, right]], typ=new_typ, pos=pos, ) else: # `a ** b` where neither `a` or `b` are known # TODO this is currently unreachable, once we implement a way to do it safely # remove the check in `vyper/context/types/value/numeric.py` return if arith is None: return arith = LLLnode.from_list(arith, typ=new_typ) p = [ "with", "l", left, [ "with", "r", right, # note clamp_basetype is a noop on [u]int256 # note: clamp_basetype throws on unclampable input clamp_basetype(arith), ], ] return LLLnode.from_list(p, typ=new_typ, pos=pos)
def parse_Compare(self): left = Expr.parse_value_expr(self.expr.left, self.context) right = Expr.parse_value_expr(self.expr.right, self.context) if right.value is None: return if isinstance(self.expr.op, (vy_ast.In, vy_ast.NotIn)): if isinstance(right.typ, ArrayLike): return self.build_in_comparator() else: assert isinstance(right.typ, EnumType), right.typ intersection = ["and", left, right] if isinstance(self.expr.op, vy_ast.In): return IRnode.from_list( ["iszero", ["iszero", intersection]], typ="bool") elif isinstance(self.expr.op, vy_ast.NotIn): return IRnode.from_list(["iszero", intersection], typ="bool") if isinstance(self.expr.op, vy_ast.Gt): op = "sgt" elif isinstance(self.expr.op, vy_ast.GtE): op = "sge" elif isinstance(self.expr.op, vy_ast.LtE): op = "sle" elif isinstance(self.expr.op, vy_ast.Lt): op = "slt" elif isinstance(self.expr.op, vy_ast.Eq): op = "eq" elif isinstance(self.expr.op, vy_ast.NotEq): op = "ne" else: return # pragma: notest # Compare (limited to 32) byte arrays. if isinstance(left.typ, ByteArrayLike) and isinstance( right.typ, ByteArrayLike): left = Expr(self.expr.left, self.context).ir_node right = Expr(self.expr.right, self.context).ir_node left_keccak = keccak256_helper(self.expr, left, self.context) right_keccak = keccak256_helper(self.expr, right, self.context) if op not in ("eq", "ne"): return # raises else: # use hash even for Bytes[N<=32], because there could be dirty # bytes past the bytes data. return IRnode.from_list([op, left_keccak, right_keccak], typ="bool") # Compare other types. elif is_numeric_type(left.typ) and is_numeric_type(right.typ): if left.typ.typ == right.typ.typ == "uint256": # signed comparison ops work for any integer # type BESIDES uint256 op = self._signed_to_unsigned_comparision_op(op) elif isinstance(left.typ, BaseType) and isinstance( right.typ, BaseType): if op not in ("eq", "ne"): return else: # kludge to block behavior in #2638 # TODO actually implement equality for complex types raise TypeMismatch( f"operation not yet supported for {left.typ}, {right.typ}, see issue #2638", self.expr.op, ) return IRnode.from_list([op, left, right], typ="bool")