Skip to content

steins-infer: the value relation judges null, arrays, closures and native tops as PHP does (#951, #941, #952, #1051 S1) - #1060

Merged
zonuexe merged 18 commits into
masterfrom
claude/ta-s1-value-relation
Oct 11, 2026
Merged

zonuexe merged 18 commits into
masterfrom
claude/ta-s1-value-relation

Conversation

@zonuexe

@zonuexe zonuexe commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Slice S1 of the type/arity reproduction run (#1051). It opens the native value relation's deliberate silences for #951 a–f, #941 a/b and #952 l1, following the PHP 8.5.11 table in #1051 §C5.

What changed

  • steins-syntax: a parameter now records a native top (array, object, iterable or callable, optionally nullable) as Param.top. Before, lower_hint dropped it.
  • crates/steins-infer/src/value_tops.rs (new): top_is_type_error, the closure certainty check, and the builtin null rejection.
    • It is wired at the direct pass, propagation, method and constructor calls, closure calls, the builtin arm, return, and the typed-property seam.
  • is_type_error:
    • Null is rejected by every non-nullable native type, class, intersection and DNF types included. The = null default and the builtin scalar coercive carve-out still accept it.
    • An array is rejected by every native type.
    • A closure is judged as the final Closure class.
    • A written new or enum case reaches the typed-property seam.
  • A callable string or array literal is convicted only through the existing absence ladders: sidecar available, dam clear, and the boot surface answering absent. New legs in absence.rs handle this.

Validation

  • A generated PHP 8.5.11 grid, 24 values × 23 types × the argument, return and property seams × both modes, gives 0 false positives. True positives in strict rise from 445 to 1097 cells.
  • The coercion-grid harness now asserts zero divergences for userland and internal functions. Its array and class/null exceptions are gone.
  • value_relation_openings.rs has 69 tests. 36 fail on base; the controls and declines pass on both.
  • Two old pins were updated for the opened rows.
  • The syntax, infer, xtask and cli suites pass, as do workspace clippy and rustdoc with -D warnings.

Declined (pinned)

Measurement (ten public packages)

  • Strict: 17206 → 17207. Default: 33 → 34. The one added finding is in phpunit's tests/end-to-end/logging/_files/TypeErrorTest.php:22, a type.property-mismatch for new DateTime into a DateTimeImmutable property. It is a deliberate TypeError fixture, a true positive, and is pinned in expected_proof_findings.toml.
  • effect-diff: identical.
  • Perf on composer is within noise (median 2.85 s base, 2.73 s head).

Not measured

  • The private half of the local fp-gate. The orchestrator runs it before merge.

Review outcome

Adversarial review: four rounds, now approved.

  • Round 1: five false-positive shapes.
  • Round 2: the first fix forgot tested values after the join, which lost master true positives. Spelled-out forms of is_callable did not vouch, and the trait decline was too broad. Fixes:
    • Forgetting is now branch-scoped.
    • The syntax_only argument binds by position or by name.
    • __set is looked up through trait use chains.
  • Round 3: is_numeric was decided with PHP 8 rules under a PHP 7.4 pin. A type test is now decided on a proven value only where every PHP from 7.4 up agrees. That was checked on 18 predicates × 24 values on 7.4.33, 8.0.28 and 8.5.11. The only version-dependent cells are is_numeric on a numeric string with trailing whitespace, which stays undecided.
  • Accepted trade-off: after if (T) { return; }, where T is a test the walk cannot decide (for example ctype_* or method_exists), the tested value stays forgotten, because the surviving side may be the only live one. This loses 4 witness reports that master made. It is the sound direction.
  • Guard grid: 0 FP cells out of 27 predicates × 6 values. The value grid has 0 FPs and 1097 TPs in strict.
  • Public corpus: with and without the sidecar, nothing removed. The only addition is the pinned phpunit true positive.

@zonuexe
zonuexe force-pushed the claude/ta-s1-value-relation branch from cffef3c to 1a4064e Compare October 11, 2026 09:19
zonuexe added a commit that referenced this pull request Oct 11, 2026
…bles it reads, so a test of get_debug_type($x), $x ?? d or $x?->p names $x (#1051, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…ose type test the walk cannot decide over a proven value forgets that value on both sides (#1051, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…hod it names, and a callable name or object is judged where the branch's vouches are, so a guarded name and a guarded undefined-function call stay silent (#941, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…class's private, an unrelated scope's protected or readonly, a chain with __set, a trait or an unseen ancestor) is not judged, and a class an unminted class_alias names rejects no object (#952, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…uard table, private, protected and __set writes, and an alias of an engine class (#1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…take the review's rows: is_callable vouches, undecided type tests over proven values, writes that miss the typed slot, and unminted aliases (#951, #1060)
@zonuexe
zonuexe force-pushed the claude/ta-s1-value-relation branch from 1a4064e to cbef422 Compare October 11, 2026 09:55
zonuexe added a commit that referenced this pull request Oct 11, 2026
…bles it reads, so a test of get_debug_type($x), $x ?? d or $x?->p names $x (#1051, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…ose type test the walk cannot decide over a proven value forgets that value inside the guarded sides (#1051, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…hod it names, and a callable name or object is judged where the branch's vouches are, so a guarded name and a guarded undefined-function call stay silent (#941, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…class's private, an unrelated scope's protected or readonly, a chain with __set or an unseen ancestor) is not judged, and a class an unminted class_alias names rejects no object (#952, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…uard table, private, protected and __set writes, and an alias of an engine class (#1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…take the review's rows: is_callable vouches, undecided type tests over proven values, writes that miss the typed slot, and unminted aliases (#951, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
zonuexe added a commit that referenced this pull request Oct 11, 2026
… declares or an unresolvable trait, not on every trait use (#952, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…eral false, by position or by name, and never when it is true or unproven (#941, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…the guarded sides only and hands it back when every side falls through, and the is_* family, is_countable, is_a and is_subclass_of are decided on a proven value (#1051, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…ested value, is_callable's syntax_only binding, and traits without __set (#1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…view's rules: decided type tests, branch-scoped forgetting, is_callable's syntax_only, and __set through traits (#951, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…stays undecided, since PHP 7.4 and 8.0+ answer it differently, and every other predicate decided on a proven value is pinned as answering alike from 7.4 up (#1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…lue only where PHP 7.4 through 8.5 agree, naming is_numeric's trailing whitespace (#1060)
…inst class and scalar types, a written new against a typed property, and values against a native array, object, iterable or callable parameter (#951, #941, #952, #1051)
…nd the coercion grids assert no cell diverges from PHP (#951, #941, #952, #1051)
…the fp-gate pin for PHPUnit's TypeError fixture the property seam now reports (#951, #941, #952, #1051)
…teral, not a class constant resolving to one, and the descent decline is pinned (#941, #1051)
…bles it reads, so a test of get_debug_type($x), $x ?? d or $x?->p names $x (#1051, #1060)
…ose type test the walk cannot decide over a proven value forgets that value inside the guarded sides (#1051, #1060)
…take the review's rows: is_callable vouches, undecided type tests over proven values, writes that miss the typed slot, and unminted aliases (#951, #1060)
… declares or an unresolvable trait, not on every trait use (#952, #1060)
…eral false, by position or by name, and never when it is true or unproven (#941, #1060)
…the guarded sides only and hands it back when every side falls through, and the is_* family, is_countable, is_a and is_subclass_of are decided on a proven value (#1051, #1060)
…ested value, is_callable's syntax_only binding, and traits without __set (#1060)
…view's rules: decided type tests, branch-scoped forgetting, is_callable's syntax_only, and __set through traits (#951, #1060)
…stays undecided, since PHP 7.4 and 8.0+ answer it differently, and every other predicate decided on a proven value is pinned as answering alike from 7.4 up (#1060)
…lue only where PHP 7.4 through 8.5 agree, naming is_numeric's trailing whitespace (#1060)
@zonuexe
zonuexe force-pushed the claude/ta-s1-value-relation branch from e42ecde to ffa00a3 Compare October 11, 2026 10:51
@zonuexe
zonuexe marked this pull request as ready for review October 11, 2026 10:57
@zonuexe
zonuexe merged commit 89f465f into master Oct 11, 2026
7 checks passed
@zonuexe
zonuexe deleted the claude/ta-s1-value-relation branch October 11, 2026 10:57
zonuexe added a commit that referenced this pull request Oct 11, 2026
…bles it reads, so a test of get_debug_type($x), $x ?? d or $x?->p names $x (#1051, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…ose type test the walk cannot decide over a proven value forgets that value inside the guarded sides (#1051, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…hod it names, and a callable name or object is judged where the branch's vouches are, so a guarded name and a guarded undefined-function call stay silent (#941, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…class's private, an unrelated scope's protected or readonly, a chain with __set or an unseen ancestor) is not judged, and a class an unminted class_alias names rejects no object (#952, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…uard table, private, protected and __set writes, and an alias of an engine class (#1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…take the review's rows: is_callable vouches, undecided type tests over proven values, writes that miss the typed slot, and unminted aliases (#951, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
zonuexe added a commit that referenced this pull request Oct 11, 2026
… declares or an unresolvable trait, not on every trait use (#952, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…eral false, by position or by name, and never when it is true or unproven (#941, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…the guarded sides only and hands it back when every side falls through, and the is_* family, is_countable, is_a and is_subclass_of are decided on a proven value (#1051, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…ested value, is_callable's syntax_only binding, and traits without __set (#1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…view's rules: decided type tests, branch-scoped forgetting, is_callable's syntax_only, and __set through traits (#951, #1060)
zonuexe added a commit that referenced this pull request Oct 11, 2026
…stays undecided, since PHP 7.4 and 8.0+ answer it differently, and every other predicate decided on a proven value is pinned as answering alike from 7.4 up (#1060)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant