Skip to content

Commit 1bf186c

Browse files
authored
[mypyc] Document some of our inheritance conventions (#19370)
Our policy wasn't clear from just reading the code.
1 parent eba2336 commit 1bf186c

File tree

2 files changed

+84
-5
lines changed

2 files changed

+84
-5
lines changed

mypyc/ir/ops.py

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,31 @@
33
Opcodes operate on abstract values (Value) in a register machine. Each
44
value has a type (RType). A value can hold various things, such as:
55
6-
- local variables (Register)
6+
- local variables or temporaries (Register)
77
- intermediate values of expressions (RegisterOp subclasses)
88
- condition flags (true/false)
99
- literals (integer literals, True, False, etc.)
10+
11+
NOTE: As a convention, we don't create subclasses of concrete Value/Op
12+
subclasses (e.g. you shouldn't define a subclass of Integer, which
13+
is a concrete class).
14+
15+
If you want to introduce a variant of an existing class, you'd
16+
typically add an attribute (e.g. a flag) to an existing concrete
17+
class to enable the new behavior. Sometimes adding a new abstract
18+
base class is also an option, or just creating a new subclass
19+
without any inheritance relationship (some duplication of code
20+
is preferred over introducing complex implementation inheritance).
21+
22+
This makes it possible to use isinstance(x, <concrete Value
23+
subclass>) checks without worrying about potential subclasses.
1024
"""
1125

1226
from __future__ import annotations
1327

1428
from abc import abstractmethod
1529
from collections.abc import Sequence
16-
from typing import TYPE_CHECKING, Final, Generic, NamedTuple, TypeVar, Union
30+
from typing import TYPE_CHECKING, Final, Generic, NamedTuple, TypeVar, Union, final
1731

1832
from mypy_extensions import trait
1933

@@ -47,6 +61,7 @@
4761
T = TypeVar("T")
4862

4963

64+
@final
5065
class BasicBlock:
5166
"""IR basic block.
5267
@@ -142,6 +157,7 @@ def is_void(self) -> bool:
142157
return isinstance(self.type, RVoid)
143158

144159

160+
@final
145161
class Register(Value):
146162
"""A Register holds a value of a specific type, and it can be read and mutated.
147163
@@ -168,6 +184,7 @@ def __repr__(self) -> str:
168184
return f"<Register {self.name!r} at {hex(id(self))}>"
169185

170186

187+
@final
171188
class Integer(Value):
172189
"""Short integer literal.
173190
@@ -198,6 +215,7 @@ def numeric_value(self) -> int:
198215
return self.value
199216

200217

218+
@final
201219
class Float(Value):
202220
"""Float literal.
203221
@@ -257,13 +275,14 @@ def accept(self, visitor: OpVisitor[T]) -> T:
257275

258276

259277
class BaseAssign(Op):
260-
"""Base class for ops that assign to a register."""
278+
"""Abstract base class for ops that assign to a register."""
261279

262280
def __init__(self, dest: Register, line: int = -1) -> None:
263281
super().__init__(line)
264282
self.dest = dest
265283

266284

285+
@final
267286
class Assign(BaseAssign):
268287
"""Assign a value to a Register (dest = src)."""
269288

@@ -286,6 +305,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
286305
return visitor.visit_assign(self)
287306

288307

308+
@final
289309
class AssignMulti(BaseAssign):
290310
"""Assign multiple values to a Register (dest = src1, src2, ...).
291311
@@ -320,7 +340,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
320340

321341

322342
class ControlOp(Op):
323-
"""Control flow operation."""
343+
"""Abstract base class for control flow operations."""
324344

325345
def targets(self) -> Sequence[BasicBlock]:
326346
"""Get all basic block targets of the control operation."""
@@ -331,6 +351,7 @@ def set_target(self, i: int, new: BasicBlock) -> None:
331351
raise AssertionError(f"Invalid set_target({self}, {i})")
332352

333353

354+
@final
334355
class Goto(ControlOp):
335356
"""Unconditional jump."""
336357

@@ -360,6 +381,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
360381
return visitor.visit_goto(self)
361382

362383

384+
@final
363385
class Branch(ControlOp):
364386
"""Branch based on a value.
365387
@@ -426,6 +448,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
426448
return visitor.visit_branch(self)
427449

428450

451+
@final
429452
class Return(ControlOp):
430453
"""Return a value from a function."""
431454

@@ -455,6 +478,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
455478
return visitor.visit_return(self)
456479

457480

481+
@final
458482
class Unreachable(ControlOp):
459483
"""Mark the end of basic block as unreachable.
460484
@@ -511,6 +535,7 @@ def can_raise(self) -> bool:
511535
return self.error_kind != ERR_NEVER
512536

513537

538+
@final
514539
class IncRef(RegisterOp):
515540
"""Increase reference count (inc_ref src)."""
516541

@@ -531,6 +556,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
531556
return visitor.visit_inc_ref(self)
532557

533558

559+
@final
534560
class DecRef(RegisterOp):
535561
"""Decrease reference count and free object if zero (dec_ref src).
536562
@@ -559,6 +585,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
559585
return visitor.visit_dec_ref(self)
560586

561587

588+
@final
562589
class Call(RegisterOp):
563590
"""Native call f(arg, ...).
564591
@@ -587,6 +614,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
587614
return visitor.visit_call(self)
588615

589616

617+
@final
590618
class MethodCall(RegisterOp):
591619
"""Native method call obj.method(arg, ...)"""
592620

@@ -618,6 +646,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
618646
return visitor.visit_method_call(self)
619647

620648

649+
@final
621650
class PrimitiveDescription:
622651
"""Description of a primitive op.
623652
@@ -670,6 +699,7 @@ def __repr__(self) -> str:
670699
return f"<PrimitiveDescription {self.name!r}: {self.arg_types}>"
671700

672701

702+
@final
673703
class PrimitiveOp(RegisterOp):
674704
"""A higher-level primitive operation.
675705
@@ -707,6 +737,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
707737
return visitor.visit_primitive_op(self)
708738

709739

740+
@final
710741
class LoadErrorValue(RegisterOp):
711742
"""Load an error value.
712743
@@ -737,6 +768,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
737768
return visitor.visit_load_error_value(self)
738769

739770

771+
@final
740772
class LoadLiteral(RegisterOp):
741773
"""Load a Python literal object (dest = 'foo' / b'foo' / ...).
742774
@@ -772,6 +804,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
772804
return visitor.visit_load_literal(self)
773805

774806

807+
@final
775808
class GetAttr(RegisterOp):
776809
"""obj.attr (for a native object)"""
777810

@@ -804,6 +837,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
804837
return visitor.visit_get_attr(self)
805838

806839

840+
@final
807841
class SetAttr(RegisterOp):
808842
"""obj.attr = src (for a native object)
809843
@@ -855,6 +889,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
855889
NAMESPACE_TYPE_VAR: Final = "typevar"
856890

857891

892+
@final
858893
class LoadStatic(RegisterOp):
859894
"""Load a static name (name :: static).
860895
@@ -895,6 +930,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
895930
return visitor.visit_load_static(self)
896931

897932

933+
@final
898934
class InitStatic(RegisterOp):
899935
"""static = value :: static
900936
@@ -927,6 +963,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
927963
return visitor.visit_init_static(self)
928964

929965

966+
@final
930967
class TupleSet(RegisterOp):
931968
"""dest = (reg, ...) (for fixed-length tuple)"""
932969

@@ -959,6 +996,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
959996
return visitor.visit_tuple_set(self)
960997

961998

999+
@final
9621000
class TupleGet(RegisterOp):
9631001
"""Get item of a fixed-length tuple (src[index])."""
9641002

@@ -983,6 +1021,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
9831021
return visitor.visit_tuple_get(self)
9841022

9851023

1024+
@final
9861025
class Cast(RegisterOp):
9871026
"""cast(type, src)
9881027
@@ -1014,6 +1053,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
10141053
return visitor.visit_cast(self)
10151054

10161055

1056+
@final
10171057
class Box(RegisterOp):
10181058
"""box(type, src)
10191059
@@ -1048,6 +1088,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
10481088
return visitor.visit_box(self)
10491089

10501090

1091+
@final
10511092
class Unbox(RegisterOp):
10521093
"""unbox(type, src)
10531094
@@ -1074,6 +1115,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
10741115
return visitor.visit_unbox(self)
10751116

10761117

1118+
@final
10771119
class RaiseStandardError(RegisterOp):
10781120
"""Raise built-in exception with an optional error string.
10791121
@@ -1113,6 +1155,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
11131155
StealsDescription = Union[bool, list[bool]]
11141156

11151157

1158+
@final
11161159
class CallC(RegisterOp):
11171160
"""result = function(arg0, arg1, ...)
11181161
@@ -1167,6 +1210,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
11671210
return visitor.visit_call_c(self)
11681211

11691212

1213+
@final
11701214
class Truncate(RegisterOp):
11711215
"""result = truncate src from src_type to dst_type
11721216
@@ -1197,6 +1241,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
11971241
return visitor.visit_truncate(self)
11981242

11991243

1244+
@final
12001245
class Extend(RegisterOp):
12011246
"""result = extend src from src_type to dst_type
12021247
@@ -1231,6 +1276,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
12311276
return visitor.visit_extend(self)
12321277

12331278

1279+
@final
12341280
class LoadGlobal(RegisterOp):
12351281
"""Load a low-level global variable/pointer.
12361282
@@ -1258,6 +1304,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
12581304
return visitor.visit_load_global(self)
12591305

12601306

1307+
@final
12611308
class IntOp(RegisterOp):
12621309
"""Binary arithmetic or bitwise op on integer operands (e.g., r1 = r2 + r3).
12631310
@@ -1322,6 +1369,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
13221369
int_op_to_id: Final = {op: op_id for op_id, op in IntOp.op_str.items()}
13231370

13241371

1372+
@final
13251373
class ComparisonOp(RegisterOp):
13261374
"""Low-level comparison op for integers and pointers.
13271375
@@ -1383,6 +1431,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
13831431
return visitor.visit_comparison_op(self)
13841432

13851433

1434+
@final
13861435
class FloatOp(RegisterOp):
13871436
"""Binary float arithmetic op (e.g., r1 = r2 + r3).
13881437
@@ -1424,6 +1473,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
14241473
float_op_to_id: Final = {op: op_id for op_id, op in FloatOp.op_str.items()}
14251474

14261475

1476+
@final
14271477
class FloatNeg(RegisterOp):
14281478
"""Float negation op (r1 = -r2)."""
14291479

@@ -1444,6 +1494,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
14441494
return visitor.visit_float_neg(self)
14451495

14461496

1497+
@final
14471498
class FloatComparisonOp(RegisterOp):
14481499
"""Low-level comparison op for floats."""
14491500

@@ -1480,6 +1531,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
14801531
float_comparison_op_to_id: Final = {op: op_id for op_id, op in FloatComparisonOp.op_str.items()}
14811532

14821533

1534+
@final
14831535
class LoadMem(RegisterOp):
14841536
"""Read a memory location: result = *(type *)src.
14851537
@@ -1509,6 +1561,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
15091561
return visitor.visit_load_mem(self)
15101562

15111563

1564+
@final
15121565
class SetMem(Op):
15131566
"""Write to a memory location: *(type *)dest = src
15141567
@@ -1540,6 +1593,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
15401593
return visitor.visit_set_mem(self)
15411594

15421595

1596+
@final
15431597
class GetElementPtr(RegisterOp):
15441598
"""Get the address of a struct element.
15451599
@@ -1566,6 +1620,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
15661620
return visitor.visit_get_element_ptr(self)
15671621

15681622

1623+
@final
15691624
class LoadAddress(RegisterOp):
15701625
"""Get the address of a value: result = (type)&src
15711626
@@ -1600,6 +1655,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
16001655
return visitor.visit_load_address(self)
16011656

16021657

1658+
@final
16031659
class KeepAlive(RegisterOp):
16041660
"""A no-op operation that ensures source values aren't freed.
16051661
@@ -1647,6 +1703,7 @@ def accept(self, visitor: OpVisitor[T]) -> T:
16471703
return visitor.visit_keep_alive(self)
16481704

16491705

1706+
@final
16501707
class Unborrow(RegisterOp):
16511708
"""A no-op op to create a regular reference from a borrowed one.
16521709

0 commit comments

Comments
 (0)