Add a capi conversion no-op method to thrift python structs/unions
[hiphop-php.git] / hphp / hhbbc / type-ops.cpp
blob8b7f5a2e8a649cae99ec82f4e9bdb34a1ce662b2
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 "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 //////////////////////////////////////////////////////////////////////
28 namespace {
30 template<class Fun>
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); });
35 return std::nullopt;
38 template<class Fun>
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))) {
42 return TBottom;
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;
47 return TInitCell;
50 template<class Fun>
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;
54 return TInt;
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);
63 return TInt;
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
78 * interpreter).
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;
87 return TNum;
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) {
99 tvConcatEq(&v1, v2);
100 return v1;
102 if (tv) return loosen_staticness(*tv);
103 return TStr;
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);
120 if (!val) {
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)) {
127 return TInt;
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;
133 return TOptInt;
136 // No-op on bool, array, resource, object.
137 if (t.subtypeOf(BBool | BArrLike | BRes | BObj)) return t;
139 return TInitCell;
142 auto const inc = isInc(op);
144 // We can't constprop with this eval_cell, because of the effects
145 // on locals.
146 auto resultTy = eval_cell([inc,val] {
147 auto c = *val;
148 if (inc) {
149 tvInc(&c);
150 } else {
151 tvDec(&c);
153 return c;
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);
165 if (lhsV && rhsV) {
166 // Can't constprop at this eval_cell, because of the effects on
167 // locals.
168 auto resultTy = eval_cell([&] {
169 TypedValue c = *lhsV;
170 TypedValue rhs = *rhsV;
171 setopBody(&c, op, &rhs);
172 return c;
174 if (resultTy) {
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;
183 return *resultTy;
187 switch (op) {
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);
204 not_reached();
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;
215 return TBool;
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 :
223 TBool;
226 //////////////////////////////////////////////////////////////////////