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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions