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 <folly/Optional.h>
20 #include "hphp/runtime/base/tv-conversions.h"
21 #include "hphp/runtime/base/tv-arith.h"
23 #include "hphp/hhbbc/eval-cell.h"
24 #include "hphp/hhbbc/type-system.h"
26 namespace HPHP
{ namespace HHBBC
{
28 //////////////////////////////////////////////////////////////////////
32 folly::Optional
<Type
> usual_arith_conversions(Type t1
, Type t2
) {
34 * TODO(#3577303): some of these could be nothrow, which is probably
35 * information we have want to propagate back out through the return
36 * value here (rather than bundling everything into the
39 if (t1
.subtypeOf(BInt
) && t2
.subtypeOf(BInt
)) return TInt
;
40 if (t1
.subtypeOf(BInt
) && t2
.subtypeOf(BDbl
)) return TDbl
;
41 if (t1
.subtypeOf(BDbl
) && t2
.subtypeOf(BInt
)) return TDbl
;
42 if (t1
.subtypeOf(BDbl
) && t2
.subtypeOf(BDbl
)) return TDbl
;
43 if (t1
.subtypeOf(BNum
) && t2
.subtypeOf(BNum
)) return TNum
;
48 folly::Optional
<Type
> eval_const(Type t1
, Type t2
, Fun fun
) {
49 auto const v1
= tv(t1
);
50 auto const v2
= tv(t2
);
51 if (v1
&& v2
) return eval_cell([&] { return fun(*v1
, *v2
); });
56 Type
bitwise_impl(Type t1
, Type t2
, Fun op
) {
57 if (auto t
= eval_const(t1
, t2
, op
)) return *t
;
58 if (t1
.subtypeOf(BStr
) && t2
.subtypeOf(BStr
)) return TStr
;
59 if (!t1
.couldBe(BStr
) || !t2
.couldBe(BStr
)) return TInt
;
64 Type
shift_impl(Type t1
, Type t2
, Fun op
) {
68 if (auto const t
= eval_const(t1
, t2
, op
)) return *t
;
73 //////////////////////////////////////////////////////////////////////
75 Type
typeToInt(Type ty
) {
76 if (auto const v
= tv(ty
)) return ival(cellToInt(*v
));
77 if (ty
.subtypeOf(BNull
)) return ival(0);
81 //////////////////////////////////////////////////////////////////////
83 Type
typeAdd(Type t1
, Type t2
) {
84 if (auto t
= eval_const(t1
, t2
, cellAdd
)) return *t
;
85 if (auto t
= usual_arith_conversions(t1
, t2
)) return *t
;
86 if (t1
.subtypeOf(BArr
) && t2
.subtypeOf(BArr
)) return TArr
;
87 if (t1
.subtypeOf(BVec
) && t2
.subtypeOf(BVec
)) return TVec
;
88 if (t1
.subtypeOf(BDict
) && t2
.subtypeOf(BDict
)) return TDict
;
89 if (t1
.subtypeOf(BKeyset
) && t2
.subtypeOf(BKeyset
)) return TKeyset
;
93 Type
typeAddO(Type t1
, Type t2
) {
94 if (auto t
= eval_const(t1
, t2
, cellAddO
)) return *t
;
95 if (t1
.subtypeOf(BInt
) && t2
.subtypeOf(BInt
)) return TNum
;
96 if (auto t
= usual_arith_conversions(t1
, t2
)) return *t
;
97 if (t1
.subtypeOf(BArr
) && t2
.subtypeOf(BArr
)) return TArr
;
98 if (t1
.subtypeOf(BVec
) && t2
.subtypeOf(BVec
)) return TVec
;
99 if (t1
.subtypeOf(BDict
) && t2
.subtypeOf(BDict
)) return TDict
;
100 if (t1
.subtypeOf(BKeyset
) && t2
.subtypeOf(BKeyset
)) return TKeyset
;
104 template <class CellOp
>
105 Type
typeSubMulImpl(Type t1
, Type t2
, CellOp op
) {
106 if (auto t
= eval_const(t1
, t2
, op
)) return *t
;
107 if (auto t
= usual_arith_conversions(t1
, t2
)) return *t
;
111 template <class CellOp
>
112 Type
typeSubMulImplO(Type t1
, Type t2
, CellOp op
) {
113 if (auto t
= eval_const(t1
, t2
, op
)) return *t
;
114 if (t1
.subtypeOf(BInt
) && t2
.subtypeOf(BInt
)) return TNum
;
115 if (auto t
= usual_arith_conversions(t1
, t2
)) return *t
;
119 Type
typeSub(Type t1
, Type t2
) { return typeSubMulImpl(t1
, t2
, cellSub
); }
120 Type
typeMul(Type t1
, Type t2
) { return typeSubMulImpl(t1
, t2
, cellMul
); }
122 Type
typeSubO(Type t1
, Type t2
) { return typeSubMulImplO(t1
, t2
, cellSubO
); }
123 Type
typeMulO(Type t1
, Type t2
) { return typeSubMulImplO(t1
, t2
, cellMulO
); }
125 Type
typeDiv(Type t1
, Type t2
) {
126 if (auto t
= eval_const(t1
, t2
, cellDiv
)) return *t
;
130 Type
typeMod(Type t1
, Type t2
) {
131 if (auto t
= eval_const(t1
, t2
, cellMod
)) return *t
;
135 Type
typePow(Type t1
, Type t2
) {
136 if (auto t
= eval_const(t1
, t2
, cellPow
)) return *t
;
140 //////////////////////////////////////////////////////////////////////
142 Type
typeBitAnd(Type t1
, Type t2
) { return bitwise_impl(t1
, t2
, cellBitAnd
); }
143 Type
typeBitOr(Type t1
, Type t2
) { return bitwise_impl(t1
, t2
, cellBitOr
); }
144 Type
typeBitXor(Type t1
, Type t2
) { return bitwise_impl(t1
, t2
, cellBitXor
); }
146 Type
typeShl(Type t1
, Type t2
) { return shift_impl(t1
, t2
, cellShl
); }
147 Type
typeShr(Type t1
, Type t2
) { return shift_impl(t1
, t2
, cellShr
); }
149 //////////////////////////////////////////////////////////////////////
151 Type
typeIncDec(IncDecOp op
, Type t
) {
152 auto const overflowToDbl
= isIncDecO(op
);
153 auto const val
= tv(t
);
156 // Doubles always stay doubles
157 if (t
.subtypeOf(BDbl
)) return TDbl
;
159 if (t
.subtypeOf(BOptInt
)) {
160 // Ints stay ints unless they can overflow to doubles
161 if (t
.subtypeOf(BInt
)) {
162 return overflowToDbl
? TNum
: TInt
;
164 // Null goes to 1 on ++, stays null on --. Uninit is folded to init.
165 if (t
.subtypeOf(BNull
)) {
166 return isInc(op
) ? ival(1) : TInitNull
;
168 // Optional integer case. The union of the above two cases.
169 if (isInc(op
)) return overflowToDbl
? TNum
: TInt
;
170 return overflowToDbl
? TOptNum
: TOptInt
;
173 // No-op on bool, array, resource, object.
174 if (t
.subtypeOfAny(TBool
, TArr
, TRes
, TObj
, TVec
, TDict
, TKeyset
)) return t
;
176 // Last case: strings. These result in Int|Str because of the
177 // behavior on strictly-numeric strings.
178 if (t
.subtypeOf(TOptStr
)) {
179 return (isInc(op
) || t
.subtypeOf(TStr
)) ? TArrKey
: TOptArrKey
;
185 auto const inc
= isInc(op
);
187 // We can't constprop with this eval_cell, because of the effects
189 auto resultTy
= eval_cell([inc
,overflowToDbl
,val
] {
192 (overflowToDbl
? cellIncO
: cellInc
)(&c
);
194 (overflowToDbl
? cellDecO
: cellDec
)(&c
);
198 if (!resultTy
) resultTy
= TInitCell
;
200 // We may have inferred a TSStr or TSArr with a value here, but at
201 // runtime it will not be static.
202 resultTy
= loosen_staticness(*resultTy
);
206 Type
typeSetOp(SetOpOp op
, Type lhs
, Type rhs
) {
208 case SetOpOp::PlusEqual
: return typeAdd(lhs
, rhs
);
209 case SetOpOp::MinusEqual
: return typeSub(lhs
, rhs
);
210 case SetOpOp::MulEqual
: return typeMul(lhs
, rhs
);
212 case SetOpOp::DivEqual
: return typeDiv(lhs
, rhs
);
213 case SetOpOp::ModEqual
: return typeMod(lhs
, rhs
);
214 case SetOpOp::PowEqual
: return typePow(lhs
, rhs
);
216 case SetOpOp::AndEqual
: return typeBitAnd(lhs
, rhs
);
217 case SetOpOp::OrEqual
: return typeBitOr(lhs
, rhs
);
218 case SetOpOp::XorEqual
: return typeBitXor(lhs
, rhs
);
220 case SetOpOp::PlusEqualO
: return typeAddO(lhs
, rhs
);
221 case SetOpOp::MinusEqualO
: return typeSubO(lhs
, rhs
);
222 case SetOpOp::MulEqualO
: return typeMulO(lhs
, rhs
);
224 case SetOpOp::ConcatEqual
: return TStr
;
225 case SetOpOp::SlEqual
: return typeShl(lhs
, rhs
);
226 case SetOpOp::SrEqual
: return typeShr(lhs
, rhs
);
231 //////////////////////////////////////////////////////////////////////
233 Type
typeSame(const Type
& a
, const Type
& b
) {
234 auto const nsa
= loosen_provenance(loosen_dvarrayness(a
));
235 auto const nsb
= loosen_provenance(loosen_dvarrayness(b
));
236 if (nsa
.couldBe(BFunc
| BCls
) && nsb
.couldBe(TStr
)) return TBool
;
237 if (nsb
.couldBe(BFunc
| BCls
) && nsa
.couldBe(TStr
)) return TBool
;
238 if (nsa
.couldBe(BClsMeth
) &&
239 nsb
.couldBe(RuntimeOption::EvalHackArrDVArrs
? BVec
: BVArr
)) {
242 if (nsb
.couldBe(BClsMeth
) &&
243 nsa
.couldBe(RuntimeOption::EvalHackArrDVArrs
? BVec
: BVArr
)) {
246 if (!nsa
.couldBe(nsb
)) return TFalse
;
250 Type
typeNSame(const Type
& a
, const Type
& b
) {
251 auto const ty
= typeSame(a
, b
);
252 assert(ty
.subtypeOf(BBool
));
253 return ty
.subtypeOf(BFalse
) ? TTrue
:
254 ty
.subtypeOf(BTrue
) ? TFalse
:
258 //////////////////////////////////////////////////////////////////////