frozen=True: the refusal is right; the store guard is the work
Found 2026-09-20 (frankH) censusing That Space Program under NilPy.
Two TSP files wall on this and they are one site: director.py:37 and
provider.py:37 report the same line number, frozen=True appears only in
director.py, and provider.py imports it. The diagnostic is printing an imported
module's line number with no file name.
The class is director.Shot — a plain value record, constructed by three small
helpers and read, never mutated. Every program that respects frozenness would
run correctly against the ordinary generated class, which is what makes
"just accept it" so tempting and so wrong.
Do not delete the refusal on its own. Its reasoning in PyParseDataclassArgs
is exactly this tree's rule about loud-becoming-silent: the class would look
immutable and would not be, so a program that mutates one gets no diagnostic
from us where CPython raises FrozenInstanceError. Today's error is loud and
names the gap.
Design notes for whoever takes it:
- The compile-time half is easy and partial: refuse a store to a field of a class marked frozen wherever the receiver's class is statically known.
- The runtime half is the one that matters, because an instance held in a list, a dict or a comprehension target is a VARIANT here, and that is the ordinary shape of real code. The dynamic attribute STORE path is where the check belongs.
frozen=Truewith the defaulteq=Truealso generates__hash__in CPython, so a frozen dataclass may be used as a dict key or a set member. Accepting frozen without hashability moves the gap rather than closing it.