usse/funda-scraper/venv/lib/python3.10/site-packages/mypyc/test/test_ircheck.py

217 lines
6.8 KiB
Python
Raw Normal View History

2023-02-20 22:38:24 +00:00
import unittest
from typing import List, Optional
from mypyc.analysis.ircheck import check_func_ir, FnError, can_coerce_to
from mypyc.ir.class_ir import ClassIR
from mypyc.ir.rtypes import (
none_rprimitive, str_rprimitive, int32_rprimitive, int64_rprimitive,
RType, RUnion, bytes_rprimitive, RInstance, object_rprimitive
)
from mypyc.ir.ops import (
BasicBlock, Op, Return, Integer, Goto, Register, LoadLiteral, Assign
)
from mypyc.ir.func_ir import FuncIR, FuncDecl, FuncSignature
from mypyc.ir.pprint import format_func
def assert_has_error(fn: FuncIR, error: FnError) -> None:
errors = check_func_ir(fn)
assert errors == [error]
def assert_no_errors(fn: FuncIR) -> None:
assert not check_func_ir(fn)
NONE_VALUE = Integer(0, rtype=none_rprimitive)
class TestIrcheck(unittest.TestCase):
def setUp(self) -> None:
self.label = 0
def basic_block(self, ops: List[Op]) -> BasicBlock:
self.label += 1
block = BasicBlock(self.label)
block.ops = ops
return block
def func_decl(self, name: str, ret_type: Optional[RType] = None) -> FuncDecl:
if ret_type is None:
ret_type = none_rprimitive
return FuncDecl(
name=name,
class_name=None,
module_name="module",
sig=FuncSignature(
args=[],
ret_type=ret_type,
),
)
def test_valid_fn(self) -> None:
assert_no_errors(
FuncIR(
decl=self.func_decl(name="func_1"),
arg_regs=[],
blocks=[
self.basic_block(
ops=[
Return(value=NONE_VALUE),
]
)
],
)
)
def test_block_not_terminated_empty_block(self) -> None:
block = self.basic_block([])
fn = FuncIR(
decl=self.func_decl(name="func_1"),
arg_regs=[],
blocks=[block],
)
assert_has_error(fn, FnError(source=block, desc="Block not terminated"))
def test_valid_goto(self) -> None:
block_1 = self.basic_block([Return(value=NONE_VALUE)])
block_2 = self.basic_block([Goto(label=block_1)])
fn = FuncIR(
decl=self.func_decl(name="func_1"),
arg_regs=[],
blocks=[block_1, block_2],
)
assert_no_errors(fn)
def test_invalid_goto(self) -> None:
block_1 = self.basic_block([Return(value=NONE_VALUE)])
goto = Goto(label=block_1)
block_2 = self.basic_block([goto])
fn = FuncIR(
decl=self.func_decl(name="func_1"),
arg_regs=[],
# block_1 omitted
blocks=[block_2],
)
assert_has_error(
fn, FnError(source=goto, desc="Invalid control operation target: 1")
)
def test_invalid_register_source(self) -> None:
ret = Return(
value=Register(
type=none_rprimitive,
name="r1",
)
)
block = self.basic_block([ret])
fn = FuncIR(
decl=self.func_decl(name="func_1"),
arg_regs=[],
blocks=[block],
)
assert_has_error(
fn, FnError(source=ret, desc="Invalid op reference to register r1")
)
def test_invalid_op_source(self) -> None:
ret = Return(
value=LoadLiteral(
value="foo",
rtype=str_rprimitive,
)
)
block = self.basic_block([ret])
fn = FuncIR(
decl=self.func_decl(name="func_1"),
arg_regs=[],
blocks=[block],
)
assert_has_error(
fn,
FnError(source=ret, desc="Invalid op reference to op of type LoadLiteral"),
)
def test_invalid_return_type(self) -> None:
ret = Return(value=Integer(value=5, rtype=int32_rprimitive))
fn = FuncIR(
decl=self.func_decl(name="func_1", ret_type=int64_rprimitive),
arg_regs=[],
blocks=[self.basic_block([ret])],
)
assert_has_error(
fn,
FnError(
source=ret, desc="Cannot coerce source type int32 to dest type int64"
),
)
def test_invalid_assign(self) -> None:
arg_reg = Register(type=int64_rprimitive, name="r1")
assign = Assign(dest=arg_reg, src=Integer(value=5, rtype=int32_rprimitive))
ret = Return(value=NONE_VALUE)
fn = FuncIR(
decl=self.func_decl(name="func_1"),
arg_regs=[arg_reg],
blocks=[self.basic_block([assign, ret])],
)
assert_has_error(
fn,
FnError(
source=assign, desc="Cannot coerce source type int32 to dest type int64"
),
)
def test_can_coerce_to(self) -> None:
cls = ClassIR(name="Cls", module_name="cls")
valid_cases = [
(int64_rprimitive, int64_rprimitive),
(str_rprimitive, str_rprimitive),
(str_rprimitive, object_rprimitive),
(object_rprimitive, str_rprimitive),
(RUnion([bytes_rprimitive, str_rprimitive]), str_rprimitive),
(str_rprimitive, RUnion([bytes_rprimitive, str_rprimitive])),
(RInstance(cls), object_rprimitive),
]
invalid_cases = [
(int64_rprimitive, int32_rprimitive),
(RInstance(cls), str_rprimitive),
(str_rprimitive, bytes_rprimitive),
]
for src, dest in valid_cases:
assert can_coerce_to(src, dest)
for src, dest in invalid_cases:
assert not can_coerce_to(src, dest)
def test_duplicate_op(self) -> None:
arg_reg = Register(type=int32_rprimitive, name="r1")
assign = Assign(dest=arg_reg, src=Integer(value=5, rtype=int32_rprimitive))
block = self.basic_block([assign, assign, Return(value=NONE_VALUE)])
fn = FuncIR(
decl=self.func_decl(name="func_1"),
arg_regs=[],
blocks=[block],
)
assert_has_error(fn, FnError(source=assign, desc="Func has a duplicate op"))
def test_pprint(self) -> None:
block_1 = self.basic_block([Return(value=NONE_VALUE)])
goto = Goto(label=block_1)
block_2 = self.basic_block([goto])
fn = FuncIR(
decl=self.func_decl(name="func_1"),
arg_regs=[],
# block_1 omitted
blocks=[block_2],
)
errors = [(goto, "Invalid control operation target: 1")]
formatted = format_func(fn, errors)
assert formatted == [
"def func_1():",
"L0:",
" goto L1",
" ERR: Invalid control operation target: 1",
]