A case-insensitive literal in one alternation arm leaks its fold into the equivalent case-sensitive arm, which is enough for the latter to match lowercase input:
Pattern.compile("(?i:Z)x|Z").matcher("z").find() // true — no arm should match "z"
// arm 1 needs an x; arm 2 is case-sensitive Z
Pattern.compile("(?i:[Z])x|[Z]").matcher("z").find() // true — same via CHAR_CLASS
Pattern.compile("Zx|Z").matcher("z").find() // false — correct
java.util.regex returns false for the first two.
Cause: alternation factoring (Parser.factor, round 1) merges arms by Regexp.equals(), which for LITERAL/CHAR_CLASS compares only the runes — not FoldCase. A folded literal therefore factors with its case-sensitive twin and the factored prefix carries the fold into arms that never asked for it (in the repro the case-sensitive arm is effectively deleted). Go's regexp/syntax compares Flags&FoldCase in its equality; the port dropped it.
Fix: compare FoldCase in Regexp.equals()/hashCode() for LITERAL/CHAR_CLASS (hash must change too, since factor groups via hashCode first).
Disclosure: found while fuzzing another library (https://github.com/jemmix/tdfa-jvm) against re2j; analysis and patch composed agentically with GLM 5.3 assistance.
A case-insensitive literal in one alternation arm leaks its fold into the equivalent case-sensitive arm, which is enough for the latter to match lowercase input:
java.util.regexreturns false for the first two.Cause: alternation factoring (
Parser.factor, round 1) merges arms byRegexp.equals(), which forLITERAL/CHAR_CLASScompares only the runes — notFoldCase. A folded literal therefore factors with its case-sensitive twin and the factored prefix carries the fold into arms that never asked for it (in the repro the case-sensitive arm is effectively deleted). Go'sregexp/syntaxcomparesFlags&FoldCasein its equality; the port dropped it.Fix: compare
FoldCaseinRegexp.equals()/hashCode()forLITERAL/CHAR_CLASS(hash must change too, sincefactorgroups via hashCode first).Disclosure: found while fuzzing another library (https://github.com/jemmix/tdfa-jvm) against re2j; analysis and patch composed agentically with GLM 5.3 assistance.