Type typeNSame(const Type& a, const Type& b) { auto const ty = typeSame(a, b); assert(ty.subtypeOf(TBool)); return ty.subtypeOf(TFalse) ? TTrue : ty.subtypeOf(TTrue) ? TFalse : TBool; }
TEST(Type, Interface) { auto const program = folly::make_unique<php::Program>(); program->units.push_back(make_test_unit()); auto const unit = borrow(program->units.back()); auto const func = [&]() -> borrowed_ptr<php::Func> { for (auto& f : unit->funcs) { if (f->name->isame(s_test.get())) return borrow(f); } return nullptr; }(); EXPECT_TRUE(func != nullptr); auto const ctx = Context { unit, func }; Index idx{borrow(program)}; // load classes in hierarchy auto const clsIA = idx.resolve_class(ctx, s_IA.get()); if (!clsIA) EXPECT_TRUE(false); auto const clsIAA = idx.resolve_class(ctx, s_IAA.get()); if (!clsIAA) EXPECT_TRUE(false); auto const clsA = idx.resolve_class(ctx, s_A.get()); if (!clsA) EXPECT_TRUE(false); auto const clsAA = idx.resolve_class(ctx, s_AA.get()); if (!clsAA) EXPECT_TRUE(false); // make sometypes and objects auto const subObjIATy = subObj(*clsIA); auto const subClsIATy = subCls(*clsIA); auto const subObjIAATy = subObj(*clsIAA); auto const subClsIAATy = subCls(*clsIAA); auto const subObjATy = subObj(*clsA); auto const clsExactATy = clsExact(*clsA); auto const subClsATy = subCls(*clsA); auto const subObjAATy = subObj(*clsAA); auto const subClsAATy = subCls(*clsAA); // we don't support interfaces quite yet so let's put few tests // that will fail once interfaces are supported // first 2 are "not precise" - should be true EXPECT_FALSE(subClsATy.subtypeOf(objcls(subObjIATy))); EXPECT_FALSE(objcls(subObjATy).strictSubtypeOf(subClsIATy)); EXPECT_TRUE(subClsATy.couldBe(objcls(subObjIATy))); // first 2 are "not precise" - should be true EXPECT_FALSE(subClsAATy.subtypeOf(objcls(subObjIAATy))); EXPECT_FALSE(objcls(subObjAATy).strictSubtypeOf(objcls(subObjIAATy))); EXPECT_TRUE(subClsAATy.couldBe(objcls(subObjIAATy))); // 3rd one is not precise - should be false EXPECT_FALSE(subClsATy.subtypeOf(objcls(subObjIAATy))); EXPECT_FALSE(objcls(subObjATy).strictSubtypeOf(objcls(subObjIAATy))); EXPECT_TRUE(clsExactATy.couldBe(objcls(subObjIAATy))); }
Type Type::relaxToGuardable() const { auto const ty = unspecialize(); if (ty.isKnownDataType()) return ty; if (ty.subtypeOf(UncountedInit)) return Type::UncountedInit; if (ty.subtypeOf(Uncounted)) return Type::Uncounted; if (ty.subtypeOf(Cell)) return Type::Cell; if (ty.subtypeOf(BoxedCell)) return Type::BoxedCell; if (ty.subtypeOf(Gen)) return Type::Gen; not_reached(); }
SSATmp* TraceBuilder::preOptimizeAssertLoc(IRInstruction* inst) { auto const locId = inst->extra<AssertLoc>()->locId; auto const prevType = localType(locId, DataTypeGeneric); auto const typeParam = inst->typeParam(); if (!prevType.equals(Type::None) && !typeParam.strictSubtypeOf(prevType)) { if (!prevType.subtypeOf(typeParam)) { /* Task #2553746 * This is triggering for a case where the tracked state says the local is * InitNull but the AssertLoc says it's Str. */ static auto const error = StringData::GetStaticString("Internal error: static analysis was " "wrong about a local variable's type."); auto* errorInst = m_irFactory.gen(RaiseError, inst->marker(), cns(error)); inst->become(&m_irFactory, errorInst); // It's not a disaster to generate this in unreachable code for // now. t2590033. if (false) { assert_log(false, [&]{ IRTrace& mainTrace = trace()->isMain() ? *trace() : *(trace()->main()); return folly::format("\npreOptimizeAssertLoc: prevType: {} " "typeParam: {}\nin instr: {}\nin trace: {}\n", prevType.toString(), typeParam.toString(), inst->toString(), mainTrace.toString()).str(); }); } } else { inst->convertToNop(); } } return nullptr; }
SSATmp* TraceBuilder::preOptimizeCheckLoc(IRInstruction* inst) { auto const locId = inst->extra<CheckLoc>()->locId; Type typeParam = inst->typeParam(); if (auto const prevValue = localValue(locId, DataTypeGeneric)) { return gen(CheckType, typeParam, inst->taken(), prevValue); } auto const prevType = localType(locId, DataTypeSpecific); if (prevType.subtypeOf(typeParam)) { return inst->src(0); } else { // // Normally, it doesn't make sense to be checking something that's // deemed to fail. Incompatible boxed types are ok though, since // we don't track them precisely, but instead check them at every // use. // // However, in JitPGO mode right now, this pathological case can // happen, because profile counters are not accurate and we // currently don't analyze Block post-conditions when picking its // successors during region selection. This can lead to // incompatible types in blocks selected for the same region. // if (!typeParam.isBoxed() || !prevType.isBoxed()) { if ((typeParam & prevType) == Type::Bottom) { assert(RuntimeOption::EvalJitPGO); return gen(Jmp, inst->taken()); } } } return nullptr; }
RuntimeType Type::toRuntimeType() const { assert(!isPtr()); auto const outer = isBoxed() ? KindOfRef : toDataType(); auto const inner = isBoxed() ? innerType().toDataType() : KindOfNone; auto rtt = RuntimeType{outer, inner}; if (isSpecialized()) { if (subtypeOf(Type::Arr)) { return rtt.setArrayKind(getArrayKind()); } else if (subtypeOf(Type::Obj)) { return rtt.setKnownClass(getClass()); } } return rtt; }
TEST(Type, PtrKinds) { auto const frameGen = Type::Gen.ptr(Ptr::Frame); auto const frameUninit = Type::Uninit.ptr(Ptr::Frame); auto const frameBool = Type::Bool.ptr(Ptr::Frame); auto const unknownBool = Type::Bool.ptr(Ptr::Unk); auto const unknownGen = Type::Gen.ptr(Ptr::Unk); auto const stackObj = Type::Obj.ptr(Ptr::Stk); auto const stackBool = Type::Bool.ptr(Ptr::Stk); EXPECT_EQ("PtrToFrameGen", frameGen.toString()); EXPECT_EQ("PtrToFrameBool", frameBool.toString()); EXPECT_EQ("PtrToBool", unknownBool.toString()); EXPECT_EQ("PtrToStkObj", stackObj.toString()); EXPECT_EQ("Nullptr|PtrToPropCell", (Type::PtrToPropCell|Type::Nullptr).toString()); EXPECT_EQ(Ptr::Frame, (frameUninit|frameBool).ptrKind()); EXPECT_TRUE(frameBool.subtypeOf(unknownBool)); EXPECT_TRUE(frameBool.subtypeOf(frameGen)); EXPECT_TRUE(!frameBool.subtypeOf(frameUninit)); EXPECT_TRUE(frameBool.maybe(frameGen)); EXPECT_TRUE(frameBool.maybe(unknownBool)); EXPECT_TRUE(!frameUninit.maybe(frameBool)); EXPECT_TRUE(frameUninit.maybe(frameGen)); EXPECT_TRUE(!frameUninit.maybe(unknownBool)); EXPECT_TRUE(!Type::PtrToUninit.maybe(Type::PtrToBool)); EXPECT_TRUE(!unknownBool.subtypeOf(frameBool)); EXPECT_EQ(unknownBool, frameBool | unknownBool); EXPECT_EQ(unknownGen, Type::unionOf(frameGen, unknownBool)); EXPECT_EQ(Type::Bottom, frameBool & stackBool); EXPECT_EQ(frameBool, frameBool & unknownBool); EXPECT_EQ(Type::PtrToCell, Type::PtrToPropCell|Type::PtrToFrameCell); EXPECT_EQ(Ptr::Prop, (Type::PtrToPropCell|Type::Nullptr).ptrKind()); EXPECT_EQ(Ptr::RProp, (Type::PtrToRPropCell|Type::Nullptr).ptrKind()); EXPECT_EQ(Type::PtrToPropCell, (Type::PtrToPropCell|Type::Nullptr) - Type::Nullptr); }
static void printConst(std::ostream& os, IRInstruction* inst) { os << color(ANSI_COLOR_LIGHT_BLUE); SCOPE_EXIT { os << color(ANSI_COLOR_END); }; auto t = inst->typeParam(); auto c = inst->extra<DefConst>(); if (t == Type::Int) { os << c->as<int64_t>(); } else if (t == Type::Dbl) { os << c->as<double>(); } else if (t == Type::Bool) { os << (c->as<bool>() ? "true" : "false"); } else if (t.isString()) { auto str = c->as<const StringData*>(); os << "\"" << Util::escapeStringForCPP(str->data(), str->size()) << "\""; } else if (t.isArray()) { auto arr = inst->extra<DefConst>()->as<const ArrayData*>(); if (arr->empty()) { os << "array()"; } else { os << "Array(" << arr << ")"; } } else if (t.isNull() || t.subtypeOf(Type::Nullptr)) { os << t.toString(); } else if (t.subtypeOf(Type::Func)) { auto func = c->as<const Func*>(); os << "Func(" << (func ? func->fullName()->data() : "0") << ")"; } else if (t.subtypeOf(Type::Cls)) { auto cls = c->as<const Class*>(); os << "Cls(" << (cls ? cls->name()->data() : "0") << ")"; } else if (t.subtypeOf(Type::NamedEntity)) { auto ne = c->as<const NamedEntity*>(); os << "NamedEntity(" << ne << ")"; } else if (t.subtypeOf(Type::TCA)) { TCA tca = c->as<TCA>(); auto name = Util::getNativeFunctionName(tca); SCOPE_EXIT { free(name); }; os << folly::format("TCA: {}({})", tca, boost::trim_copy(std::string(name))); } else if (t.subtypeOf(Type::None)) {
SSATmp* TraceBuilder::preOptimizeAssertLoc(IRInstruction* inst) { auto const locId = inst->getExtra<AssertLoc>()->locId; auto const prevType = getLocalType(locId); auto const typeParam = inst->getTypeParam(); if (!prevType.equals(Type::None) && !typeParam.strictSubtypeOf(prevType)) { assert(prevType.subtypeOf(typeParam)); inst->convertToNop(); } return nullptr; }
SSATmp* TraceBuilder::preOptimizeStLoc(IRInstruction* inst) { // Guard relaxation might change the current local type, so don't try to // change to StLocNT until after relaxation happens. if (!inReoptimize()) return nullptr; auto locId = inst->extra<StLoc>()->locId; auto const curType = localType(locId, DataTypeGeneric); auto const newType = inst->src(1)->type(); assert(inst->typeParam() == Type::None); /* * There's no need to store the type if it's going to be the same * KindOfFoo. We'll still have to store string types because we * aren't specific about storing KindOfStaticString * vs. KindOfString, and a Type::Null might mean KindOfUninit or * KindOfNull. */ auto const bothBoxed = curType.isBoxed() && newType.isBoxed(); auto const sameUnboxed = [&] { auto avoidable = { Type::Uninit, Type::InitNull, Type::Int, Type::Dbl, // No strings. Type::Arr, Type::Obj, Type::Res }; for (auto& t : avoidable) { if (curType.subtypeOf(t) && newType.subtypeOf(t)) return true; } return false; }; if (bothBoxed || sameUnboxed()) { inst->setOpcode(StLocNT); } return nullptr; }
Type native_function_return_type(const php::Func* f, bool include_coercion_failures) { assert(f->nativeInfo); // If the function returns by ref, we can't say much about it. It can be a ref // or null. if (f->attrs & AttrReference) { return union_of(TRef, TInitNull); } // Infer the type from the HNI declaration auto t = [&]{ auto const hni = f->nativeInfo->returnType; return hni ? from_DataType(*hni) : TInitCell; }(); if (t.subtypeOf(BArr)) { if (f->retTypeConstraint.isVArray()) { assertx(!RuntimeOption::EvalHackArrDVArrs); t = TVArr; } else if (f->retTypeConstraint.isDArray()) { assertx(!RuntimeOption::EvalHackArrDVArrs); t = TDArr; } else if (f->retTypeConstraint.isArray()) { t = TPArr; } } // Non-simple types (ones that are represented by pointers) can always // possibly be null. if (t.subtypeOfAny(TStr, TArr, TVec, TDict, TKeyset, TObj, TRes)) { t |= TInitNull; } else { // Otherwise it should be a simple type or possibly everything. assert(t == TInitCell || t.subtypeOfAny(TBool, TInt, TDbl, TNull)); } if (include_coercion_failures) { // If parameter coercion fails, we can also get null or false depending on // the function. if (f->attrs & AttrParamCoerceModeNull) { t |= TInitNull; } if (f->attrs & AttrParamCoerceModeFalse) { t |= TFalse; } } return remove_uninit(t); }
Type resolveArrayChain(MIS& env, Type val) { static UNUSED const char prefix[] = " "; FTRACE(5, "{}chain\n", prefix, show(val)); do { auto arr = std::move(env.arrayChain.back().first); auto key = std::move(env.arrayChain.back().second); assert(arr.subtypeOf(TArr)); env.arrayChain.pop_back(); FTRACE(5, "{} | {} := {} in {}\n", prefix, show(key), show(val), show(arr)); val = array_set(std::move(arr), key, val); } while (!env.arrayChain.empty()); FTRACE(5, "{} = {}\n", prefix, show(val)); return val; }
TEST(Type, NonUnique) { auto const program = folly::make_unique<php::Program>(); program->units.push_back(make_test_unit()); auto const unit = borrow(program->units.back()); auto const func = [&]() -> borrowed_ptr<php::Func> { for (auto& f : unit->funcs) { if (f->name->isame(s_test.get())) return borrow(f); } return nullptr; }(); EXPECT_TRUE(func != nullptr); auto const ctx = Context { unit, func }; Index idx{borrow(program)}; auto const clsA = idx.resolve_class(ctx, s_A.get()); if (!clsA) EXPECT_TRUE(false); auto const clssNonUnique = idx.resolve_class(ctx, s_NonUnique.get()); if (!clssNonUnique) EXPECT_TRUE(false); auto const clssNonUniqueA = idx.resolve_class(ctx, s_NonUniqueA.get()); if (!clssNonUniqueA) EXPECT_TRUE(false); // non unique types are funny because we cannot really make any conclusion // about them so they resolve to "non precise" subtype relationship auto const subObjATy = subObj(*clsA); auto const subClsATy = subCls(*clsA); auto const subObjNonUniqueTy = subObj(*clssNonUnique); auto const subClsNonUniqueTy = subCls(*clssNonUnique); auto const subObjNonUniqueATy = subObj(*clssNonUniqueA); auto const subClsNonUniqueATy = subCls(*clssNonUniqueA); // all are obviously "non precise" but what can you do?.... EXPECT_FALSE(subClsNonUniqueATy.subtypeOf(objcls(subObjNonUniqueTy))); EXPECT_FALSE(objcls(subObjNonUniqueATy).strictSubtypeOf(subClsNonUniqueTy)); EXPECT_TRUE(subClsATy.couldBe(objcls(subObjNonUniqueTy))); }
TEST(Type, IndexBased) { auto const program = folly::make_unique<php::Program>(); program->units.push_back(make_test_unit()); auto const unit = borrow(program->units.back()); auto const func = [&]() -> borrowed_ptr<php::Func> { for (auto& f : unit->funcs) { if (f->name->isame(s_test.get())) return borrow(f); } return nullptr; }(); EXPECT_TRUE(func != nullptr); auto const ctx = Context { unit, func }; Index idx{borrow(program)}; auto const cls = idx.resolve_class(ctx, s_TestClass.get()); if (!cls) EXPECT_TRUE(false); auto const objExactTy = objExact(*cls); auto const subObjTy = subObj(*cls); auto const clsExactTy = clsExact(*cls); auto const subClsTy = subCls(*cls); // Basic relationship between the class types and object types. EXPECT_EQ(objcls(objExactTy), clsExactTy); EXPECT_EQ(objcls(subObjTy), subClsTy); // =TestClass <: <=TestClass, and not vice versa. EXPECT_TRUE(objExactTy.subtypeOf(subObjTy)); EXPECT_TRUE(!subObjTy.subtypeOf(objExactTy)); // =TestClass <: <=TestClass, and not vice versa. EXPECT_TRUE(clsExactTy.subtypeOf(subClsTy)); EXPECT_TRUE(!subClsTy.subtypeOf(clsExactTy)); // =TestClass couldBe <= TestClass, and vice versa. EXPECT_TRUE(objExactTy.couldBe(subObjTy)); EXPECT_TRUE(subObjTy.couldBe(objExactTy)); EXPECT_TRUE(clsExactTy.couldBe(subClsTy)); EXPECT_TRUE(subClsTy.couldBe(clsExactTy)); // Foo= and Foo<= are both subtypes of Foo, and couldBe Foo. EXPECT_TRUE(objExactTy.subtypeOf(TObj)); EXPECT_TRUE(subObjTy.subtypeOf(TObj)); EXPECT_TRUE(objExactTy.couldBe(TObj)); EXPECT_TRUE(subObjTy.couldBe(TObj)); EXPECT_TRUE(TObj.couldBe(objExactTy)); EXPECT_TRUE(TObj.couldBe(subObjTy)); EXPECT_TRUE(clsExactTy.subtypeOf(TCls)); EXPECT_TRUE(subClsTy.subtypeOf(TCls)); EXPECT_TRUE(clsExactTy.couldBe(TCls)); EXPECT_TRUE(subClsTy.couldBe(TCls)); EXPECT_TRUE(TCls.couldBe(clsExactTy)); EXPECT_TRUE(TCls.couldBe(subClsTy)); // Obj= and Obj<= both couldBe ?Obj, and vice versa. EXPECT_TRUE(objExactTy.couldBe(TOptObj)); EXPECT_TRUE(subObjTy.couldBe(TOptObj)); EXPECT_TRUE(TOptObj.couldBe(objExactTy)); EXPECT_TRUE(TOptObj.couldBe(subObjTy)); // Obj= and Obj<= are subtypes of ?Obj. EXPECT_TRUE(objExactTy.subtypeOf(TOptObj)); EXPECT_TRUE(subObjTy.subtypeOf(TOptObj)); // Obj= is a subtype of ?Obj=, and also ?Obj<=. EXPECT_TRUE(objExactTy.subtypeOf(opt(objExactTy))); EXPECT_TRUE(objExactTy.subtypeOf(opt(subObjTy))); EXPECT_TRUE(!opt(objExactTy).subtypeOf(objExactTy)); EXPECT_TRUE(!opt(subObjTy).subtypeOf(objExactTy)); // Obj= couldBe ?Obj= and ?Obj<=, and vice versa. EXPECT_TRUE(objExactTy.couldBe(opt(objExactTy))); EXPECT_TRUE(opt(objExactTy).couldBe(objExactTy)); EXPECT_TRUE(objExactTy.couldBe(opt(subObjTy))); EXPECT_TRUE(opt(subObjTy).couldBe(objExactTy)); // Obj<= is not a subtype of ?Obj=, it is overlapping but // potentially contains other types. (We might eventually check // whether objects are final as part of this, but not right now.) EXPECT_TRUE(!subObjTy.subtypeOf(opt(objExactTy))); EXPECT_TRUE(!opt(objExactTy).subtypeOf(subObjTy)); // Obj<= couldBe ?Obj= and vice versa. EXPECT_TRUE(subObjTy.couldBe(opt(objExactTy))); EXPECT_TRUE(opt(objExactTy).couldBe(subObjTy)); }
DataType Type::toDataType() const { assert(!isPtr()); if (isBoxed()) { return KindOfRef; } // Order is important here: types must progress from more specific // to less specific to return the most specific DataType. if (subtypeOf(Uninit)) return KindOfUninit; if (subtypeOf(Null)) return KindOfNull; if (subtypeOf(Bool)) return KindOfBoolean; if (subtypeOf(Int)) return KindOfInt64; if (subtypeOf(Dbl)) return KindOfDouble; if (subtypeOf(StaticStr)) return KindOfStaticString; if (subtypeOf(Str)) return KindOfString; if (subtypeOf(Arr)) return KindOfArray; if (subtypeOf(Obj)) return KindOfObject; if (subtypeOf(Res)) return KindOfResource; if (subtypeOf(Cls)) return KindOfClass; if (subtypeOf(UncountedInit)) return KindOfUncountedInit; if (subtypeOf(Uncounted)) return KindOfUncounted; if (subtypeOf(Gen)) return KindOfAny; not_reached(); }
DataType Type::toDataType() const { assert(!isPtr()); assert(isKnownDataType()); // Order is important here: types must progress from more specific // to less specific to return the most specific DataType. if (subtypeOf(Uninit)) return KindOfUninit; if (subtypeOf(InitNull)) return KindOfNull; if (subtypeOf(Bool)) return KindOfBoolean; if (subtypeOf(Int)) return KindOfInt64; if (subtypeOf(Dbl)) return KindOfDouble; if (subtypeOf(StaticStr)) return KindOfStaticString; if (subtypeOf(Str)) return KindOfString; if (subtypeOf(Arr)) return KindOfArray; if (subtypeOf(Obj)) return KindOfObject; if (subtypeOf(Res)) return KindOfResource; if (subtypeOf(BoxedCell)) return KindOfRef; if (subtypeOf(Cls)) return KindOfClass; always_assert_flog(false, "Bad Type {} in Type::toDataType()", *this); }
TEST(Type, Hierarchies) { auto const program = folly::make_unique<php::Program>(); program->units.push_back(make_test_unit()); auto const unit = borrow(program->units.back()); auto const func = [&]() -> borrowed_ptr<php::Func> { for (auto& f : unit->funcs) { if (f->name->isame(s_test.get())) return borrow(f); } return nullptr; }(); EXPECT_TRUE(func != nullptr); auto const ctx = Context { unit, func }; Index idx{borrow(program)}; // load classes in hierarchy auto const clsBase = idx.resolve_class(ctx, s_Base.get()); if (!clsBase) EXPECT_TRUE(false); auto const clsA = idx.resolve_class(ctx, s_A.get()); if (!clsA) EXPECT_TRUE(false); auto const clsB = idx.resolve_class(ctx, s_B.get()); if (!clsB) EXPECT_TRUE(false); auto const clsAA = idx.resolve_class(ctx, s_AA.get()); if (!clsAA) EXPECT_TRUE(false); auto const clsAB = idx.resolve_class(ctx, s_AB.get()); if (!clsAB) EXPECT_TRUE(false); auto const clsBA = idx.resolve_class(ctx, s_BA.get()); if (!clsBA) EXPECT_TRUE(false); auto const clsBB = idx.resolve_class(ctx, s_BB.get()); if (!clsBB) EXPECT_TRUE(false); auto const clsBAA = idx.resolve_class(ctx, s_BAA.get()); if (!clsBAA) EXPECT_TRUE(false); auto const clsTestClass = idx.resolve_class(ctx, s_TestClass.get()); if (!clsTestClass) EXPECT_TRUE(false); auto const clsNonUnique = idx.resolve_class(ctx, s_NonUnique.get()); if (!clsNonUnique) EXPECT_TRUE(false); // make *exact type* and *sub type* types and objects for all loaded classes auto const objExactBaseTy = objExact(*clsBase); auto const subObjBaseTy = subObj(*clsBase); auto const clsExactBaseTy = clsExact(*clsBase); auto const subClsBaseTy = subCls(*clsBase); auto const objExactATy = objExact(*clsA); auto const subObjATy = subObj(*clsA); auto const clsExactATy = clsExact(*clsA); auto const subClsATy = subCls(*clsA); auto const objExactAATy = objExact(*clsAA); auto const subObjAATy = subObj(*clsAA); auto const clsExactAATy = clsExact(*clsAA); auto const subClsAATy = subCls(*clsAA); auto const objExactABTy = objExact(*clsAB); auto const subObjABTy = subObj(*clsAB); auto const clsExactABTy = clsExact(*clsAB); auto const subClsABTy = subCls(*clsAB); auto const objExactBTy = objExact(*clsB); auto const subObjBTy = subObj(*clsB); auto const clsExactBTy = clsExact(*clsB); auto const subClsBTy = subCls(*clsB); auto const objExactBATy = objExact(*clsBA); auto const subObjBATy = subObj(*clsBA); auto const clsExactBATy = clsExact(*clsBA); auto const subClsBATy = subCls(*clsBA); auto const objExactBBTy = objExact(*clsBB); auto const subObjBBTy = subObj(*clsBB); auto const clsExactBBTy = clsExact(*clsBB); auto const subClsBBTy = subCls(*clsBB); auto const objExactBAATy = objExact(*clsBAA); auto const subObjBAATy = subObj(*clsBAA); auto const clsExactBAATy = clsExact(*clsBAA); auto const subClsBAATy = subCls(*clsBAA); auto const objExactTestClassTy = objExact(*clsTestClass); auto const subObjTestClassTy = subObj(*clsTestClass); auto const clsExactTestClassTy = clsExact(*clsTestClass); auto const subClsTestClassTy = subCls(*clsTestClass); auto const objExactNonUniqueTy = objExact(*clsNonUnique); auto const subObjNonUniqueTy = subObj(*clsNonUnique); auto const clsExactNonUniqueTy = clsExact(*clsNonUnique); auto const subClsNonUniqueTy = subCls(*clsNonUnique); // check that type from object and type are the same (obnoxious test) EXPECT_EQ(objcls(objExactBaseTy), clsExactBaseTy); EXPECT_EQ(objcls(subObjBaseTy), subClsBaseTy); EXPECT_EQ(objcls(objExactATy), clsExactATy); EXPECT_EQ(objcls(subObjATy), subClsATy); EXPECT_EQ(objcls(objExactAATy), clsExactAATy); EXPECT_EQ(objcls(subObjAATy), subClsAATy); EXPECT_EQ(objcls(objExactABTy), clsExactABTy); EXPECT_EQ(objcls(subObjABTy), subClsABTy); EXPECT_EQ(objcls(objExactBTy), clsExactBTy); EXPECT_EQ(objcls(subObjBTy), subClsBTy); EXPECT_EQ(objcls(objExactBATy), clsExactBATy); EXPECT_EQ(objcls(subObjBATy), subClsBATy); EXPECT_EQ(objcls(objExactBBTy), clsExactBBTy); EXPECT_EQ(objcls(subObjBBTy), subClsBBTy); EXPECT_EQ(objcls(objExactBAATy), clsExactBAATy); EXPECT_EQ(objcls(subObjBAATy), subClsBAATy); // both subobj(A) and subcls(A) of no_override class A change to exact types EXPECT_EQ(objcls(objExactABTy), subClsABTy); EXPECT_EQ(objcls(subObjABTy), clsExactABTy); // a T= is a subtype of itself but not a strict subtype // also a T= is in a "could be" relationship with itself. EXPECT_TRUE(objcls(objExactBaseTy).subtypeOf(clsExactBaseTy)); EXPECT_FALSE(objcls(objExactBaseTy).strictSubtypeOf(objcls(objExactBaseTy))); EXPECT_TRUE(objcls(objExactBAATy).subtypeOf(clsExactBAATy)); EXPECT_FALSE(clsExactBAATy.strictSubtypeOf(objcls(objExactBAATy))); EXPECT_TRUE(clsExactBAATy.couldBe(clsExactBAATy)); // Given the hierarchy A <- B <- C where A is the base then: // B= is not in any subtype relationshipt with a A= or C=. // Neither they are in "could be" relationships. // Overall T= sets are always disjoint. EXPECT_FALSE(objcls(objExactBATy).subtypeOf(clsExactBaseTy)); EXPECT_FALSE(objcls(objExactBATy).subtypeOf(clsExactBTy)); EXPECT_FALSE(objcls(objExactBATy).subtypeOf(clsExactBAATy)); EXPECT_FALSE(clsExactBATy.strictSubtypeOf(objcls(objExactBaseTy))); EXPECT_FALSE(clsExactBATy.strictSubtypeOf(objcls(objExactBTy))); EXPECT_FALSE(clsExactBATy.strictSubtypeOf(objcls(objExactBAATy))); EXPECT_FALSE(clsExactBATy.couldBe(objcls(objExactBaseTy))); EXPECT_FALSE(objcls(objExactBATy).couldBe(clsExactBTy)); EXPECT_FALSE(clsExactBATy.couldBe(objcls(objExactBAATy))); // any T= is both a subtype and strict subtype of T<=. // Given the hierarchy A <- B <- C where A is the base then: // C= is a subtype and a strict subtype of B<=, ?B<=, A<= and ?A<=. // The "could be" relationship also holds. EXPECT_TRUE(objcls(objExactATy).subtypeOf(subClsATy)); EXPECT_TRUE(objcls(objExactBAATy).subtypeOf(subClsBaseTy)); EXPECT_TRUE(objExactBAATy.subtypeOf(opt(subObjBaseTy))); EXPECT_TRUE(objcls(objExactBAATy).subtypeOf(subClsBTy)); EXPECT_TRUE(objExactBAATy.subtypeOf(opt(subObjBTy))); EXPECT_TRUE(clsExactBAATy.subtypeOf(objcls(subObjBATy))); EXPECT_TRUE(objExactBAATy.subtypeOf(opt(subObjBATy))); EXPECT_TRUE(clsExactBAATy.subtypeOf(objcls(subObjBAATy))); EXPECT_TRUE(objExactBAATy.subtypeOf(opt(subObjBAATy))); EXPECT_TRUE(objcls(objExactATy).strictSubtypeOf(subClsATy)); EXPECT_TRUE(objcls(objExactBAATy).strictSubtypeOf(subClsBaseTy)); EXPECT_TRUE(objExactBAATy.strictSubtypeOf(opt(subObjBaseTy))); EXPECT_TRUE(objcls(objExactBAATy).strictSubtypeOf(subClsBTy)); EXPECT_TRUE(objExactBAATy.strictSubtypeOf(opt(subObjBTy))); EXPECT_TRUE(clsExactBAATy.strictSubtypeOf(objcls(subObjBATy))); EXPECT_TRUE(objExactBAATy.strictSubtypeOf(opt(subObjBATy))); EXPECT_TRUE(clsExactBAATy.strictSubtypeOf(objcls(subObjBAATy))); EXPECT_TRUE(objExactBAATy.strictSubtypeOf(opt(subObjBAATy))); EXPECT_TRUE(objcls(objExactATy).couldBe(subClsATy)); EXPECT_TRUE(objcls(objExactBAATy).couldBe(subClsBaseTy)); EXPECT_TRUE(objExactBAATy.couldBe(opt(subObjBaseTy))); EXPECT_TRUE(objcls(objExactBAATy).couldBe(subClsBTy)); EXPECT_TRUE(objExactBAATy.couldBe(opt(subObjBTy))); EXPECT_TRUE(clsExactBAATy.couldBe(objcls(subObjBATy))); EXPECT_TRUE(objExactBAATy.couldBe(opt(subObjBATy))); EXPECT_TRUE(clsExactBAATy.couldBe(objcls(subObjBAATy))); EXPECT_TRUE(objExactBAATy.couldBe(opt(subObjBAATy))); // a T<= is a subtype of itself but not a strict subtype // also a T<= is in a "could be" relationship with itself EXPECT_TRUE(objcls(subObjBaseTy).subtypeOf(subClsBaseTy)); EXPECT_FALSE(objcls(subObjBaseTy).strictSubtypeOf(objcls(subObjBaseTy))); EXPECT_TRUE(objcls(subObjBAATy).subtypeOf(subClsBAATy)); EXPECT_FALSE(subClsBAATy.strictSubtypeOf(objcls(subObjBAATy))); EXPECT_TRUE(subClsBAATy.couldBe(subClsBAATy)); // a T<= type is in no subtype relationship with T=. // However a T<= is in a "could be" relationship with T=. EXPECT_FALSE(objcls(subObjATy).subtypeOf(clsExactATy)); EXPECT_FALSE(objcls(subObjATy).strictSubtypeOf(clsExactATy)); EXPECT_TRUE(clsExactATy.couldBe(objcls(subObjATy))); // Given 2 types A and B in no inheritance relationship then // A<= and B<= are in no subtype or "could be" relationship. // Same if one of the 2 types is an optional type EXPECT_FALSE(objcls(subObjATy).subtypeOf(clsExactBTy)); EXPECT_FALSE(objcls(subObjATy).strictSubtypeOf(clsExactBTy)); EXPECT_FALSE(subObjATy.subtypeOf(opt(objExactBTy))); EXPECT_FALSE(subObjATy.strictSubtypeOf(opt(objExactBTy))); EXPECT_FALSE(clsExactATy.couldBe(objcls(subObjBTy))); EXPECT_FALSE(objExactATy.couldBe(opt(subObjBTy))); EXPECT_FALSE(objcls(subObjBTy).subtypeOf(clsExactATy)); EXPECT_FALSE(subObjBTy.subtypeOf(opt(objExactATy))); EXPECT_FALSE(objcls(subObjBTy).strictSubtypeOf(clsExactATy)); EXPECT_FALSE(subObjBTy.strictSubtypeOf(opt(objExactATy))); EXPECT_FALSE(clsExactBTy.couldBe(objcls(subObjATy))); EXPECT_FALSE(objExactBTy.couldBe(opt(subObjATy))); // Given the hierarchy A <- B <- C where A is the base then: // C<= is a subtype and a strict subtype of B<=, ?B<=, A<= and ?A<=. // It is also in a "could be" relationship with all its ancestors // (including optional) EXPECT_TRUE(objcls(subObjBAATy).subtypeOf(subClsBaseTy)); EXPECT_TRUE(subObjBAATy.subtypeOf(opt(subObjBaseTy))); EXPECT_TRUE(objcls(subObjBAATy).subtypeOf(subClsBTy)); EXPECT_TRUE(subObjBAATy.subtypeOf(opt(subObjBTy))); EXPECT_TRUE(subClsBAATy.subtypeOf(objcls(subObjBATy))); EXPECT_TRUE(subObjBAATy.subtypeOf(opt(subObjBATy))); EXPECT_TRUE(objcls(subObjBAATy).strictSubtypeOf(subClsBaseTy)); EXPECT_TRUE(subObjBAATy.strictSubtypeOf(opt(subObjBaseTy))); EXPECT_TRUE(objcls(subObjBAATy).strictSubtypeOf(subClsBTy)); EXPECT_TRUE(subObjBAATy.strictSubtypeOf(opt(subObjBTy))); EXPECT_TRUE(subClsBAATy.strictSubtypeOf(objcls(subObjBATy))); EXPECT_TRUE(subObjBAATy.strictSubtypeOf(opt(subObjBATy))); EXPECT_TRUE(objcls(subObjBAATy).couldBe(subClsBaseTy)); EXPECT_TRUE(subObjBAATy.couldBe(opt(subObjBaseTy))); EXPECT_TRUE(objcls(subObjBAATy).couldBe(subClsBTy)); EXPECT_TRUE(subObjBAATy.couldBe(opt(subObjBTy))); EXPECT_TRUE(subClsBAATy.couldBe(objcls(subObjBATy))); EXPECT_TRUE(subObjBAATy.couldBe(opt(subObjBATy))); // Given the hierarchy A <- B <- C where A is the base then: // A<= is not in a subtype neither a strict subtype with B<=, ?B<=, A<= // ?A<=. However A<= is in a "could be" relationship with all its // children (including optional) EXPECT_FALSE(objcls(subObjBaseTy).subtypeOf(subClsATy)); EXPECT_FALSE(subObjBaseTy.subtypeOf(opt(subObjATy))); EXPECT_FALSE(objcls(subObjBaseTy).subtypeOf(subClsBTy)); EXPECT_FALSE(subObjBaseTy.subtypeOf(opt(subObjBTy))); EXPECT_FALSE(subClsBaseTy.subtypeOf(objcls(subObjAATy))); EXPECT_FALSE(subObjBaseTy.subtypeOf(opt(subObjAATy))); EXPECT_FALSE(subClsBaseTy.subtypeOf(objcls(subObjABTy))); EXPECT_FALSE(subObjBaseTy.subtypeOf(opt(subObjABTy))); EXPECT_FALSE(objcls(subObjBaseTy).subtypeOf(subClsBATy)); EXPECT_FALSE(subObjBaseTy.subtypeOf(opt(subObjBATy))); EXPECT_FALSE(subClsBaseTy.subtypeOf(objcls(subObjBBTy))); EXPECT_FALSE(subObjBaseTy.subtypeOf(opt(subObjBBTy))); EXPECT_FALSE(subClsBaseTy.subtypeOf(objcls(subObjBAATy))); EXPECT_FALSE(subObjBaseTy.subtypeOf(opt(subObjBAATy))); EXPECT_FALSE(objcls(subObjBaseTy).strictSubtypeOf(subClsATy)); EXPECT_FALSE(subObjBaseTy.strictSubtypeOf(opt(subObjATy))); EXPECT_FALSE(objcls(subObjBaseTy).strictSubtypeOf(subClsBTy)); EXPECT_FALSE(subObjBaseTy.strictSubtypeOf(opt(subObjBTy))); EXPECT_FALSE(subClsBaseTy.strictSubtypeOf(objcls(subObjAATy))); EXPECT_FALSE(subObjBaseTy.strictSubtypeOf(opt(subObjAATy))); EXPECT_FALSE(subClsBaseTy.strictSubtypeOf(objcls(subObjABTy))); EXPECT_FALSE(subObjBaseTy.strictSubtypeOf(opt(subObjABTy))); EXPECT_FALSE(objcls(subObjBaseTy).strictSubtypeOf(subClsBATy)); EXPECT_FALSE(subObjBaseTy.strictSubtypeOf(opt(subObjBATy))); EXPECT_FALSE(subClsBaseTy.strictSubtypeOf(objcls(subObjBBTy))); EXPECT_FALSE(subObjBaseTy.strictSubtypeOf(opt(subObjBBTy))); EXPECT_FALSE(subClsBaseTy.strictSubtypeOf(objcls(subObjBAATy))); EXPECT_FALSE(subObjBaseTy.strictSubtypeOf(opt(subObjBAATy))); EXPECT_TRUE(objcls(subObjBaseTy).couldBe(subClsATy)); EXPECT_TRUE(subObjBaseTy.couldBe(opt(subObjATy))); EXPECT_TRUE(objcls(subObjBaseTy).couldBe(subClsBTy)); EXPECT_TRUE(subObjBaseTy.couldBe(opt(subObjBTy))); EXPECT_TRUE(subClsBaseTy.couldBe(objcls(subObjAATy))); EXPECT_TRUE(subObjBaseTy.couldBe(opt(subObjAATy))); EXPECT_TRUE(subClsBaseTy.couldBe(objcls(subObjABTy))); EXPECT_TRUE(subObjBaseTy.couldBe(opt(subObjABTy))); EXPECT_TRUE(objcls(subObjBaseTy).couldBe(subClsBATy)); EXPECT_TRUE(subObjBaseTy.couldBe(opt(subObjBATy))); EXPECT_TRUE(subClsBaseTy.couldBe(objcls(subObjBBTy))); EXPECT_TRUE(subObjBaseTy.couldBe(opt(subObjBBTy))); EXPECT_TRUE(subClsBaseTy.couldBe(objcls(subObjBAATy))); EXPECT_TRUE(subObjBaseTy.couldBe(opt(subObjBAATy))); // check union_of and commonAncestor API EXPECT_TRUE((*(*clsA).commonAncestor(*clsB)).same(*clsBase)); EXPECT_TRUE((*(*clsB).commonAncestor(*clsA)).same(*clsBase)); EXPECT_TRUE((*(*clsAA).commonAncestor(*clsAB)).same(*clsA)); EXPECT_TRUE((*(*clsAB).commonAncestor(*clsAA)).same(*clsA)); EXPECT_TRUE((*(*clsA).commonAncestor(*clsBAA)).same(*clsBase)); EXPECT_TRUE((*(*clsBAA).commonAncestor(*clsA)).same(*clsBase)); EXPECT_TRUE((*(*clsBAA).commonAncestor(*clsB)).same(*clsB)); EXPECT_TRUE((*(*clsB).commonAncestor(*clsBAA)).same(*clsB)); EXPECT_TRUE((*(*clsBAA).commonAncestor(*clsBB)).same(*clsB)); EXPECT_TRUE((*(*clsBB).commonAncestor(*clsBAA)).same(*clsB)); EXPECT_TRUE((*(*clsAA).commonAncestor(*clsBase)).same(*clsBase)); EXPECT_TRUE((*(*clsBase).commonAncestor(*clsAA)).same(*clsBase)); EXPECT_FALSE((*clsAA).commonAncestor(*clsTestClass)); EXPECT_FALSE((*clsTestClass).commonAncestor(*clsAA)); EXPECT_FALSE((*clsBAA).commonAncestor(*clsNonUnique)); EXPECT_FALSE((*clsNonUnique).commonAncestor(*clsBAA)); // check union_of // union of subCls EXPECT_EQ(union_of(subClsATy, subClsBTy), subClsBaseTy); EXPECT_EQ(union_of(subClsAATy, subClsABTy), subClsATy); EXPECT_EQ(union_of(subClsATy, subClsBAATy), subClsBaseTy); EXPECT_EQ(union_of(subClsBAATy, subClsBTy), subClsBTy); EXPECT_EQ(union_of(subClsBAATy, subClsBBTy), subClsBTy); EXPECT_EQ(union_of(subClsAATy, subClsBaseTy), subClsBaseTy); EXPECT_EQ(union_of(subClsAATy, subClsTestClassTy), TCls); EXPECT_EQ(union_of(subClsBAATy, subClsNonUniqueTy), TCls); // union of subCls and clsExact mixed EXPECT_EQ(union_of(clsExactATy, subClsBTy), subClsBaseTy); EXPECT_EQ(union_of(subClsAATy, clsExactABTy), subClsATy); EXPECT_EQ(union_of(clsExactATy, subClsBAATy), subClsBaseTy); EXPECT_EQ(union_of(subClsBAATy, clsExactBTy), subClsBTy); EXPECT_EQ(union_of(clsExactBAATy, subClsBBTy), subClsBTy); EXPECT_EQ(union_of(subClsAATy, clsExactBaseTy), subClsBaseTy); EXPECT_EQ(union_of(clsExactAATy, subClsTestClassTy), TCls); EXPECT_EQ(union_of(subClsBAATy, clsExactNonUniqueTy), TCls); // union of clsExact EXPECT_EQ(union_of(clsExactATy, clsExactBTy), subClsBaseTy); EXPECT_EQ(union_of(clsExactAATy, clsExactABTy), subClsATy); EXPECT_EQ(union_of(clsExactATy, clsExactBAATy), subClsBaseTy); EXPECT_EQ(union_of(clsExactBAATy, clsExactBTy), subClsBTy); EXPECT_EQ(union_of(clsExactBAATy, clsExactBBTy), subClsBTy); EXPECT_EQ(union_of(clsExactAATy, clsExactBaseTy), subClsBaseTy); EXPECT_EQ(union_of(clsExactAATy, subClsTestClassTy), TCls); EXPECT_EQ(union_of(clsExactBAATy, clsExactNonUniqueTy), TCls); // union of subObj EXPECT_EQ(union_of(subObjATy, subObjBTy), subObjBaseTy); EXPECT_EQ(union_of(subObjAATy, subObjABTy), subObjATy); EXPECT_EQ(union_of(subObjATy, subObjBAATy), subObjBaseTy); EXPECT_EQ(union_of(subObjBAATy, subObjBTy), subObjBTy); EXPECT_EQ(union_of(subObjBAATy, subObjBBTy), subObjBTy); EXPECT_EQ(union_of(subObjAATy, subObjBaseTy), subObjBaseTy); EXPECT_EQ(union_of(subObjAATy, subObjTestClassTy), TObj); EXPECT_EQ(union_of(subObjBAATy, subObjNonUniqueTy), TObj); // union of subObj and objExact mixed EXPECT_EQ(union_of(objExactATy, subObjBTy), subObjBaseTy); EXPECT_EQ(union_of(subObjAATy, objExactABTy), subObjATy); EXPECT_EQ(union_of(objExactATy, subObjBAATy), subObjBaseTy); EXPECT_EQ(union_of(subObjBAATy, objExactBTy), subObjBTy); EXPECT_EQ(union_of(objExactBAATy, subObjBBTy), subObjBTy); EXPECT_EQ(union_of(subObjAATy, objExactBaseTy), subObjBaseTy); EXPECT_EQ(union_of(objExactAATy, subObjTestClassTy), TObj); EXPECT_EQ(union_of(subObjBAATy, objExactNonUniqueTy), TObj); // union of objExact EXPECT_EQ(union_of(objExactATy, objExactBTy), subObjBaseTy); EXPECT_EQ(union_of(objExactAATy, objExactABTy), subObjATy); EXPECT_EQ(union_of(objExactATy, objExactBAATy), subObjBaseTy); EXPECT_EQ(union_of(objExactBAATy, objExactBTy), subObjBTy); EXPECT_EQ(union_of(objExactBAATy, objExactBBTy), subObjBTy); EXPECT_EQ(union_of(objExactAATy, objExactBaseTy), subObjBaseTy); EXPECT_EQ(union_of(objExactAATy, objExactTestClassTy), TObj); EXPECT_EQ(union_of(objExactBAATy, objExactNonUniqueTy), TObj); // optional sub obj EXPECT_EQ(union_of(opt(subObjATy), opt(subObjBTy)), opt(subObjBaseTy)); EXPECT_EQ(union_of(subObjAATy, opt(subObjABTy)), opt(subObjATy)); EXPECT_EQ(union_of(opt(subObjATy), subObjBAATy), opt(subObjBaseTy)); EXPECT_EQ(union_of(opt(subObjBAATy), opt(subObjBTy)), opt(subObjBTy)); EXPECT_EQ(union_of(opt(subObjBAATy), subObjBBTy), opt(subObjBTy)); EXPECT_EQ(union_of(opt(subObjAATy), opt(subObjBaseTy)), opt(subObjBaseTy)); EXPECT_EQ(union_of(subObjAATy, opt(subObjTestClassTy)), opt(TObj)); EXPECT_EQ(union_of(subObjBAATy, opt(subObjNonUniqueTy)), opt(TObj)); // optional sub and exact obj mixed EXPECT_EQ(union_of(opt(objExactATy), subObjBTy), opt(subObjBaseTy)); EXPECT_EQ(union_of(subObjAATy, opt(objExactABTy)), opt(subObjATy)); EXPECT_EQ(union_of(opt(objExactATy), objExactBAATy), opt(subObjBaseTy)); EXPECT_EQ(union_of(subObjBAATy, opt(objExactBTy)), opt(subObjBTy)); EXPECT_EQ(union_of(opt(subObjBAATy), objExactBBTy), opt(subObjBTy)); EXPECT_EQ(union_of(objExactAATy, opt(objExactBaseTy)), opt(subObjBaseTy)); EXPECT_EQ(union_of(opt(subObjAATy), objExactTestClassTy), opt(TObj)); EXPECT_EQ(union_of(subObjBAATy, opt(objExactNonUniqueTy)), opt(TObj)); }