Skip to content

fix(translator): constructor, private-method and abstract redeclaration override rules - #56

Open
AlessioGiacobbe wants to merge 3 commits into
swoole:masterfrom
AlessioGiacobbe:split/parent-override-rules
Open

fix(translator): constructor, private-method and abstract redeclaration override rules#56
AlessioGiacobbe wants to merge 3 commits into
swoole:masterfrom
AlessioGiacobbe:split/parent-override-rules

Conversation

@AlessioGiacobbe

Copy link
Copy Markdown
Contributor

Three gaps in the parent-method override checks, all in checkParentMethodCanBeOverridden:

  • __construct overrides skipped every check via an early return — including final parent constructors (Zend: "Cannot override final method A::__construct()") and abstract-constructor signature contracts. Ordinary concrete parent constructors remain exempt from LSP variance, as in Zend (even visibility narrowing is legal there — probed).
  • Redeclaring a parent's private method was rejected, but private methods are not inherited in PHP: any signature is legal. Verified safe for this compiler's dispatch before changing: private-resolved calls devirtualize to the declaring class's body (canDevirtualize), matching PHP's private-scope binding, and Native classes give private methods no virtual slot. Two pre-existing tests encoded the rejects-valid behavior (both run under Zend) and were updated.
  • Abstract method redeclarations were never validated: turning a concrete inherited method abstract compiled (Zend: "Cannot make non abstract method A::f() abstract in class B") and abstract-over-abstract redeclarations skipped the signature check. Trait-originated abstract requirements stay exempt — Zend lets an inherited concrete method satisfy them (probed).

Verified against Zend 8.4.13 for every rule and direction.

Part of the split of #39.

checkParentMethodCanBeOverridden() returned immediately for
__construct, so overriding a FINAL parent constructor was accepted
(Zend: "Cannot override final method A::__construct()") and an
ABSTRACT parent constructor's signature was never validated (Zend
checks it exactly like an interface constructor).

Zend's constructor rules (zend_do_inheritance):

  - a concrete parent constructor imposes no signature contract: the
    child may change parameters and even narrow visibility — this
    exemption is kept;
  - a private parent constructor may be redeclared freely, but FINAL
    still wins: `final private function __construct()` cannot be
    overridden (constructors are the one place PHP allows final
    private);
  - an abstract parent constructor's signature is a real contract.

Keep walking the parent chain for constructors, skipping only the
private-override error and the concrete-signature validation; final
checks (userland and built-in parents) and abstract-constructor
validation now run.
checkParentMethodCanBeOverridden() fataled with "Cannot override
private method" when a child declared a method whose nearest parent
declaration is PRIVATE. Zend inherits no private methods: a child may
redeclare one with any signature, visibility or staticness, and FINAL
is ignored on non-constructor private methods (declaring one only
raises "Private methods cannot be final..."). Only the final private
CONSTRUCTOR remains protected, which the constructor path already
enforces.

Dispatch stays correct after removing the fatal:

  - canDevirtualize() (Parser/MethodCallTrait) devirtualizes any call
    whose resolved method is private to the DECLARING class's body.
    That is exactly PHP's private-scope binding (zend_std_get_method
    prefers the calling scope's private copy), verified against the
    manual's Bar/Foo::testPrivate example;
  - method resolution walks from the receiver's static class, so code
    in the child binds the child's redeclaration;
  - a call on a receiver statically typed as the declaring class from
    OUTSIDE its scope is rejected by getNativeMethod()'s accessibility
    check, and dynamically typed receivers go through Zend dispatch;
  - Native (C++) classes give private methods no virtual slot
    (isNativeVirtualMethod() excludes PRIVATE), so no C++ override can
    reroute a parent's internal private call.

The two tests asserting the old fatal encoded rejects-valid programs
(both run fine under Zend 8.4, printing the parent's private result);
they now assert successful compilation.
…parent chain

An abstract method declared by a class was never checked against its
parent: turning a concrete inherited method abstract compiled (Zend:
"Cannot make non abstract method A::f() abstract in class B"), and an
abstract redeclaration of an inherited abstract contract skipped the
signature check entirely. Both now run through
checkParentMethodCanBeOverridden with a childIsAbstract mode, covering
userland and built-in parents.

Trait-originated abstract requirements are exempt: Zend lets an
inherited concrete method satisfy them, so only abstract methods the
class itself declares participate.
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