[ Typechecker ] <<__Memoize>> can cause the same object to be typed in incompatible ways
Author: lexidorCreated Jun 10, 2026Updated Jun 10, 2026
Labelshack
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:
- <<__Memoize>> says, if the arguments (all zero) are the same, return the old object.
- The hack typechecker believes that
$int_boxand$string_boxare different objects. - Setting
->valuevia either typed reference updates the other to a value of the wrong type. - 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: 768e783da4ee9ceac6534045f6369f37c27f9539Additional context A reified type argument gets a separate memoization cache, closing the typehole.
Source: facebook/hhvm