Major fixes and new features
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
199
venv/lib/python3.12/site-packages/mypyc/test/test_ircheck.py
Normal file
199
venv/lib/python3.12/site-packages/mypyc/test/test_ircheck.py
Normal file
@@ -0,0 +1,199 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from mypyc.analysis.ircheck import FnError, can_coerce_to, check_func_ir
|
||||
from mypyc.ir.class_ir import ClassIR
|
||||
from mypyc.ir.func_ir import FuncDecl, FuncIR, FuncSignature
|
||||
from mypyc.ir.ops import (
|
||||
Assign,
|
||||
BasicBlock,
|
||||
Goto,
|
||||
Integer,
|
||||
LoadAddress,
|
||||
LoadLiteral,
|
||||
Op,
|
||||
Register,
|
||||
Return,
|
||||
)
|
||||
from mypyc.ir.pprint import format_func
|
||||
from mypyc.ir.rtypes import (
|
||||
RInstance,
|
||||
RType,
|
||||
RUnion,
|
||||
bytes_rprimitive,
|
||||
int32_rprimitive,
|
||||
int64_rprimitive,
|
||||
none_rprimitive,
|
||||
object_rprimitive,
|
||||
pointer_rprimitive,
|
||||
str_rprimitive,
|
||||
)
|
||||
|
||||
|
||||
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: RType | None = 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 i32 to dest type i64")
|
||||
)
|
||||
|
||||
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 i32 to dest type i64")
|
||||
)
|
||||
|
||||
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",
|
||||
]
|
||||
|
||||
def test_load_address_declares_register(self) -> None:
|
||||
rx = Register(str_rprimitive, "x")
|
||||
ry = Register(pointer_rprimitive, "y")
|
||||
load_addr = LoadAddress(pointer_rprimitive, rx)
|
||||
assert_no_errors(
|
||||
FuncIR(
|
||||
decl=self.func_decl(name="func_1"),
|
||||
arg_regs=[],
|
||||
blocks=[
|
||||
self.basic_block(
|
||||
ops=[load_addr, Assign(ry, load_addr), Return(value=NONE_VALUE)]
|
||||
)
|
||||
],
|
||||
)
|
||||
)
|
||||
Reference in New Issue
Block a user