Skip to content

engine: record \documentclass's options in \@classoptionslist - #91

Merged
tannevaled merged 1 commit into
mainfrom
classoptionslist
Aug 27, 2026
Merged

engine: record \documentclass's options in \@classoptionslist#91
tannevaled merged 1 commit into
mainfrom
classoptionslist

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

\usepackage{beamerthemesplit} lost the body of the talk. So did the fifteen
other beamer themes built the same way.

The theme was not at fault: the file is 24 lines. Bisecting it gives
\ProcessOptionsBeamer; bisecting that gives the \@for inside it; and inside
the loop, one control sequence the engine never defined.

The rule

\@for over an undefined control sequence does not iterate over nothing — it
swallows what follows. LaTeX's own \ProcessOptions guards the loop:

\ifx\@classoptionslist\relax\else
  \@for\CurrentOption:=\@classoptionslist\do{…}

but beamer's \ProcessOptionsBeamer guards on \@currext instead and runs the
loop unguarded, as does \beamer@filterclassoptions. Both therefore rely on
\documentclass having set the list — and the engine never set it.

ltclass.dtx is followed on all four points:

§547 \@classoptionslist and \@raw@classoptionslist both start at \relax
§2165 \documentclass fills them on the first class load
\protected@xdef\@classoptionslist{\zap@space#2 \@empty} — spaces stripped
the \ifx…\relax guard means a class that loads another class does not overwrite the document's own list

That last point is the easy one to miss, and it has its own test.

Measured

200 real beamer talks: 0 lost, one more renders, 12 outputs change —
a narrow change, as it should be. Page-count error against tectonic on the 14
talks with a reference: 11 → 6, none moving away, 9 of 14 landing exactly
on it
.

200 real arXiv papers: 0 lost, two more render, 29 outputs change.

Not fixed here

A frame title does not render — with or without a theme, before or after this
change. Pre-existing and separate.

\usepackage{beamerthemesplit} lost the body of the talk. So did the fifteen other
beamer themes built the same way. The theme was not at fault: the file is 24
lines, and the loss came from \ProcessOptionsBeamer, from the \@for inside it,
and from one control sequence the engine never defined.

\@for over an UNDEFINED control sequence does not iterate over nothing — it
swallows what follows. LaTeX's own \ProcessOptions guards the loop,

	\ifx\@classoptionslist\relax\else
	  \@for\CurrentOption:=\@classoptionslist\do{…}

but beamer's \ProcessOptionsBeamer guards on \@currext instead and runs the loop
unguarded, as does \beamer@filterclassoptions. Both therefore rely on
\documentclass having set the list — and the engine never set it.

ltclass.dtx is followed on all four points: \@classoptionslist and
\@raw@classoptionslist both start at \relax (§547), \documentclass fills them on
the FIRST class load,

	\ifx\@classoptionslist\relax
	  \protected@xdef\@classoptionslist{\zap@space#2 \@empty}%
	  \gdef\@raw@classoptionslist{#2}%

and the \ifx guard means a class that loads another class does not overwrite the
document's own list. A test pins that last part, which is easy to miss.

Measured over 200 real beamer talks: 0 lost, one MORE renders, 12 outputs change
— a narrow change, as it should be — and the page-count error against tectonic on
the 14 talks with a reference falls 11 -> 6, with none moving away and 9 of the
14 landing exactly on it. Over 200 real arXiv papers: 0 lost, two more render, 29
outputs change.

Not fixed here, and pre-existing: a frame TITLE does not render, with or without
a theme, before or after this change.
@tannevaled
tannevaled merged commit c1c0023 into main Aug 27, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant