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::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; }
void insert_assertions_step(const php::Func& func, const Bytecode& bcode, const State& state, std::bitset<kMaxTrackedLocals> mayReadLocalSet, bool lastStackOutputObvious, Gen gen) { for (size_t i = 0; i < state.locals.size(); ++i) { if (options.FilterAssertions) { if (i < mayReadLocalSet.size() && !mayReadLocalSet.test(i)) { continue; } } auto const realT = state.locals[i]; auto const op = makeAssert<bc::AssertObjL,bc::AssertTL>( borrow(func.locals[i]), realT ); if (op) gen(*op); } if (!options.InsertStackAssertions) return; // Skip asserting the top of the stack if it just came immediately // out of an 'obvious' instruction. (See hasObviousStackOutput.) assert(state.stack.size() >= bcode.numPop()); auto i = size_t{0}; auto stackIdx = state.stack.size() - 1; if (lastStackOutputObvious) { ++i, --stackIdx; } /* * This doesn't need to account for ActRecs on the fpiStack, because * no instruction in an FPI region can ever consume a stack value * from above the pre-live ActRec. */ for (; i < bcode.numPop(); ++i, --stackIdx) { auto const realT = state.stack[stackIdx]; if (options.FilterAssertions && !realT.strictSubtypeOf(stack_flav(realT))) { continue; } auto const op = makeAssert<bc::AssertObjStk,bc::AssertTStk>( static_cast<int32_t>(i), realT ); if (op) gen(*op); } }
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)); }