Skip to content

[ Typechecker ] <<__Memoize>> can cause the same object to be typed in incompatible ways #9797

Description

@lexidor

Describe the bug
<<__Memoize>> can cause the same object to be typed in incompatible ways.

Standalone code, or other way to reproduce the problem

final class Box<T> {
  public function __construct(public ?T $value)[] {}
}

<<__Memoize>>
function smuggling_boxes<Tv>()[]: Box<Tv> {
  return new Box<Tv>(null);
}

<<__EntryPoint>>
function main()[write_props]: void {
  $int_box = smuggling_boxes<int>();
  $int_box->value = 2;

  $string_box = smuggling_boxes<string>();
  $string_box->value = 'Oh no!';

  $oh_no = $int_box->value;

  if ($oh_no !== null) {
    $_ = $oh_no * 6;
  }
}

Steps to reproduce the behavior:

  1. <<__Memoize>> says, if the arguments (all zero) are the same, return the old object.
  2. The hack typechecker believes that $int_box and $string_box are different objects.
  3. Setting ->value via either typed reference updates the other to a value of the wrong type.
  4. In this example, multiplying 'Oh no!' by 6 blows up at runtime.

Expected behavior

This typehole exists in strict Hack. Closing this typehole would cause churn in correct code. I am not sure if it should be closed.

Actual behavior

hh_client No errors!

Fatal error: Uncaught exception 'InvalidOperationException' with message 'Cannot perform mathematical operation on string and int'

Environment

  • Operating system

For example, 'Docker Ubuntu 26.04 on host, 24.04 in container'

  • Installation method

'docker hersheltheodorelayton/hhvm-basic'

  • HHVM Version
HipHop VM 26.3.28 (rel) (non-lowptr)
Compiler: heads/hhvm-oss-20260328-0-gcf488896c50fc865bc5d3e15608f9f1503f1343f
Repo schema: 768e783da4ee9ceac6534045f6369f37c27f9539

Additional context
A reified type argument gets a separate memoization cache, closing the typehole.

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