in hphp/runtime/vm/jit/irgen-types.cpp [769:948]
bool emitIsTypeStructWithoutResolvingIfPossible(
IRGS& env,
const ArrayData* ts
) {
// Top of the stack is the type structure, so the thing we are checking is
// the next element
auto const t = topC(env, BCSPRelOffset { 1 });
auto const is_nullable_ts = is_ts_nullable(ts);
auto const cnsResult = [&] (bool value) {
popC(env); // pop the ts that's on the stack
popC(env); // pop the cell
push(env, cns(env, value));
return true;
};
auto const success = [&] { return cnsResult(true); };
auto const fail = [&] { return cnsResult(false); };
auto const primitive = [&] (Type ty, bool should_negate = false) {
auto const nty = is_nullable_ts ? ty|TNull : ty;
if (t->isA(nty)) return should_negate ? fail() : success();
if (!t->type().maybe(nty)) return should_negate ? success() : fail();
popC(env); // pop the ts that's on the stack
auto const c = popC(env);
auto const res = gen(env, should_negate ? IsNType : IsType, ty, c);
push(env, is_nullable_ts ? check_nullable(env, res, c) : res);
return true;
};
// We explicitly bind is_nullable_ts because failing to do so causes a
// spurious compiler error on some g++ versions.
auto const unionOf = [&,is_nullable_ts] (Type ty1, Type ty2,
auto&&... rest) {
auto const ty = Type::unionAll(ty1, ty2, rest...) |
(is_nullable_ts ? TNull : TBottom);
if (t->isA(ty)) return success();
if (!t->type().maybe(ty)) return fail();
popC(env); // pop the ts that's on the stack
auto const c = popC(env);
chain_is_type(env, c, is_nullable_ts, ty1, ty2, rest...);
return true;
};
if (t->isA(TNull) && is_nullable_ts) return success();
auto kind = get_ts_kind(ts);
switch (kind) {
case TypeStructure::Kind::T_int: return primitive(TInt);
case TypeStructure::Kind::T_bool: return primitive(TBool);
case TypeStructure::Kind::T_float: return primitive(TDbl);
case TypeStructure::Kind::T_string: {
if (t->type().maybe(TLazyCls) &&
RuntimeOption::EvalClassIsStringNotices) {
ifElse(env,
[&] (Block* taken) {
gen(env, CheckType, TLazyCls, taken, t);
},
[&] {
gen(env, RaiseNotice, cns(env, s_CLASS_IS_STRING.get()));
}
);
}
if (t->type().maybe(TCls) &&
RuntimeOption::EvalClassIsStringNotices) {
ifElse(env,
[&] (Block* taken) {
gen(env, CheckType, TCls, taken, t);
},
[&] {
gen(env, RaiseNotice, cns(env, s_CLASS_IS_STRING.get()));
}
);
}
return unionOf(TStr, TLazyCls, TCls);
}
case TypeStructure::Kind::T_null: return primitive(TNull);
case TypeStructure::Kind::T_void: return primitive(TNull);
case TypeStructure::Kind::T_keyset: return primitive(TKeyset);
case TypeStructure::Kind::T_nonnull: return primitive(TNull, true);
case TypeStructure::Kind::T_mixed:
case TypeStructure::Kind::T_dynamic:
return success();
case TypeStructure::Kind::T_num: return unionOf(TInt, TDbl);
case TypeStructure::Kind::T_arraykey: {
if (t->type().maybe(TLazyCls) &&
RuntimeOption::EvalClassIsStringNotices) {
ifElse(env,
[&] (Block* taken) {
gen(env, CheckType, TLazyCls, taken, t);
},
[&] {
gen(env, RaiseNotice, cns(env, s_CLASS_IS_STRING.get()));
}
);
}
if (t->type().maybe(TCls) &&
RuntimeOption::EvalClassIsStringNotices) {
ifElse(env,
[&] (Block* taken) {
gen(env, CheckType, TCls, taken, t);
},
[&] {
gen(env, RaiseNotice, cns(env, s_CLASS_IS_STRING.get()));
}
);
}
return unionOf(TInt, TStr, TLazyCls, TCls);
}
case TypeStructure::Kind::T_any_array:
return unionOf(TVec, TDict, TKeyset);
case TypeStructure::Kind::T_vec_or_dict:
case TypeStructure::Kind::T_varray_or_darray:
case TypeStructure::Kind::T_dict:
case TypeStructure::Kind::T_vec:
case TypeStructure::Kind::T_darray:
case TypeStructure::Kind::T_varray: {
popC(env); // pop the ts that's on the stack
auto const c = popC(env);
auto const res = [&]{
if (kind == TypeStructure::Kind::T_dict ||
kind == TypeStructure::Kind::T_darray) {
return isDictImpl(env, c);
} else if (kind == TypeStructure::Kind::T_vec ||
kind == TypeStructure::Kind::T_varray) {
return isVecImpl(env, c);
} else {
assertx(kind == TypeStructure::Kind::T_vec_or_dict ||
kind == TypeStructure::Kind::T_varray_or_darray);
return cond(
env,
[&](Block* taken) { gen(env, JmpZero, taken, isVecImpl(env, c)); },
[&] { return cns(env, true); },
[&] { return isDictImpl(env, c); }
);
}
}();
push(env, is_nullable_ts ? check_nullable(env, res, c) : res);
return true;
}
case TypeStructure::Kind::T_class:
case TypeStructure::Kind::T_interface:
case TypeStructure::Kind::T_xhp: {
auto const clsname = get_ts_classname(ts);
auto cls = lookupUniqueClass(env, clsname);
if (ts->exists(s_generic_types) &&
((classIsPersistentOrCtxParent(env, cls) &&
cls->hasReifiedGenerics()) ||
!isTSAllWildcards(ts))) {
// If it is a reified class or has non wildcard generics,
// we need to bail
return false;
}
popC(env); // pop the ts that's on the stack
auto const c = popC(env);
auto const res = implInstanceOfD(env, c, clsname);
push(env, is_nullable_ts ? check_nullable(env, res, c) : res);
return true;
}
case TypeStructure::Kind::T_nothing:
case TypeStructure::Kind::T_noreturn:
return fail();
case TypeStructure::Kind::T_typevar:
case TypeStructure::Kind::T_fun:
case TypeStructure::Kind::T_trait:
// Not supported, will throw an error on these at the resolution phase
return false;
case TypeStructure::Kind::T_enum:
case TypeStructure::Kind::T_tuple:
case TypeStructure::Kind::T_shape:
case TypeStructure::Kind::T_typeaccess:
case TypeStructure::Kind::T_unresolved:
case TypeStructure::Kind::T_resource:
case TypeStructure::Kind::T_reifiedtype:
// TODO(T28423611): Implement these
return false;
}
not_reached();
}