[libc-commits] [libc] [libc][regex] Wire regex entrypoints to ExprPool matcher (PR #232291)
via libc-commits
libc-commits at lists.llvm.org
Thu Oct 15 06:16:19 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libc
Author: Jeff Bailey (kaladron)
<details>
<summary>Changes</summary>
The initial regcomp, regexec, and regfree stubs copied the raw pattern string into a heap buffer and delegated matching to inline_strstr, leaving the hash-consed ExprPool arena unused by the public entrypoints. In addition, POSIX requires regexec to be thread-safe across concurrent callers sharing a compiled const regex_t handle, whereas lazy derivative evaluation interns new AST states into the pool at match time.
Replace the string-copy stub with RegexInternal and a Brzozowski derivative matcher (derivative and match) backed by ExprPool. regcomp initialises preg->__internal to nullptr before allocating RegexInternal so regfree is a safe no-op after a failed compilation, and folds the pattern into the pool. regexec serialises lazy derivative interning on the shared pool using SpinLock and cpp::lock_guard.
Assisted-by: Automated tooling, human reviewed.
---
Patch is 29.34 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/232291.diff
13 Files Affected:
- (modified) libc/src/__support/regex/CMakeLists.txt (+22)
- (modified) libc/src/__support/regex/regex_expr_pool.h (+3)
- (added) libc/src/__support/regex/regex_internal.h (+41)
- (added) libc/src/__support/regex/regex_matcher.cpp (+89)
- (added) libc/src/__support/regex/regex_matcher.h (+45)
- (modified) libc/src/regex/CMakeLists.txt (+13-8)
- (modified) libc/src/regex/regcomp.cpp (+41-13)
- (modified) libc/src/regex/regexec.cpp (+26-19)
- (modified) libc/src/regex/regfree.cpp (+7-5)
- (modified) libc/test/src/__support/regex/CMakeLists.txt (+14)
- (modified) libc/test/src/__support/regex/expr_test.cpp (+32)
- (added) libc/test/src/__support/regex/matcher_test.cpp (+154)
- (modified) libc/test/src/regex/regex_basic_test.cpp (+58)
``````````diff
diff --git a/libc/src/__support/regex/CMakeLists.txt b/libc/src/__support/regex/CMakeLists.txt
index e72e74cd634518..4b1e772212c933 100644
--- a/libc/src/__support/regex/CMakeLists.txt
+++ b/libc/src/__support/regex/CMakeLists.txt
@@ -19,3 +19,25 @@ add_object_library(
libc.src.__support.macros.config
libc.src.string.memory_utils.inline_memset
)
+
+add_header_library(
+ regex_internal
+ HDRS
+ regex_internal.h
+ DEPENDS
+ .regex_expr_pool
+ libc.src.__support.macros.config
+ libc.src.__support.threads.spin_lock
+)
+
+add_object_library(
+ regex_matcher
+ SRCS
+ regex_matcher.cpp
+ HDRS
+ regex_matcher.h
+ DEPENDS
+ .regex_expr_pool
+ libc.src.__support.CPP.string_view
+ libc.src.__support.error_or
+)
diff --git a/libc/src/__support/regex/regex_expr_pool.h b/libc/src/__support/regex/regex_expr_pool.h
index fe3e9724c2a036..ec377863d6a8c2 100644
--- a/libc/src/__support/regex/regex_expr_pool.h
+++ b/libc/src/__support/regex/regex_expr_pool.h
@@ -118,6 +118,9 @@ class ExprPool {
/// O(1) nullability query for a valid ExprId.
bool is_nullable(ExprId id) const { return get(id).nullable; }
+ /// Returns the number of unique AST nodes currently interned in this pool.
+ size_t size() const { return node_count; }
+
/// Returns the current heap bytes allocated by this pool.
size_t allocated_bytes() const {
return (block_count * sizeof(Block)) + blocks.size_bytes() +
diff --git a/libc/src/__support/regex/regex_internal.h b/libc/src/__support/regex/regex_internal.h
new file mode 100644
index 00000000000000..d3fcac732b900b
--- /dev/null
+++ b/libc/src/__support/regex/regex_internal.h
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Internal compiled regular expression representation owned by regex_t.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_REGEX_REGEX_INTERNAL_H
+#define LLVM_LIBC_SRC___SUPPORT_REGEX_REGEX_INTERNAL_H
+
+#include "src/__support/macros/config.h"
+#include "src/__support/regex/regex_ast.h"
+#include "src/__support/regex/regex_expr_pool.h"
+#include "src/__support/threads/spin_lock.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace regex {
+
+/// Internal state stored in regex_t::__internal after a successful regcomp.
+struct RegexInternal {
+ /// Arena and hash-consing table for the pattern AST and derivative states.
+ ExprPool pool;
+ /// Root ExprId of the compiled regular expression.
+ ExprId root = INVALID_EXPR_ID;
+ /// Compilation flags passed to regcomp.
+ int cflags = 0;
+ /// Spinlock serialising lazy derivative interning across concurrent regexec
+ /// calls sharing the same const regex_t*.
+ SpinLock lock{};
+};
+
+} // namespace regex
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_REGEX_REGEX_INTERNAL_H
diff --git a/libc/src/__support/regex/regex_matcher.cpp b/libc/src/__support/regex/regex_matcher.cpp
new file mode 100644
index 00000000000000..d60097f0e228cc
--- /dev/null
+++ b/libc/src/__support/regex/regex_matcher.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Brzozowski derivative evaluation and regular expression matching
+/// implementation.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/regex/regex_matcher.h"
+
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/regex/regex_ast.h"
+#include "src/__support/regex/regex_expr_pool.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace regex {
+
+ErrorOr<ExprId> derivative(ExprId id, char c, ExprPool &pool) {
+ const Expr e = pool.get(id);
+ switch (e.kind) {
+ case ExprKind::EmptySet:
+ case ExprKind::EmptyStr:
+ return EMPTY_SET_ID;
+ case ExprKind::Literal:
+ return (c == e.ch) ? EMPTY_STR_ID : EMPTY_SET_ID;
+ case ExprKind::Concat: {
+ // Product Rule:
+ // D_c(L . R) = (D_c(L) . R) | (nullable(L) ? D_c(R) : EmptySet)
+ const bool left_nullable = pool.is_nullable(e.bin.left);
+ ErrorOr<ExprId> d_l = derivative(e.bin.left, c, pool);
+ if (!d_l.has_value())
+ return d_l;
+ ErrorOr<ExprId> lhs = pool.make_concat(d_l.value(), e.bin.right);
+ if (!lhs.has_value() || !left_nullable)
+ return lhs;
+ ErrorOr<ExprId> d_r = derivative(e.bin.right, c, pool);
+ if (!d_r.has_value())
+ return d_r;
+ return pool.make_alt(lhs.value(), d_r.value());
+ }
+ case ExprKind::Alt: {
+ // Sum Rule: D_c(L | R) = D_c(L) | D_c(R)
+ ErrorOr<ExprId> d_l = derivative(e.bin.left, c, pool);
+ if (!d_l.has_value())
+ return d_l;
+ ErrorOr<ExprId> d_r = derivative(e.bin.right, c, pool);
+ if (!d_r.has_value())
+ return d_r;
+ return pool.make_alt(d_l.value(), d_r.value());
+ }
+ }
+ __builtin_unreachable();
+}
+
+MatchResult match(ExprPool &pool, const ExprId pattern,
+ cpp::string_view input) {
+ if (pool.is_nullable(pattern))
+ return MatchResult::Match;
+
+ // Unanchored substring search: at each byte, step active partial matches via
+ // derivative(state, c) and union with pattern so a new match can begin after
+ // the current byte (equivalent to matching .* . pattern without constructing
+ // a prefix wildcard node).
+ ExprId state = pattern;
+ for (char c : input) {
+ ErrorOr<ExprId> d = derivative(state, c, pool);
+ if (!d.has_value())
+ return MatchResult::OutOfMemory;
+ if (pool.is_nullable(d.value()))
+ return MatchResult::Match;
+ ErrorOr<ExprId> next = pool.make_alt(pattern, d.value());
+ if (!next.has_value())
+ return MatchResult::OutOfMemory;
+ state = next.value();
+ }
+
+ return MatchResult::NoMatch;
+}
+
+} // namespace regex
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/__support/regex/regex_matcher.h b/libc/src/__support/regex/regex_matcher.h
new file mode 100644
index 00000000000000..759c7ebdafb258
--- /dev/null
+++ b/libc/src/__support/regex/regex_matcher.h
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Brzozowski derivative evaluation and regular expression matching interface.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_REGEX_REGEX_MATCHER_H
+#define LLVM_LIBC_SRC___SUPPORT_REGEX_REGEX_MATCHER_H
+
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/regex/regex_ast.h"
+#include "src/__support/regex/regex_expr_pool.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace regex {
+
+/// Outcome of evaluating a regular expression against an input string.
+enum class MatchResult {
+ Match,
+ NoMatch,
+ OutOfMemory,
+};
+
+/// Computes the Brzozowski derivative D_c(id) with respect to character c.
+///
+/// Formally, D_c(L) = { w | c . w in L }.
+ErrorOr<ExprId> derivative(ExprId id, char c, ExprPool &pool);
+
+/// Evaluates whether pattern matches any substring of input using lazy
+/// Brzozowski derivatives.
+MatchResult match(ExprPool &pool, const ExprId pattern, cpp::string_view input);
+
+} // namespace regex
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_REGEX_REGEX_MATCHER_H
diff --git a/libc/src/regex/CMakeLists.txt b/libc/src/regex/CMakeLists.txt
index 3db701645b26ad..4c984802554e59 100644
--- a/libc/src/regex/CMakeLists.txt
+++ b/libc/src/regex/CMakeLists.txt
@@ -5,13 +5,14 @@ add_entrypoint_object(
HDRS
regcomp.h
DEPENDS
- libc.hdr.types.regex_t
libc.hdr.regex_macros
- libc.src.__support.common
+ libc.hdr.types.regex_t
+ libc.src.__support.alloc_checker
libc.src.__support.CPP.new
libc.src.__support.CPP.string_view
- libc.src.__support.alloc_checker
- libc.src.string.memory_utils.inline_memcpy
+ libc.src.__support.error_or
+ libc.src.__support.macros.null_check
+ libc.src.__support.regex.regex_internal
)
add_entrypoint_object(
@@ -21,12 +22,15 @@ add_entrypoint_object(
HDRS
regexec.h
DEPENDS
+ libc.hdr.regex_macros
libc.hdr.types.regex_t
libc.hdr.types.regmatch_t
libc.hdr.types.size_t
- libc.hdr.regex_macros
- libc.src.__support.common
- libc.src.string.memory_utils.inline_strstr
+ libc.src.__support.CPP.mutex
+ libc.src.__support.CPP.string_view
+ libc.src.__support.macros.null_check
+ libc.src.__support.regex.regex_internal
+ libc.src.__support.regex.regex_matcher
)
add_entrypoint_object(
@@ -52,6 +56,7 @@ add_entrypoint_object(
regfree.h
DEPENDS
libc.hdr.types.regex_t
- libc.src.__support.common
libc.src.__support.CPP.new
+ libc.src.__support.macros.null_check
+ libc.src.__support.regex.regex_internal
)
diff --git a/libc/src/regex/regcomp.cpp b/libc/src/regex/regcomp.cpp
index f4c339f70f9d47..b1fd5e218333f8 100644
--- a/libc/src/regex/regcomp.cpp
+++ b/libc/src/regex/regcomp.cpp
@@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// Implementation of regcomp (stub).
+/// Implementation of regcomp.
///
//===----------------------------------------------------------------------===//
@@ -17,34 +17,62 @@
#include "src/__support/CPP/new.h"
#include "src/__support/CPP/string_view.h"
#include "src/__support/alloc-checker.h"
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
#include "src/__support/macros/config.h"
-#include "src/string/memory_utils/inline_memcpy.h"
+#include "src/__support/macros/null_check.h"
+#include "src/__support/regex/regex_ast.h"
+#include "src/__support/regex/regex_internal.h"
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, regcomp,
(regex_t *__restrict preg, const char *__restrict pattern,
int cflags)) {
- // Silencing unused parameter warning for the stub.
- (void)cflags;
+ LIBC_CRASH_ON_NULLPTR(preg);
+ LIBC_CRASH_ON_NULLPTR(pattern);
// Note: POSIX requires callers to call regfree() before reusing a preg
// object. We therefore do not attempt to free any previous __internal here
- // — preg is uninitialized on first use and the pointer would be garbage.
+ // as preg is uninitialised on first use and the pointer would be garbage.
+ // However, we clear __internal and re_nsub immediately so calling regfree()
+ // after a failed regcomp() is guaranteed to be a safe no-op.
+ preg->__internal = nullptr;
+ preg->re_nsub = 0;
- cpp::string_view pattern_view(pattern);
- size_t len = pattern_view.size();
+ // TODO: Once cpp::unique_ptr and AllocChecker-backed cpp::make_unique are
+ // available in src/__support/CPP/, manage RegexInternal via unique_ptr so
+ // error paths do not require manual delete.
AllocChecker ac;
- char *copy = new (ac) char[len + 1];
+ regex::RegexInternal *internal = new (ac) regex::RegexInternal();
if (!ac)
return REG_ESPACE;
- inline_memcpy(copy, pattern, len + 1);
+ ErrorOr<regex::ExprId> cur = internal->pool.empty_str();
+ if (!cur.has_value()) {
+ delete internal;
+ return cur.error();
+ }
- // TODO: This is a stub. re_nsub is always 0 because parenthesised
- // subexpressions are not yet parsed. REG_NOSUB is effectively always active.
- preg->re_nsub = 0;
- preg->__internal = copy;
+ // TODO: Replace direct literal concatenation with the ERE/BRE parser as
+ // regular expression syntax support is wired up.
+ cpp::string_view pattern_view(pattern);
+ for (char c : pattern_view) {
+ ErrorOr<regex::ExprId> lit = internal->pool.make_lit(c);
+ if (!lit.has_value()) {
+ delete internal;
+ return lit.error();
+ }
+ cur = internal->pool.make_concat(cur.value(), lit.value());
+ if (!cur.has_value()) {
+ delete internal;
+ return cur.error();
+ }
+ }
+
+ internal->root = cur.value();
+ internal->cflags = cflags;
+ preg->__internal = internal;
return 0;
}
diff --git a/libc/src/regex/regexec.cpp b/libc/src/regex/regexec.cpp
index 7d3927a2db8eeb..fdeab64029b092 100644
--- a/libc/src/regex/regexec.cpp
+++ b/libc/src/regex/regexec.cpp
@@ -7,16 +7,20 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// Implementation of regexec (stub).
+/// Implementation of regexec.
///
//===----------------------------------------------------------------------===//
#include "src/regex/regexec.h"
#include "hdr/regex_macros.h"
+#include "src/__support/CPP/mutex.h"
+#include "src/__support/CPP/string_view.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
-#include "src/string/memory_utils/inline_strstr.h"
+#include "src/__support/macros/null_check.h"
+#include "src/__support/regex/regex_internal.h"
+#include "src/__support/regex/regex_matcher.h"
namespace LIBC_NAMESPACE_DECL {
@@ -24,31 +28,34 @@ LLVM_LIBC_FUNCTION(int, regexec,
(const regex_t *__restrict preg,
const char *__restrict string, size_t nmatch,
regmatch_t *__restrict pmatch, int eflags)) {
- // TODO: This is a stub. The following are not yet implemented:
- // - Regex metacharacters (., *, +, ?, [], {}, (), |, ^, $).
- // - REG_EXTENDED / REG_ICASE / REG_NEWLINE compile flags.
- // - REG_NOTBOL / REG_NOTEOL eflags.
- // - pmatch[] filling (subexpression offsets).
- // - Only literal substring search (strstr) is performed.
+ LIBC_CRASH_ON_NULLPTR(preg);
+ LIBC_CRASH_ON_NULLPTR(string);
+
+ // TODO: Submatch extraction (nmatch/pmatch) and execution flags (eflags)
+ // will be wired in subsequent patches.
(void)nmatch;
(void)pmatch;
(void)eflags;
// Guard against a null internal pointer (e.g. called after regfree).
- const char *pattern = static_cast<const char *>(preg->__internal);
- if (!pattern)
+ auto *internal = static_cast<regex::RegexInternal *>(preg->__internal);
+ if (!internal)
return REG_NOMATCH;
- // An empty pattern always matches.
- if (*pattern == '\0')
- return 0;
-
- // Use inline_strstr for literal substring matching.
- auto comp = [](char l, char r) -> int { return l - r; };
- if (inline_strstr(string, pattern, comp))
+ // POSIX requires regexec to be thread-safe across concurrent calls sharing a
+ // const regex_t*. Serialise access to internal->pool during lazy derivative
+ // evaluation.
+ cpp::lock_guard guard(internal->lock);
+ switch (
+ regex::match(internal->pool, internal->root, cpp::string_view(string))) {
+ case regex::MatchResult::Match:
return 0;
-
- return REG_NOMATCH;
+ case regex::MatchResult::NoMatch:
+ return REG_NOMATCH;
+ case regex::MatchResult::OutOfMemory:
+ return REG_ESPACE;
+ }
+ __builtin_unreachable();
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/regex/regfree.cpp b/libc/src/regex/regfree.cpp
index 06fd791f628ff0..f7550d53eeada2 100644
--- a/libc/src/regex/regfree.cpp
+++ b/libc/src/regex/regfree.cpp
@@ -16,15 +16,17 @@
#include "src/__support/CPP/new.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+#include "src/__support/regex/regex_internal.h"
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(void, regfree, (regex_t * preg)) {
- if (preg->__internal) {
- char *ptr = static_cast<char *>(preg->__internal);
- delete[] ptr;
- preg->__internal = nullptr;
- }
+ LIBC_CRASH_ON_NULLPTR(preg);
+ // TODO: Once RegexInternal is managed via cpp::unique_ptr, reset the smart
+ // pointer here instead of calling delete manually.
+ delete static_cast<regex::RegexInternal *>(preg->__internal);
+ preg->__internal = nullptr;
preg->re_nsub = 0;
}
diff --git a/libc/test/src/__support/regex/CMakeLists.txt b/libc/test/src/__support/regex/CMakeLists.txt
index 0576eaea3684c1..772626c4055130 100644
--- a/libc/test/src/__support/regex/CMakeLists.txt
+++ b/libc/test/src/__support/regex/CMakeLists.txt
@@ -8,3 +8,17 @@ add_libc_test(
libc.hdr.regex_macros
libc.src.__support.regex.regex_expr_pool
)
+
+add_libc_test(
+ matcher_test
+ SUITE
+ libc-support-tests
+ SRCS
+ matcher_test.cpp
+ DEPENDS
+ libc.hdr.regex_macros
+ libc.src.__support.CPP.string_view
+ libc.src.__support.error_or
+ libc.src.__support.regex.regex_expr_pool
+ libc.src.__support.regex.regex_matcher
+)
diff --git a/libc/test/src/__support/regex/expr_test.cpp b/libc/test/src/__support/regex/expr_test.cpp
index baba79cb582b15..1e66fcd02a5a03 100644
--- a/libc/test/src/__support/regex/expr_test.cpp
+++ b/libc/test/src/__support/regex/expr_test.cpp
@@ -69,9 +69,14 @@ TEST(LlvmLibcRegexExprTest, RecursiveInterning) {
TEST(LlvmLibcRegexExprTest, AlgebraicIdentitiesConcat) {
ExprPool pool;
auto lit_a = pool.make_lit('a').value();
+ auto lit_b = pool.make_lit('b').value();
auto empty_set = pool.empty_set().value();
auto empty_str = pool.empty_str().value();
+ // Invalid IDs return REG_BADPAT.
+ EXPECT_EQ(pool.make_concat(INVALID_EXPR_ID, lit_a).error(), REG_BADPAT);
+ EXPECT_EQ(pool.make_concat(lit_a, INVALID_EXPR_ID).error(), REG_BADPAT);
+
// (Ø · R) or (R · Ø) => Ø
EXPECT_EQ(pool.make_concat(empty_set, lit_a).value(), empty_set);
EXPECT_EQ(pool.make_concat(lit_a, empty_set).value(), empty_set);
@@ -79,6 +84,22 @@ TEST(LlvmLibcRegexExprTest, AlgebraicIdentitiesConcat) {
// (ε · R) or (R · ε) => R
EXPECT_EQ(pool.make_concat(empty_str, lit_a).value(), lit_a);
EXPECT_EQ(pool.make_concat(lit_a, empty_str).value(), lit_a);
+
+ // Nullability of (L · R) requires both L and R to be nullable.
+ auto opt_a = pool.make_alt(lit_a, empty_str).value();
+ auto opt_b = pool.make_alt(lit_b, empty_str).value();
+ EXPECT_FALSE(pool.is_nullable(pool.make_concat(lit_a, opt_b).value()));
+ EXPECT_FALSE(pool.is_nullable(pool.make_concat(opt_a, lit_b).value()));
+ EXPECT_TRUE(pool.is_nullable(pool.make_concat(opt_a, opt_b).value()));
+
+ // Expr::operator== structural comparisons across kinds, literals, and binary
+ // children.
+ EXPECT_FALSE(Expr::make_empty_set() == Expr::make_literal('a'));
+ EXPECT_FALSE(Expr::make_literal('a') == Expr::make_literal('b'));
+ EXPECT_FALSE(Expr::make_concat(lit_a, lit_b) ==
+ Expr::make_concat(lit_a, opt_b));
+ EXPECT_FALSE(Expr::make_concat(lit_a, lit_b) ==
+ Expr::make_concat(lit_b, lit_b));
}
TEST(LlvmLibcRegexExprTest, AlgebraicIdentitiesAlt) {
@@ -86,6 +107,11 @@ TEST(LlvmLibcRegexExprTest, AlgebraicIdentitiesAlt) {
auto lit_a = pool.make_lit('a').value();
auto lit_b = pool.make_lit('b').value();
auto empty_set = pool.empty_set().value();
+ auto empty_str = pool.empty_str().value();
+
+ // Invalid IDs return REG_BADPAT.
+ EXPECT_EQ(pool.make_alt(INVALID_EXPR_ID, lit_a).error(), REG_BADPAT);
+ EXPECT_EQ(pool.make_alt(lit_a, INVALID_EXPR_ID).error(), REG_BADPAT);
// (Ø | R) or (R | Ø) => R
EXPECT_EQ(pool.make_alt(empty_set, lit_a).value(), lit_a);
@@ -97,6 +123,12 @@ TEST(LlvmLibcRegexExprTest, AlgebraicIdentitiesAlt) {
// (A | B) == (B | A) (Commutativity)
EXPECT_EQ(pool.make_alt(lit_a, lit_b).value(),
pool.make_alt(lit_b, lit_a).value());
+
+ // Nullability of (L | R) when L is non-nullable and R (with higher ExprId) is
+ // nullable.
+ auto opt_b = pool.make_alt(lit_b, empty_str).val...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/232291
More information about the libc-commits
mailing list