feat(system): allow a newer Bash to skip a fork the 3.0 floor requires - #1362
Merged
Conversation
bashunit floors at Bash 3.0, so it forks where a newer shell has a
builtin, and there was no way to ship a faster body for the newer shell:
the compatibility rules reject a too-new construct anywhere in src/,
including inside a branch 3.0 never takes. Deliberately blunt, because
the Bash 3.0 job only catches a too-new construct when a test happens to
execute the line.
This is the mechanism, not a speed run. The floor does not move.
src/system/bash.sh declares one flag per version boundary in use, named
for its tier, and a leaf module picks between two bodies with a column-0
if/else. Measured over 100k calls, best of 5, against a single ungated
definition: picking the definition once costs between -0.07 and +0.09
microseconds per call, while a predicate function per call costs +17.9
on 3.0 and +4.3 on 5.3. Selection is free; the pattern already in use is
the slow one. The five existing runtime predicates are correct and stay.
Each compatibility rule now declares its construct's minimum version as
a tier, and a construct is allowed only inside a column-0 gate whose
tier is at least that. Tier 0 means no gate is ever enough: the
parse-time constructs, which kill the file from inside a branch that
shell never takes, and the rules whose boundary is not a version at all
-- `[[ =~ ]]` changed semantics between 3.0 and 3.2, and the
temporary-locale prefix is a 5.3.9 segfault.
Rejected, and why, since both are tempting: a trailing pragma is the
author's claim rather than a structural fact, so honouring it is a
smuggling path by construction; a per-file allow-list lets an ungated
construct elsewhere in the same file ship.
The first gated helper is lpad, chosen because it removes a fork rather
than because a builtin exists: `printf -v` writes into a variable, the
3.0 body has to capture. Both bodies hand the same format and the same
value to the same printf, so the output is identical by construction --
the textbook `${v,,}` example would not be, since it disagrees with `tr`
on non-ASCII (#1351), which is the whole reason a gated helper needs an
equivalence test.
Verified end to end: the build emits both blocks intact, the artifact
parses on 3.0, 3.2, 4.4, 5.2 and 5, and the helper's tests pass on each
with the branch its tier calls for -- `tr`-free `printf -v` above 3.1,
the capture on real 3.00.0.
Two guards the mechanism needs and did not have: every flag a gate names
must be one the flags file declares, or a typo leaves the fast body
unreachable and the fallback in use everywhere, green and slower; and
build_test's duplicate check anchors at column 0, so it cannot see
either body of a gated helper, which is defined twice by design.
Closes #1352
Claude-Session: https://claude.ai/code/session_01EXYWTGLjf7qM8Ru3GakDRm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤔 Background
Related #1352
bashunit floors at Bash 3.0, so it forks where a newer shell has a builtin — and there was no way to ship a faster body for the newer shell, because the compatibility rules reject a too-new construct anywhere in
src/, including inside a branch 3.0 never takes.This is the mechanism, not a speed run. The floor does not move.
💡 Changes
src/system/bash.shdeclares one flag per version boundary, named for its tier; a leaf module picks between two bodies with a column-0if/else. Selection at load time costs between -0.07 and +0.09 µs per call, against +17.9 on Bash 3.0 for the runtime-predicate pattern.[[ =~ ]], the 5.3.9 locale segfault). A pragma and a per-file allow-list were both rejected; the commit says why.printf -vwrites into a variable where the 3.0 body must capture. Both bodies hand the same format and value to the sameprintf, so equivalence holds by construction — unlike the textbook${v,,}example, which disagrees withtron non-ASCII (assert_contains_ignore_case: the non-ASCII folding comment is wrong for GNUtr#1351).adrs/adr-013, a parse test over everysrc/file, and two guards the mechanism needed: every gate must name a declared flag, andbuild_test's column-0 duplicate check cannot see either body of a gated helper.https://claude.ai/code/session_01EXYWTGLjf7qM8Ru3GakDRm