GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — [RFC] Pattern matching by iluuu1994 · Pull Request #20574 · php/php-src

Conversation

@iluuu1994

@iluuu1994 iluuu1994 commented Nov 24, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

https://wiki.php.net/rfc/pattern-matching

Known issues / TODOs:

  • Array elements are fetched with FETCH_DIM_IS to avoid erroring for accessign non-existent indexes. Because undefined indexes evalute to null, ['a' => 42] is ['b' => null] will evaluate to true as a side-effect. This may be solved by returning IS_UNDEF from FETCH_DIM_IS. This may break some expectations, as we currently don't store IS_UNDEF in VARs. This approach was suggested by @bwoebi.
  • The RFC specifies 42 is float should return true. This does not currently work. It's unclear what the behavior for ints outside the range of precisely representable floats should be.
  • Prevent $foo is [$bar, $bar].
  • Prototype variable & constant pinning to decide whether to include them right away.
  • Prototype ranges to decide whether to include them right away.
  • Use JMPZ with smart branches disabled instead of JMPZ_EX.

Further technical considerations:

  • The implementation is currently emitting various COPY_TMP ops. These could be avoided by creating specialized ops that don't free op1. This is the case for:
    • QM_ASSIGN for bindings. This one may be avoidable by pulling bindings to the tail.
    • TYPE_CHECK for array patterns. Alternatively we could use the new HAS_TYPE opcode.
    • COUNT for array patterns.
    • FETCH_DIM_IS for array patterns. Effectively, we want an IS variant of FETCH_LIST_*.
    • INSTANCEOF for object patterns. Alternatively we could use the new HAS_TYPE opcode.
    • FETCH_OBJ_R for object patterns.
  • mixed could be a no-op. This has some some implications on branching, so it might be enough to let the optimizer elide it (which already happens).
  • The new HAS_TYPE opcode allows performing arbitrary type checks. However, pattern matching doesn't currently combine these types discovered at compile time. I.e. int|float should be a simple type mask, but currently compiles to two HAS_TYPE checks.
  • The implementaiton currently uses JMP[N]Z_EX to immediately redeclare result which it consumes. This is mostly unnecessary, as bools aren't consumed at all. The other side-effect is that JMP[N]Z_EX isn't a smart branch. This is relevant because smart branches skip assigning to result, which is necessary here. We may be able to use JMP[N]Z by explicitly removing the smart branch flags, and making sure the optimizer doesn't re-add them.
  • Avoid zend_pm_kill_last_op_if_jmp(). Instead, make sure the final jump is not emitted in the first place. This is somewhat annoying since it needs handling in all patterns, sometimes in multiple places (zend_pm_compile_container()).
  • CVs are currently used directly. However, some operations (currently only FETCH_DIM_R) may mutate CVs as a side-effect (e.g. in get through references, or globals). IMO, guarding against this is not worth it, as that would require an initial copy and subsequent COPY_TMPs. It's just worth pointing out.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

javiereguiluz, frederikbosch, imliam, foremtehan, rekmixa, MrLanzelord, and fire1 reacted with thumbs up emoji KennedyTedesco, javiereguiluz, medabkari, davidbyoung, klimick, nuernbergerA, rekmixa, and MrLanzelord reacted with hooray emoji KennedyTedesco, javiereguiluz, ClaudioEyzaguirre, jack-worman, medabkari, klimick, nuernbergerA, rekmixa, MrLanzelord, and fire1 reacted with heart emoji KennedyTedesco, javiereguiluz, jack-worman, medabkari, Novynn, klimick, nuernbergerA, rekmixa, and MrLanzelord reacted with rocket emoji KennedyTedesco, Sam-Mear, Danil42Russia, rekmixa, and MrLanzelord reacted with eyes emoji
All reactions
  • 👍 7 reactions
  • 🎉 8 reactions
  • ❤️ 10 reactions
  • 🚀 9 reactions
  • 👀 5 reactions
iluuu1994 added 4 commits November 22, 2025 18:54
@iluuu1994
Implement pattern matching
febcdb8
@iluuu1994
Switch to alternative object syntax
c7024e4
Topic of discussion for the community.
@iluuu1994
[skip ci] Add support for IS_TYPE to zend_dump.c
9c9eedc
@iluuu1994
Improve sccp
542df1c
Allow propagating inferred true/false, which doesn't require a concrete value.
@github-actions github-actions Bot added Category: Engine Extension: opcache Extension: tokenizer Category: Optimizer ABI break labels Nov 24, 2025
@iluuu1994 iluuu1994 mentioned this pull request Nov 24, 2025
Pattern matching 3 iluuu1994/php-src#165
Closed
TimWolla
TimWolla reviewed Nov 25, 2025
View reviewed changes
Comment thread Zend/zend_ast.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

TimWolla
TimWolla reviewed Nov 25, 2025
View reviewed changes
Comment thread Zend/zend_compile.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Zend/tests/pattern_matching/is/bail.phpt Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Zend/tests/pattern_matching/is/bail.phpt Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Zend/zend_compile.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Zend/zend_compile.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Zend/zend_compile.c Outdated
zend_stack_push(&context->bindings, &binding);

// FIXME: This can be elided most of the time. It may not for:
// - $a is $b

@TimWolla TimWolla Nov 25, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

As I think I noted in the discussion, I believe $a is $b with a bare variable on the right should just be a compile-time error.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@iluuu1994 iluuu1994 Nov 25, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I understand your reservation here is the confusion of $a is $b as $a === $b, but this seems like an arbitrary restriction. For match specifically, it seems somewhat reasonable. For example:

match ($userOrError) {
    is User & $user => login($user),
    is $error => showError($error),
};

The rename serves a purpose in the readability sense. Only preventing standalone bindings in is but not match is possible, but seems even more arbitrary.

I don't have too strong feelings on this, so if you can convince @Crell I'm happy to make the change.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@TimWolla TimWolla Nov 25, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I understand your reservation here is the confusion of $a is $b as $a === $b,

Yes. Especially since Python's is for example is a comparison operator.

but this seems like an arbitrary restriction

I feel like this is something that would be pointed out by static analysis tools / IDEs anyways as a possible source of bug, because there is rarely (if ever) a reason to write this. To me it would therefore be helpful to take this footgun away from folks within the language itself so that they don't need to rely on third party tools. I'd be okay with allowing it as $a is ($b) which is similar to how the extra parentheses in:

if (($user = $repository->find($id))) {

}

generally suppress the warnings from IDEs that ask about possible = and == confusion.

Only preventing standalone bindings in is but not match is possible, but seems even more arbitrary.

Even in match it is misleading, because it effectively is a default that doesn't look like one. is mixed&$error or possibly is default&$error would both be clearer to me.


But I'm happy to figure this out as part of the proper ML discussion rather than inside this PR where no one will find it in the future.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Zend/zend_compile.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Zend/zend_language_parser.y Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Zend/zend_language_scanner.l Resolved

Uh oh!

There was an error while loading. Please reload this page.

@iluuu1994
Use system rounded_up_to_pow2() if available
1d3eefe
@github-actions github-actions Bot added the Category: Build System label Nov 25, 2025
iluuu1994 added 5 commits November 25, 2025 16:36
@iluuu1994
[skip ci] Name structs
20943c0
@iluuu1994
[skip ci] Improve test output for exceptions
08727b2
@iluuu1994
[skip ci] Add comment to explain why we're using negation for label o…
65b0b10
…ffsets
@iluuu1994
[skip ci] Remove fixme
522d4af
It's unclear as of yet whether eliding mixed is useful, especially given the
optimizer already supports it. For objects, mixed is useless. For arrays, we
need some comparison to check whether the offset is IS_UNDEF, so it can't be
fully elided anyway.

Hence, forget about this for now.
@iluuu1994
[skip ci] Remove leftover ..< and ..= operators
7eaa949
TimWolla
TimWolla reviewed Nov 25, 2025
View reviewed changes
Comment thread Zend/tests/pattern_matching/is/binding_destruct.phpt Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Zend/tests/pattern_matching/is/object.phpt
Comment on lines +83 to +100
bool(true)
bool(false)
bool(false)
bool(false)
bool(false)
bool(true)
bool(true)
bool(true)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(true)
bool(true)
bool(false)

@TimWolla TimWolla Nov 25, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

It would help the readability of the test if this would be structured into “these should match” and “these shouldn't”, possibly with a clear marker in-between. The one doesn't need to cross-reference every single line from the expectation and the test code. Or alternatively clearly separated for each of the variables “$foo up next”, “$bar up next”, “null up next”.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Zend/tests/type_declarations/static_type_return.phpt Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Zend/zend_compile.h Resolved

Uh oh!

There was an error while loading. Please reload this page.

iluuu1994 added 3 commits November 25, 2025 18:05
@iluuu1994
[skip ci] Remove unused macro
47e16f1
@iluuu1994
Fix conflicting ast flags
91a4b45
@iluuu1994
[skip ci] Specify error class in test
249c88c
TimWolla
TimWolla reviewed Nov 26, 2025
View reviewed changes

@TimWolla TimWolla left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Test-wise I believe a test for $foo is "$foo" and $foo is "\x00" is missing (i.e. for interpolation and escape characters).

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Zend/tests/pattern_matching/is/rcd_expr.phpt
@@ -0,0 +1,19 @@
--TEST--
Refcoutned expression

@TimWolla TimWolla Nov 26, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Typo in description. It's also awfully generic. I generally try to prefix to prefix the description with the “component name”, this makes it easier to distinguish tests in the output without looking at the file name.

Perhaps something like Pattern Matching: Refcounted match expression or something like this?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Zend/zend_ast.c
uint32_t rhs_count = rhs->kind == kind ? zend_ast_get_list(rhs)->children : 1;
uint32_t count = lhs_count + rhs_count;
uint32_t size = stdc_bit_ceil_ui(count);
if (size < 4) size = 4;

@TimWolla TimWolla Nov 26, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Suggested change
if (size < 4) size = 4;
if (size < 4) {
size = 4;
}

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
iluuu1994 added 4 commits November 26, 2025 20:04
@iluuu1994
Implement variable and constant pinning
8333a54
This is basically free.
@iluuu1994
Implement range pattern
1d8c8cf
@iluuu1994
Implement comparison pattern
ed0b82d
We'll go with either ranges or this, yet to decide.
@iluuu1994
Undefined array key should not match null
b45c947
@thekid thekid mentioned this pull request Dec 21, 2025
Make compatible with PHP's pattern matching RFC xp-lang/php-is-operator#2
Merged
8 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@TimWolla TimWolla TimWolla left review comments
@kocsismate kocsismate Awaiting requested review from kocsismate kocsismate will be requested when the pull request is marked ready for review kocsismate is a code owner
@dstogov dstogov Awaiting requested review from dstogov dstogov will be requested when the pull request is marked ready for review dstogov is a code owner

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

ABI break Category: Build System Category: Engine Category: Optimizer Extension: opcache Extension: tokenizer

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@iluuu1994 @TimWolla
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.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.