Skip to content

Compile-time return fatals have no id, and type.property-mismatch misses class values, static and trait properties, and destructure targets #952

Open

Description

@zonuexe

Witness

<?php
function k1(): void { return 5; }          // Fatal error: A void function must not return a value
<?php
function k2(): int { return; }             // Fatal error: A function with return type must return a value
<?php
declare(strict_types=1);
class A {} class B {}
class P { public A $a; public static int $n = 0; }
trait T { public int $t = 0; }
class Q { use T; }
function l1(): void { $p = new P(); $p->a = new B(); }   // TypeError: Cannot assign B to property P::$a of type A
function l2(): void { P::$n = "x"; }                      // TypeError: Cannot assign string to property P::$n of type int
function l3(): void { $q = new Q(); $q->t = "x"; }        // TypeError: Cannot assign string to property Q::$t of type int
function l4(): void { $p = new P(); [$p->a] = [new B()]; } // TypeError, through a destructure

The first two are compile-time fatals: the file never loads, so nothing in it runs. Steins reports nothing on any of the three files.

Two gaps

  1. Compile-time return fatals have no id. return <value>; in a void function (or a constructor) and a bare return; in a function with a non-void return type are rejected when the file compiles. That is a stronger claim than any runtime finding, and a purely syntactic one.
  2. type.property-mismatch covers only a scalar written to an instance property by plain assignment. It misses an object of the wrong class (l1), static properties (l2), properties declared in a trait (l3, with A class that uses any trait loses the declaration fatals and arity, and trait method bodies run only part of the walk #940), a destructure target (l4), braced or dynamic property names, and a set hook writing its own backing store.

How it was found

mago invalid-return-statement / invalid-property-assignment-value and mir InvalidReturnType / InvalidPropertyAssignmentValue fixtures.

Acceptance

k1/k2 report under a new compile-fatal id (or type.return-mismatch with a compile-time tail), and l1–l4 report type.property-mismatch.

Activity

  1. 10 remaining items

  2. zonuexe commented on Oct 11, 2026

    @zonuexe
    ContributorAuthor

    Part landed in #1060 (type/arity S1): l1 ($p->a = new B() into a typed property) now reports. Still open: l2 (static-property writes), l3 (trait properties, #1051 S8) and l4 (destructure targets).

  3. zonuexe commented on Oct 14, 2026

    @zonuexe
    ContributorAuthor

    l3 (typed property declared in a trait) landed in #1082. Still open: l2 (static-property writes) and l4 (destructure targets).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestready-for-agentFully specified, ready for an AFK agent

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions