/* * Note: this is currently separate from typeFromRAT for now, just because we * don't want to enable every single type for assertions yet. * * (Some of them currently regress performance, presumably because the IR * doesn't always handle the additional type information very well. It is * possibly a compile-time slowdown only, but we haven't investigated yet.) */ folly::Optional<Type> ratToAssertType(IRGS& env, RepoAuthType rat) { using T = RepoAuthType::Tag; switch (rat.tag()) { case T::Uninit: case T::InitNull: case T::Null: case T::Bool: case T::Int: case T::Dbl: case T::Res: case T::SStr: case T::Str: case T::Obj: case T::SArr: case T::Arr: case T::Cell: case T::Ref: case T::InitUnc: case T::Unc: return typeFromRAT(rat); case T::OptExactObj: case T::OptSubObj: case T::ExactObj: case T::SubObj: { auto ty = typeFromRAT(rat); auto const cls = Unit::lookupClassOrUniqueClass(rat.clsName()); if (!classIsUniqueOrCtxParent(env, cls)) { ty |= TObj; // Kill specialization. } return ty; } // Type assertions can't currently handle Init-ness. case T::InitCell: return TCell; case T::InitGen: return folly::none; case T::Gen: return folly::none; case T::OptInt: case T::OptObj: case T::OptDbl: case T::OptBool: case T::OptSStr: case T::OptStr: case T::OptRes: return folly::none; case T::OptSArr: case T::OptArr: // TODO(#4205897): optional array types. return folly::none; } not_reached(); }
// Simple stats about opcodes (that don't require full type // information---those cases are only enabled when extendedStats is // on). void collect_simple(Stats& stats, const Bytecode& bc) { ++stats.op_counts[static_cast<uint64_t>(bc.op)]; RepoAuthType rat; switch (bc.op) { case Op::AssertRATL: rat = bc.AssertRATL.rat; break; case Op::AssertRATStk: rat = bc.AssertRATStk.rat; break; default: return; } using U = std::underlying_type<RepoAuthType::Tag>::type; auto const tagInt = static_cast<U>(rat.tag()); assert(tagInt < stats.ratL_tags.size()); if (bc.op == Op::AssertRATL) { ++stats.ratL_tags[tagInt]; } else { ++stats.ratStk_tags[tagInt]; } if (rat.mayHaveArrData()) { if (rat.array()) { if (bc.op == Op::AssertRATL) { ++stats.ratL_specialized_array; } else { ++stats.ratStk_specialized_array; } } } }
Type convertToType(RepoAuthType ty) { using T = RepoAuthType::Tag; switch (ty.tag()) { case T::OptBool: return Type::Bool | Type::InitNull; case T::OptInt: return Type::Int | Type::InitNull; case T::OptSStr: return Type::StaticStr | Type::InitNull; case T::OptStr: return Type::Str | Type::InitNull; case T::OptDbl: return Type::Dbl | Type::InitNull; case T::OptRes: return Type::Res | Type::InitNull; case T::OptObj: return Type::Obj | Type::InitNull; case T::Uninit: return Type::Uninit; case T::InitNull: return Type::InitNull; case T::Null: return Type::Null; case T::Bool: return Type::Bool; case T::Int: return Type::Int; case T::Dbl: return Type::Dbl; case T::Res: return Type::Res; case T::SStr: return Type::StaticStr; case T::Str: return Type::Str; case T::Obj: return Type::Obj; case T::Cell: return Type::Cell; case T::Ref: return Type::BoxedCell; case T::InitUnc: return Type::UncountedInit; case T::Unc: return Type::Uncounted; case T::InitCell: return Type::InitCell; case T::InitGen: return Type::Init; case T::Gen: return Type::Gen; // TODO(#4205897): option specialized array types case T::OptArr: return Type::Arr | Type::InitNull; case T::OptSArr: return Type::StaticArr | Type::InitNull; case T::SArr: if (auto const ar = ty.array()) return Type::StaticArr.specialize(ar); return Type::StaticArr; case T::Arr: if (auto const ar = ty.array()) return Type::Arr.specialize(ar); return Type::Arr; case T::SubObj: case T::ExactObj: if (auto const cls = Unit::lookupUniqueClass(ty.clsName())) { return Type::Obj.specialize(cls); } return Type::Obj; case T::OptSubObj: case T::OptExactObj: if (auto const cls = Unit::lookupUniqueClass(ty.clsName())) { return Type::Obj.specialize(cls) | Type::InitNull; } return Type::Obj | Type::InitNull; } not_reached(); }
bool RepoAuthType::operator==(RepoAuthType o) const { using T = Tag; if (tag() != o.tag()) return false; switch (tag()) { case T::OptBool: case T::OptInt: case T::OptSStr: case T::OptStr: case T::OptDbl: case T::OptRes: case T::OptObj: case T::Null: case T::Cell: case T::Ref: case T::InitUnc: case T::Unc: case T::InitCell: case T::InitGen: case T::Gen: case T::Uninit: case T::InitNull: case T::Bool: case T::Int: case T::Dbl: case T::Res: case T::SStr: case T::Str: case T::Obj: return true; case T::OptSArr: case T::OptArr: // Can't currently have array() info. return true; case T::SArr: case T::Arr: if (array() == nullptr && o.array() == nullptr) { return true; } if ((array() == nullptr) != (o.array() == nullptr)) { return false; } return array()->id() == o.array()->id(); case T::SubObj: case T::ExactObj: case T::OptSubObj: case T::OptExactObj: return clsName() == o.clsName(); } not_reached(); }
void merge_repo_auth_type(UnitEmitter& ue, RepoAuthType rat) { using T = RepoAuthType::Tag; switch (rat.tag()) { case T::OptBool: case T::OptInt: case T::OptSStr: case T::OptStr: case T::OptDbl: case T::OptRes: case T::OptObj: case T::Null: case T::Cell: case T::Ref: case T::InitUnc: case T::Unc: case T::InitCell: case T::InitGen: case T::Gen: case T::Uninit: case T::InitNull: case T::Bool: case T::Int: case T::Dbl: case T::Res: case T::SStr: case T::Str: case T::Obj: return; case T::OptSArr: case T::OptArr: case T::SArr: case T::Arr: // We don't need to merge the litstrs in the array, because rats // in arrays in the array type table must be using global litstr // ids. (As the array type table itself is not associated with // any unit.) return; case T::OptSubObj: case T::OptExactObj: case T::SubObj: case T::ExactObj: ue.mergeLitstr(rat.clsName()); return; } }
folly::Optional<DataType> convertToDataType(RepoAuthType ty) { using T = RepoAuthType::Tag; switch (ty.tag()) { case T::OptBool: case T::OptInt: case T::OptSArr: case T::OptArr: case T::OptSVec: case T::OptVec: case T::OptSDict: case T::OptDict: case T::OptSKeyset: case T::OptKeyset: case T::OptSStr: case T::OptStr: case T::OptDbl: case T::OptRes: case T::OptSubObj: case T::OptExactObj: case T::OptObj: case T::Null: return folly::none; case T::Cell: case T::Ref: case T::InitUnc: case T::Unc: case T::InitCell: case T::InitGen: case T::Gen: return folly::none; case T::Uninit: return KindOfUninit; case T::InitNull: return KindOfNull; case T::Bool: return KindOfBoolean; case T::Int: return KindOfInt64; case T::Dbl: return KindOfDouble; case T::Res: return KindOfResource; case T::SStr: case T::Str: return KindOfString; case T::SArr: case T::Arr: return KindOfArray; case T::SVec: case T::Vec: return KindOfVec; case T::SDict: case T::Dict: return KindOfDict; case T::SKeyset: case T::Keyset: return KindOfKeyset; case T::Obj: case T::SubObj: case T::ExactObj: return KindOfObject; } not_reached(); }
void encodeRAT(UnitEmitter& ue, RepoAuthType rat) { using T = RepoAuthType::Tag; switch (rat.tag()) { case T::Uninit: case T::InitNull: case T::Null: case T::Int: case T::OptInt: case T::Dbl: case T::OptDbl: case T::Res: case T::OptRes: case T::Bool: case T::OptBool: case T::SStr: case T::OptSStr: case T::Str: case T::OptStr: case T::SVec: case T::OptSVec: case T::Vec: case T::OptVec: case T::SDict: case T::OptSDict: case T::Dict: case T::OptDict: case T::SKeyset: case T::OptSKeyset: case T::Keyset: case T::OptKeyset: case T::Obj: case T::OptObj: case T::UncArrKey: case T::ArrKey: case T::OptUncArrKey: case T::OptArrKey: case T::InitUnc: case T::Unc: case T::InitCell: case T::Cell: case T::Ref: case T::InitGen: case T::Gen: ue.emitByte(static_cast<uint8_t>(rat.tag())); break; case T::SArr: case T::OptSArr: case T::Arr: case T::OptArr: case T::SVArr: case T::OptSVArr: case T::VArr: case T::OptVArr: case T::SDArr: case T::OptSDArr: case T::DArr: case T::OptDArr: { auto tagByte = static_cast<uint8_t>(rat.tag()); if (rat.hasArrData()) tagByte |= kRATArrayDataBit; ue.emitByte(tagByte); if (rat.hasArrData()) { ue.emitInt32(rat.arrayId()); } break; } case T::ExactObj: case T::SubObj: case T::OptExactObj: case T::OptSubObj: ue.emitByte(static_cast<uint8_t>(rat.tag())); ue.emitInt32(ue.mergeLitstr(rat.clsName())); break; } }
Type typeFromRAT(RepoAuthType ty) { using T = RepoAuthType::Tag; switch (ty.tag()) { case T::OptBool: return TBool | TInitNull; case T::OptInt: return TInt | TInitNull; case T::OptSStr: return TStaticStr | TInitNull; case T::OptStr: return TStr | TInitNull; case T::OptDbl: return TDbl | TInitNull; case T::OptRes: return TRes | TInitNull; case T::OptObj: return TObj | TInitNull; case T::Uninit: return TUninit; case T::InitNull: return TInitNull; case T::Null: return TNull; case T::Bool: return TBool; case T::Int: return TInt; case T::Dbl: return TDbl; case T::Res: return TRes; case T::SStr: return TStaticStr; case T::Str: return TStr; case T::Obj: return TObj; case T::Cell: return TCell; case T::Ref: return TBoxedInitCell; case T::InitUnc: return TUncountedInit; case T::Unc: return TUncounted; case T::InitCell: return TInitCell; case T::InitGen: return TInitGen; case T::Gen: return TGen; // TODO(#4205897): option specialized array types case T::OptArr: return TArr | TInitNull; case T::OptSArr: return TStaticArr | TInitNull; case T::SArr: if (auto const ar = ty.array()) return Type::StaticArray(ar); return TStaticArr; case T::Arr: if (auto const ar = ty.array()) return Type::Array(ar); return TArr; case T::SubObj: case T::ExactObj: case T::OptSubObj: case T::OptExactObj: { auto base = TObj; if (auto const cls = Unit::lookupClassOrUniqueClass(ty.clsName())) { if (ty.tag() == T::ExactObj || ty.tag() == T::OptExactObj) { base = Type::ExactObj(cls); } else { base = Type::SubObj(cls); } } if (ty.tag() == T::OptSubObj || ty.tag() == T::OptExactObj) { base |= TInitNull; } return base; } } not_reached(); }
std::string show(RepoAuthType rat) { auto const tag = rat.tag(); using T = RepoAuthType::Tag; switch (tag) { case T::OptBool: return "?Bool"; case T::OptInt: return "?Int"; case T::OptSStr: return "?SStr"; case T::OptStr: return "?Str"; case T::OptDbl: return "?Dbl"; case T::OptRes: return "?Res"; case T::OptObj: return "?Obj"; case T::Null: return "Null"; case T::Cell: return "Cell"; case T::Ref: return "Ref"; case T::InitUnc: return "InitUnc"; case T::Unc: return "Unc"; case T::InitCell: return "InitCell"; case T::InitGen: return "InitGen"; case T::Gen: return "Gen"; case T::Uninit: return "Uninit"; case T::InitNull: return "InitNull"; case T::Bool: return "Bool"; case T::Int: return "Int"; case T::Dbl: return "Dbl"; case T::Res: return "Res"; case T::SStr: return "SStr"; case T::Str: return "Str"; case T::Obj: return "Obj"; case T::OptSArr: case T::OptArr: case T::SArr: case T::Arr: { auto ret = std::string{}; if (tag == T::OptArr || tag == T::OptSArr) { ret += '?'; } if (tag == T::SArr || tag == T::OptSArr) { ret += 'S'; } ret += "Arr"; if (auto const ar = rat.array()) { folly::format(&ret, "{}", show(*ar)); } return ret; } break; case T::OptSubObj: case T::OptExactObj: case T::SubObj: case T::ExactObj: { auto ret = std::string{}; if (tag == T::OptSubObj || tag == T::OptExactObj) { ret += '?'; } ret += "Obj"; if (tag == T::OptSubObj || tag == T::SubObj) { ret += "<"; } ret += '='; ret += rat.clsName()->data(); return ret; } } not_reached(); }
bool tvMatchesRepoAuthType(TypedValue tv, RepoAuthType ty) { assert(tvIsPlausible(tv)); bool const initNull = tv.m_type == KindOfNull; using T = RepoAuthType::Tag; switch (ty.tag()) { case T::Uninit: return tv.m_type == KindOfUninit; case T::InitNull: return initNull; case T::OptBool: if (initNull) return true; // fallthrough case T::Bool: return tv.m_type == KindOfBoolean; case T::OptInt: if (initNull) return true; // fallthrough case T::Int: return tv.m_type == KindOfInt64; case T::OptDbl: if (initNull) return true; // fallthrough case T::Dbl: return tv.m_type == KindOfDouble; case T::OptRes: if (initNull) return true; // fallthrough case T::Res: return tv.m_type == KindOfResource; case T::OptObj: if (initNull) return true; // fallthrough case T::Obj: return tv.m_type == KindOfObject; case T::OptSStr: if (initNull) return true; // fallthrough case T::SStr: return tv.m_type == KindOfStaticString || (tv.m_type == KindOfString && tv.m_data.pstr->isStatic()); case T::OptStr: if (initNull) return true; // fallthrough case T::Str: return IS_STRING_TYPE(tv.m_type); case T::OptSArr: if (initNull) return true; // fallthrough case T::SArr: if (tv.m_type != KindOfArray || !tv.m_data.parr->isStatic()) { return false; } if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptArr: if (initNull) return true; // fallthrough case T::Arr: if (tv.m_type != KindOfArray) return false; if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::Null: return initNull || tv.m_type == KindOfUninit; case T::OptSubObj: if (initNull) return true; // fallthrough case T::SubObj: { auto const cls = Unit::lookupClass(ty.clsName()); if (!cls) return false; return tv.m_type == KindOfObject && tv.m_data.pobj->getVMClass()->classof(cls); } case T::OptExactObj: if (initNull) return true; // fallthrough case T::ExactObj: { auto const cls = Unit::lookupClass(ty.clsName()); if (!cls) return false; return tv.m_type == KindOfObject && tv.m_data.pobj->getVMClass() == cls; } case T::InitUnc: if (tv.m_type == KindOfUninit) return false; // fallthrough case T::Unc: return !IS_REFCOUNTED_TYPE(tv.m_type) || (tv.m_type == KindOfString && tv.m_data.pstr->isStatic()) || (tv.m_type == KindOfArray && tv.m_data.parr->isStatic()); case T::InitCell: if (tv.m_type == KindOfUninit) return false; // fallthrough case T::Cell: return tv.m_type != KindOfRef; case T::Ref: return tv.m_type == KindOfRef; case T::InitGen: if (tv.m_type == KindOfUninit) return false; // fallthrough case T::Gen: return true; } not_reached(); }
std::string show(RepoAuthType rat) { auto const tag = rat.tag(); using T = RepoAuthType::Tag; switch (tag) { case T::OptBool: return "?Bool"; case T::OptInt: return "?Int"; case T::OptSStr: return "?SStr"; case T::OptStr: return "?Str"; case T::OptDbl: return "?Dbl"; case T::OptRes: return "?Res"; case T::OptObj: return "?Obj"; case T::OptUncArrKey: return "?UncArrKey"; case T::OptArrKey: return "?ArrKey"; case T::Null: return "Null"; case T::Cell: return "Cell"; case T::Ref: return "Ref"; case T::InitUnc: return "InitUnc"; case T::Unc: return "Unc"; case T::UncArrKey:return "UncArrKey"; case T::ArrKey: return "ArrKey"; case T::InitCell: return "InitCell"; case T::InitGen: return "InitGen"; case T::Gen: return "Gen"; case T::Uninit: return "Uninit"; case T::InitNull: return "InitNull"; case T::Bool: return "Bool"; case T::Int: return "Int"; case T::Dbl: return "Dbl"; case T::Res: return "Res"; case T::SStr: return "SStr"; case T::Str: return "Str"; case T::Obj: return "Obj"; case T::OptSArr: case T::OptArr: case T::SArr: case T::Arr: case T::OptSVArr: case T::OptVArr: case T::SVArr: case T::VArr: case T::OptSDArr: case T::OptDArr: case T::SDArr: case T::DArr: { auto ret = std::string{}; if (tag == T::OptArr || tag == T::OptSArr || tag == T::OptVArr || tag == T::OptSVArr || tag == T::OptDArr || tag == T::OptSDArr) { ret += '?'; } if (tag == T::SArr || tag == T::OptSArr || tag == T::SVArr || tag == T::OptSVArr || tag == T::SDArr || tag == T::OptSDArr) { ret += 'S'; } if (tag == T::OptArr || tag == T::Arr || tag == T::OptSArr || tag == T::SArr) { ret += "Arr"; } else if (tag == T::OptVArr || tag == T::VArr || tag == T::OptSVArr || tag == T::SVArr) { ret += "VArr"; } else { ret += "DArr"; } if (rat.hasArrData()) folly::format(&ret, "{}", show(*rat.array())); return ret; } break; case T::SVec: return "SVec"; case T::Vec: return "Vec"; case T::OptSVec: return "?SVec"; case T::OptVec: return "?Vec"; case T::SDict: return "SDict"; case T::Dict: return "Dict"; case T::OptSDict: return "?SDict"; case T::OptDict: return "?Dict"; case T::SKeyset: return "SKeyset"; case T::Keyset: return "Keyset"; case T::OptSKeyset: return "?SKeyset"; case T::OptKeyset: return "?Keyset"; case T::OptSubObj: case T::OptExactObj: case T::SubObj: case T::ExactObj: { auto ret = std::string{}; if (tag == T::OptSubObj || tag == T::OptExactObj) { ret += '?'; } ret += "Obj"; if (tag == T::OptSubObj || tag == T::SubObj) { ret += "<"; } ret += '='; ret += rat.clsName()->data(); return ret; } } not_reached(); }
bool tvMatchesRepoAuthType(TypedValue tv, RepoAuthType ty) { assert(tvIsPlausible(tv)); bool const initNull = tv.m_type == KindOfNull; using T = RepoAuthType::Tag; switch (ty.tag()) { case T::Uninit: return tv.m_type == KindOfUninit; case T::InitNull: return initNull; case T::OptBool: if (initNull) return true; // fallthrough case T::Bool: return tv.m_type == KindOfBoolean; case T::OptInt: if (initNull) return true; // fallthrough case T::Int: return tv.m_type == KindOfInt64; case T::OptDbl: if (initNull) return true; // fallthrough case T::Dbl: return tv.m_type == KindOfDouble; case T::OptRes: if (initNull) return true; // fallthrough case T::Res: return tv.m_type == KindOfResource; case T::OptObj: if (initNull) return true; // fallthrough case T::Obj: return tv.m_type == KindOfObject; case T::OptSStr: if (initNull) return true; // fallthrough case T::SStr: return isStringType(tv.m_type) && tv.m_data.pstr->isStatic(); case T::OptStr: if (initNull) return true; // fallthrough case T::Str: return isStringType(tv.m_type); case T::OptSArr: if (initNull) return true; // fallthrough case T::SArr: if (!isArrayType(tv.m_type) || !tv.m_data.parr->isStatic()) { return false; } if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptArr: if (initNull) return true; // fallthrough case T::Arr: if (!isArrayType(tv.m_type)) return false; if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptSVArr: if (initNull) return true; // fallthrough case T::SVArr: if (!isArrayType(tv.m_type) || !tv.m_data.parr->isStatic() || !tv.m_data.parr->isVArray()) { return false; } if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptVArr: if (initNull) return true; // fallthrough case T::VArr: if (!isArrayType(tv.m_type) || !tv.m_data.parr->isVArray()) return false; if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptSDArr: if (initNull) return true; // fallthrough case T::SDArr: if (!isArrayType(tv.m_type) || !tv.m_data.parr->isStatic() || !tv.m_data.parr->isDArray()) { return false; } if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptDArr: if (initNull) return true; // fallthrough case T::DArr: if (!isArrayType(tv.m_type) || !tv.m_data.parr->isDArray()) return false; if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptSVec: if (initNull) return true; // fallthrough case T::SVec: return isVecType(tv.m_type) && tv.m_data.parr->isStatic(); case T::OptVec: if (initNull) return true; // fallthrough case T::Vec: return isVecType(tv.m_type); case T::OptSDict: if (initNull) return true; // fallthrough case T::SDict: return isDictType(tv.m_type) && tv.m_data.parr->isStatic(); case T::OptDict: if (initNull) return true; // fallthrough case T::Dict: return isDictType(tv.m_type); case T::OptSKeyset: if (initNull) return true; // fallthrough case T::SKeyset: return isKeysetType(tv.m_type) && tv.m_data.parr->isStatic(); case T::OptKeyset: if (initNull) return true; // fallthrough case T::Keyset: return isKeysetType(tv.m_type); case T::Null: return initNull || tv.m_type == KindOfUninit; case T::OptSubObj: if (initNull) return true; // fallthrough case T::SubObj: { auto const cls = Unit::lookupClass(ty.clsName()); if (!cls) return false; return tv.m_type == KindOfObject && tv.m_data.pobj->getVMClass()->classof(cls); } case T::OptExactObj: if (initNull) return true; // fallthrough case T::ExactObj: { auto const cls = Unit::lookupClass(ty.clsName()); if (!cls) return false; return tv.m_type == KindOfObject && tv.m_data.pobj->getVMClass() == cls; } case T::InitUnc: if (tv.m_type == KindOfUninit) return false; // fallthrough case T::Unc: return !isRefcountedType(tv.m_type) || (tv.m_type == KindOfString && tv.m_data.pstr->isStatic()) || (isArrayLikeType(tv.m_type) && tv.m_data.parr->isStatic()); case T::OptArrKey: if (initNull) return true; // fallthrough case T::ArrKey: return isStringType(tv.m_type) || tv.m_type == KindOfInt64; case T::OptUncArrKey: if (initNull) return true; // fallthrough case T::UncArrKey: return (isStringType(tv.m_type) && !tv.m_data.pstr->isRefCounted()) || tv.m_type == KindOfInt64; case T::InitCell: if (tv.m_type == KindOfUninit) return false; // fallthrough case T::Cell: return tv.m_type != KindOfRef; case T::Ref: return tv.m_type == KindOfRef; case T::InitGen: if (tv.m_type == KindOfUninit) return false; // fallthrough case T::Gen: return true; } not_reached(); }
bool RepoAuthType::operator==(RepoAuthType o) const { using T = Tag; if (tag() != o.tag()) return false; switch (tag()) { case T::OptBool: case T::OptInt: case T::OptSStr: case T::OptStr: case T::OptDbl: case T::OptRes: case T::OptObj: case T::OptArrKey: case T::OptUncArrKey: case T::Null: case T::Cell: case T::Ref: case T::InitUnc: case T::Unc: case T::ArrKey: case T::UncArrKey: case T::InitCell: case T::InitGen: case T::Gen: case T::Uninit: case T::InitNull: case T::Bool: case T::Int: case T::Dbl: case T::Res: case T::SStr: case T::Str: case T::Obj: return true; case T::SVec: case T::Vec: case T::OptSVec: case T::OptVec: case T::SDict: case T::Dict: case T::OptSDict: case T::OptDict: case T::SKeyset: case T::Keyset: case T::OptSKeyset: case T::OptKeyset: return true; case T::OptSArr: case T::OptArr: case T::OptSVArr: case T::OptVArr: case T::OptSDArr: case T::OptDArr: // Can't currently have array() info. return true; case T::SArr: case T::Arr: case T::SVArr: case T::VArr: case T::SDArr: case T::DArr: // array id equals to either kInvalidArrayId for null array info, or a // regular id. in each case, we just need to compare their id. return arrayId() == o.arrayId(); case T::SubObj: case T::ExactObj: case T::OptSubObj: case T::OptExactObj: return clsName() == o.clsName(); } not_reached(); }