Skip to content

Fix Issue 11025 - case-insensitive backreferences in std.regex - #11070

Merged
thewilsonator merged 9 commits into
dlang:masterfrom
niy-ati:fix-11025-regex-backref-casefold
Jul 23, 2026
Merged

Fix Issue 11025 - case-insensitive backreferences in std.regex#11070
thewilsonator merged 9 commits into
dlang:masterfrom
niy-ati:fix-11025-regex-backref-casefold

Conversation

@niy-ati

@niy-ati niy-ati commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Under /i, IR.Backref compared captures with ==, so (.)\1 matched BB but not Aa.
  • Compare with simple case folding when RegexOption.casefold is set (backtracking, Thompson, and ctRegex).
  • Add regression vectors for issue 11025.

Fixes #11025

Test plan

  • Added TestVectors for Aa / aA / BB / non-match / multi-char / (?i).
  • CI

Under /i, literals are casefolded at parse time but IR.Backref still
compared codepoints with ==. Use simple case folding when matching
backrefs in the backtracking and Thompson engines (and ctRegex).
@niy-ati
niy-ati requested a review from DmitryOlshansky as a code owner July 18, 2026 15:49
Comment thread std/regex/internal/tests.d Outdated
Comment thread std/regex/internal/ir.d Outdated
@DmitryOlshansky

Copy link
Copy Markdown
Member

What I meant was not using sicmp as is but getting its internal compare function and using that to compare two dchar case-insensitively. Plain call to sicmp brings its own overheads which are not acceptable.

@niy-ati

niy-ati commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the clarification ,
I’ll add/reuse the internal simple-case per-dchar compare from sicmp (trie tables) and use that from equalCasefold in std.regex.internal.ir, without calling sicmp itself. I’ll refactor so we don’t duplicate the trie logic if that fits Phobos style.

@DmitryOlshansky

Copy link
Copy Markdown
Member

LGTM

@kubo39

kubo39 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Literals are folded at parse time using the flag active at their position, but a backref here uses one global flag for the entire pattern.
That diverges as soon as (?i)/(?-i) toggles case-insensitivity around the backref:

// over-match: (?i) appears *after* the backref, so \1 should stay case-sensitive
assert( matchFirst("Aax", regex(`(.)\1(?i)X`)).empty);   // fails: matches "Aax"

// under-match: casefold is on at \1 but cleared before X
assert(!matchFirst("AaX", regex(`(?i)(.)\1(?-i)X`)).empty); // fails: no match

@DmitryOlshansky

Copy link
Copy Markdown
Member

The way out is adding a new opcode for caseinsensitive backreference

@niy-ati

niy-ati commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the inline (?i) / (?-i) issue by introducing IR.BackrefI: the parser records whether casefold is active at each backref site and emits Backref vs BackrefI accordingly. Matchers no longer use re.flags for backref comparison. Added tests for (.)\1(?i)X / (?i)(.)\1(?-i)X alongside the existing #11025 vectors. Case-insensitive comparison still uses simpleCaseCmp via equalCasefold on the BackrefI path only.

@kubo39 , backrefs now follow the same per-position casefold as literals (Backref / BackrefI at parse time). @DmitryOlshansky , implemented the separate case-insensitive backref opcode as suggested.

@DmitryOlshansky

Copy link
Copy Markdown
Member

LGTM

@niy-ati

niy-ati commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@rikkimax whenever you have a moment pls review it, open to address anything else.

@rikkimax

Copy link
Copy Markdown
Contributor

@rikkimax whenever you have a moment pls review it, open to address anything else.

I have nothing to add std.regex isn't really my area. But I do get to say that I'd love for someone to finish case handling in std.uni with the same vigor!

Would be very helpful to get that finished before PhobosV3 so I don't have to deal with that.

@0xEAB
0xEAB removed the request for review from rikkimax July 21, 2026 21:31
@niy-ati

niy-ati commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

FreeBSD 14.4 failed during pkg install (mpdecimal fetch / disk write) before the suite ran. Other checks are green , could someone re-run the failed FreeBSD job?

@0xEAB

0xEAB commented Jul 22, 2026

Copy link
Copy Markdown
Member

@niy-ati see also dlang/ci#497

@0xEAB 0xEAB left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  • LGTM
  • Passes testsuite.
    • Does not remove or alter any tests.
    • Adds new tests.
  • Has code owner’s approval: #11070 (comment)
  • I’ve added nitpick format changes.

@thewilsonator
thewilsonator merged commit 4d47a92 into dlang:master Jul 23, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Std.regex: backreferences do not match text with different casing

6 participants