Enable silent index builds
[hiphop-php.git] / hphp / hhbbc / type-ops.cpp
blobc560450e6d403bd9534f511e467e416dd3c277cd
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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 //////////////////////////////////////////////////////////////////////
30 namespace {
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
37 * interpreter).
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;
44 return folly::none;
47 template<class Fun>
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); });
52 return folly::none;
55 template<class Fun>
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;
60 return TInitCell;
63 template<class Fun>
64 Type shift_impl(Type t1, Type t2, Fun op) {
65 t1 = typeToInt(t1);
66 t2 = typeToInt(t2);
68 if (auto const t = eval_const(t1, t2, op)) return *t;
69 return TInt;
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);
78 return TInt;
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;
90 return TInitCell;
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;
101 return TInitCell;
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;
108 return TInitPrim;
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;
116 return TInitPrim;
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;
127 return TInitPrim;
130 Type typeMod(Type t1, Type t2) {
131 if (auto t = eval_const(t1, t2, cellMod)) return *t;
132 return TInitPrim;
135 Type typePow(Type t1, Type t2) {
136 if (auto t = eval_const(t1, t2, cellPow)) return *t;
137 return TNum;
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);
155 if (!val) {
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;
182 return TInitCell;
185 auto const inc = isInc(op);
187 // We can't constprop with this eval_cell, because of the effects
188 // on locals.
189 auto resultTy = eval_cell([inc,overflowToDbl,val] {
190 auto c = *val;
191 if (inc) {
192 (overflowToDbl ? cellIncO : cellInc)(&c);
193 } else {
194 (overflowToDbl ? cellDecO : cellDec)(&c);
196 return 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);
203 return *resultTy;
206 Type typeSetOp(SetOpOp op, Type lhs, Type rhs) {
207 switch (op) {
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);
228 not_reached();
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)) {
240 return TBool;
242 if (nsb.couldBe(BClsMeth) &&
243 nsa.couldBe(RuntimeOption::EvalHackArrDVArrs ? BVec : BVArr)) {
244 return TBool;
246 if (!nsa.couldBe(nsb)) return TFalse;
247 return TBool;
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 :
255 TBool;
258 //////////////////////////////////////////////////////////////////////