Skip to content

Commit

Permalink
pythongh-104504: cases generator: Add --warn-unreachable to the myp…
Browse files Browse the repository at this point in the history
…y config (python#108112)
  • Loading branch information
AlexWaygood authored Aug 20, 2023
1 parent 6323bc3 commit 05ef4ca
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 28 deletions.
15 changes: 15 additions & 0 deletions Tools/cases_generator/_typing_backports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""Backports from newer versions of the typing module.
We backport these features here so that Python can still build
while using an older Python version for PYTHON_FOR_REGEN.
"""

from typing import NoReturn


def assert_never(obj: NoReturn) -> NoReturn:
"""Statically assert that a line of code is unreachable.
Backport of typing.assert_never (introduced in Python 3.11).
"""
raise AssertionError(f"Expected code to be unreachable, but got: {obj}")
7 changes: 4 additions & 3 deletions Tools/cases_generator/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import sys
import typing

from _typing_backports import assert_never
from flags import InstructionFlags, variable_used
from formatting import prettify_filename, UNUSED
from instructions import (
Expand Down Expand Up @@ -172,7 +173,7 @@ def parse_file(self, filename: str, instrs_idx: dict[str, int]) -> None:
self.pseudos[name] = thing
self.everything.append(thing)
case _:
typing.assert_never(thing)
assert_never(thing)
if not psr.eof():
raise psr.make_syntax_error(f"Extra stuff at the end of {filename}")

Expand Down Expand Up @@ -368,7 +369,7 @@ def analyze_macro(self, macro: parsing.Macro) -> MacroInstruction:
# SAVE_IP in a macro is a no-op in Tier 1
flags.add(instr.instr_flags)
case _:
typing.assert_never(component)
assert_never(component)
format = "IB" if flags.HAS_ARG_FLAG else "IX"
if offset:
format += "C" + "0" * (offset - 1)
Expand Down Expand Up @@ -409,5 +410,5 @@ def check_macro_components(
case parsing.CacheEffect():
components.append(uop)
case _:
typing.assert_never(uop)
assert_never(uop)
return components
33 changes: 13 additions & 20 deletions Tools/cases_generator/generate_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from collections.abc import Iterator

import stacking # Early import to avoid circular import
from _typing_backports import assert_never
from analysis import Analyzer
from formatting import Formatter, list_effect_size
from flags import InstructionFlags, variable_used
Expand Down Expand Up @@ -146,16 +147,14 @@
class Generator(Analyzer):
def get_stack_effect_info(
self, thing: parsing.InstDef | parsing.Macro | parsing.Pseudo
) -> tuple[AnyInstruction | None, str | None, str | None]:
) -> tuple[AnyInstruction | None, str, str]:
def effect_str(effects: list[StackEffect]) -> str:
n_effect, sym_effect = list_effect_size(effects)
if sym_effect:
return f"{sym_effect} + {n_effect}" if n_effect else sym_effect
return str(n_effect)

instr: AnyInstruction | None
popped: str | None
pushed: str | None
match thing:
case parsing.InstDef():
if thing.kind != "op" or self.instrs[thing.name].is_viable_uop():
Expand All @@ -171,25 +170,23 @@ def effect_str(effects: list[StackEffect]) -> str:
popped, pushed = stacking.get_stack_effect_info_for_macro(instr)
case parsing.Pseudo():
instr = self.pseudo_instrs[thing.name]
popped = pushed = None
# Calculate stack effect, and check that it's the the same
# for all targets.
for target in self.pseudos[thing.name].targets:
for idx, target in enumerate(self.pseudos[thing.name].targets):
target_instr = self.instrs.get(target)
# Currently target is always an instr. This could change
# in the future, e.g., if we have a pseudo targetting a
# macro instruction.
assert target_instr
target_popped = effect_str(target_instr.input_effects)
target_pushed = effect_str(target_instr.output_effects)
if pushed is None:
assert popped is None
if idx == 0:
popped, pushed = target_popped, target_pushed
else:
assert popped == target_popped
assert pushed == target_pushed
case _:
typing.assert_never(thing)
assert_never(thing)
return instr, popped, pushed

@contextlib.contextmanager
Expand All @@ -209,7 +206,6 @@ def write_stack_effect_functions(self) -> None:
continue
instr, popped, pushed = self.get_stack_effect_info(thing)
if instr is not None:
assert popped is not None and pushed is not None
popped_data.append((instr, popped))
pushed_data.append((instr, pushed))

Expand Down Expand Up @@ -379,7 +375,6 @@ def write_metadata(self, metadata_filename: str, pymetadata_filename: str) -> No
# Compute the set of all instruction formats.
all_formats: set[str] = set()
for thing in self.everything:
format: str | None
match thing:
case OverriddenInstructionPlaceHolder():
continue
Expand All @@ -388,17 +383,15 @@ def write_metadata(self, metadata_filename: str, pymetadata_filename: str) -> No
case parsing.Macro():
format = self.macro_instrs[thing.name].instr_fmt
case parsing.Pseudo():
format = None
for target in self.pseudos[thing.name].targets:
for idx, target in enumerate(self.pseudos[thing.name].targets):
target_instr = self.instrs.get(target)
assert target_instr
if format is None:
if idx == 0:
format = target_instr.instr_fmt
else:
assert format == target_instr.instr_fmt
assert format is not None
case _:
typing.assert_never(thing)
assert_never(thing)
all_formats.add(format)

# Turn it into a sorted list of enum values.
Expand Down Expand Up @@ -488,7 +481,7 @@ def write_metadata(self, metadata_filename: str, pymetadata_filename: str) -> No
self.pseudo_instrs[thing.name]
)
case _:
typing.assert_never(thing)
assert_never(thing)

with self.metadata_item(
"const struct opcode_macro_expansion "
Expand Down Expand Up @@ -525,7 +518,7 @@ def write_metadata(self, metadata_filename: str, pymetadata_filename: str) -> No
case parsing.Pseudo():
pass
case _:
typing.assert_never(thing)
assert_never(thing)

with self.metadata_item(
"const char * const _PyOpcode_uop_name[OPCODE_UOP_NAME_SIZE]", "=", ";"
Expand Down Expand Up @@ -774,7 +767,7 @@ def write_instructions(
case parsing.Pseudo():
pass
case _:
typing.assert_never(thing)
assert_never(thing)

print(
f"Wrote {n_instrs} instructions and {n_macros} macros "
Expand Down Expand Up @@ -818,7 +811,7 @@ def write_executor_instructions(
case parsing.Pseudo():
pass
case _:
typing.assert_never(thing)
assert_never(thing)
print(
f"Wrote {n_instrs} instructions and {n_uops} ops to {executor_filename}",
file=sys.stderr,
Expand Down Expand Up @@ -850,7 +843,7 @@ def write_abstract_interpreter_instructions(
case parsing.Pseudo():
pass
case _:
typing.assert_never(thing)
assert_never(thing)
print(
f"Wrote some stuff to {abstract_interpreter_filename}",
file=sys.stderr,
Expand Down
9 changes: 4 additions & 5 deletions Tools/cases_generator/mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
files = Tools/cases_generator/
pretty = True

# Make sure Python can still be built
# using Python 3.10 for `PYTHON_FOR_REGEN`...
python_version = 3.10

# Be strict:
# ...And be strict:
strict = True
strict_concatenate = True
enable_error_code = ignore-without-code,redundant-expr,truthy-bool

# Don't enable this one yet;
# it has a lot of false positives on `cases_generator`
warn_unreachable = False
warn_unreachable = True

0 comments on commit 05ef4ca

Please sign in to comment.