Type Type::operator&(Type rhs) const { auto lhs = *this; // When intersecting a constant value with another type, the result will be // the constant value if the other value is a supertype of the constant, and // Bottom otherwise. if (lhs.m_hasConstVal) return lhs <= rhs ? lhs : TBottom; if (rhs.m_hasConstVal) return rhs <= lhs ? rhs : TBottom; auto bits = lhs.m_bits & rhs.m_bits; auto ptr = lhs.ptrKind() & rhs.ptrKind(); auto arrSpec = lhs.arrSpec() & rhs.arrSpec(); auto clsSpec = lhs.clsSpec() & rhs.clsSpec(); // Filter out bits and pieces that no longer exist due to other components // going to Bottom, starting with bits. if (ptr == Ptr::Bottom) bits &= ~kGen; if (arrSpec == ArraySpec::Bottom) bits &= ~kArrSpecBits; if (clsSpec == ClassSpec::Bottom) bits &= ~kClsSpecBits; // ptr if ((bits & kGen) == 0) ptr = Ptr::Bottom; // specs if (!supports(bits, SpecKind::Array)) arrSpec = ArraySpec::Bottom; if (!supports(bits, SpecKind::Class)) clsSpec = ClassSpec::Bottom; return Type{bits, ptr}.specialize({arrSpec, clsSpec}); }
TEST(Type, SpecializedObjects) { auto const A = SystemLib::s_IteratorClass; auto const B = SystemLib::s_TraversableClass; EXPECT_TRUE(A->classof(B)); auto const obj = TObj; auto const exactA = Type::ExactObj(A); auto const exactB = Type::ExactObj(B); auto const subA = Type::SubObj(A); auto const subB = Type::SubObj(B); EXPECT_EQ(exactA.clsSpec().cls(), A); EXPECT_EQ(subA.clsSpec().cls(), A); EXPECT_EQ(exactA.clsSpec().exactCls(), A); EXPECT_EQ(subA.clsSpec().exactCls(), nullptr); EXPECT_LE(exactA, exactA); EXPECT_LE(subA, subA); EXPECT_LT(exactA, obj); EXPECT_LT(subA, obj); EXPECT_LE(TBottom, subA); EXPECT_LE(TBottom, exactA); EXPECT_LT(exactA, subA); EXPECT_LT(exactA, subB); EXPECT_LT(subA, subB); EXPECT_FALSE(exactA <= exactB); EXPECT_FALSE(subA <= exactB); EXPECT_EQ(exactA & subA, exactA); EXPECT_EQ(subA & exactA, exactA); EXPECT_EQ(exactB & subB, exactB); EXPECT_EQ(subB & exactB, exactB); EXPECT_EQ(TObj, TObj - subA); // conservative EXPECT_EQ(subA, subA - exactA); // conservative }
Type Type::operator-(Type rhs) const { auto lhs = *this; if (rhs == TBottom) return lhs; if (lhs <= rhs) return TBottom; if (lhs.hasConstVal()) return lhs; // not covered by rhs. // If `rhs' has a constant value, but `lhs' doesn't, conservatively return // `lhs', rather than trying to represent things like "everything except // Int<24>". Boolean is a special case. if (rhs.m_hasConstVal) { if (rhs <= TBool && lhs <= TBool) { auto const res = !rhs.boolVal(); if (lhs.hasConstVal() && lhs.boolVal() != res) return TBottom; return cns(res); } return lhs; } // For each component C, we should subtract C_rhs from C_lhs iff every other // component of lhs that can intersect with C is subsumed by the // corresponding component of rhs. This prevents us from removing members of // lhs that weren't present in rhs, but would be casualties of removing // certain bits in lhs. // // As an example, consider PtrToRMembInt - PtrToRefStr. Simple subtraction of // each component would yield PtrToMembInt, but that would mean we removed // PtrToRefInt from the lhs despite it not being in rhs. Checking if Int is a // subset of Str shows us that removing Ref from lhs would erase types not // present in rhs. // // In practice, it's more concise to eagerly do each subtraction, then check // for components that went to Bottom as a way of seeing which components of // lhs were subsets of the corresponding components in rhs. When we find a // component that we weren't supposed to subtract, just restore lhs's // original value. auto bits = lhs.m_bits & ~rhs.m_bits; auto ptr = lhs.ptrKind() - rhs.ptrKind(); auto arrSpec = lhs.arrSpec() - rhs.arrSpec(); auto clsSpec = lhs.clsSpec() - rhs.clsSpec(); auto const have_gen_bits = (bits & kGen) != 0; auto const have_arr_bits = supports(bits, SpecKind::Array); auto const have_cls_bits = supports(bits, SpecKind::Class); auto const have_ptr = ptr != Ptr::Bottom; auto const have_arr_spec = arrSpec != ArraySpec::Bottom; auto const have_cls_spec = clsSpec != ClassSpec::Bottom; // ptr can only interact with clsSpec if lhs.m_bits has at least one kGen // member of kClsSpecBits. auto const have_ptr_cls = supports(lhs.m_bits & kGen, SpecKind::Class); // bits if (have_ptr) bits |= lhs.m_bits & kGen; if (have_arr_spec) bits |= lhs.m_bits & kArrSpecBits; if (have_cls_spec) bits |= lhs.m_bits & kClsSpecBits; // ptr if (have_gen_bits || have_arr_spec || (have_cls_spec && have_ptr_cls)) { ptr = lhs.ptrKind(); } // specs if (have_ptr || have_arr_bits) arrSpec = lhs.arrSpec(); if ((have_ptr && have_ptr_cls) || have_cls_bits) clsSpec = lhs.clsSpec(); return Type{bits, ptr}.specialize({arrSpec, clsSpec}); }