Type typeIncDec(IncDecOp op, Type t) { auto const overflowToDbl = isIncDecO(op); auto const val = tv(t); if (!val) { // Doubles always stay doubles if (t.subtypeOf(TDbl)) return TDbl; // Ints stay ints unless they can overflow to doubles if (t.subtypeOf(TInt)) { return overflowToDbl ? TNum : TInt; } // Null goes to 1 on ++, stays null on --. Uninit is folded to init. if (t.subtypeOf(TNull)) { return isInc(op) ? ival(1) : TInitNull; } // No-op on bool, array, resource, object. if (t.subtypeOfAny(TBool, TArr, TRes, TObj, TVec, TDict, TKeyset)) return t; // Last unhandled case: strings. These result in Int|Str because of the // behavior on strictly-numeric strings, and we can't express that yet. return TInitCell; } auto const inc = isInc(op); // We can't constprop with this eval_cell, because of the effects // on locals. auto resultTy = eval_cell([inc,overflowToDbl,val] { auto c = *val; if (inc) { (overflowToDbl ? cellIncO : cellInc)(c); } else { (overflowToDbl ? cellDecO : cellDec)(c); } return c; }); if (!resultTy) resultTy = TInitCell; // We may have inferred a TSStr or TSArr with a value here, but at // runtime it will not be static. resultTy = loosen_staticness(*resultTy); return *resultTy; }
void IRTranslator::translateIncDecL(const NormalizedInstruction& i) { auto const op = static_cast<IncDecOp>(i.imm[1].u_OA); HHIR_EMIT(IncDecL, isPre(op), isInc(op), isIncDecO(op), i.imm[0].u_LA); }