Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Only trigger new assertions in tests
  • Loading branch information
p-sawicki committed Jan 26, 2026
commit f4d483cf52bc0ac7a766bf281c4d1b0a1a58d4a4
6 changes: 5 additions & 1 deletion mypyc/codegen/emit.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class EmitterContext:
def __init__(
self,
names: NameGenerator,
strict_traceback_checks: bool,
group_name: str | None = None,
group_map: dict[str, str | None] | None = None,
) -> None:
Expand Down Expand Up @@ -130,6 +131,8 @@ def __init__(
self.declarations: dict[str, HeaderDeclaration] = {}

self.literals = Literals()
# See mypyc/options.py for context.
self.strict_traceback_checks = strict_traceback_checks


class ErrorHandler:
Expand Down Expand Up @@ -1201,7 +1204,8 @@ def _emit_traceback(
type_str: str = "",
src: str = "",
) -> None:
assert traceback_entry[1] >= 0, "Traceback cannot have a negative line number"
if self.context.strict_traceback_checks:
assert traceback_entry[1] >= 0, "Traceback cannot have a negative line number"
globals_static = self.static_name("globals", module_name)
line = '%s("%s", "%s", %d, %s' % (
func,
Expand Down
7 changes: 4 additions & 3 deletions mypyc/codegen/emitfunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -921,9 +921,10 @@ def emit_traceback(self, op: Branch) -> None:

def emit_attribute_error(self, op: Branch, class_name: str, attr: str) -> None:
assert op.traceback_entry is not None
assert (
op.traceback_entry[1] >= 0
), "AttributeError traceback cannot have a negative line number"
if self.emitter.context.strict_traceback_checks:
assert (
op.traceback_entry[1] >= 0
), "AttributeError traceback cannot have a negative line number"
globals_static = self.emitter.static_name("globals", self.module_name)
self.emit_line(
'CPy_AttributeError("%s", "%s", "%s", "%s", %d, %s);'
Expand Down
6 changes: 3 additions & 3 deletions mypyc/codegen/emitmodule.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,9 +249,9 @@ def compile_scc_to_ir(
for module in modules.values():
for fn in module.functions:
# Insert checks for uninitialized values.
insert_uninit_checks(fn)
insert_uninit_checks(fn, compiler_options.strict_traceback_checks)
# Insert exception handling.
insert_exception_handling(fn)
insert_exception_handling(fn, compiler_options.strict_traceback_checks)
# Insert reference count handling.
insert_ref_count_opcodes(fn)

Expand Down Expand Up @@ -535,7 +535,7 @@ def __init__(
"""
self.modules = modules
self.source_paths = source_paths
self.context = EmitterContext(names, group_name, group_map)
self.context = EmitterContext(names, compiler_options.strict_traceback_checks, group_name, group_map)
self.names = names
# Initializations of globals to simple values that we can't
# do statically because the windows loader is bad.
Expand Down
1 change: 0 additions & 1 deletion mypyc/ir/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -1216,7 +1216,6 @@ class RaiseStandardError(RegisterOp):

def __init__(self, class_name: str, value: str | Value | None, line: int) -> None:
super().__init__(line)
assert line >= 0, "RaiseStandardError cannot have a negative line number"
self.class_name = class_name
self.value = value
self.type = bool_rprimitive
Expand Down
13 changes: 13 additions & 0 deletions mypyc/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def __init__(
log_trace: bool = False,
depends_on_librt_internal: bool = False,
experimental_features: bool = False,
strict_traceback_checks: bool = False,
) -> None:
self.strip_asserts = strip_asserts
self.multi_file = multi_file
Expand Down Expand Up @@ -60,3 +61,15 @@ def __init__(
# experimental mode (e.g. use _experimental suffix in librt run test).
# These can't be used with a librt wheel installed from PyPI.
self.experimental_features = experimental_features
# If enabled, mypyc will assert that every traceback it generates has a
# positive line number.
# Currently each AST node is assigned line number -1 by default to indicate
# that it's unset. If the line number is never set and a traceback is
# generated that points at such node, then the line number will be interpreted
# as None instead of an integer by Python and potentially crash code that
# expects an integer, such as pytest.
# The goal is to prevent the incorrect tracebacks but it will require a lot
# of changes across mypyc. In the meantime, this option should be enabled in
# tests to make sure that no new code which leads to incorrect tracebacks is
# added.
self.strict_traceback_checks = strict_traceback_checks
2 changes: 1 addition & 1 deletion mypyc/test/test_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
for fn in ir:
if fn.name == TOP_LEVEL_NAME and not testcase.name.endswith("_toplevel"):
continue
exceptions.insert_exception_handling(fn)
exceptions.insert_exception_handling(fn, True)
actual.extend(format_func(fn))
cfg = dataflow.get_cfg(fn.blocks)
args: set[Value] = set(fn.arg_regs)
Expand Down
2 changes: 1 addition & 1 deletion mypyc/test/test_emit.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
class TestEmitter(unittest.TestCase):
def setUp(self) -> None:
self.n = Register(int_rprimitive, "n")
self.context = EmitterContext(NameGenerator([["mod"]]))
self.context = EmitterContext(NameGenerator([["mod"]]), True)
self.emitter = Emitter(self.context, {})

ir = ClassIR("A", "mod")
Expand Down
6 changes: 3 additions & 3 deletions mypyc/test/test_emitfunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def add_local(name: str, rtype: RType) -> Register:
)
self.st = add_local("st", self.struct_type)

self.context = EmitterContext(NameGenerator([["mod"]]))
self.context = EmitterContext(NameGenerator([["mod"]]), True)

def test_goto(self) -> None:
self.assert_emit(Goto(BasicBlock(2)), "goto CPyL2;")
Expand Down Expand Up @@ -974,7 +974,7 @@ def test_simple(self) -> None:
[self.block],
)
value_names = generate_names_for_ir(fn.arg_regs, fn.blocks)
emitter = Emitter(EmitterContext(NameGenerator([["mod"]])), value_names)
emitter = Emitter(EmitterContext(NameGenerator([["mod"]]), True), value_names)
generate_native_function(fn, emitter, "prog.py", "prog")
result = emitter.fragments
assert_string_arrays_equal(
Expand All @@ -994,7 +994,7 @@ def test_register(self) -> None:
[self.block],
)
value_names = generate_names_for_ir(fn.arg_regs, fn.blocks)
emitter = Emitter(EmitterContext(NameGenerator([["mod"]])), value_names)
emitter = Emitter(EmitterContext(NameGenerator([["mod"]]), True), value_names)
generate_native_function(fn, emitter, "prog.py", "prog")
result = emitter.fragments
assert_string_arrays_equal(
Expand Down
2 changes: 1 addition & 1 deletion mypyc/test/test_emitwrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

class TestArgCheck(unittest.TestCase):
def setUp(self) -> None:
self.context = EmitterContext(NameGenerator([["mod"]]))
self.context = EmitterContext(NameGenerator([["mod"]]), True)

def test_check_list(self) -> None:
emitter = Emitter(self.context)
Expand Down
4 changes: 2 additions & 2 deletions mypyc/test/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
for fn in ir:
if fn.name == TOP_LEVEL_NAME and not testcase.name.endswith("_toplevel"):
continue
insert_uninit_checks(fn)
insert_exception_handling(fn)
insert_uninit_checks(fn, True)
insert_exception_handling(fn, True)
insert_ref_count_opcodes(fn)
actual.extend(format_func(fn))
if testcase.name.endswith("_freq"):
Expand Down
6 changes: 3 additions & 3 deletions mypyc/test/test_lowering.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
for fn in ir:
if fn.name == TOP_LEVEL_NAME and not testcase.name.endswith("_toplevel"):
continue
options = CompilerOptions()
options = CompilerOptions(strict_traceback_checks=True)
# Lowering happens after exception handling and ref count opcodes have
# been added. Any changes must maintain reference counting semantics.
insert_uninit_checks(fn)
insert_exception_handling(fn)
insert_uninit_checks(fn, True)
insert_exception_handling(fn, True)
insert_ref_count_opcodes(fn)
lower_ir(fn, options)
do_flag_elimination(fn, options)
Expand Down
2 changes: 1 addition & 1 deletion mypyc/test/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
class TestMisc(unittest.TestCase):
def test_debug_op(self) -> None:
block = BasicBlock()
builder = LowLevelIRBuilder(errors=None, options=CompilerOptions())
builder = LowLevelIRBuilder(errors=None, options=CompilerOptions(strict_traceback_checks=True))
builder.activate_block(block)
builder.debug_print("foo")

Expand Down
6 changes: 3 additions & 3 deletions mypyc/test/test_optimizations.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
for fn in ir:
if fn.name == TOP_LEVEL_NAME and not testcase.name.endswith("_toplevel"):
continue
insert_uninit_checks(fn)
insert_uninit_checks(fn, True)
self.do_optimizations(fn)
actual.extend(format_func(fn))

Expand All @@ -58,11 +58,11 @@ class TestCopyPropagation(OptimizationSuite):
files = ["opt-copy-propagation.test"]

def do_optimizations(self, fn: FuncIR) -> None:
do_copy_propagation(fn, CompilerOptions())
do_copy_propagation(fn, CompilerOptions(strict_traceback_checks=True))


class TestFlagElimination(OptimizationSuite):
files = ["opt-flag-elimination.test"]

def do_optimizations(self, fn: FuncIR) -> None:
do_flag_elimination(fn, CompilerOptions())
do_flag_elimination(fn, CompilerOptions(strict_traceback_checks=True))
2 changes: 1 addition & 1 deletion mypyc/test/test_refcount.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
for fn in ir:
if fn.name == TOP_LEVEL_NAME and not testcase.name.endswith("_toplevel"):
continue
insert_uninit_checks(fn)
insert_uninit_checks(fn, True)
insert_ref_count_opcodes(fn)
actual.extend(format_func(fn))

Expand Down
1 change: 1 addition & 0 deletions mypyc/test/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) ->
strict_dunder_typing=self.strict_dunder_typing,
depends_on_librt_internal=librt_internal,
experimental_features=experimental_features,
strict_traceback_checks=True,
)
result = emitmodule.parse_and_typecheck(
sources=sources,
Expand Down
2 changes: 1 addition & 1 deletion mypyc/test/testutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ def infer_ir_build_options_from_test_name(name: str) -> CompilerOptions | None:
return None
if "_32bit" in name and not IS_32_BIT_PLATFORM:
return None
options = CompilerOptions(strip_asserts="StripAssert" in name, capi_version=(3, 9))
options = CompilerOptions(strip_asserts="StripAssert" in name, capi_version=(3, 9), strict_traceback_checks=True)
# A suffix like _python3_9 is used to set the target C API version.
m = re.search(r"_python([3-9]+)_([0-9]+)(_|\b)", name)
if m:
Expand Down
13 changes: 7 additions & 6 deletions mypyc/transform/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
from mypyc.primitives.registry import CFunctionDescription


def insert_exception_handling(ir: FuncIR) -> None:
def insert_exception_handling(ir: FuncIR, strict_traceback_checks: bool) -> None:
# Generate error block if any ops may raise an exception. If an op
# fails without its own error handler, we'll branch to this
# block. The block just returns an error value.
Expand All @@ -49,7 +49,7 @@ def insert_exception_handling(ir: FuncIR) -> None:
if error_label is None and any(op.can_raise() for op in block.ops):
error_label = add_default_handler_block(ir)
if error_label:
ir.blocks = split_blocks_at_errors(ir.blocks, error_label, ir.traceback_name)
ir.blocks = split_blocks_at_errors(ir.blocks, error_label, ir.traceback_name, strict_traceback_checks)


def add_default_handler_block(ir: FuncIR) -> BasicBlock:
Expand All @@ -62,7 +62,7 @@ def add_default_handler_block(ir: FuncIR) -> BasicBlock:


def split_blocks_at_errors(
blocks: list[BasicBlock], default_error_handler: BasicBlock, func_name: str | None
blocks: list[BasicBlock], default_error_handler: BasicBlock, func_name: str | None, strict_traceback_checks: bool,
) -> list[BasicBlock]:
new_blocks: list[BasicBlock] = []

Expand Down Expand Up @@ -130,9 +130,10 @@ def split_blocks_at_errors(
)
branch.negated = negated
if op.line != NO_TRACEBACK_LINE_NO and func_name is not None:
assert (
op.line >= 0
), f"Cannot add a traceback entry with a negative line number for op {op}"
if strict_traceback_checks:
assert (
op.line >= 0
), f"Cannot add a traceback entry with a negative line number for op {op}"
branch.traceback_entry = (func_name, op.line)
cur_block.ops.append(branch)
cur_block = new_block
Expand Down
13 changes: 7 additions & 6 deletions mypyc/transform/uninit.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from mypyc.ir.rtypes import bitmap_rprimitive


def insert_uninit_checks(ir: FuncIR) -> None:
def insert_uninit_checks(ir: FuncIR, strict_traceback_checks: bool) -> None:
# Remove dead blocks from the CFG, which helps avoid spurious
# checks due to unused error handling blocks.
cleanup_cfg(ir.blocks)
Expand All @@ -33,11 +33,11 @@ def insert_uninit_checks(ir: FuncIR) -> None:
ir.blocks, cfg, set(ir.arg_regs), all_values(ir.arg_regs, ir.blocks)
)

ir.blocks = split_blocks_at_uninits(ir.blocks, must_defined.before)
ir.blocks = split_blocks_at_uninits(ir.blocks, must_defined.before, strict_traceback_checks)


def split_blocks_at_uninits(
blocks: list[BasicBlock], pre_must_defined: AnalysisDict[Value]
blocks: list[BasicBlock], pre_must_defined: AnalysisDict[Value], strict_traceback_checks: bool,
) -> list[BasicBlock]:
new_blocks: list[BasicBlock] = []

Expand Down Expand Up @@ -104,9 +104,10 @@ def split_blocks_at_uninits(
op.line,
)

assert (
op.line >= 0
), f"Cannot raise an error with a negative line number for op {op}"
if strict_traceback_checks:
assert (
op.line >= 0
), f"Cannot raise an error with a negative line number for op {op}"
raise_std = RaiseStandardError(
RaiseStandardError.UNBOUND_LOCAL_ERROR,
f'local variable "{src.name}" referenced before assignment',
Expand Down