Skip to content

spec: Typecheck padding#670

Open
RobinJadoul wants to merge 13 commits into
spec/explicit-subfrom
spec/typecheck-padding
Open

spec: Typecheck padding#670
RobinJadoul wants to merge 13 commits into
spec/explicit-subfrom
spec/typecheck-padding

Conversation

@RobinJadoul

Copy link
Copy Markdown
Collaborator

This manages to catch both the observed issue with the CPUs padding for half_instruction_length and an previously unknown issue in the padding for DVRM.

@RobinJadoul RobinJadoul requested a review from erik-3milabs June 16, 2026 11:20
@RobinJadoul RobinJadoul self-assigned this Jun 16, 2026
@RobinJadoul RobinJadoul added the spec Updates and improvements to the spec document label Jun 16, 2026
@github-actions

Copy link
Copy Markdown

Codex Code Review

No issues found in the PR diff.

I reviewed the changed TOML padding values and the new recursive padding/template checker in spec/tooling/chip.py. I also ran:

python spec/tooling/chip.py spec/src/config.toml spec/src/signatures.toml spec/src/*.toml

It completed with No issues were found.

Comment thread spec/tooling/chip.py
Comment thread spec/tooling/chip.py Outdated
@claude

claude Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review: spec/typecheck-padding

Summary

This PR introduces type-checking for padding values in chip specs, fixing two concrete bugs:

  • half_instruction_length had pad 2 (incorrect) → now 0
  • q in DVRM had pad 0 (incorrect for a 4-element array type) → now ["arr", 65535, 65535, 65535, 65535]

It also reorders carry_a/carry_e in sha256round so they appear after temp1/temp2 (which they depend on), required by the new populate_env ordering contract.

The overall approach is sound. Two issues worth addressing:

Issues

Medium – Signature.matches ignores the new condition field (see inline)
The field is populated via build_signature and InteractionLike.typecheck, but matches never compares it. If condition types are intentionally excluded from structural matching, a comment explaining why would prevent future confusion.

Medium – Unhandled KeyError in check_assignment (see inline)
check_template[sig.tag] will crash with a Python traceback for any unknown template tag. Since tags come from TOML files, a typo would produce an unguarded exception instead of a reporter.error. It also leaves the context stack dirty (push without pop) which corrupts subsequent error locations.

Minor notes

  • The push_context/pop_context pattern appears in several places without try/finally. This is fine as long as reporter.error never raises — but the KeyError above shows a case where it can. Worth keeping in mind as the checker grows.
  • The workaround comment in CastExpr.typecheck ("This may become cleaner if we eventually get to the cast rework from spec: Tighter range checks #326") is a good breadcrumb; no action needed now.

Comment thread spec/src/dvrm.toml
Comment thread spec/tooling/chip.py Outdated
Comment thread spec/tooling/chip.py Outdated
continue
chips.append(Chip.from_file(config, file))
chips.append(chip := Chip.from_file(config, file))
template_checkers[chip.name] = template_checker(template_checkers, chip)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this recursion is trippy: to set template_checkers[some chip]: Callable, it passes in all of template_checkers, which gets bound (?) into a function which is then returned?

This, I believe, requires proper documentation, either directly here, or by wrapping this in a well-named function and documenting that function. I cannot make heads or tails of what is happening here, nor what template_checkers even means / is supposed to do.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

template_checkers is a collection of functions, indexed by template name/tag, that checks whether a given assignment satisfies the template.

Passing in template_checkers could be delayed to the invocations of these checkers as well, we're just tying the knot a bit earlier here.
We need to pass template_checkers to an individual checker somewhere to be able to deal with templates inside of a template definition (such as IS_BIT inside of ADD).
The downside of delaying the access to all templates to later is that the typing of template_checkers itself needs to become recursive, since now the Callable needs an argument of the type dict[str, Callable[dict[...], Optional[Type], ...]. Which could in itself work out

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm.

Having reviewed again, I'm not convinced the recursion is the best way to go. Why not introduce a check_assignment for templates and have a (global?) tag_to_chip map?
Then the last part on check_assignment on chips would look something like this:

Suggested change
template_checkers[chip.name] = template_checker(template_checkers, chip)
for c in self.constraints:
with reporter.context(repr(c)):
all_signatures = c.typecheck(env)
templates = filter(lambda s: isinstance(s, TemplateSignature))
for sig in templates:
tag_to_chip[sig.tag].check_assignment(sig)

which I find a lot easier to read.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a big difference, besides now getting a global variable that I don't love having to setup if we ever do end up using this as a "library" for potential other tooling.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The one other difference is that now check_assignment takes a Signature, which I find to not as cleanly represents what an assignment should be. Perhaps there's a way to decompose it into something that maps sig into an assignment given only the chip and then chaining a check_assignment — which is currently encoded in the template checker.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aha, and after a quick attempt at implementation, another downside appears: we can't easily have this mapping for SUB, which needs to rearrange some things before passing it on to the logic for ADD.
So the replacement would end up being a map from tag to (chip, extractor) where extractor needs to give you the assignment for a signature, which is comparable to the current approach, but now implemented as a suggestion.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now simplified after stacking the PR on top of #695

Comment thread spec/tooling/chip.py Outdated
Comment thread spec/tooling/chip.py
@RobinJadoul RobinJadoul requested a review from erik-3milabs June 16, 2026 15:16
Comment thread spec/tooling/chip.py
Comment on lines +1043 to +1047
def check_assignment(
self,
template_checkers: dict[str, "TemplateChecker"],
values: Optional[dict[str, Type]] = None,
):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than having check_assignment default to checking the padding when no values are provided, I'd instead propose introducing check_padding which calls check_assignment on the padding.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also: should we additionally verify that self.variables INSECTION values.keys() = self.variables? That way we can strain out assignments that propose setting non-existent variables.

Comment thread spec/tooling/chip.py Outdated
for c in self.constraints:
for sig in c.typecheck(env):
# Recurse on templates
if isinstance(sig, TemplateSignature) and sig.tag in template_checkers:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if sig.tag not in template_checkers? Wouldn't that indicate a bug?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really, we could be checking a chip in isolation and not invoking chip.py with all templates present

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we at least have the reporter mention this? Not as an issue, but at least as an heads up.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, yeah. Added

Comment thread spec/tooling/chip.py Outdated
type TemplateChecker = Callable[[dict[str, TemplateChecker], Optional[Type], list[Type], Optional[Type]], None]

def build_template_checker(chip: Chip) -> TemplateChecker:
def check(template_checkers: dict[str, TemplateChecker], cond: Optional[Type], input: list[Type], output: Optional[Type]) -> None:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider passing in sig: TemplateSignature rather than decomposing it into cond, input and output.

Comment thread spec/tooling/chip.py Outdated
continue
chips.append(Chip.from_file(config, file))
chips.append(chip := Chip.from_file(config, file))
template_checkers[chip.name] = template_checker(template_checkers, chip)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm.

Having reviewed again, I'm not convinced the recursion is the best way to go. Why not introduce a check_assignment for templates and have a (global?) tag_to_chip map?
Then the last part on check_assignment on chips would look something like this:

Suggested change
template_checkers[chip.name] = template_checker(template_checkers, chip)
for c in self.constraints:
with reporter.context(repr(c)):
all_signatures = c.typecheck(env)
templates = filter(lambda s: isinstance(s, TemplateSignature))
for sig in templates:
tag_to_chip[sig.tag].check_assignment(sig)

which I find a lot easier to read.

@RobinJadoul RobinJadoul requested a review from erik-3milabs June 22, 2026 13:36
@RobinJadoul RobinJadoul changed the base branch from spec/main to spec/explicit-sub June 22, 2026 15:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

spec Updates and improvements to the spec document

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants