2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/hhbbc/type-ops.h"
18 #include "hphp/runtime/base/tv-conversions.h"
19 #include "hphp/runtime/base/tv-arith.h"
21 #include "hphp/hhbbc/eval-cell.h"
22 #include "hphp/hhbbc/type-system.h"
24 namespace HPHP::HHBBC
{
26 //////////////////////////////////////////////////////////////////////
31 Optional
<Type
> eval_const(Type t1
, Type t2
, Fun fun
) {
32 auto const v1
= tv(t1
);
33 auto const v2
= tv(t2
);
34 if (v1
&& v2
) return eval_cell([&] { return fun(*v1
, *v2
); });
39 Type
bitwise_impl(Type t1
, Type t2
, Fun op
) {
40 if (!(t1
.couldBe(BInt
) && t2
.couldBe(BInt
)) &&
41 !(t1
.couldBe(BStr
) && t2
.couldBe(BStr
))) {
44 if (auto t
= eval_const(t1
, t2
, op
)) return *t
;
45 if (t1
.subtypeOf(BStr
) && t2
.subtypeOf(BStr
)) return TStr
;
46 if (!t1
.couldBe(BStr
) || !t2
.couldBe(BStr
)) return TInt
;
51 Type
shift_impl(Type t1
, Type t2
, Fun op
) {
52 if (!t1
.couldBe(BInt
) || !t2
.couldBe(BInt
)) return TBottom
;
53 if (auto const t
= eval_const(t1
, t2
, op
)) return *t
;
58 //////////////////////////////////////////////////////////////////////
60 Type
typeToInt(Type ty
) {
61 if (auto const v
= tv(ty
)) return ival(tvToInt(*v
));
62 if (ty
.subtypeOf(BNull
)) return ival(0);
66 //////////////////////////////////////////////////////////////////////
68 template <class CellOp
>
69 Type
typeArithImpl(Type t1
, Type t2
, CellOp op
) {
70 if (!t1
.couldBe(BNum
) || !t2
.couldBe(BNum
)) return TBottom
;
71 if (auto t
= eval_const(t1
, t2
, op
)) return *t
;
72 if (op
== tvMod
) return TInt
;
75 * TODO(#3577303): some of these could be nothrow, which is probably
76 * information we have want to propagate back out through the return
77 * value here (rather than bundling everything into the
80 if (t1
.subtypeOf(BInt
) && t2
.subtypeOf(BInt
)) {
81 // can't switch on pointers, so use template magic
82 auto is_any
= [](auto first
, auto ...t
) { return ((first
== t
) || ...); };
83 return is_any(op
, tvDiv
, tvPow
) ? TNum
: TInt
;
85 if (t1
.subtypeOf(BNum
) && t2
.subtypeOf(BNum
) &&
86 (t1
.subtypeOf(BDbl
) || t2
.subtypeOf(BDbl
))) return TDbl
;
90 Type
typeAdd(Type t1
, Type t2
) { return typeArithImpl(t1
, t2
, tvAdd
); }
91 Type
typeSub(Type t1
, Type t2
) { return typeArithImpl(t1
, t2
, tvSub
); }
92 Type
typeMul(Type t1
, Type t2
) { return typeArithImpl(t1
, t2
, tvMul
); }
93 Type
typeDiv(Type t1
, Type t2
) { return typeArithImpl(t1
, t2
, tvDiv
); }
94 Type
typePow(Type t1
, Type t2
) { return typeArithImpl(t1
, t2
, tvPow
); }
95 Type
typeMod(Type t1
, Type t2
) { return typeArithImpl(t1
, t2
, tvMod
); }
97 Type
typeConcat(Type t1
, Type t2
) {
98 auto const tv
= eval_const(t1
, t2
, [&] (auto v1
, auto v2
) {
102 if (tv
) return loosen_staticness(*tv
);
106 //////////////////////////////////////////////////////////////////////
108 Type
typeBitAnd(Type t1
, Type t2
) { return bitwise_impl(t1
, t2
, tvBitAnd
); }
109 Type
typeBitOr(Type t1
, Type t2
) { return bitwise_impl(t1
, t2
, tvBitOr
); }
110 Type
typeBitXor(Type t1
, Type t2
) { return bitwise_impl(t1
, t2
, tvBitXor
); }
112 Type
typeShl(Type t1
, Type t2
) { return shift_impl(t1
, t2
, tvShl
); }
113 Type
typeShr(Type t1
, Type t2
) { return shift_impl(t1
, t2
, tvShr
); }
115 //////////////////////////////////////////////////////////////////////
117 Type
typeIncDec(IncDecOp op
, Type t
) {
118 auto const val
= tv(t
);
121 // Doubles always stay doubles
122 if (t
.subtypeOf(BDbl
)) return TDbl
;
124 if (t
.subtypeOf(BOptInt
)) {
125 // Ints stay ints unless they can overflow to doubles
126 if (t
.subtypeOf(BInt
)) {
129 // ++ on null throws, stays null on --. Uninit is folded to init.
130 if (t
.subtypeOf(BNull
)) return isInc(op
) ? TBottom
: TInitNull
;
131 // Optional integer case. The union of the above two cases.
132 if (isInc(op
)) return TInt
;
136 // No-op on bool, array, resource, object.
137 if (t
.subtypeOf(BBool
| BArrLike
| BRes
| BObj
)) return t
;
142 auto const inc
= isInc(op
);
144 // We can't constprop with this eval_cell, because of the effects
146 auto resultTy
= eval_cell([inc
,val
] {
156 // We may have inferred a TSStr or TSArr with a value here, but at
157 // runtime it will not be static.
158 return resultTy
? loosen_staticness(*resultTy
) : TInitCell
;
161 Type
typeSetOp(SetOpOp op
, Type lhs
, Type rhs
) {
162 auto const lhsV
= tv(lhs
);
163 auto const rhsV
= tv(rhs
);
166 // Can't constprop at this eval_cell, because of the effects on
168 auto resultTy
= eval_cell([&] {
169 TypedValue c
= *lhsV
;
170 TypedValue rhs
= *rhsV
;
171 setopBody(&c
, op
, &rhs
);
175 // We may have inferred a TSStr or TSArr with a value here, but
176 // at runtime it will not be static. For now just throw that
177 // away. TODO(#3696042): should be able to loosen_staticness here.
178 if (resultTy
->subtypeOf(BStr
)) return TStr
;
179 else if (resultTy
->subtypeOf(BVec
)) return TVec
;
180 else if (resultTy
->subtypeOf(BDict
)) return TDict
;
181 else if (resultTy
->subtypeOf(BKeyset
)) return TKeyset
;
188 case SetOpOp::PlusEqual
: return typeAdd(lhs
, rhs
);
189 case SetOpOp::MinusEqual
: return typeSub(lhs
, rhs
);
190 case SetOpOp::MulEqual
: return typeMul(lhs
, rhs
);
192 case SetOpOp::DivEqual
: return typeDiv(lhs
, rhs
);
193 case SetOpOp::ModEqual
: return typeMod(lhs
, rhs
);
194 case SetOpOp::PowEqual
: return typePow(lhs
, rhs
);
196 case SetOpOp::AndEqual
: return typeBitAnd(lhs
, rhs
);
197 case SetOpOp::OrEqual
: return typeBitOr(lhs
, rhs
);
198 case SetOpOp::XorEqual
: return typeBitXor(lhs
, rhs
);
200 case SetOpOp::ConcatEqual
: return typeConcat(lhs
, rhs
);
201 case SetOpOp::SlEqual
: return typeShl(lhs
, rhs
);
202 case SetOpOp::SrEqual
: return typeShr(lhs
, rhs
);
207 //////////////////////////////////////////////////////////////////////
209 Type
typeSame(const Type
& a
, const Type
& b
) {
210 // The comparison will recurse into array values, so we need to
211 // loosen the likeness recursively (unlike normal).
212 auto const nsa
= loosen_likeness_recursively(loosen_staticness(a
));
213 auto const nsb
= loosen_likeness_recursively(loosen_staticness(b
));
214 if (!nsa
.couldBe(nsb
)) return TFalse
;
218 Type
typeNSame(const Type
& a
, const Type
& b
) {
219 auto const ty
= typeSame(a
, b
);
220 assertx(ty
.subtypeOf(BBool
));
221 return ty
.subtypeOf(BFalse
) ? TTrue
:
222 ty
.subtypeOf(BTrue
) ? TFalse
:
226 //////////////////////////////////////////////////////////////////////