TEST(Type, SpecificExamples) { // Random examples to stress option types, values, etc: EXPECT_TRUE(!TInt.subtypeOf(ival(1))); EXPECT_TRUE(TInitCell.couldBe(ival(1))); EXPECT_TRUE(TInitCell.subtypeOf(TGen)); EXPECT_TRUE(ival(2).subtypeOf(TInt)); EXPECT_TRUE(!ival(2).subtypeOf(TBool)); EXPECT_TRUE(ival(3).subtypeOf(TOptInt)); EXPECT_TRUE(TInt.subtypeOf(TOptInt)); EXPECT_TRUE(!TBool.subtypeOf(TOptInt)); EXPECT_TRUE(TInitNull.subtypeOf(TOptInt)); EXPECT_TRUE(!TNull.subtypeOf(TOptInt)); EXPECT_TRUE(TNull.couldBe(TOptInt)); EXPECT_TRUE(TNull.couldBe(TOptBool)); EXPECT_TRUE(TInitNull.subtypeOf(TInitCell)); EXPECT_TRUE(TInitNull.subtypeOf(TCell)); EXPECT_TRUE(!TUninit.subtypeOf(TInitNull)); EXPECT_TRUE(ival(3).subtypeOf(TOptInt)); EXPECT_TRUE(ival(3).subtypeOf(opt(ival(3)))); EXPECT_TRUE(ival(3).couldBe(opt(ival(3)))); EXPECT_TRUE(ival(3).couldBe(TInt)); EXPECT_TRUE(TInitNull.couldBe(opt(ival(3)))); EXPECT_TRUE(TNull.couldBe(opt(ival(3)))); EXPECT_TRUE(TInitNull.subtypeOf(opt(ival(3)))); EXPECT_TRUE(!TNull.subtypeOf(opt(ival(3)))); EXPECT_EQ(TStr, union_of(sval(s_test.get()), TCStr)); EXPECT_EQ(TStr, union_of(TCStr, sval(s_test.get()))); EXPECT_EQ(TGen, union_of(TRef, TUninit)); }
Type native_function_return_type(borrowed_ptr<const php::Func> f) { if (!f->nativeInfo->returnType) { if (f->attrs & AttrReference) { return TRef; } return TInitCell; } auto t = from_DataType(*f->nativeInfo->returnType); // Regardless of ParamCoerceMode, native functions can return null if // too many arguments are passed. t = union_of(t, TInitNull); if (f->attrs & AttrParamCoerceModeFalse) { t = union_of(t, TFalse); } return t; }
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); }
void merge_closure_use_vars_into(ClosureUseVarMap& dst, borrowed_ptr<php::Class> clo, std::vector<Type> types) { auto& current = dst[clo]; if (current.empty()) { current = std::move(types); return; } assert(types.size() == current.size()); for (auto i = uint32_t{0}; i < current.size(); ++i) { current[i] = union_of(std::move(current[i]), std::move(types[i])); } }
Type native_function_return_type(borrowed_ptr<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 const hni = f->nativeInfo->returnType; auto t = hni ? from_DataType(*hni) : TInitCell; // 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 = union_of(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 = union_of(t, TInitNull); } if (f->attrs & AttrParamCoerceModeFalse) { t = union_of(t, TFalse); } } return remove_uninit(t); }
FuncAnalysis do_analyze_collect(const Index& index, Context const inputCtx, CollectedInfo& collect, ClassAnalysis* clsAnalysis, const std::vector<Type>* knownArgs) { auto const ctx = adjust_closure_context(inputCtx); FuncAnalysis ai(ctx); Trace::Bump bumper{Trace::hhbbc, kTraceFuncBump, is_trace_function(ctx.cls, ctx.func)}; FTRACE(2, "{:-^70}\n-- {}\n", "Analyze", show(ctx)); /* * Set of RPO ids that still need to be visited. * * Initially, we need each entry block in this list. As we visit * blocks, we propagate states to their successors and across their * back edges---when state merges cause a change to the block * stateIn, we will add it to this queue so it gets visited again. */ auto incompleteQ = prepare_incompleteQ(index, ai, clsAnalysis, knownArgs); /* * There are potentially infinitely growing types when we're using * union_of to merge states, so occasonially we need to apply a * widening operator. * * Currently this is done by having a straight-forward hueristic: if * you visit a block too many times, we'll start doing all the * merges with the widening operator until we've had a chance to * visit the block again. We must then continue iterating in case * the actual fixed point is higher than the result of widening. * * Terminiation is guaranteed because the widening operator has only * finite chains in the type lattice. */ auto nonWideVisits = std::vector<uint32_t>(ctx.func->nextBlockId); // For debugging, count how many times basic blocks get interpreted. auto interp_counter = uint32_t{0}; /* * Iterate until a fixed point. * * Each time a stateIn for a block changes, we re-insert the block's * rpo ID in incompleteQ. Since incompleteQ is ordered, we'll * always visit blocks with earlier RPO ids first, which hopefully * means less iterations. */ while (!incompleteQ.empty()) { auto const blk = ai.rpoBlocks[incompleteQ.pop()]; if (nonWideVisits[blk->id]++ > options.analyzeFuncWideningLimit) { nonWideVisits[blk->id] = 0; } FTRACE(2, "block #{}\nin {}{}", blk->id, state_string(*ctx.func, ai.bdata[blk->id].stateIn), property_state_string(collect.props)); ++interp_counter; auto propagate = [&] (php::Block& target, const State& st) { auto const needsWiden = nonWideVisits[target.id] >= options.analyzeFuncWideningLimit; // We haven't optimized the widening operator much, because it // doesn't happen in practice right now. We want to know when // it starts happening: if (needsWiden) { std::fprintf(stderr, "widening in %s on %s\n", ctx.unit->filename->data(), ctx.func->name->data()); } FTRACE(2, " {}-> {}\n", needsWiden ? "widening " : "", target.id); FTRACE(4, "target old {}", state_string(*ctx.func, ai.bdata[target.id].stateIn)); auto const changed = needsWiden ? widen_into(ai.bdata[target.id].stateIn, st) : merge_into(ai.bdata[target.id].stateIn, st); if (changed) { incompleteQ.push(rpoId(ai, &target)); } FTRACE(4, "target new {}", state_string(*ctx.func, ai.bdata[target.id].stateIn)); }; auto stateOut = ai.bdata[blk->id].stateIn; auto interp = Interp { index, ctx, collect, blk, stateOut }; auto flags = run(interp, propagate); if (flags.returned) { ai.inferredReturn = union_of(std::move(ai.inferredReturn), std::move(*flags.returned)); } } ai.closureUseTypes = std::move(collect.closureUseTypes); if (ctx.func->isGenerator) { if (ctx.func->isAsync) { // Async generators always return AsyncGenerator object. ai.inferredReturn = objExact(index.builtin_class(s_AsyncGenerator.get())); } else { // Non-async generators always return Generator object. ai.inferredReturn = objExact(index.builtin_class(s_Generator.get())); } } else if (ctx.func->isAsync) { // Async functions always return WaitH<T>, where T is the type returned // internally. ai.inferredReturn = wait_handle(index, ai.inferredReturn); } /* * If inferredReturn is TBottom, the callee didn't execute a return * at all. (E.g. it unconditionally throws, or is an abstract * function body.) * * In this case, we leave the return type as TBottom, to indicate * the same to callers. */ assert(ai.inferredReturn.subtypeOf(TGen)); // For debugging, print the final input states for each block. FTRACE(2, "{}", [&] { auto const bsep = std::string(60, '=') + "\n"; auto const sep = std::string(60, '-') + "\n"; auto ret = folly::format( "{}function {} ({} block interps):\n{}", bsep, show(ctx), interp_counter, bsep ).str(); for (auto& bd : ai.bdata) { ret += folly::format( "{}block {}:\nin {}", sep, ai.rpoBlocks[bd.rpoId]->id, state_string(*ctx.func, bd.stateIn) ).str(); } ret += sep + bsep; folly::format(&ret, "Inferred return type: {}\n", show(ai.inferredReturn)); ret += bsep; return ret; }()); return ai; }
TEST(Type, OptUnionOf) { EXPECT_EQ(opt(ival(2)), union_of(ival(2), TInitNull)); EXPECT_EQ(opt(dval(2.0)), union_of(TInitNull, dval(2.0))); EXPECT_EQ(opt(sval(s_test.get())), union_of(sval(s_test.get()), TInitNull)); EXPECT_EQ(opt(sval(s_test.get())), union_of(TInitNull, sval(s_test.get()))); EXPECT_EQ(TOptBool, union_of(TOptFalse, TOptTrue)); EXPECT_EQ(TOptBool, union_of(TOptTrue, TOptFalse)); EXPECT_EQ(TOptCArr, union_of(TCArr, TInitNull)); EXPECT_EQ(TOptCArr, union_of(TInitNull, TCArr)); EXPECT_EQ(TOptSArr, union_of(TInitNull, TOptSArr)); EXPECT_EQ(TOptSArr, union_of(TOptSArr, TInitNull)); EXPECT_EQ(TOptArr, union_of(TOptArr, TInitNull)); EXPECT_EQ(TOptArr, union_of(TInitNull, TOptArr)); EXPECT_EQ(TInitUnc, union_of(TOptSArr, TSStr)); EXPECT_EQ(TInitUnc, union_of(TSStr, TOptSArr)); }
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)); }
TEST(Type, Num) { EXPECT_EQ(union_of(TInt, TDbl), TNum); EXPECT_EQ(union_of(ival(2), dval(1.0)), TNum); EXPECT_EQ(union_of(TInt, dval(1.0)), TNum); }