Skip to content

Commit 1ea9373

Browse files
authored
[mypyc] Add more comments about overlapping error values (#18963)
This is a pretty tricky feature, so let's document it better.
1 parent bbca30b commit 1ea9373

File tree

3 files changed

+36
-5
lines changed

3 files changed

+36
-5
lines changed

mypyc/ir/class_ir.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,12 @@ def __init__(
180180
self.attrs_with_defaults: set[str] = set()
181181

182182
# Attributes that are always initialized in __init__ or class body
183-
# (inferred in mypyc.analysis.attrdefined using interprocedural analysis)
183+
# (inferred in mypyc.analysis.attrdefined using interprocedural analysis).
184+
# These can never raise AttributeError when accessed. If an attribute
185+
# is *not* always initialized, we normally use the error value for
186+
# an undefined value. If the attribute byte has an overlapping error value
187+
# (the error_overlap attribute is true for the RType), we use a bitmap
188+
# to track if the attribute is defined instead (see bitmap_attrs).
184189
self._always_initialized_attrs: set[str] = set()
185190

186191
# Attributes that are sometimes initialized in __init__
@@ -191,8 +196,8 @@ def __init__(
191196

192197
# Definedness of these attributes is backed by a bitmap. Index in the list
193198
# indicates the bit number. Includes inherited attributes. We need the
194-
# bitmap for types such as native ints that can't have a dedicated error
195-
# value that doesn't overlap a valid value. The bitmap is used if the
199+
# bitmap for types such as native ints (i64 etc.) that can't have a dedicated
200+
# error value that doesn't overlap a valid value. The bitmap is used if the
196201
# value of an attribute is the same as the error value.
197202
self.bitmap_attrs: list[str] = []
198203

mypyc/ir/rtypes.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,21 @@ class RType:
5858
# to checking for error value as the return value of a function.
5959
#
6060
# For example, no i64 value can be reserved for error value, so we
61-
# pick an arbitrary value (e.g. -113) to signal error, but this is
62-
# also a valid non-error value.
61+
# pick an arbitrary value (-113) to signal error, but this is
62+
# also a valid non-error value. The chosen value is rare as a
63+
# normal, non-error value, so most of the time we can avoid calling
64+
# PyErr_Occurred() when checking for errors raised by called
65+
# functions.
66+
#
67+
# This also means that if an attribute with this type might be
68+
# undefined, we can't just rely on the error value to signal this.
69+
# Instead, we add a bitfield to keep track whether attributes with
70+
# "error overlap" have a value. If there is no value, AttributeError
71+
# is raised on attribute read. Parameters with default values also
72+
# use the bitfield trick to indicate whether the caller passed a
73+
# value. (If we can determine that an attribute is "always defined",
74+
# we never raise an AttributeError and don't need the bitfield
75+
# entry.)
6376
error_overlap = False
6477

6578
@abstractmethod

mypyc/test-data/run-async.test

+13
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,19 @@ def run(x: object) -> object: ...
457457
async def sleep(t: float) -> None: ...
458458

459459
[case testRunAsyncMiscTypesInEnvironment]
460+
# Here we test that values of various kinds of types can be spilled to the
461+
# environment. In particular, types with "overlapping error values" such as
462+
# i64 can be tricky, since they require extra work to support undefined
463+
# attribute values (which raise AttributeError when accessed). For these,
464+
# the object struct has a bitfield which keeps track of whether certain
465+
# attributes have an assigned value.
466+
#
467+
# In practice we mark these attributes as "always defined", which causes these
468+
# checks to be skipped on attribute access, and thus we don't require the
469+
# bitfield to exist.
470+
#
471+
# See the comment of RType.error_overlap for more information.
472+
460473
import asyncio
461474

462475
from mypy_extensions import i64, i32, i16, u8

0 commit comments

Comments
 (0)