def _assert_reason(self, test_expr, msg): if isinstance(msg, vy_ast.Name) and msg.id == "UNREACHABLE": return IRnode.from_list(["assert_unreachable", test_expr], error_msg="assert unreachable") # set constant so that revert reason str is well behaved try: tmp = self.context.constancy self.context.constancy = Constancy.Constant msg_ir = Expr(msg, self.context).ir_node finally: self.context.constancy = tmp # TODO this is probably useful in codegen.core # compare with eval_seq. def _get_last(ir): if len(ir.args) == 0: return ir.value return _get_last(ir.args[-1]) # TODO maybe use ensure_in_memory if msg_ir.location != MEMORY: buf = self.context.new_internal_variable(msg_ir.typ) instantiate_msg = make_byte_array_copier(buf, msg_ir) else: buf = _get_last(msg_ir) if not isinstance(buf, int): raise CompilerPanic(f"invalid bytestring {buf}\n{self}") instantiate_msg = msg_ir # offset of bytes in (bytes,) method_id = util.abi_method_id("Error(string)") # abi encode method_id + bytestring assert buf >= 36, "invalid buffer" # we don't mind overwriting other memory because we are # getting out of here anyway. _runtime_length = ["mload", buf] revert_seq = [ "seq", instantiate_msg, zero_pad(buf), ["mstore", buf - 64, method_id], ["mstore", buf - 32, 0x20], [ "revert", buf - 36, ["add", 4 + 32 + 32, ["ceil32", _runtime_length]] ], ] if test_expr is not None: ir_node = ["if", ["iszero", test_expr], revert_seq] else: ir_node = revert_seq return IRnode.from_list(ir_node, error_msg="user revert with reason")
def _parse_For_range(self): # attempt to use the type specified by type checking, fall back to `int256` # this is a stopgap solution to allow uint256 - it will be properly solved # once we refactor type system iter_typ = "int256" if "type" in self.stmt.target._metadata: iter_typ = self.stmt.target._metadata["type"]._id # Get arg0 arg0 = self.stmt.iter.args[0] num_of_args = len(self.stmt.iter.args) # Type 1 for, e.g. for i in range(10): ... if num_of_args == 1: arg0_val = self._get_range_const_value(arg0) start = IRnode.from_list(0, typ=iter_typ) rounds = arg0_val # Type 2 for, e.g. for i in range(100, 110): ... elif self._check_valid_range_constant(self.stmt.iter.args[1], raise_exception=False)[0]: arg0_val = self._get_range_const_value(arg0) arg1_val = self._get_range_const_value(self.stmt.iter.args[1]) start = IRnode.from_list(arg0_val, typ=iter_typ) rounds = IRnode.from_list(arg1_val - arg0_val, typ=iter_typ) # Type 3 for, e.g. for i in range(x, x + 10): ... else: arg1 = self.stmt.iter.args[1] rounds = self._get_range_const_value(arg1.right) start = Expr.parse_value_expr(arg0, self.context) r = rounds if isinstance(rounds, int) else rounds.value if r < 1: return varname = self.stmt.target.id i = IRnode.from_list(self.context.fresh_varname("range_ix"), typ="uint256") iptr = self.context.new_variable(varname, BaseType(iter_typ)) self.context.forvars[varname] = True loop_body = ["seq"] # store the current value of i so it is accessible to userland loop_body.append(["mstore", iptr, i]) loop_body.append(parse_body(self.stmt.body, self.context)) ir_node = IRnode.from_list( ["repeat", i, start, rounds, rounds, loop_body]) del self.context.forvars[varname] return ir_node
def _parse_For_list(self): with self.context.range_scope(): iter_list = Expr(self.stmt.iter, self.context).ir_node # override with type inferred at typechecking time # TODO investigate why stmt.target.type != stmt.iter.type.subtype target_type = new_type_to_old_type(self.stmt.target._metadata["type"]) iter_list.typ.subtype = target_type # user-supplied name for loop variable varname = self.stmt.target.id loop_var = IRnode.from_list(self.context.new_variable( varname, target_type), typ=target_type, location=MEMORY) i = IRnode.from_list(self.context.fresh_varname("for_list_ix"), typ="uint256") self.context.forvars[varname] = True ret = ["seq"] # list literal, force it to memory first if isinstance(self.stmt.iter, vy_ast.List): tmp_list = IRnode.from_list( self.context.new_internal_variable(iter_list.typ), typ=iter_list.typ, location=MEMORY, ) ret.append(make_setter(tmp_list, iter_list)) iter_list = tmp_list # set up the loop variable e = get_element_ptr(iter_list, i, array_bounds_check=False) body = [ "seq", make_setter(loop_var, e), parse_body(self.stmt.body, self.context), ] repeat_bound = iter_list.typ.count if isinstance(iter_list.typ, DArrayType): array_len = get_dyn_array_count(iter_list) else: array_len = repeat_bound ret.append(["repeat", i, 0, array_len, repeat_bound, body]) del self.context.forvars[varname] return IRnode.from_list(ret)
def parse_Assert(self): test_expr = Expr.parse_value_expr(self.stmt.test, self.context) if test_expr.typ.is_literal: if test_expr.value == 1: # skip literal assertions that always pass # TODO move this to optimizer return IRnode.from_list(["pass"]) else: test_expr = test_expr.value if self.stmt.msg: return self._assert_reason(test_expr, self.stmt.msg) else: return IRnode.from_list(["assert", test_expr])
def convert(expr, context): if len(expr.args) != 2: raise StructureException( "The convert function expects two parameters.", expr) arg_ast = expr.args[0] arg = Expr(arg_ast, context).ir_node out_typ = context.parse_type(expr.args[1]) if isinstance(arg.typ, BaseType): arg = unwrap_location(arg) with arg.cache_when_complex("arg") as (b, arg): if is_base_type(out_typ, "bool"): ret = to_bool(arg_ast, arg, out_typ) elif is_base_type(out_typ, "address"): ret = to_address(arg_ast, arg, out_typ) elif is_integer_type(out_typ): ret = to_int(arg_ast, arg, out_typ) elif is_bytes_m_type(out_typ): ret = to_bytes_m(arg_ast, arg, out_typ) elif is_decimal_type(out_typ): ret = to_decimal(arg_ast, arg, out_typ) elif isinstance(out_typ, ByteArrayType): ret = to_bytes(arg_ast, arg, out_typ) elif isinstance(out_typ, StringType): ret = to_string(arg_ast, arg, out_typ) else: raise StructureException(f"Conversion to {out_typ} is invalid.", arg_ast) ret = b.resolve(ret) return IRnode.from_list(ret)
def to_address(expr, arg, out_typ): # question: should this be allowed? if is_integer_type(arg.typ): if arg.typ._int_info.is_signed: _FAIL(arg.typ, out_typ, expr) # TODO once we introduce uint160, we can just check that arg # is unsigned, and then call to_int(expr, arg, uint160) because # the logic is equivalent. # disallow casting from Bytes[N>20] _check_bytes(expr, arg, out_typ, 32) if isinstance(arg.typ, ByteArrayType): arg_typ = arg.typ arg = _bytes_to_num(arg, out_typ, signed=False) # clamp after shift if arg_typ.maxlen > 20: arg = int_clamp(arg, 160, signed=False) if is_bytes_m_type(arg.typ): info = arg.typ._bytes_info arg = _bytes_to_num(arg, out_typ, signed=False) # clamp after shift if info.m > 20: arg = int_clamp(arg, 160, signed=False) elif is_integer_type(arg.typ): arg_info = arg.typ._int_info if arg_info.bits > 160 or arg_info.is_signed: arg = int_clamp(arg, 160, signed=False) return IRnode.from_list(arg, typ=out_typ)
def parse_AnnAssign(self): typ = parse_type( self.stmt.annotation, sigs=self.context.sigs, custom_structs=self.context.structs, ) varname = self.stmt.target.id pos = self.context.new_variable(varname, typ) if self.stmt.value is None: return sub = Expr(self.stmt.value, self.context).ir_node is_literal_bytes32_assign = (isinstance(sub.typ, ByteArrayType) and sub.typ.maxlen == 32 and isinstance(typ, BaseType) and typ.typ == "bytes32" and sub.typ.is_literal) # If bytes[32] to bytes32 assignment rewrite sub as bytes32. if is_literal_bytes32_assign: sub = IRnode( util.bytes_to_int(self.stmt.value.s), typ=BaseType("bytes32"), ) variable_loc = IRnode.from_list(pos, typ=typ, location=MEMORY) ir_node = make_setter(variable_loc, sub) return ir_node
def to_bytes(expr, arg, out_typ): _check_bytes(expr, arg, out_typ, out_typ.maxlen) # TODO: more casts # NOTE: this is a pointer cast return IRnode.from_list(arg, typ=out_typ)
def to_int(expr, arg, out_typ): int_info = out_typ._int_info assert int_info.bits % 8 == 0 _check_bytes(expr, arg, out_typ, 32) if isinstance(expr, vy_ast.Constant): return _literal_int(expr, arg.typ, out_typ) elif isinstance(arg.typ, ByteArrayType): arg_typ = arg.typ arg = _bytes_to_num(arg, out_typ, signed=int_info.is_signed) if arg_typ.maxlen * 8 > int_info.bits: arg = int_clamp(arg, int_info.bits, signed=int_info.is_signed) elif is_bytes_m_type(arg.typ): arg_info = arg.typ._bytes_info arg = _bytes_to_num(arg, out_typ, signed=int_info.is_signed) if arg_info.m_bits > int_info.bits: arg = int_clamp(arg, int_info.bits, signed=int_info.is_signed) elif is_decimal_type(arg.typ): arg = _fixed_to_int(arg, out_typ) elif is_integer_type(arg.typ): arg = _int_to_int(arg, out_typ) elif is_base_type(arg.typ, "address"): if int_info.is_signed: # TODO if possible, refactor to move this validation close to the entry of the function _FAIL(arg.typ, out_typ, expr) if int_info.bits < 160: arg = int_clamp(arg, int_info.bits, signed=False) return IRnode.from_list(arg, typ=out_typ)
def parse_Assert(self): test_expr = Expr.parse_value_expr(self.stmt.test, self.context) if self.stmt.msg: return self._assert_reason(test_expr, self.stmt.msg) else: return IRnode.from_list(["assert", test_expr])
def to_enum(expr, arg, out_typ): if not is_base_type(arg.typ, "uint256"): _FAIL(arg.typ, out_typ, expr) if len(out_typ.members) < 256: arg = int_clamp(arg, bits=len(out_typ.members), signed=False) return IRnode.from_list(arg, typ=out_typ)
def to_bytes_m(expr, arg, out_typ): out_info = out_typ._bytes_info _check_bytes(expr, arg, out_typ, max_bytes_allowed=out_info.m) if isinstance(arg.typ, ByteArrayType): bytes_val = LOAD(bytes_data_ptr(arg)) # zero out any dirty bytes (which can happen in the last # word of a bytearray) len_ = get_bytearray_length(arg) num_zero_bits = IRnode.from_list(["mul", ["sub", 32, len_], 8]) with num_zero_bits.cache_when_complex("bits") as (b, num_zero_bits): arg = shl(num_zero_bits, shr(num_zero_bits, bytes_val)) arg = b.resolve(arg) elif is_bytes_m_type(arg.typ): arg_info = arg.typ._bytes_info # clamp if it's a downcast if arg_info.m > out_info.m: arg = bytes_clamp(arg, out_info.m) elif is_integer_type(arg.typ) or is_base_type(arg.typ, "address"): int_bits = arg.typ._int_info.bits if out_info.m_bits < int_bits: # question: allow with runtime clamp? # arg = int_clamp(m_bits, signed=int_info.signed) _FAIL(arg.typ, out_typ, expr) # note: neg numbers not OOB. keep sign bit arg = shl(256 - out_info.m_bits, arg) elif is_decimal_type(arg.typ): if out_info.m_bits < arg.typ._decimal_info.bits: _FAIL(arg.typ, out_typ, expr) # note: neg numbers not OOB. keep sign bit arg = shl(256 - out_info.m_bits, arg) else: # bool arg = shl(256 - out_info.m_bits, arg) return IRnode.from_list(arg, typ=out_typ)
def convert(expr, context): if len(expr.args) != 2: raise StructureException( "The convert function expects two parameters.", expr) arg_ast = expr.args[0] arg = Expr(arg_ast, context).ir_node original_arg = arg out_typ = context.parse_type(expr.args[1]) if isinstance(arg.typ, BaseType): arg = unwrap_location(arg) with arg.cache_when_complex("arg") as (b, arg): if is_base_type(out_typ, "bool"): ret = to_bool(arg_ast, arg, out_typ) elif is_base_type(out_typ, "address"): ret = to_address(arg_ast, arg, out_typ) elif isinstance(out_typ, EnumType): ret = to_enum(arg_ast, arg, out_typ) elif is_integer_type(out_typ): ret = to_int(arg_ast, arg, out_typ) elif is_bytes_m_type(out_typ): ret = to_bytes_m(arg_ast, arg, out_typ) elif is_decimal_type(out_typ): ret = to_decimal(arg_ast, arg, out_typ) elif isinstance(out_typ, ByteArrayType): ret = to_bytes(arg_ast, arg, out_typ) elif isinstance(out_typ, StringType): ret = to_string(arg_ast, arg, out_typ) else: raise StructureException(f"Conversion to {out_typ} is invalid.", arg_ast) # test if arg actually changed. if not, we do not need to use # unwrap_location (this can reduce memory traffic for downstream # operations which are in-place, like the returndata routine) test_arg = IRnode.from_list(arg, typ=out_typ) if test_arg == ret: original_arg.typ = out_typ return original_arg return IRnode.from_list(b.resolve(ret))
def _int_to_fixed(x, out_typ): info = out_typ._decimal_info lo, hi = info.bounds decimals = info.decimals clamp_op = "clamp" if info.is_signed else "uclamp" # TODO is this clamp redundant with later num clamps? return IRnode.from_list(["mul", [clamp_op, lo, x, hi], 10**decimals], typ=out_typ)
def to_bool(expr, arg, out_typ): _check_bytes(expr, arg, out_typ, 32) # should we restrict to Bytes[1]? if isinstance(arg.typ, ByteArrayType): # no clamp. checks for any nonzero bytes. arg = _bytes_to_num(arg, out_typ, signed=False) # NOTE: for decimal, the behavior is x != 0.0, # (we do not issue an `sdiv DECIMAL_DIVISOR`) return IRnode.from_list(["iszero", ["iszero", arg]], typ=out_typ)
def parse_If(self): if self.stmt.orelse: with self.context.block_scope(): add_on = [parse_body(self.stmt.orelse, self.context)] else: add_on = [] with self.context.block_scope(): test_expr = Expr.parse_value_expr(self.stmt.test, self.context) body = ["if", test_expr, parse_body(self.stmt.body, self.context)] + add_on ir_node = IRnode.from_list(body) return ir_node
def _int_to_fixed(arg, out_typ): arg_info = arg.typ._int_info out_info = out_typ._decimal_info DIVISOR = out_info.divisor # block inputs which are out of bounds before promotion out_lo, out_hi = out_info.bounds out_lo = round_towards_zero(out_lo / decimal.Decimal(DIVISOR)) out_hi = round_towards_zero(out_hi / decimal.Decimal(DIVISOR)) clamped_arg = _clamp_numeric_convert(arg, arg_info.bounds, (out_lo, out_hi), arg_info.is_signed) return IRnode.from_list(["mul", clamped_arg, DIVISOR], typ=out_typ)
def _literal_int(expr, out_typ): # TODO: possible to reuse machinery from expr.py? int_info = out_typ._int_info if isinstance(expr, vy_ast.Hex): val = int(expr.value, 16) elif isinstance(expr, vy_ast.Bytes): val = int.from_bytes(expr.value, "big") else: # Int, Decimal val = int(expr.value) (lo, hi) = int_info.bounds if not (lo <= val <= hi): raise InvalidLiteral("Number out of range", expr) return IRnode.from_list(val, typ=out_typ)
def _literal_decimal(expr, out_typ): # TODO: possible to reuse machinery from expr.py? if isinstance(expr, vy_ast.Hex): val = decimal.Decimal(int(expr.value, 16)) else: val = decimal.Decimal(expr.value) # should work for Int, Decimal val = val * DECIMAL_DIVISOR if not SizeLimits.in_bounds("decimal", val): raise InvalidLiteral("Number out of range", expr) # sanity check type checker did its job assert math.ceil(val) == math.floor(val) return IRnode.from_list(int(val), typ=out_typ)
def _fixed_to_int(arg, out_typ): arg_info = arg.typ._decimal_info out_info = out_typ._int_info DIVISOR = arg_info.divisor # block inputs which are out of bounds before truncation. # e.g., convert(255.1, uint8) should revert or fail to compile. out_lo, out_hi = out_info.bounds out_lo = out_lo * DIVISOR out_hi = out_hi * DIVISOR clamped_arg = _clamp_numeric_convert(arg, arg_info.bounds, (out_lo, out_hi), arg_info.is_signed) assert arg_info.is_signed, "should use unsigned div" # stub in case we ever add ufixed return IRnode.from_list(["sdiv", clamped_arg, DIVISOR], typ=out_typ)
def parse_body(code, context, ensure_terminated=False): if not isinstance(code, list): return parse_stmt(code, context) ir_node = ["seq"] for stmt in code: ir = parse_stmt(stmt, context) ir_node.append(ir) # force using the return routine / exit_to cleanup for end of function if ensure_terminated and context.return_type is None and not _is_terminated( code): ir_node.append(parse_stmt(vy_ast.Return(value=None), context)) # force zerovalent, even last statement ir_node.append("pass") # CMC 2022-01-16 is this necessary? return IRnode.from_list(ir_node)
def _int_to_int(arg, out_typ): arg_info = arg.typ._int_info out_info = out_typ._int_info # do the same thing as # _clamp_numeric_convert(arg, arg_info.bounds, out_info.bounds, arg_info.is_signed) # but with better code size and gas. if arg_info.is_signed and not out_info.is_signed: # e.g. (clample (clampge arg 0) (2**128 - 1)) # note that when out_info.bits == 256, # (clample arg 2**256 - 1) does not make sense. # see similar assertion in _clamp_numeric_convert. if out_info.bits < arg_info.bits: assert out_info.bits < 256, "unreachable" # note: because of the usage of signed=False, and the fact # that out_bits < 256 in this branch, below implies # not only (clample arg 2**128 - 1) but also (clampge arg 0). arg = int_clamp(arg, out_info.bits, signed=False) else: # note: this also works for out_bits == 256. arg = clamp("sge", arg, 0) elif not arg_info.is_signed and out_info.is_signed: # e.g. (uclample (uclampge arg 0) (2**127 - 1)) # (note that (uclampge arg 0) always evaluates to true.) arg = int_clamp(arg, out_info.bits - 1, signed=False) elif out_info.bits < arg_info.bits: assert out_info.bits < 256, "unreachable" # narrowing conversion, signs are the same. # we can just use regular int clampers. arg = int_clamp(arg, out_info.bits, out_info.is_signed) else: # widening conversion, signs are the same. # we do not have to do any clamps. assert arg_info.is_signed == out_info.is_signed and out_info.bits >= arg_info.bits return IRnode.from_list(arg, typ=out_typ)
def parse_AugAssign(self): target = self._get_target(self.stmt.target) sub = Expr.parse_value_expr(self.stmt.value, self.context) if not isinstance(target.typ, BaseType): return with target.cache_when_complex("_loc") as (b, target): rhs = Expr.parse_value_expr( vy_ast.BinOp( left=IRnode.from_list(LOAD(target), typ=target.typ), right=sub, op=self.stmt.op, lineno=self.stmt.lineno, col_offset=self.stmt.col_offset, end_lineno=self.stmt.end_lineno, end_col_offset=self.stmt.end_col_offset, node_source_code=self.stmt.get("node_source_code"), ), self.context, ) return b.resolve(STORE(target, rhs))
def _literal_decimal(expr, arg_typ, out_typ): if isinstance(expr, vy_ast.Hex): val = decimal.Decimal(int(expr.value, 16)) else: val = decimal.Decimal(expr.value) # should work for Int, Decimal val *= DECIMAL_DIVISOR # sanity check type checker did its job assert math.ceil(val) == math.floor(val) val = int(val) # apply sign extension, if expected out_info = out_typ._decimal_info if isinstance(expr, (vy_ast.Hex, vy_ast.Bytes)) and out_info.is_signed: val = _signextend(expr, val, arg_typ) if not SizeLimits.in_bounds("decimal", val): raise InvalidLiteral("Number out of range", expr) return IRnode.from_list(val, typ=out_typ)
def to_decimal(expr, arg, out_typ): # question: is converting from Bytes to decimal allowed? _check_bytes(expr, arg, out_typ, max_bytes_allowed=16) if isinstance(expr, vy_ast.Constant): return _literal_decimal(expr, out_typ) if isinstance(arg.typ, ByteArrayType): arg_typ = arg.typ arg = _bytes_to_num(arg, out_typ, signed=True) # TODO revisit this condition once we have more decimal types # and decimal bounds expand # will be something like: if info.m_bits > 168 if arg_typ.maxlen * 8 > 128: arg = IRnode.from_list(arg, typ=out_typ) arg = clamp_basetype(arg) return IRnode.from_list(arg, typ=out_typ) elif is_bytes_m_type(arg.typ): info = arg.typ._bytes_info arg = _bytes_to_num(arg, out_typ, signed=True) # TODO revisit this condition once we have more decimal types # and decimal bounds expand # will be something like: if info.m_bits > 168 if info.m_bits > 128: arg = IRnode.from_list(arg, typ=out_typ) arg = clamp_basetype(arg) return IRnode.from_list(arg, typ=out_typ) elif is_integer_type(arg.typ): int_info = arg.typ._int_info arg = _int_to_fixed(arg, out_typ) out_info = out_typ._decimal_info if int_info.bits > out_info.bits: # TODO: _num_clamp probably not necessary bc already # clamped in _int_to_fixed arg = _num_clamp(arg, out_info, int_info) return IRnode.from_list(arg, typ=out_typ) elif is_base_type(arg.typ, "bool"): arg = _int_to_fixed(arg, out_typ) return IRnode.from_list(arg, typ=out_typ) else: raise CompilerPanic("unreachable") # pragma: notest
def _literal_int(expr, arg_typ, out_typ): # TODO: possible to reuse machinery from expr.py? int_info = out_typ._int_info if isinstance(expr, vy_ast.Hex): val = int(expr.value, 16) elif isinstance(expr, vy_ast.Bytes): val = int.from_bytes(expr.value, "big") elif isinstance(expr, (vy_ast.Int, vy_ast.Decimal, vy_ast.NameConstant)): val = expr.value else: # pragma: no cover raise CompilerPanic("unreachable") if isinstance(expr, (vy_ast.Hex, vy_ast.Bytes)) and int_info.is_signed: val = _signextend(expr, val, arg_typ) (lo, hi) = int_info.bounds if not (lo <= val <= hi): raise InvalidLiteral("Number out of range", expr) # cast to int AFTER bounds check (ensures decimal is in bounds before truncation) val = int(val) return IRnode.from_list(val, typ=out_typ)
def _bytes_to_num(arg, out_typ, signed): # converting a bytestring to a number: # bytestring and bytes_m are right-padded with zeroes, int is left-padded. # convert by shr or sar the number of zero bytes (converted to bits) # e.g. "abcd000000000000" -> bitcast(000000000000abcd, output_type) if isinstance(arg.typ, ByteArrayLike): _len = get_bytearray_length(arg) arg = LOAD(bytes_data_ptr(arg)) num_zero_bits = ["mul", 8, ["sub", 32, _len]] elif is_bytes_m_type(arg.typ): info = arg.typ._bytes_info num_zero_bits = 8 * (32 - info.m) else: raise CompilerPanic("unreachable") # pragma: notest if signed: ret = sar(num_zero_bits, arg) else: ret = shr(num_zero_bits, arg) annotation = (f"__intrinsic__byte_array_to_num({out_typ})", ) return IRnode.from_list(ret, annotation=annotation)
def to_decimal(expr, arg, out_typ): _check_bytes(expr, arg, out_typ, 32) out_info = out_typ._decimal_info if isinstance(expr, vy_ast.Constant): return _literal_decimal(expr, arg.typ, out_typ) if isinstance(arg.typ, ByteArrayType): arg_typ = arg.typ arg = _bytes_to_num(arg, out_typ, signed=True) if arg_typ.maxlen * 8 > 168: arg = IRnode.from_list(arg, typ=out_typ) arg = clamp_basetype(arg) return IRnode.from_list(arg, typ=out_typ) elif is_bytes_m_type(arg.typ): info = arg.typ._bytes_info arg = _bytes_to_num(arg, out_typ, signed=True) if info.m_bits > 168: arg = IRnode.from_list(arg, typ=out_typ) arg = clamp_basetype(arg) return IRnode.from_list(arg, typ=out_typ) elif is_integer_type(arg.typ): arg = _int_to_fixed(arg, out_typ) return IRnode.from_list(arg, typ=out_typ) elif is_base_type(arg.typ, "bool"): # TODO: consider adding _int_info to bool so we can use _int_to_fixed arg = ["mul", arg, 10**out_info.decimals] return IRnode.from_list(arg, typ=out_typ) else: raise CompilerPanic("unreachable") # pragma: notest
def parse_Pass(self): return IRnode.from_list("pass")
def parse_Break(self): return IRnode.from_list("break")