Permalink
Browse files

Make isTypeStruct propagate types identically to instanceOf

Reviewed By: markw65

Differential Revision: D10853559

fbshipit-source-id: ddf129929c42ce1696ae509c09d1a3036e4183e2
  • Loading branch information...
mofarrell authored and hhvm-bot committed Nov 3, 2018
1 parent b8a49c7 commit f3e0189e229f5bc8fc112c92d5a70b5025d7cc18
Showing with 71 additions and 34 deletions.
  1. +43 −34 hphp/hhbbc/interp.cpp
  2. +27 −0 hphp/test/slow/hhbbc/interface_bug2.php
  3. +1 −0 hphp/test/slow/hhbbc/interface_bug2.php.expect
@@ -1445,43 +1445,52 @@ void isTypeStructCJmpImpl(ISS& env,
const JmpOp& jmp) {
auto bail = [&] { impl(env, inst, jmp); };
auto const a = tv(topC(env));
if (!a || isValidTSType(*a, false)) return bail();
auto ts_type = type_of_type_structure(a->m_data.parr);
if (!ts_type) return bail();
auto const locId = topStkEquiv(env, 1);
if (locId == NoLocalId) return bail();
// TODO(T26859386): refine if ($x is nonnull) case
popC(env);
popC(env);
auto const negate = jmp.op == Op::JmpNZ;
auto const result = [&] (Type t, bool pass) {
if (!pass) {
auto tinit = remove_uninit(t);
if (tinit.subtypeOf(*ts_type)) return TBottom;
if (t.couldBe(BNull)) {
auto tnonnull = is_opt(tinit) ? unopt(tinit) : tinit;
if (ts_type->couldBe(BNull)) {
return tnonnull;
}
if (tnonnull.subtypeOf(*ts_type)) {
return TNull;
}
}
if (!a || !isValidTSType(*a, false)) return bail();
auto const is_nullable_ts = is_ts_nullable(a->m_data.parr);
auto const ts_kind = get_ts_kind(a->m_data.parr);
// type_of_type_structure does not resolve these types. It is important we
// do resolve them here, or we may have issues when we reduce the checks to
// InstanceOfD checks. This logic performs the same exact refinement as
// instanceOfD will.
if (!is_nullable_ts &&
(ts_kind == TypeStructure::Kind::T_class ||
ts_kind == TypeStructure::Kind::T_interface ||
ts_kind == TypeStructure::Kind::T_xhp ||
ts_kind == TypeStructure::Kind::T_unresolved)) {
auto const clsName = get_ts_classname(a->m_data.parr);
auto const rcls = env.index.resolve_class(env.ctx, clsName);
if (!rcls || !rcls->resolved() || rcls->cls()->attrs & AttrEnum) {
return bail();
}
return t;
auto const locId = topStkEquiv(env, 1);
if (locId == NoLocalId || interface_supports_non_objects(clsName)) {
return bail();
}
if (t.couldBe(BUninit) && ts_type->couldBe(BNull)) {
return union_of(intersection_of(std::move(t),
std::move(ts_type.value())),
TUninit);
auto const val = topC(env, 1);
auto const instTy = subObj(*rcls);
if (val.subtypeOf(instTy) || !val.couldBe(instTy)) {
return bail();
}
return intersection_of(std::move(t), std::move(ts_type.value()));
};
auto const pre = [&] (Type t) { return result(std::move(t), negate); };
auto const post = [&] (Type t) { return result(std::move(t), !negate); };
refineLocation(env, locId, pre, jmp.target, post);
// If we have an optional type, whose unopt is guaranteed to pass
// the instanceof check, then failing to pass implies it was null.
auto const fail_implies_null = is_opt(val) && unopt(val).subtypeOf(instTy);
popC(env);
popC(env);
auto const negate = jmp.op == Op::JmpNZ;
auto const result = [&] (Type t, bool pass) {
return pass ? instTy : fail_implies_null ? TNull : t;
};
auto const pre = [&] (Type t) { return result(t, negate); };
auto const post = [&] (Type t) { return result(t, !negate); };
refineLocation(env, locId, pre, jmp.target, post);
} else {
return bail();
}
}
} // namespace
@@ -0,0 +1,27 @@
<?hh
class Foo {}
interface IFoo {
require extends Foo;
}
class Bar extends Foo implements IFoo {}
function bar() {
define('BUZ', 42);
global $g;
return $g ? new Foo() : new Bar();
}
function foo() {
define('FIZ', 42);
$f = bar();
if ($f !== null && $f is IFoo) {
return $f;
}
return $f;
}
echo "foo";

0 comments on commit f3e0189

Please sign in to comment.