bool emitIsTypeStructWithoutResolvingIfPossible()

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();
}