bool typeFitsConstraint(Type t, TypeConstraint tc) { switch (tc.category) { case DataTypeGeneric: return true; case DataTypeCountness: // Consumers using this constraint expect the type to be relaxed to // Uncounted or left alone, so something like Arr|Obj isn't specific // enough. return !t.maybe(TCounted) || t.subtypeOfAny(TStr, TArr, TObj, TRes, TBoxedCell); case DataTypeCountnessInit: return typeFitsConstraint(t, DataTypeCountness) && (t <= TUninit || !t.maybe(TUninit)); case DataTypeSpecific: return t.isKnownDataType(); case DataTypeSpecialized: // Type::isSpecialized() returns true for types like {Arr<Packed>|Int} // and Arr has non-specialized subtypes, so we require that t is // specialized, a strict subtype of Obj or Arr, and that it fits the // specific requirements of tc. assertx(tc.wantClass() ^ tc.wantArrayKind()); if (t < TObj && t.clsSpec()) { return tc.wantClass() && t.clsSpec().cls()->classof(tc.desiredClass()); } if (t < TArr && t.arrSpec()) { auto arrSpec = t.arrSpec(); if (tc.wantArrayShape() && !arrSpec.shape()) return false; if (tc.wantArrayKind() && !arrSpec.kind()) return false; return true; } return false; } not_reached(); }
TypeConstraint applyConstraint(TypeConstraint tc, const TypeConstraint newTc) { tc.category = std::max(newTc.category, tc.category); if (newTc.wantArrayKind()) tc.setWantArrayKind(); if (newTc.wantClass()) { if (tc.wantClass()) { // It only makes sense to constrain tc with a class that's related to its // existing class, and we want to preserve the more derived of the two. auto cls1 = tc.desiredClass(); auto cls2 = newTc.desiredClass(); tc.setDesiredClass(cls1->classof(cls2) ? cls1 : cls2); } else { tc.setDesiredClass(newTc.desiredClass()); } } return tc; }
bool typeFitsConstraint(Type t, TypeConstraint tc) { always_assert(t != Type::Bottom); switch (tc.category) { case DataTypeGeneric: return true; case DataTypeCountness: // Consumers using this constraint expect the type to be relaxed to // Uncounted or left alone, so something like Arr|Obj isn't specific // enough. return t.notCounted() || t.subtypeOfAny(Type::Str, Type::Arr, Type::Obj, Type::Res, Type::BoxedCell); case DataTypeCountnessInit: return typeFitsConstraint(t, DataTypeCountness) && (t <= Type::Uninit || t.not(Type::Uninit)); case DataTypeSpecific: return t.isKnownDataType(); case DataTypeSpecialized: // Type::isSpecialized() returns true for types like {Arr<Packed>|Int} // and Arr has non-specialized subtypes, so we require that t is // specialized, a strict subtype of Obj or Arr, and that it fits the // specific requirements of tc. assert(tc.wantClass() ^ tc.wantArrayKind()); if (!t.isSpecialized()) return false; if (t < Type::Obj) { return tc.wantClass() && t.getClass()->classof(tc.desiredClass()); } if (t < Type::Arr) { return tc.wantArrayKind() && t.hasArrayKind(); } return false; } not_reached(); }
/* * relaxConstraint returns the least specific TypeConstraint 'tc' that doesn't * prevent the intersection of knownType and relaxType(toRelax, tc) from * satisfying origTc. It is used in IRBuilder::constrainValue and * IRBuilder::constrainStack to determine how to constrain the typeParam and * src values of CheckType/CheckStk instructions, and the src values of * AssertType/AssertStk instructions. * * AssertType example: * t24:Obj<C> = AssertType<{Obj<C>|InitNull}> t4:Obj * * If constrainValue is called with (t24, DataTypeSpecialized), relaxConstraint * will be called with (DataTypeSpecialized, Obj<C>|InitNull, Obj). After a few * iterations it will determine that constraining Obj with DataTypeCountness * will still allow the result type of the AssertType instruction to satisfy * DataTypeSpecialized, because relaxType(Obj, DataTypeCountness) == Obj. */ TypeConstraint relaxConstraint(const TypeConstraint origTc, const Type knownType, const Type toRelax) { ITRACE(4, "relaxConstraint({}, knownType = {}, toRelax = {})\n", origTc, knownType, toRelax); Trace::Indent _i; auto const dstType = refineType(knownType, toRelax); always_assert_flog(typeFitsConstraint(dstType, origTc), "refine({}, {}) doesn't fit {}", knownType, toRelax, origTc); // Preserve origTc's weak property. TypeConstraint newTc{DataTypeGeneric, DataTypeGeneric}; newTc.weak = origTc.weak; while (true) { if (newTc.isSpecialized()) { // We need to ask for the right kind of specialization, so grab it from // origTc. if (origTc.wantArrayKind()) newTc.setWantArrayKind(); if (origTc.wantClass()) newTc.setDesiredClass(origTc.desiredClass()); } auto const relaxed = relaxType(toRelax, newTc); auto const newDstType = refineType(relaxed, knownType); if (typeFitsConstraint(newDstType, origTc)) break; ITRACE(5, "newDstType = {}, newTc = {}; ", newDstType, newTc); if (newTc.category == DataTypeGeneric || !typeFitsOuterConstraint(newDstType, origTc)) { FTRACE(5, "incrementing outer\n"); incCategory(newTc.category); } else if (!typeFitsInnerConstraint(newDstType, origTc)) { FTRACE(5, "incrementing inner\n"); incCategory(newTc.innerCat); } else { not_reached(); } } ITRACE(4, "Returning {}\n", newTc); // newTc shouldn't be any more specific than origTc. always_assert(newTc.category <= origTc.category && newTc.innerCat <= origTc.innerCat); return newTc; }