Skip to content

classname<T> and private covariant type permits type violation #7216

@acrylic-origami

Description

@acrylic-origami

HHVM Version

$ hhvm --version
HipHop VM 3.13.1 (rel)

Standalone code, or other way to reproduce the problem

Define a class covariant on one of its type parameters and assert the constructor is consistent:

<?hh // strict
<<__ConsistentConstruct>>
abstract class A<+T> {
    abstract public function __construct(T $v);
    abstract public function foo(): int;
}

Then extend that class and constrain that same type parameter to a derived type, and act on that type in some methods:

<?hh // strict
class Base {}
class Derived extends Base {
    public function __construct(public int $foo) {}
}
class B<+T as Derived> extends A<T> {
    public function __construct(private T $v) {}
    public function foo(): int {
        return $this->v->foo; // we assume here T is a subtype of Derived
    }
}

Cast the classname of the extended class to the classname of the base class parameterized with a supertype of the extended class's constraint. Invoke a method that acts on the type parameter in the extended class.

<?hh // strict
class C {
    public static function foo(): void {
        self::bar(B::class);
    }
    public static function bar(classname<A<Base>> $v): void {
        echo (new $v(new Base()))->foo(); // `B::foo` attempts to summon an int from thin air! 
                                          // It's not very effective.
    }
}

This is where B needs to be covariant on T: the typechecker doesn't allow this cast if it isn't.

Expected result

Some sort of variance violation? I don't know which is the more illegal step: allowing private properties of a covariant type, or the classname cast.

Actual result

No errors! from the typechecker, but

Notice: Undefined property: Base::$foo in B.php on line 10

Catchable fatal error: Value returned from method B::foo() must be of type int, null given in B.php on line 10

from executing C::foo().

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions