Skip to content

Commit bb5e5d6

Browse files
vassilmladenovfacebook-github-bot
authored andcommitted
Build POISON_MARKER and TANY_MARKER
Summary: We introduce two new type aliases to the `HH\FIXME` namespace such that | Alias | Current Value | Sound Dynamic Value | | -- | -- | -- | | POISON_MARKER<T> | T | ~T | | TANY_MARKER<T> | Tany | T | > NOTE: in the examples below, commented out method declarations represent the code before migration. `POISON_MARKER` will be used for hierarchy poisoning when an unenforceable type overrides an enforced one e.g. a closure ``` class C { // public function f(): nonnull {} public function f(): HH\FIXME\POISON_MARKER<nonnull> {} } class D extends C { public function f(): (function (): void) { /* HH_FIXME[4110] */ return null; } } /** * POISON_MARKER will retain current behavior -- expression has type nonnull * but will become ~nonnull under sound dynamic */ function get_nonnull(C $c): nonnull { return $c->f(); // type hint violation, returning null } function test(): void { get_nonnull(new D()); } ``` `TANY_MARKER` will be used for migrating existing uses of Tany to desired types under sound dynamic without affecting current inference. This is why it points to `T` and not `~T` -- we want full manual control here. In some cases we may want a combination of TANY_MARKER and POISON_MARKER. ``` class C { // public function f(): int { public function f(): HH\FIXME\POISON_MARKER<arraykey> { // slightly weaken enforcement return 1; } } class D extends C { // public function f(): DECL_TANY { public function f(): HH\FIXME\TANY_MARKER<string> { return "hello"; } } class E extends D { public function f(): string { return "hello"; } } ``` ## Implementation There is some sleight of hand here - despite outward appearance, the implementation of these aliases diverges. - For `POISON_MARKER`, I've stripped off the alias for both type constraints and type structures, now matches like types in hint positions and almost matches for type structures (see test cases). The upshot is the runtime doesn't know about POISON_MARKER's existence. - For `TANY_MARKER`, I strip off the type arguments to simulate how we do decl Tanys, but HHVM still needs to have the symbol name in scope to create the OF_GENERIC type structure, so it's declared in systemlib. See the `tany.php` example from D40516837 (9e8c173) for an analogue. Reviewed By: andrewjkennedy Differential Revision: D40371013 fbshipit-source-id: d2d9e4f4d57f6f88d1c31201d60d9c9e19656173
1 parent 8159883 commit bb5e5d6

17 files changed

Lines changed: 198 additions & 0 deletions

hphp/hack/hhi/soundness.hhi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,8 @@ function UNSAFE_CAST<<<__Explicit>> Tin, <<__Explicit>> Tout>(Tin $t, ?\HH\Forma
4646
* ```
4747
*/
4848
function UNSAFE_NONNULL_CAST<T as nonnull>(?T $t, ?\HH\FormatString<nothing> $msg = null)[]: T;
49+
50+
/* Acts as Tany under current semantics, and T under sound dynamic */
51+
type TANY_MARKER<T> = T;
52+
/* Acts as T under current semantics, and ~T under sound dynamic */
53+
type POISON_MARKER<T> = T;

hphp/hack/src/hackc/emitter/emit_type_constant.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use hhbc::TypedValue;
1212
use hhbc_string_utils as string_utils;
1313
use hhvm_types_ffi::ffi::TypeStructureKind;
1414
use naming_special_names_rust::classes;
15+
use naming_special_names_rust::typehints;
1516
use options::Options;
1617
use oxidized::aast;
1718
use oxidized::aast_defs;
@@ -287,6 +288,20 @@ fn hint_to_type_constant_list<'arena>(
287288
]);
288289
}
289290
}
291+
let hints = match &hints[..] {
292+
[h] if name == typehints::POISON_MARKER => {
293+
return hint_to_type_constant_list(
294+
alloc,
295+
opts,
296+
tparams,
297+
targ_map,
298+
type_refinement_in_hint,
299+
h,
300+
);
301+
}
302+
[_h] if name == typehints::TANY_MARKER => <&[Hint]>::default(),
303+
_ => hints,
304+
};
290305
let (classname, s_res) = resolve_classname(alloc, tparams, name.to_owned());
291306
let mut r = bumpalo::vec![in alloc];
292307
if s_res.eq_ignore_ascii_case("tuple") || s_res.eq_ignore_ascii_case("shape") {

hphp/hack/src/hackc/emitter/emit_type_hint.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,12 @@ fn hint_to_type_constraint<'arena>(
285285
_ => hint_to_type_constraint(alloc, kind, tparams, false, &hs[0]),
286286
};
287287
}
288+
[h] if s == typehints::POISON_MARKER => {
289+
return hint_to_type_constraint(alloc, kind, tparams, false, h);
290+
}
291+
[_h] if s == typehints::TANY_MARKER => {
292+
return Ok(Constraint::default());
293+
}
288294
_ => {}
289295
};
290296
type_application_helper(alloc, tparams, kind, s)?

hphp/hack/src/naming/naming_special_names.ml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,12 @@ module HH = struct
10901090
let contains = "\\HH\\Lib\\C\\contains"
10911091

10921092
let contains_key = "\\HH\\Lib\\C\\contains_key"
1093+
1094+
module FIXME = struct
1095+
let tTanyMarker = "\\HH\\FIXME\\TANY_MARKER"
1096+
1097+
let tPoisonMarker = "\\HH\\FIXME\\POISON_MARKER"
1098+
end
10931099
end
10941100

10951101
module Shapes = struct

hphp/hack/src/naming/naming_special_names.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,9 @@ pub mod typehints {
661661

662662
pub const HH_SUPPORTDYN: &str = "\\HH\\supportdyn";
663663

664+
pub const TANY_MARKER: &str = "\\HH\\FIXME\\TANY_MARKER";
665+
pub const POISON_MARKER: &str = "\\HH\\FIXME\\POISON_MARKER";
666+
664667
pub const WILDCARD: &str = "_";
665668

666669
lazy_static! {

hphp/hack/src/typing/typing_phase.ml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,21 @@ let rec localize ~(ety_env : expand_env) env (dty : decl_ty) =
227227
when String.equal x Naming_special_names.FB.cIncorrectType
228228
&& Env.is_typedef env x ->
229229
localize ~ety_env env (mk (get_reason dty, Tlike arg))
230+
| Tapply ((_, x), [arg])
231+
when String.equal x Naming_special_names.HH.FIXME.tTanyMarker ->
232+
if TypecheckerOptions.enable_sound_dynamic (Env.get_tcopt env) then
233+
localize ~ety_env env arg
234+
else
235+
((env, None), mk (r, Typing_utils.tany env))
236+
| Tapply ((_, x), [arg])
237+
when String.equal x Naming_special_names.HH.FIXME.tPoisonMarker ->
238+
let decl_ty =
239+
if TypecheckerOptions.enable_sound_dynamic (Env.get_tcopt env) then
240+
mk (get_reason dty, Tlike arg)
241+
else
242+
arg
243+
in
244+
localize ~ety_env env decl_ty
230245
| Tapply (((_p, cid) as cls), argl) ->
231246
begin
232247
match Env.get_class_or_typedef env cid with
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?hh
2+
3+
function f(
4+
HH\FIXME\TANY_MARKER<int> $tany,
5+
HH\FIXME\POISON_MARKER<string> $poison,
6+
vec<HH\FIXME\TANY_MARKER<float>> $vec_float,
7+
vec<HH\FIXME\TANY_MARKER<HH\FIXME\POISON_MARKER<float>>> $vec_like_float,
8+
): void {
9+
hh_show($tany);
10+
hh_show($poison);
11+
hh_show($vec_float);
12+
hh_show($vec_like_float);
13+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
File "marker_types.good.php", line 9, characters 3-16:
2+
int
3+
File "marker_types.good.php", line 10, characters 3-18:
4+
~string
5+
File "marker_types.good.php", line 11, characters 3-21:
6+
vec<float>
7+
File "marker_types.good.php", line 12, characters 3-26:
8+
vec<~float>
9+
No errors
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?hh
2+
3+
function f(
4+
HH\FIXME\TANY_MARKER<int> $tany,
5+
HH\FIXME\POISON_MARKER<string> $poison,
6+
vec<HH\FIXME\TANY_MARKER<float>> $vec_tany,
7+
vec<HH\FIXME\TANY_MARKER<HH\FIXME\POISON_MARKER<float>>> $vec_tany2,
8+
): void {
9+
hh_show($tany);
10+
hh_show($poison);
11+
hh_show($vec_tany);
12+
hh_show($vec_tany2);
13+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
File "sound_dynamic_marker_types.php", line 9, characters 3-16:
2+
_
3+
File "sound_dynamic_marker_types.php", line 10, characters 3-18:
4+
string
5+
File "sound_dynamic_marker_types.php", line 11, characters 3-20:
6+
vec<_>
7+
File "sound_dynamic_marker_types.php", line 12, characters 3-21:
8+
vec<_>
9+
No errors

0 commit comments

Comments
 (0)