allow undefined data-* and aria-* XHP attributes with ->:
[hiphop-php.git] / hphp / hhbbc / type-system.cpp
blobab526a62ba2d7f6e4446c617c3780342c5566e32
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 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-system.h"
18 #include <type_traits>
19 #include <cmath>
20 #include <algorithm>
21 #include <iterator>
23 #include <folly/Optional.h>
24 #include <folly/Traits.h>
25 #include <folly/Hash.h>
27 #include "hphp/runtime/base/repo-auth-type.h"
28 #include "hphp/runtime/base/repo-auth-type-array.h"
29 #include "hphp/runtime/base/array-iterator.h"
30 #include "hphp/hhbbc/index.h"
32 namespace HPHP { namespace HHBBC {
34 TRACE_SET_MOD(hhbbc);
36 namespace {
38 //////////////////////////////////////////////////////////////////////
40 const StaticString s_WaitHandle("HH\\WaitHandle");
41 const StaticString s_empty("");
43 //////////////////////////////////////////////////////////////////////
45 // Legal to call with !isPredefined(bits)
46 bool mayHaveData(trep bits) {
47 switch (bits) {
48 case BSStr: case BObj: case BInt: case BDbl:
49 case BOptSStr: case BOptObj: case BOptInt: case BOptDbl:
50 case BCls:
51 case BArr: case BSArr: case BCArr:
52 case BArrN: case BSArrN: case BCArrN:
53 case BOptArr: case BOptSArr: case BOptCArr:
54 case BOptArrN: case BOptSArrN: case BOptCArrN:
55 case BRef:
56 return true;
58 case BBottom:
59 case BUninit:
60 case BInitNull:
61 case BFalse:
62 case BTrue:
63 case BCStr:
64 case BSArrE:
65 case BCArrE:
66 case BRes:
67 case BNull:
68 case BNum:
69 case BBool:
70 case BStr:
71 case BArrE:
72 case BInitPrim:
73 case BPrim:
74 case BInitUnc:
75 case BUnc:
76 case BOptTrue:
77 case BOptFalse:
78 case BOptBool:
79 case BOptNum:
80 case BOptCStr:
81 case BOptStr:
82 case BOptSArrE:
83 case BOptCArrE:
84 case BOptArrE:
85 case BOptRes:
86 case BInitCell:
87 case BCell:
88 case BInitGen:
89 case BGen:
90 case BTop:
91 break;
93 return false;
97 * Note: currently we're limiting all represented types to predefined
98 * bit patterns (instead of arbitrary unions), so this function is
99 * around for assertions.
101 * This may be relaxed later if we think we can get more out of it,
102 * but for now the thought is that the likelihood of getting
103 * significantly better types from the inference algorithm might be
104 * counter-balanced by the increased chance of hard-to-track
105 * type-system bugs, so at least for now (at the time of this writing,
106 * before shipping hhbbc) we're ruling out a bunch of edge cases.
108 * Aside from types like Obj<= or things like TSStr, a lot of cases
109 * with arbitrary-unions may not lead to better code at JIT time
110 * (unless the inference turns them back into a predefined type),
111 * since we'll have to guard anyway to see which one it was. We'll
112 * try it later.
114 bool isPredefined(trep bits) {
115 switch (bits) {
116 case BBottom:
117 case BUninit:
118 case BInitNull:
119 case BFalse:
120 case BTrue:
121 case BInt:
122 case BDbl:
123 case BSStr:
124 case BCStr:
125 case BSArrE:
126 case BCArrE:
127 case BSArrN:
128 case BCArrN:
129 case BObj:
130 case BRes:
131 case BCls:
132 case BRef:
133 case BNull:
134 case BNum:
135 case BBool:
136 case BStr:
137 case BSArr:
138 case BCArr:
139 case BArrE:
140 case BArrN:
141 case BArr:
142 case BInitPrim:
143 case BPrim:
144 case BInitUnc:
145 case BUnc:
146 case BOptTrue:
147 case BOptFalse:
148 case BOptBool:
149 case BOptInt:
150 case BOptDbl:
151 case BOptNum:
152 case BOptSStr:
153 case BOptCStr:
154 case BOptStr:
155 case BOptSArrN:
156 case BOptCArrN:
157 case BOptSArrE:
158 case BOptCArrE:
159 case BOptSArr:
160 case BOptCArr:
161 case BOptArrE:
162 case BOptArrN:
163 case BOptArr:
164 case BOptObj:
165 case BOptRes:
166 case BInitCell:
167 case BCell:
168 case BInitGen:
169 case BGen:
170 case BTop:
171 return true;
173 return false;
176 // Pre: isPredefined(bits)
177 bool canBeOptional(trep bits) {
178 switch (bits) {
179 case BBottom:
180 return false;
182 case BUninit:
183 case BInitNull:
184 return false;
185 case BFalse:
186 case BTrue:
187 case BInt:
188 case BDbl:
189 case BSStr:
190 case BCStr:
191 case BSArrE:
192 case BSArrN:
193 case BCArrE:
194 case BCArrN:
195 case BObj:
196 case BRes:
197 return true;
199 case BNull:
200 case BNum:
201 case BBool:
202 case BStr:
203 case BSArr:
204 case BCArr:
205 case BArrE:
206 case BArrN:
207 case BArr:
208 return true;
210 case BCls:
211 case BRef:
212 return false;
214 case BOptTrue:
215 case BOptFalse:
216 case BOptBool:
217 case BOptInt:
218 case BOptDbl:
219 case BOptNum:
220 case BOptSStr:
221 case BOptCStr:
222 case BOptStr:
223 case BOptSArrE:
224 case BOptCArrE:
225 case BOptSArrN:
226 case BOptCArrN:
227 case BOptSArr:
228 case BOptCArr:
229 case BOptArrN:
230 case BOptArrE:
231 case BOptArr:
232 case BOptObj:
233 case BOptRes:
234 return false;
236 case BInitPrim:
237 case BPrim:
238 case BInitUnc:
239 case BUnc:
240 case BInitCell:
241 case BInitGen:
242 case BCell:
243 case BGen:
244 case BTop:
245 return false;
247 not_reached();
251 * Combine array bits. Our type system currently avoids arbitrary unions (see
252 * rationale above), so we don't have predefined types like CArr|SArrN, or
253 * SArrN|CArrE. This function checks a few cases to ensure combining array
254 * type bits leaves it predefined.
256 trep combine_arr_bits(trep a, trep b) {
257 assert((a & BArr) == a || (a & BOptArr) == a);
258 assert((b & BArr) == b || (b & BOptArr) == b);
259 auto const combined = static_cast<trep>(a | b);
260 auto const arr_part = combined & BArr;
261 // 2 bit cases:
262 if (arr_part == (BSArrN|BCArrE)) return static_cast<trep>(combined|BArr);
263 if (arr_part == (BSArrE|BCArrN)) return static_cast<trep>(combined|BArr);
264 // 3 bit cases:
265 if (arr_part == (BArrN|BCArrE)) return static_cast<trep>(combined|BArr);
266 if (arr_part == (BArrN|BSArrE)) return static_cast<trep>(combined|BArr);
267 if (arr_part == (BArrE|BCArrN)) return static_cast<trep>(combined|BArr);
268 if (arr_part == (BArrE|BSArrN)) return static_cast<trep>(combined|BArr);
269 assert(isPredefined(combined));
270 return combined;
273 //////////////////////////////////////////////////////////////////////
276 * The following functions make DArr* structs out of static arrays, to
277 * simplify implementing some of the type system operations on them.
279 * When they return folly::none it is not a conservative thing: it
280 * implies the array is definitely not packed, packedN, struct-like,
281 * etc (we use this to return false in couldBe).
284 folly::Optional<DArrPacked> toDArrPacked(SArray ar) {
285 assert(!ar->empty());
287 std::vector<Type> elems;
288 auto idx = size_t{0};
289 for (ArrayIter iter(ar); iter; ++iter, ++idx) {
290 auto const key = *iter.first().asTypedValue();
291 if (key.m_type != KindOfInt64) return folly::none;
292 if (key.m_data.num != idx) return folly::none;
293 elems.push_back(
294 from_cell(*iter.secondRef().asTypedValue())
298 return DArrPacked { std::move(elems) };
301 folly::Optional<DArrPackedN> toDArrPackedN(SArray ar) {
302 assert(!ar->empty());
304 auto t = TBottom;
305 auto idx = size_t{0};
306 for (ArrayIter iter(ar); iter; ++iter, ++idx) {
307 auto const key = *iter.first().asTypedValue();
308 if (key.m_type != KindOfInt64) return folly::none;
309 if (key.m_data.num != idx) return folly::none;
310 t = union_of(t, from_cell(*iter.secondRef().asTypedValue()));
313 return DArrPackedN { std::move(t) };
316 folly::Optional<DArrStruct> toDArrStruct(SArray ar) {
317 assert(!ar->empty());
319 auto map = StructMap{};
320 for (ArrayIter iter(ar); iter; ++iter) {
321 auto const key = *iter.first().asTypedValue();
322 if (!IS_STRING_TYPE(key.m_type)) return folly::none;
323 map[key.m_data.pstr] = from_cell(*iter.secondRef().asTypedValue());
326 return DArrStruct { std::move(map) };
329 DArrMapN toDArrMapN(SArray ar) {
330 assert(!ar->empty());
332 auto k = TBottom;
333 auto v = TBottom;
334 for (ArrayIter iter(ar); iter; ++iter) {
335 auto const key = *iter.first().asTypedValue();
336 k = union_of(k, from_cell(key));
337 v = union_of(v, from_cell(*iter.secondRef().asTypedValue()));
340 return DArrMapN { k, v };
343 //////////////////////////////////////////////////////////////////////
345 bool subtypePacked(const DArrPacked& a, const DArrPacked& b) {
346 auto const asz = a.elems.size();
347 auto const bsz = b.elems.size();
348 if (asz != bsz) return false;
349 for (auto i = size_t{0}; i < asz; ++i) {
350 if (!a.elems[i].subtypeOf(b.elems[i])) {
351 return false;
354 return true;
357 bool subtypeStruct(const DArrStruct& a, const DArrStruct& b) {
358 if (a.map.size() != b.map.size()) return false;
359 auto aIt = begin(a.map);
360 auto bIt = begin(b.map);
361 for (; aIt != end(a.map); ++aIt, ++bIt) {
362 if (aIt->first != bIt->first) return false;
363 if (!aIt->second.subtypeOf(bIt->second)) return false;
365 return true;
368 bool couldBePacked(const DArrPacked& a, const DArrPacked& b) {
369 auto const asz = a.elems.size();
370 auto const bsz = b.elems.size();
371 if (asz != bsz) return false;
372 for (auto i = size_t{0}; i < asz; ++i) {
373 if (!a.elems[i].couldBe(b.elems[i])) {
374 return false;
377 return true;
380 bool couldBeStruct(const DArrStruct& a, const DArrStruct& b) {
381 if (a.map.size() != b.map.size()) return false;
382 auto aIt = begin(a.map);
383 auto bIt = begin(b.map);
384 for (; aIt != end(a.map); ++aIt, ++bIt) {
385 if (aIt->first != bIt->first) return false;
386 if (!aIt->second.couldBe(bIt->second)) return false;
388 return true;
391 Type struct_values(const DArrStruct& a) {
392 auto ret = TBottom;
393 for (auto& kv : a.map) ret = union_of(ret, kv.second);
394 return ret;
397 Type packed_values(const DArrPacked& a) {
398 auto ret = TBottom;
399 for (auto& e : a.elems) ret = union_of(ret, e);
400 return ret;
403 //////////////////////////////////////////////////////////////////////
406 * Helper for dealing with disjointDataFn's---most of them are commutative.
407 * This shuffles values to the right in a canonical order to need less
408 * overloads.
410 template<class InnerFn>
411 struct Commute : InnerFn {
412 using result_type = typename InnerFn::result_type;
414 using InnerFn::operator();
416 template<class B>
417 result_type operator()(SArray a, const B& b) const {
418 return InnerFn::operator()(b, a);
421 template<class B>
422 result_type operator()(const DArrStruct& a, const B& b) const {
423 return InnerFn::operator()(b, a);
426 template<class B>
427 result_type operator()(const DArrPackedN& a, const B& b) const {
428 return InnerFn::operator()(b, a);
431 template<class B>
432 result_type operator()(const DArrMapN& a, const B& b) const {
433 return InnerFn::operator()(b, a);
437 struct DisjointEqImpl {
438 using result_type = bool;
440 bool operator()() const { return false; }
442 bool operator()(const DArrPacked& a, SArray b) const {
443 if (a.elems.size() != b->size()) return false;
444 auto const p = toDArrPacked(b);
445 return p && a.elems == p->elems;
448 bool operator()(const DArrStruct& a, SArray b) const {
449 if (a.map.size() != b->size()) return false;
450 auto const p = toDArrStruct(b);
451 return p && a.map == p->map;
454 bool operator()(const DArrPackedN& a, SArray b) const {
455 return false;
457 bool operator()(const DArrMapN& a, SArray b) const {
458 return false;
460 bool operator()(const DArrPacked& a, const DArrPackedN& b) const {
461 return false;
463 bool operator()(const DArrPacked& a, const DArrStruct& b) const {
464 return false;
466 bool operator()(const DArrPacked&, const DArrMapN&) const {
467 return false;
469 bool operator()(const DArrPackedN& a, const DArrStruct& b) const {
470 return false;
472 bool operator()(const DArrPackedN&, const DArrMapN&) const {
473 return false;
475 bool operator()(const DArrStruct&, const DArrMapN&) const {
476 return false;
480 struct DisjointCouldBeImpl {
481 using result_type = bool;
483 bool operator()() const { return false; }
485 bool operator()(const DArrPacked& a, SArray b) const {
486 if (a.elems.size() != b->size()) return false;
487 auto const p = toDArrPacked(b);
488 return p && couldBePacked(a, *p);
491 bool operator()(const DArrStruct& a, SArray b) const {
492 if (a.map.size() != b->size()) return false;
493 auto const p = toDArrStruct(b);
494 return p && couldBeStruct(a, *p);
497 bool operator()(const DArrPackedN& a, SArray b) const {
498 auto const p = toDArrPackedN(b);
499 return p && a.type.couldBe(p->type);
502 bool operator()(const DArrMapN& a, SArray b) const {
503 auto const p = toDArrMapN(b);
504 return p.key.couldBe(a.key) && p.val.couldBe(a.val);
507 bool operator()(const DArrPacked& a, const DArrPackedN& b) const {
508 for (auto& t : a.elems) {
509 if (!t.couldBe(b.type)) return false;
511 return true;
514 bool operator()(const DArrPackedN& a, const DArrMapN& b) const {
515 return TInt.couldBe(b.key) && a.type.couldBe(b.val);
518 bool operator()(const DArrStruct& a, const DArrMapN& b) const {
519 if (!TSStr.couldBe(b.key)) return false;
520 for (auto& kv : a.map) {
521 if (!kv.second.couldBe(b.val)) return false;
523 return true;
526 bool operator()(const DArrPacked&, const DArrStruct&) const {
527 return false;
529 bool operator()(const DArrPacked&, const DArrMapN&) const {
530 return false;
532 bool operator()(const DArrPackedN&, const DArrStruct&) const {
533 return false;
537 // The countedness or possible-emptiness of the arrays is handled
538 // outside of this function, so it's ok to just return TArr from all
539 // of these here.
541 struct ArrUnionImpl {
542 using result_type = Type;
544 Type operator()() const { not_reached(); }
546 Type operator()(const DArrPacked& a, const DArrPacked& b) const {
547 if (a.elems.size() != b.elems.size()) {
548 return arr_packedn(union_of(packed_values(a), packed_values(b)));
550 auto ret = a.elems;
551 for (auto i = size_t{0}; i < a.elems.size(); ++i) {
552 ret[i] = union_of(ret[i], b.elems[i]);
554 return arr_packed(std::move(ret));
557 Type operator()(const DArrPacked& a, const DArrPackedN& b) const {
558 return arr_packedn(union_of(packed_values(a), b.type));
561 Type operator()(const DArrStruct& a, const DArrStruct& b) const {
562 auto to_map = [&] {
563 auto all = union_of(struct_values(a), struct_values(b));
564 return arr_mapn(TSStr, std::move(all));
568 * With the current meaning of structs, if the keys are different, we can't
569 * do anything better than going to a map type. The reason for this is
570 * that our struct types currently are implying the presence of all the
571 * keys in the struct (it might be worth adding some more types for struct
572 * subtyping to handle this better.)
574 if (a.map.size() != b.map.size()) return to_map();
576 auto retStruct = StructMap{};
577 auto aIt = begin(a.map);
578 auto bIt = begin(b.map);
579 for (; aIt != end(a.map); ++aIt, ++bIt) {
580 if (aIt->first != bIt->first) return to_map();
581 retStruct[aIt->first] = union_of(aIt->second, bIt->second);
583 return arr_struct(std::move(retStruct));
586 Type operator()(SArray a, SArray b) const {
587 assert(a != b); // Should've been handled earlier in union_of.
589 auto const p1 = toDArrPacked(a);
590 auto const p2 = toDArrPacked(b);
591 if (p1 && p2) {
592 return (*this)(*p1, *p2);
594 if (p1) {
595 if (auto const p2n = toDArrPackedN(b)) {
596 return (*this)(*p1, *p2n);
599 if (p2) {
600 if (auto const p1n = toDArrPackedN(a)) {
601 return (*this)(*p2, *p1n);
606 auto const s1 = toDArrStruct(a);
607 auto const s2 = toDArrStruct(b);
608 if (s1 && s2) return (*this)(*s1, *s2);
610 return (*this)(toDArrMapN(a), b);
613 Type operator()(const DArrPacked& a, SArray b) const {
614 auto p = toDArrPacked(b);
615 if (!p) return TArr;
616 return (*this)(a, *p);
619 Type operator()(const DArrPackedN& a, SArray b) const {
620 auto const p = toDArrPackedN(b);
621 if (!p) return TArr;
622 return arr_packedn(union_of(a.type, p->type));
625 Type operator()(const DArrStruct& a, SArray b) const {
626 auto const p = toDArrStruct(b);
627 if (!p) return TArr;
628 return (*this)(a, *p);
631 Type operator()(const DArrMapN& a, SArray b) const {
632 auto const p = toDArrMapN(b);
633 return arr_mapn(union_of(a.key, p.key), union_of(a.val, p.val));
636 Type operator()(const DArrPacked& a, const DArrStruct& b) const {
637 return arr_mapn(union_of(TInt, TSStr),
638 union_of(packed_values(a), struct_values(b)));
641 Type operator()(const DArrPacked& a, const DArrMapN& b) const {
642 return arr_mapn(union_of(b.key, TInt),
643 union_of(packed_values(a), b.val));
646 Type operator()(const DArrPackedN& a, const DArrStruct& b) const {
647 return arr_mapn(union_of(TInt, TSStr),
648 union_of(a.type, struct_values(b)));
651 Type operator()(const DArrPackedN& a, const DArrMapN& b) const {
652 return arr_mapn(union_of(TInt, b.key),
653 union_of(a.type, b.val));
656 Type operator()(const DArrStruct& a, const DArrMapN& b) const {
657 return arr_mapn(union_of(TSStr, b.key),
658 union_of(struct_values(a), b.val));
663 * Subtype is not a commutative relation, so this is the only
664 * disjointDataFn helper that doesn't use Commute<>.
666 struct DisjointSubtype {
667 using result_type = bool;
669 bool operator()() const { return false; }
671 bool operator()(const DArrStruct& a, SArray b) const {
672 if (a.map.size() != b->size()) return false;
673 auto const p = toDArrStruct(b);
674 return p && subtypeStruct(a, *p);
677 bool operator()(SArray a, const DArrStruct& b) const {
678 if (a->size() != b.map.size()) return false;
679 auto const p = toDArrStruct(a);
680 return p && subtypeStruct(*p, b);
683 bool operator()(SArray a, const DArrPacked& b) const {
684 if (a->size() != b.elems.size()) return false;
685 auto const p = toDArrPacked(a);
686 return p && subtypePacked(*p, b);
689 bool operator()(const DArrPacked& a, SArray b) const {
690 if (a.elems.size() != b->size()) return false;
691 auto const p = toDArrPacked(b);
692 return p && subtypePacked(a, *p);
695 bool operator()(const DArrPackedN& a, const DArrMapN& b) const {
696 return TInt.subtypeOf(b.key) && a.type.subtypeOf(b.val);
699 bool operator()(const DArrPacked& a, const DArrMapN& b) const {
700 if (!TInt.subtypeOf(b.key)) return false;
701 for (auto& v : a.elems) {
702 if (!v.subtypeOf(b.val)) return false;
704 return true;
707 bool operator()(const DArrStruct& a, const DArrMapN& b) const {
708 if (!TSStr.subtypeOf(b.key)) return false;
709 for (auto& kv : a.map) {
710 if (!kv.second.subtypeOf(b.val)) return false;
712 return true;
715 bool operator()(SArray a, const DArrMapN& b) const {
716 auto const p = toDArrMapN(a);
717 return p.key.subtypeOf(b.key) && p.val.subtypeOf(b.val);
720 bool operator()(const DArrPacked& a, const DArrPackedN& b) const {
721 for (auto& t : a.elems) {
722 if (!t.subtypeOf(b.type)) return false;
724 return true;
727 bool operator()(SArray a, const DArrPackedN& b) const {
728 auto p = toDArrPackedN(a);
729 return p && p->type.subtypeOf(b.type);
732 bool operator()(const DArrPackedN& a, const DArrPacked& b) const {
733 return false;
735 bool operator()(const DArrPackedN& a, SArray b) const {
736 return false;
738 bool operator()(const DArrStruct& a, const DArrPacked& b) const {
739 return false;
741 bool operator()(const DArrStruct& a, const DArrPackedN& b) const {
742 return false;
744 bool operator()(const DArrPacked& a, const DArrStruct& b) const {
745 return false;
747 bool operator()(const DArrPackedN& a, const DArrStruct& b) const {
748 return false;
750 bool operator()(const DArrMapN&, const DArrPackedN&) const {
751 return false;
753 bool operator()(const DArrMapN&, const DArrPacked&) const {
754 return false;
756 bool operator()(const DArrMapN&, const DArrStruct&) const {
757 return false;
759 bool operator()(const DArrMapN&, SArray) const {
760 return false;
764 using DisjointEq = Commute<DisjointEqImpl>;
765 using DisjointCouldBe = Commute<DisjointCouldBeImpl>;
766 using ArrUnion = Commute<ArrUnionImpl>;
768 //////////////////////////////////////////////////////////////////////
772 //////////////////////////////////////////////////////////////////////
774 Type::Type(const Type& o) noexcept
775 : m_bits(o.m_bits)
776 , m_dataTag(o.m_dataTag)
778 SCOPE_EXIT { assert(checkInvariants()); };
779 switch (m_dataTag) {
780 case DataTag::None: return;
781 case DataTag::Str: m_data.sval = o.m_data.sval; return;
782 case DataTag::ArrVal: m_data.aval = o.m_data.aval; return;
783 case DataTag::Int: m_data.ival = o.m_data.ival; return;
784 case DataTag::Dbl: m_data.dval = o.m_data.dval; return;
785 case DataTag::Obj: new (&m_data.dobj) DObj(o.m_data.dobj); return;
786 case DataTag::Cls: new (&m_data.dcls) DCls(o.m_data.dcls); return;
787 case DataTag::RefInner:
788 new (&m_data.inner) copy_ptr<Type>(o.m_data.inner);
789 return;
790 case DataTag::ArrPacked:
791 new (&m_data.apacked) copy_ptr<DArrPacked>(o.m_data.apacked);
792 return;
793 case DataTag::ArrPackedN:
794 new (&m_data.apackedn) copy_ptr<DArrPackedN>(o.m_data.apackedn);
795 return;
796 case DataTag::ArrStruct:
797 new (&m_data.astruct) copy_ptr<DArrStruct>(o.m_data.astruct);
798 return;
799 case DataTag::ArrMapN:
800 new (&m_data.amapn) copy_ptr<DArrMapN>(o.m_data.amapn);
801 return;
803 not_reached();
806 Type::Type(Type&& o) noexcept
807 : m_bits(o.m_bits)
808 , m_dataTag(o.m_dataTag)
810 SCOPE_EXIT { assert(checkInvariants());
811 assert(o.checkInvariants()); };
812 using std::move;
813 o.m_dataTag = DataTag::None;
814 switch (m_dataTag) {
815 case DataTag::None: return;
816 case DataTag::Str: m_data.sval = o.m_data.sval; return;
817 case DataTag::ArrVal: m_data.aval = o.m_data.aval; return;
818 case DataTag::Int: m_data.ival = o.m_data.ival; return;
819 case DataTag::Dbl: m_data.dval = o.m_data.dval; return;
820 case DataTag::Obj: new (&m_data.dobj) DObj(move(o.m_data.dobj)); return;
821 case DataTag::Cls: new (&m_data.dcls) DCls(move(o.m_data.dcls)); return;
822 case DataTag::RefInner:
823 new (&m_data.inner) copy_ptr<Type>(move(o.m_data.inner));
824 return;
825 case DataTag::ArrPacked:
826 new (&m_data.apacked) copy_ptr<DArrPacked>(move(o.m_data.apacked));
827 return;
828 case DataTag::ArrPackedN:
829 new (&m_data.apackedn) copy_ptr<DArrPackedN>(move(o.m_data.apackedn));
830 return;
831 case DataTag::ArrStruct:
832 new (&m_data.astruct) copy_ptr<DArrStruct>(move(o.m_data.astruct));
833 return;
834 case DataTag::ArrMapN:
835 new (&m_data.amapn) copy_ptr<DArrMapN>(move(o.m_data.amapn));
836 return;
838 not_reached();
841 Type& Type::operator=(const Type& o) noexcept {
842 SCOPE_EXIT { assert(checkInvariants()); };
843 this->~Type();
844 new (this) Type(o);
845 return *this;
848 Type& Type::operator=(Type&& o) noexcept {
849 SCOPE_EXIT { assert(checkInvariants());
850 assert(o.checkInvariants()); };
851 this->~Type();
852 new (this) Type(std::move(o));
853 return *this;
856 Type::~Type() noexcept {
857 assert(checkInvariants());
859 switch (m_dataTag) {
860 case DataTag::None:
861 case DataTag::Str:
862 case DataTag::ArrVal:
863 case DataTag::Int:
864 case DataTag::Dbl:
865 return;
866 case DataTag::RefInner:
867 m_data.inner.~copy_ptr<Type>();
868 return;
869 case DataTag::Obj:
870 m_data.dobj.~DObj();
871 return;
872 case DataTag::Cls:
873 m_data.dcls.~DCls();
874 return;
875 case DataTag::ArrPacked:
876 m_data.apacked.~copy_ptr<DArrPacked>();
877 return;
878 case DataTag::ArrPackedN:
879 m_data.apackedn.~copy_ptr<DArrPackedN>();
880 return;
881 case DataTag::ArrStruct:
882 m_data.astruct.~copy_ptr<DArrStruct>();
883 return;
884 case DataTag::ArrMapN:
885 m_data.amapn.~copy_ptr<DArrMapN>();
886 return;
888 not_reached();
891 //////////////////////////////////////////////////////////////////////
893 template<class Ret, class T, class Function>
894 struct Type::DJHelperFn {
895 template<class Y> Ret operator()(const Y& y) const { return f(t, y); }
896 Ret operator()(const T& t) const { not_reached(); }
897 Ret operator()() const { return f(); }
898 Function f;
899 const T& t;
902 template<class Ret, class T, class Function>
903 Type::DJHelperFn<Ret,T,Function> Type::djbind(const Function& f,
904 const T& t) const {
905 return { f, t };
908 // Dispatcher for the second argument for disjointDataFn.
909 template<class Ret, class T, class Function>
910 Ret Type::dj2nd(const Type& o, DJHelperFn<Ret,T,Function> f) const {
911 switch (o.m_dataTag) {
912 case DataTag::None: not_reached();
913 case DataTag::Str: return f();
914 case DataTag::Obj: return f();
915 case DataTag::Int: return f();
916 case DataTag::Dbl: return f();
917 case DataTag::Cls: return f();
918 case DataTag::RefInner: return f();
919 case DataTag::ArrVal: return f(o.m_data.aval);
920 case DataTag::ArrPacked: return f(*o.m_data.apacked);
921 case DataTag::ArrPackedN: return f(*o.m_data.apackedn);
922 case DataTag::ArrStruct: return f(*o.m_data.astruct);
923 case DataTag::ArrMapN: return f(*o.m_data.amapn);
925 not_reached();
929 * Dispatch an operation on data when this->m_dataTag is different
930 * from o.m_dataTag. Right now these operations only need to do work
931 * for array shapes, so the default case (zero-arg call) is applied to
932 * most other types.
934 * See the disjoint data function objects above.
936 template<class Function>
937 typename Function::result_type
938 Type::disjointDataFn(const Type& o, Function f) const {
939 assert(m_dataTag != o.m_dataTag);
940 using R = typename Function::result_type;
941 switch (m_dataTag) {
942 case DataTag::None: not_reached();
943 case DataTag::Str: return f();
944 case DataTag::Obj: return f();
945 case DataTag::Int: return f();
946 case DataTag::Dbl: return f();
947 case DataTag::Cls: return f();
948 case DataTag::RefInner: return f();
949 case DataTag::ArrVal: return dj2nd(o, djbind<R>(f, m_data.aval));
950 case DataTag::ArrPacked: return dj2nd(o, djbind<R>(f, *m_data.apacked));
951 case DataTag::ArrPackedN: return dj2nd(o, djbind<R>(f, *m_data.apackedn));
952 case DataTag::ArrStruct: return dj2nd(o, djbind<R>(f, *m_data.astruct));
953 case DataTag::ArrMapN: return dj2nd(o, djbind<R>(f, *m_data.amapn));
955 not_reached();
958 //////////////////////////////////////////////////////////////////////
960 bool Type::hasData() const {
961 return m_dataTag != DataTag::None;
964 bool Type::equivData(const Type& o) const {
965 if (m_dataTag != o.m_dataTag) {
966 return disjointDataFn(o, DisjointEq{});
969 switch (m_dataTag) {
970 case DataTag::None:
971 not_reached();
972 case DataTag::Str:
973 return m_data.sval == o.m_data.sval;
974 case DataTag::ArrVal:
975 return m_data.aval == o.m_data.aval;
976 case DataTag::Int:
977 return m_data.ival == o.m_data.ival;
978 case DataTag::Dbl:
979 // For purposes of Type equivalence, NaNs are equal.
980 return m_data.dval == o.m_data.dval ||
981 (std::isnan(m_data.dval) && std::isnan(o.m_data.dval));
982 case DataTag::Obj:
983 assert(!m_data.dobj.whType);
984 assert(!o.m_data.dobj.whType);
985 return m_data.dobj.type == o.m_data.dobj.type &&
986 m_data.dobj.cls.same(o.m_data.dobj.cls);
987 case DataTag::Cls:
988 return m_data.dcls.type == o.m_data.dcls.type &&
989 m_data.dcls.cls.same(o.m_data.dcls.cls);
990 case DataTag::RefInner:
991 return *m_data.inner == *o.m_data.inner;
992 case DataTag::ArrPacked:
993 return m_data.apacked->elems == o.m_data.apacked->elems;
994 case DataTag::ArrPackedN:
995 return m_data.apackedn->type == o.m_data.apackedn->type;
996 case DataTag::ArrStruct:
997 return m_data.astruct->map == o.m_data.astruct->map;
998 case DataTag::ArrMapN:
999 return m_data.amapn->key == o.m_data.amapn->key &&
1000 m_data.amapn->val == o.m_data.amapn->val;
1002 not_reached();
1005 bool Type::subtypeData(const Type& o) const {
1006 if (m_dataTag != o.m_dataTag) {
1007 return disjointDataFn(o, DisjointSubtype{});
1010 switch (m_dataTag) {
1011 case DataTag::Obj:
1012 assert(!m_data.dobj.whType);
1013 assert(!o.m_data.dobj.whType);
1014 if (m_data.dobj.type == o.m_data.dobj.type &&
1015 m_data.dobj.cls.same(o.m_data.dobj.cls)) {
1016 return true;
1018 if (o.m_data.dobj.type == DObj::Sub) {
1019 return m_data.dobj.cls.subtypeOf(o.m_data.dobj.cls);
1021 return false;
1022 case DataTag::Cls:
1023 if (m_data.dcls.type == o.m_data.dcls.type &&
1024 m_data.dcls.cls.same(o.m_data.dcls.cls)) {
1025 return true;
1027 if (o.m_data.dcls.type == DCls::Sub) {
1028 return m_data.dcls.cls.subtypeOf(o.m_data.dcls.cls);
1030 return false;
1031 case DataTag::Str:
1032 case DataTag::ArrVal:
1033 case DataTag::Int:
1034 case DataTag::Dbl:
1035 case DataTag::None:
1036 return equivData(o);
1037 case DataTag::RefInner:
1038 return m_data.inner->subtypeOf(*o.m_data.inner);
1039 case DataTag::ArrPacked:
1040 return subtypePacked(*m_data.apacked, *o.m_data.apacked);
1041 case DataTag::ArrPackedN:
1042 return m_data.apackedn->type.subtypeOf(o.m_data.apackedn->type);
1043 case DataTag::ArrStruct:
1044 return subtypeStruct(*m_data.astruct, *o.m_data.astruct);
1045 case DataTag::ArrMapN:
1046 return m_data.amapn->key.subtypeOf(o.m_data.amapn->key) &&
1047 m_data.amapn->val.subtypeOf(o.m_data.amapn->val);
1049 not_reached();
1052 bool Type::couldBeData(const Type& o) const {
1053 if (m_dataTag != o.m_dataTag) {
1054 return disjointDataFn(o, DisjointCouldBe{});
1057 switch (m_dataTag) {
1058 case DataTag::None:
1059 not_reached();
1060 case DataTag::Obj:
1061 assert(!m_data.dobj.whType);
1062 assert(!o.m_data.dobj.whType);
1063 if (m_data.dobj.type == o.m_data.dobj.type &&
1064 m_data.dobj.cls.same(o.m_data.dobj.cls)) {
1065 return true;
1067 if (m_data.dobj.type == DObj::Sub || o.m_data.dobj.type == DObj::Sub) {
1068 return m_data.dobj.cls.couldBe(o.m_data.dobj.cls);
1070 return false;
1071 case DataTag::Cls:
1072 if (m_data.dcls.type == o.m_data.dcls.type &&
1073 m_data.dcls.cls.same(o.m_data.dcls.cls)) {
1074 return true;
1076 if (m_data.dcls.type == DCls::Sub || o.m_data.dcls.type == DCls::Sub) {
1077 return m_data.dcls.cls.couldBe(o.m_data.dcls.cls);
1079 return false;
1080 case DataTag::RefInner:
1081 return m_data.inner->couldBe(*o.m_data.inner);
1082 case DataTag::ArrVal:
1083 return m_data.aval == o.m_data.aval;
1084 case DataTag::Str:
1085 return m_data.sval == o.m_data.sval;
1086 case DataTag::Int:
1087 return m_data.ival == o.m_data.ival;
1088 case DataTag::Dbl:
1089 return m_data.dval == o.m_data.dval;
1090 case DataTag::ArrPacked:
1091 return couldBePacked(*m_data.apacked, *o.m_data.apacked);
1092 case DataTag::ArrPackedN:
1093 return m_data.apackedn->type.couldBe(o.m_data.apackedn->type);
1094 case DataTag::ArrStruct:
1095 return couldBeStruct(*m_data.astruct, *o.m_data.astruct);
1096 case DataTag::ArrMapN:
1097 return m_data.amapn->key.couldBe(o.m_data.amapn->key) &&
1098 m_data.amapn->val.couldBe(o.m_data.amapn->val);
1100 not_reached();
1103 bool Type::operator==(const Type& o) const {
1104 assert(checkInvariants());
1105 assert(o.checkInvariants());
1107 if (m_bits != o.m_bits) return false;
1108 if (hasData() != o.hasData()) return false;
1109 if (!hasData() && !o.hasData()) return true;
1111 if (is_specialized_wait_handle(*this)) {
1112 if (is_specialized_wait_handle(o)) {
1113 return wait_handle_inner(*this) == wait_handle_inner(o);
1115 return false;
1117 if (is_specialized_wait_handle(o)) {
1118 return false;
1121 return equivData(o);
1124 size_t Type::hash() const {
1125 using U1 = std::underlying_type<decltype(m_bits)>::type;
1126 using U2 = std::underlying_type<decltype(m_dataTag)>::type;
1127 auto const rawBits = U1{m_bits};
1128 auto const rawTag = static_cast<U2>(m_dataTag);
1129 return folly::hash::hash_combine(rawBits, rawTag);
1132 bool Type::subtypeOf(Type o) const {
1133 assert(checkInvariants());
1134 assert(o.checkInvariants());
1136 if (is_specialized_wait_handle(*this)) {
1137 if (is_specialized_wait_handle(o)) {
1138 return
1139 wait_handle_inner(*this).subtypeOf(wait_handle_inner(o)) &&
1140 wait_handle_outer(*this).subtypeOf(wait_handle_outer(o));
1142 return wait_handle_outer(*this).subtypeOf(o);
1144 if (is_specialized_wait_handle(o)) {
1145 return subtypeOf(wait_handle_outer(o));
1148 auto const isect = static_cast<trep>(m_bits & o.m_bits);
1149 if (isect != m_bits) return false;
1150 if (!mayHaveData(isect)) return true;
1152 // No data is always more general.
1153 if (!hasData() && !o.hasData()) return true;
1154 if (!o.hasData()) {
1155 assert(hasData());
1156 return true;
1159 // Both have data, and the intersection allows it, so it depends on
1160 // what the data says.
1161 return hasData() && subtypeData(o);
1164 bool Type::strictSubtypeOf(Type o) const {
1165 assert(checkInvariants());
1166 assert(o.checkInvariants());
1167 return *this != o && subtypeOf(o);
1170 bool Type::couldBe(Type o) const {
1171 assert(checkInvariants());
1172 assert(o.checkInvariants());
1174 if (is_specialized_wait_handle(*this)) {
1175 if (is_specialized_wait_handle(o)) {
1176 return wait_handle_inner(*this).couldBe(wait_handle_inner(o));
1178 return o.couldBe(wait_handle_outer(*this));
1180 if (is_specialized_wait_handle(o)) {
1181 return couldBe(wait_handle_outer(o));
1184 auto const isect = static_cast<trep>(m_bits & o.m_bits);
1185 if (isect == 0) return false;
1186 if (subtypeOf(o) || o.subtypeOf(*this)) return true;
1187 if (!mayHaveData(isect)) return true;
1190 * From here we have an intersection that may have data, and we know
1191 * that neither type completely contains the other.
1193 * For most of our types, where m_data represents an exact constant
1194 * value, this just means the types only overlap if there is no
1195 * data.
1197 * The exception to that are option types with data,
1198 * objects/classes, and arrays.
1202 * If the intersection allowed data, and either type was an option
1203 * type, we can simplify the case to whether the unopt'd version of
1204 * the option type couldBe the other type. (The case where
1205 * TInitNull was the overlapping part would already be handled
1206 * above, because !mayHaveData(TInitNull).)
1208 if (is_opt(*this)) return is_opt(o) ? true : unopt(*this).couldBe(o);
1209 if (is_opt(o)) return unopt(o).couldBe(*this);
1211 if (hasData() && o.hasData()) {
1212 assert(mayHaveData(isect));
1213 return couldBeData(o);
1215 return true;
1218 bool Type::checkInvariants() const {
1219 assert(isPredefined(m_bits));
1220 assert(!hasData() || mayHaveData(m_bits));
1223 * TODO(#3696042): for static arrays, we could enforce that all
1224 * inner-types are also static (this may would require changes to
1225 * things like union_of to ensure that we never promote an inner
1226 * type to a counted type).
1229 switch (m_dataTag) {
1230 case DataTag::None: break;
1231 case DataTag::Str: assert(m_data.sval->isStatic()); break;
1232 case DataTag::Dbl: break;
1233 case DataTag::Int: break;
1234 case DataTag::RefInner:
1235 m_data.inner->checkInvariants();
1236 assert(!m_data.inner->couldBe(TRef));
1237 break;
1238 case DataTag::Cls: break;
1239 case DataTag::Obj:
1240 if (auto t = m_data.dobj.whType.get()) {
1241 t->checkInvariants();
1243 break;
1244 case DataTag::ArrVal:
1245 assert(m_data.aval->isStatic());
1246 assert(!m_data.aval->empty());
1247 break;
1248 case DataTag::ArrPacked:
1249 assert(!m_data.apacked->elems.empty());
1250 for (DEBUG_ONLY auto& v : m_data.apacked->elems) {
1251 assert(v.subtypeOf(TInitGen));
1253 break;
1254 case DataTag::ArrStruct:
1255 assert(!m_data.astruct->map.empty());
1256 for (DEBUG_ONLY auto& kv : m_data.astruct->map) {
1257 assert(kv.second.subtypeOf(TInitGen));
1259 break;
1260 case DataTag::ArrPackedN:
1261 assert(m_data.apackedn->type.subtypeOf(TInitGen));
1262 break;
1263 case DataTag::ArrMapN:
1264 assert(m_data.amapn->key.subtypeOf(TInitCell));
1265 assert(m_data.amapn->val.subtypeOf(TInitGen));
1266 break;
1268 return true;
1271 //////////////////////////////////////////////////////////////////////
1273 Type wait_handle(const Index& index, Type inner) {
1274 auto const rwh = index.builtin_class(s_WaitHandle.get());
1275 auto t = subObj(rwh);
1276 t.m_data.dobj.whType = make_copy_ptr<Type>(std::move(inner));
1277 return t;
1280 bool is_specialized_wait_handle(const Type& t) {
1281 return
1282 t.m_dataTag == DataTag::Obj &&
1283 !!t.m_data.dobj.whType.get();
1286 Type wait_handle_inner(const Type& t) {
1287 assert(is_specialized_wait_handle(t));
1288 return *t.m_data.dobj.whType;
1291 Type Type::wait_handle_outer(const Type& wh) {
1292 auto ret = Type{wh.m_bits};
1293 ret.m_dataTag = DataTag::Obj;
1294 new (&ret.m_data.dobj) DObj(wh.m_data.dobj.type, wh.m_data.dobj.cls);
1295 return ret;
1298 Type sval(SString val) {
1299 assert(val->isStatic());
1300 auto r = Type { BSStr };
1301 r.m_data.sval = val;
1302 r.m_dataTag = DataTag::Str;
1303 return r;
1306 Type ival(int64_t val) {
1307 auto r = Type { BInt };
1308 r.m_data.ival = val;
1309 r.m_dataTag = DataTag::Int;
1310 return r;
1313 Type dval(double val) {
1314 auto r = Type { BDbl };
1315 r.m_data.dval = val;
1316 r.m_dataTag = DataTag::Dbl;
1317 return r;
1320 Type aval(SArray val) {
1321 assert(val->isStatic());
1322 if (val->empty()) return aempty();
1323 auto r = Type { BSArrN };
1324 r.m_data.aval = val;
1325 r.m_dataTag = DataTag::ArrVal;
1326 return r;
1329 Type aempty() { return Type { BSArrE }; }
1330 Type sempty() { return sval(s_empty.get()); }
1331 Type counted_aempty() { return Type { BCArrE }; }
1332 Type some_aempty() { return Type { BArrE }; }
1334 Type subObj(res::Class val) {
1335 auto r = Type { BObj };
1336 new (&r.m_data.dobj) DObj(
1337 val.couldBeOverriden() ? DObj::Sub : DObj::Exact,
1340 r.m_dataTag = DataTag::Obj;
1341 return r;
1344 Type objExact(res::Class val) {
1345 auto r = Type { BObj };
1346 new (&r.m_data.dobj) DObj(DObj::Exact, val);
1347 r.m_dataTag = DataTag::Obj;
1348 return r;
1351 Type subCls(res::Class val) {
1352 auto r = Type { BCls };
1353 new (&r.m_data.dcls) DCls {
1354 val.couldBeOverriden() ? DCls::Sub : DCls::Exact,
1357 r.m_dataTag = DataTag::Cls;
1358 return r;
1361 Type clsExact(res::Class val) {
1362 auto r = Type { BCls };
1363 new (&r.m_data.dcls) DCls { DCls::Exact, val };
1364 r.m_dataTag = DataTag::Cls;
1365 return r;
1369 Type ref_to(Type t) {
1370 assert(t.subtypeOf(TInitCell));
1371 auto r = Type{BRef};
1372 new (&r.m_data.inner) copy_ptr<Type> {make_copy_ptr<Type>(std::move(t))};
1373 r.m_dataTag = DataTag::RefInner;
1374 return r;
1377 bool is_ref_with_inner(const Type& t) {
1378 return t.m_dataTag == DataTag::RefInner;
1381 bool is_specialized_array(const Type& t) {
1382 switch (t.m_dataTag) {
1383 case DataTag::None:
1384 case DataTag::Str:
1385 case DataTag::Obj:
1386 case DataTag::Int:
1387 case DataTag::Dbl:
1388 case DataTag::Cls:
1389 case DataTag::RefInner:
1390 return false;
1391 case DataTag::ArrVal:
1392 case DataTag::ArrPacked:
1393 case DataTag::ArrPackedN:
1394 case DataTag::ArrStruct:
1395 case DataTag::ArrMapN:
1396 return true;
1398 not_reached();
1401 Type arr_packed(std::vector<Type> elems) {
1402 assert(!elems.empty());
1403 auto r = Type { BArrN };
1404 new (&r.m_data.apacked) copy_ptr<DArrPacked>(
1405 make_copy_ptr<DArrPacked>(std::move(elems))
1407 r.m_dataTag = DataTag::ArrPacked;
1408 return r;
1411 Type sarr_packed(std::vector<Type> elems) {
1412 auto t = arr_packed(std::move(elems));
1413 t.m_bits = BSArrN;
1414 return t;
1417 Type carr_packed(std::vector<Type> elems) {
1418 auto t = arr_packed(std::move(elems));
1419 t.m_bits = BCArrN;
1420 return t;
1423 Type arr_packedn(Type t) {
1424 auto r = Type { BArrN };
1425 new (&r.m_data.apackedn) copy_ptr<DArrPackedN>(
1426 make_copy_ptr<DArrPackedN>(std::move(t))
1428 r.m_dataTag = DataTag::ArrPackedN;
1429 return r;
1432 Type sarr_packedn(Type t) {
1433 auto r = arr_packedn(std::move(t));
1434 r.m_bits = BSArrN;
1435 return r;
1438 Type carr_packedn(Type t) {
1439 auto r = arr_packedn(std::move(t));
1440 r.m_bits = BCArrN;
1441 return r;
1444 Type arr_struct(StructMap m) {
1445 assert(!m.empty());
1446 auto r = Type { BArrN };
1447 new (&r.m_data.astruct) copy_ptr<DArrStruct>(
1448 make_copy_ptr<DArrStruct>(std::move(m))
1450 r.m_dataTag = DataTag::ArrStruct;
1451 return r;
1454 Type sarr_struct(StructMap m) {
1455 auto r = arr_struct(std::move(m));
1456 r.m_bits = BSArrN;
1457 return r;
1460 Type carr_struct(StructMap m) {
1461 auto r = arr_struct(std::move(m));
1462 r.m_bits = BCArrN;
1463 return r;
1466 Type arr_mapn(Type k, Type v) {
1467 auto r = Type { BArrN };
1468 new (&r.m_data.amapn) copy_ptr<DArrMapN>(
1469 make_copy_ptr<DArrMapN>(std::move(k), std::move(v))
1471 r.m_dataTag = DataTag::ArrMapN;
1472 return r;
1475 Type sarr_mapn(Type k, Type v) {
1476 auto r = arr_mapn(k, v);
1477 r.m_bits = BSArrN;
1478 return r;
1481 Type carr_mapn(Type k, Type v) {
1482 auto r = arr_mapn(std::move(k), std::move(v));
1483 r.m_bits = BCArrN;
1484 return r;
1487 Type opt(Type t) {
1488 assert(canBeOptional(t.m_bits));
1489 auto ret = t;
1490 ret.m_bits = static_cast<trep>(ret.m_bits | BInitNull);
1491 return ret;
1494 Type unopt(Type t) {
1495 assert(is_opt(t));
1496 t.m_bits = static_cast<trep>(t.m_bits & ~BInitNull);
1497 assert(!is_opt(t));
1498 return t;
1501 bool is_opt(Type t) {
1502 if (t.m_bits == BInitNull) return false;
1503 if (!t.couldBe(TInitNull)) return false;
1504 auto const nonNullBits = static_cast<trep>(t.m_bits & ~BInitNull);
1505 return isPredefined(nonNullBits) && canBeOptional(nonNullBits);
1508 bool is_specialized_obj(const Type& t) {
1509 return t.m_dataTag == DataTag::Obj;
1512 bool is_specialized_cls(const Type& t) {
1513 return t.m_dataTag == DataTag::Cls;
1516 Type objcls(const Type& t) {
1517 if (t.subtypeOf(TObj) && is_specialized_obj(t)) {
1518 auto const d = dobj_of(t);
1519 return d.type == DObj::Exact ? clsExact(d.cls) : subCls(d.cls);
1521 return TCls;
1524 //////////////////////////////////////////////////////////////////////
1526 folly::Optional<Cell> tv(Type t) {
1527 assert(t.checkInvariants());
1529 switch (t.m_bits) {
1530 case BUninit: return make_tv<KindOfUninit>();
1531 case BInitNull: return make_tv<KindOfNull>();
1532 case BTrue: return make_tv<KindOfBoolean>(true);
1533 case BFalse: return make_tv<KindOfBoolean>(false);
1534 case BCArrE: /* fallthrough */
1535 case BSArrE: return make_tv<KindOfArray>(staticEmptyArray());
1536 default:
1537 if (is_opt(t)) {
1538 break;
1540 switch (t.m_dataTag) {
1541 case DataTag::Int: return make_tv<KindOfInt64>(t.m_data.ival);
1542 case DataTag::Dbl: return make_tv<KindOfDouble>(t.m_data.dval);
1543 case DataTag::Str: return make_tv<KindOfStaticString>(t.m_data.sval);
1544 case DataTag::ArrVal:
1545 if ((t.m_bits & BArrN) == t.m_bits) {
1546 return make_tv<KindOfArray>(const_cast<ArrayData*>(t.m_data.aval));
1548 break;
1549 case DataTag::ArrStruct:
1550 case DataTag::ArrPacked:
1551 // TODO(#3696042): we could materialize a static array here if
1552 // we check if a whole specialized array is constants, and if it
1553 // can't be empty.
1554 break;
1555 case DataTag::RefInner:
1556 case DataTag::ArrPackedN:
1557 case DataTag::ArrMapN:
1558 case DataTag::Obj:
1559 case DataTag::Cls:
1560 case DataTag::None:
1561 break;
1565 return folly::none;
1568 Type type_of_istype(IsTypeOp op) {
1569 switch (op) {
1570 case IsTypeOp::Null: return TNull;
1571 case IsTypeOp::Bool: return TBool;
1572 case IsTypeOp::Int: return TInt;
1573 case IsTypeOp::Dbl: return TDbl;
1574 case IsTypeOp::Str: return TStr;
1575 case IsTypeOp::Arr: return TArr;
1576 case IsTypeOp::Obj: return TObj;
1577 case IsTypeOp::Scalar: always_assert(0);
1579 not_reached();
1582 DObj dobj_of(const Type& t) {
1583 assert(t.checkInvariants());
1584 assert(is_specialized_obj(t));
1585 return t.m_data.dobj;
1588 DCls dcls_of(Type t) {
1589 assert(t.checkInvariants());
1590 assert(is_specialized_cls(t));
1591 return t.m_data.dcls;
1594 Type from_cell(Cell cell) {
1595 assert(cellIsPlausible(cell));
1597 switch (cell.m_type) {
1598 case KindOfUninit: return TUninit;
1599 case KindOfNull: return TInitNull;
1600 case KindOfBoolean: return cell.m_data.num ? TTrue : TFalse;
1601 case KindOfInt64: return ival(cell.m_data.num);
1602 case KindOfDouble: return dval(cell.m_data.dbl);
1604 case KindOfStaticString:
1605 case KindOfString:
1606 always_assert(cell.m_data.pstr->isStatic());
1607 return sval(cell.m_data.pstr);
1609 case KindOfArray:
1610 always_assert(cell.m_data.parr->isStatic());
1611 return aval(cell.m_data.parr);
1613 case KindOfRef:
1614 case KindOfObject:
1615 case KindOfResource:
1616 default:
1617 break;
1619 always_assert(0 && "reference counted type in from_cell");
1622 Type from_DataType(DataType dt) {
1623 switch (dt) {
1624 case KindOfUninit: return TUninit;
1625 case KindOfNull: return TInitNull;
1626 case KindOfBoolean: return TBool;
1627 case KindOfInt64: return TInt;
1628 case KindOfDouble: return TDbl;
1629 case KindOfStaticString:
1630 case KindOfString: return TStr;
1631 case KindOfArray: return TArr;
1632 case KindOfRef: return TRef;
1633 case KindOfObject: return TObj;
1634 case KindOfResource: return TRes;
1635 default:
1636 break;
1638 always_assert(0 && "dt in from_DataType didn't satisfy preconditions");
1641 Type from_hni_constraint(SString s) {
1642 if (!s) return TGen;
1644 auto p = s->data();
1645 auto ret = TBottom;
1647 if (*p == '?') {
1648 ret = union_of(ret, TInitNull);
1649 ++p;
1652 if (!strcasecmp(p, "HH\\resource")) return union_of(ret, TRes);
1653 if (!strcasecmp(p, "HH\\bool")) return union_of(ret, TBool);
1654 if (!strcasecmp(p, "HH\\int")) return union_of(ret, TInt);
1655 if (!strcasecmp(p, "HH\\float")) return union_of(ret, TDbl);
1656 if (!strcasecmp(p, "HH\\num")) return union_of(ret, TNum);
1657 if (!strcasecmp(p, "HH\\string")) return union_of(ret, TStr);
1658 if (!strcasecmp(p, "array")) return union_of(ret, TArr);
1659 if (!strcasecmp(p, "HH\\mixed")) return TInitGen;
1661 // It might be an object, or we might want to support type aliases in HNI at
1662 // some point. For now just be conservative.
1663 return TGen;
1666 Type Type::unionArr(const Type& a, const Type& b) {
1667 assert(!a.subtypeOf(b));
1668 assert(!b.subtypeOf(a));
1669 assert(is_specialized_array(a));
1670 assert(is_specialized_array(b));
1672 auto ret = Type{};
1673 auto const newBits = combine_arr_bits(a.m_bits, b.m_bits);
1675 if (a.m_dataTag != b.m_dataTag) {
1676 ret = a.disjointDataFn(b, ArrUnion{});
1677 ret.m_bits = newBits;
1678 return ret;
1681 switch (a.m_dataTag) {
1682 case DataTag::None:
1683 case DataTag::Str:
1684 case DataTag::Obj:
1685 case DataTag::Int:
1686 case DataTag::Dbl:
1687 case DataTag::Cls:
1688 case DataTag::RefInner:
1689 not_reached();
1690 case DataTag::ArrVal:
1692 ret = ArrUnion{}(a.m_data.aval, b.m_data.aval);
1693 ret.m_bits = newBits;
1694 return ret;
1696 case DataTag::ArrPacked:
1698 ret = ArrUnion{}(*a.m_data.apacked, *b.m_data.apacked);
1699 ret.m_bits = newBits;
1700 return ret;
1702 case DataTag::ArrPackedN:
1704 ret = a;
1705 ret.m_bits = newBits;
1706 ret.m_data.apackedn->type = union_of(a.m_data.apackedn->type,
1707 b.m_data.apackedn->type);
1708 return ret;
1710 case DataTag::ArrStruct:
1712 ret = ArrUnion{}(*a.m_data.astruct, *b.m_data.astruct);
1713 ret.m_bits = newBits;
1714 return ret;
1716 case DataTag::ArrMapN:
1718 ret = arr_mapn(union_of(a.m_data.amapn->key, b.m_data.amapn->key),
1719 union_of(a.m_data.amapn->val, b.m_data.amapn->val));
1720 ret.m_bits = newBits;
1721 return ret;
1724 not_reached();
1727 Type union_of(Type a, Type b) {
1728 if (a.subtypeOf(b)) return b;
1729 if (b.subtypeOf(a)) return a;
1732 * We need to check this before specialized objects, including the case where
1733 * one of them was TInitNull, because otherwise we'll go down the
1734 * is_specialized_obj paths and lose the wait handle information.
1736 if (is_specialized_wait_handle(a)) {
1737 if (is_specialized_wait_handle(b)) {
1738 *a.m_data.dobj.whType = union_of(
1739 *a.m_data.dobj.whType,
1740 *b.m_data.dobj.whType
1742 return a;
1744 if (b == TInitNull) return opt(a);
1746 if (is_specialized_wait_handle(b)) {
1747 if (a == TInitNull) return opt(b);
1750 // When both types are strict subtypes of TObj or TOptObj or both
1751 // are strict subtypes of TCls we look for a common ancestor if one
1752 // exists.
1753 if (is_specialized_obj(a) && is_specialized_obj(b)) {
1754 auto keepOpt = is_opt(a) || is_opt(b);
1755 auto t = a.m_data.dobj.cls.commonAncestor(dobj_of(b).cls);
1756 // We need not to distinguish between Obj<=T and Obj=T, and always
1757 // return an Obj<=Ancestor, because that is the single type that
1758 // includes both children.
1759 if (t) return keepOpt ? opt(subObj(*t)) : subObj(*t);
1760 return keepOpt ? TOptObj : TObj;
1762 if (a.strictSubtypeOf(TCls) && b.strictSubtypeOf(TCls)) {
1763 auto t = a.m_data.dcls.cls.commonAncestor(dcls_of(b).cls);
1764 // Similar to above, this must always return an Obj<=Ancestor.
1765 return t ? subCls(*t) : TCls;
1768 if (is_specialized_array(a)) {
1769 if (is_specialized_array(b)) {
1770 DEBUG_ONLY auto const shouldBeOpt = is_opt(a) || is_opt(b);
1771 auto const t = Type::unionArr(a, b);
1772 if (shouldBeOpt) assert(is_opt(t));
1773 return t;
1775 if (b.subtypeOf(TOptArrE)) {
1776 // Keep a's data.
1777 a.m_bits = combine_arr_bits(a.m_bits, b.m_bits);
1778 return a;
1780 if (b.subtypeOf(TOptArr)) {
1781 // We can't keep a's data, since it contains unknown non-empty
1782 // arrays. (If it were specialized we'd be in the unionArr
1783 // path, which handles trying to keep as much data as we can.)
1784 return Type { combine_arr_bits(a.m_bits, b.m_bits) };
1786 } else {
1787 if (is_specialized_array(b)) {
1788 // Flip args and do the above.
1789 return union_of(b, a);
1793 if (is_ref_with_inner(a) && is_ref_with_inner(b)) {
1794 return ref_to(union_of(*a.m_data.inner, *b.m_data.inner));
1797 #define X(y) if (a.subtypeOf(y) && b.subtypeOf(y)) return y;
1798 X(TInt)
1799 X(TDbl)
1800 X(TSStr)
1801 X(TCStr)
1802 X(TSArr)
1803 X(TCArr)
1804 X(TArrE)
1805 X(TArrN)
1806 X(TObj)
1807 X(TCls)
1808 X(TNull)
1809 X(TBool)
1810 X(TNum)
1811 X(TStr)
1812 X(TArr)
1815 * Merging option types tries to preserve subtype information where it's
1816 * possible. E.g. if you union InitNull and Obj<=Foo, we want OptObj<=Foo to
1817 * be the result.
1819 if (a == TInitNull && canBeOptional(b.m_bits)) return opt(b);
1820 if (b == TInitNull && canBeOptional(a.m_bits)) return opt(a);
1822 // Optional types where the non-Null part is already a union or can have a
1823 // value need to be manually tried (e.g. if we are merging TOptTrue and TOptFalse,
1824 // we want TOptBool, or merging TOptInt=1 and TOptInt=2 should give us TOptInt).
1825 X(TOptBool)
1826 X(TOptInt)
1827 X(TOptDbl)
1828 X(TOptNum)
1829 X(TOptSStr)
1830 X(TOptStr)
1831 X(TOptArrN)
1832 X(TOptArrE)
1833 X(TOptSArr)
1834 X(TOptCArr)
1835 X(TOptArr)
1836 X(TOptObj)
1838 X(TInitPrim)
1839 X(TPrim)
1840 X(TInitUnc)
1841 X(TUnc)
1842 X(TInitCell)
1843 X(TCell)
1844 X(TInitGen)
1845 X(TGen)
1846 #undef X
1848 return TTop;
1851 Type promote_emptyish(Type a, Type b) {
1852 if (is_opt(a)) a = unopt(a);
1853 if (a.subtypeOf(sempty())) {
1854 return b;
1856 auto t = trep(a.m_bits & ~(BNull | BFalse));
1857 if (!isPredefined(t)) {
1858 if (trep(t & BInitPrim) == t) {
1859 t = BInitPrim;
1860 } else if (trep(t & BInitUnc) == t) {
1861 t = BInitUnc;
1862 } else if (trep(t & BInitCell) == t) {
1863 t = BInitCell;
1864 } else {
1865 t = BInitGen;
1867 return union_of(Type { t }, b);
1869 a.m_bits = t;
1870 return union_of(a, b);
1873 Type widening_union(const Type& a, const Type& b) {
1874 if (a == b) return a;
1876 // Currently the only types in our typesystem that have infinitely
1877 // growing chains of union_of are specialized arrays.
1878 if (!is_specialized_array(a) || !is_specialized_array(b)) {
1879 return union_of(a, b);
1882 // This (throwing away the data) is overly conservative, but works
1883 // for now.
1884 auto const newBits = combine_arr_bits(a.m_bits, b.m_bits);
1885 return Type { newBits };
1888 Type stack_flav(Type a) {
1889 if (a.subtypeOf(TUninit)) return TUninit;
1890 if (a.subtypeOf(TInitCell)) return TInitCell;
1891 if (a.subtypeOf(TRef)) return TRef;
1892 if (a.subtypeOf(TGen)) return TGen;
1893 if (a.subtypeOf(TCls)) return TCls;
1894 always_assert(0 && "stack_flav passed invalid type");
1897 Type loosen_statics(Type a) {
1898 // TODO(#3696042): this should be modified to keep specialized array
1899 // information, including whether the array is possibly empty.
1900 if (a.couldBe(TSStr)) a = union_of(a, TStr);
1901 if (a.couldBe(TSArr)) a = union_of(a, TArr);
1902 return a;
1905 Type loosen_values(Type a) {
1906 return a.strictSubtypeOf(TInt) ? TInt :
1907 a.strictSubtypeOf(TDbl) ? TDbl :
1908 a.strictSubtypeOf(TBool) ? TBool :
1909 a.strictSubtypeOf(TSStr) ? TSStr :
1910 a.strictSubtypeOf(TSArr) ? TSArr :
1911 a == TOptFalse || a == TOptTrue ? TOptBool :
1915 Type remove_uninit(Type t) {
1916 assert(t.subtypeOf(TCell));
1917 if (!t.couldBe(TUninit)) return t;
1918 if (t.subtypeOf(TUninit)) return TBottom;
1919 if (t.subtypeOf(TNull)) return TInitNull;
1920 if (t.subtypeOf(TPrim)) return TInitPrim;
1921 if (t.subtypeOf(TUnc)) return TInitUnc;
1922 return TInitCell;
1925 //////////////////////////////////////////////////////////////////////
1928 * For known strings that are strictly integers, we'll set both the
1929 * known integer and string keys, so generally the int case should be
1930 * checked first below.
1932 * For keys that could be strings, we have to assume they could be
1933 * strictly-integer strings. After disection, the effective type we
1934 * can assume for the array key is in `type'.
1937 struct ArrKey {
1938 folly::Optional<int64_t> i;
1939 folly::Optional<SString> s;
1940 Type type;
1943 ArrKey disect_key(const Type& keyTy) {
1944 auto ret = ArrKey{};
1946 if (keyTy.strictSubtypeOf(TInt)) {
1947 ret.i = keyTy.m_data.ival;
1948 ret.type = keyTy;
1949 return ret;
1951 if (keyTy.strictSubtypeOf(TStr) && keyTy.m_dataTag == DataTag::Str) {
1952 ret.s = keyTy.m_data.sval;
1953 ret.type = keyTy;
1954 int64_t i;
1955 if (keyTy.m_data.sval->isStrictlyInteger(i)) {
1956 ret.i = i;
1957 ret.type = TInt;
1959 return ret;
1961 if (keyTy.strictSubtypeOf(TDbl)) {
1962 ret.i = static_cast<int64_t>(keyTy.m_data.dval);
1963 ret.type = TInt;
1964 return ret;
1966 if (keyTy.subtypeOf(TNum)) {
1967 ret.type = TInt;
1968 return ret;
1971 if (keyTy.subtypeOf(TNull)) {
1972 ret.s = s_empty.get();
1973 ret.type = sempty();
1974 return ret;
1976 if (keyTy.subtypeOf(TRes)) {
1977 ret.type = TInt;
1978 return ret;
1981 if (keyTy.subtypeOf(TTrue)) {
1982 ret.i = 1;
1983 ret.type = TInt;
1984 return ret;
1986 if (keyTy.subtypeOf(TFalse)) {
1987 ret.i = 0;
1988 ret.type = TInt;
1989 return ret;
1991 if (keyTy.subtypeOf(TBool)) {
1992 ret.type = TInt;
1993 return ret;
1996 // If we have an OptStr with a value, we can at least exclude the
1997 // possibility of integer-like strings by looking at that value.
1998 // But we can't use the value itself, because if it is null the key
1999 // will act like the empty string. In that case, the code uses the
2000 // static empty string, so if it was an OptCStr it needs to
2001 // incorporate SStr, but an OptSStr can stay as SStr.
2002 if (keyTy.strictSubtypeOf(TOptStr) && keyTy.m_dataTag == DataTag::Str) {
2003 int64_t ignore;
2004 if (!keyTy.m_data.sval->isStrictlyInteger(ignore)) {
2005 ret.type = keyTy.strictSubtypeOf(TOptSStr) ? TSStr : TStr;
2006 return ret;
2010 // Nothing we can do in other cases that could be strings (without
2011 // statically known values)---they may behave like integers at
2012 // runtime.
2014 // TODO(#3774082): We should be able to set this to a Str|Int type.
2015 ret.type = TInitCell;
2016 return ret;
2019 Type array_elem(const Type& arr, const Type& undisectedKey) {
2020 assert(arr.subtypeOf(TArr));
2022 auto const key = disect_key(undisectedKey);
2023 auto ty = [&]() -> Type {
2024 switch (arr.m_dataTag) {
2025 case DataTag::Str:
2026 case DataTag::Obj:
2027 case DataTag::Int:
2028 case DataTag::Dbl:
2029 case DataTag::Cls:
2030 case DataTag::RefInner:
2031 not_reached();
2033 case DataTag::None:
2034 return arr.subtypeOf(TSArr) ? TInitUnc : TInitCell;
2036 case DataTag::ArrVal:
2037 if (key.i) {
2038 if (auto const r = arr.m_data.aval->nvGet(*key.i)) {
2039 return from_cell(*r);
2041 return TInitNull;
2043 if (key.s) {
2044 if (auto const r = arr.m_data.aval->nvGet(*key.s)) {
2045 return from_cell(*r);
2047 return TInitNull;
2049 return arr.subtypeOf(TSArr) ? TInitUnc : TInitCell;
2052 * In the following cases, note that if you get an elem out of an
2053 * array that doesn't exist, php semantics are to return null (after
2054 * a warning). So in cases where we don't know the key statically
2055 * we need to union TInitNull into the result.
2058 case DataTag::ArrPacked:
2059 if (!key.i) {
2060 return union_of(packed_values(*arr.m_data.apacked), TInitNull);
2062 if (*key.i >= 0 && *key.i < arr.m_data.apacked->elems.size()) {
2063 return arr.m_data.apacked->elems[*key.i];
2065 return TInitNull;
2067 case DataTag::ArrPackedN:
2068 return union_of(arr.m_data.apackedn->type, TInitNull);
2070 case DataTag::ArrStruct:
2071 if (key.s) {
2072 auto it = arr.m_data.astruct->map.find(*key.s);
2073 return it != end(arr.m_data.astruct->map)
2074 ? it->second
2075 : TInitNull;
2077 return union_of(struct_values(*arr.m_data.astruct), TInitNull);
2079 case DataTag::ArrMapN:
2080 return union_of(arr.m_data.amapn->val, TInitNull);
2082 not_reached();
2083 }();
2085 if (!ty.subtypeOf(TInitCell)) {
2086 ty = TInitCell;
2087 } else if (arr.couldBe(TArrE)) {
2088 ty = union_of(std::move(ty), TInitNull);
2090 return ty;
2094 * Note: for now we're merging counted arrays into whatever type it used to
2095 * have in the following set functions, and returning arr_*'s in some cases
2096 * where we could know it was a carr_*.
2098 * To be able to assume it is actually counted it if used to be static, we need
2099 * to add code checking for keys that are one of the "illegal offset type" of
2100 * keys.
2102 * A similar issue applies if you want to take out emptiness when a set occurs.
2103 * If the key could be an illegal key type, the array may remain empty.
2106 // Do the effects of array_set but without handling possibly emptiness
2107 // of `arr'.
2108 Type arrayN_set(Type arr, const Type& undisectedKey, const Type& val) {
2109 auto const key = disect_key(undisectedKey);
2111 auto ensure_counted = [&] {
2112 // TODO(#3696042): this same logic should be in loosen_statics.
2113 if (arr.m_bits & BCArr) return;
2114 if (arr.m_bits == BSArrN) {
2115 arr.m_bits = combine_arr_bits(arr.m_bits, BCArrN);
2116 } else if (arr.m_bits == BSArrE) {
2117 arr.m_bits = combine_arr_bits(arr.m_bits, BCArrE);
2118 } else {
2119 arr.m_bits = combine_arr_bits(arr.m_bits, BCArr);
2123 switch (arr.m_dataTag) {
2124 case DataTag::Str:
2125 case DataTag::Obj:
2126 case DataTag::Int:
2127 case DataTag::Dbl:
2128 case DataTag::Cls:
2129 case DataTag::RefInner:
2130 not_reached();
2132 case DataTag::None:
2133 ensure_counted();
2134 return arr;
2136 case DataTag::ArrVal:
2138 if (auto d = toDArrStruct(arr.m_data.aval)) {
2139 return arrayN_set(arr_struct(std::move(d->map)), undisectedKey, val);
2141 if (auto d = toDArrPacked(arr.m_data.aval)) {
2142 return arrayN_set(arr_packed(std::move(d->elems)), undisectedKey,
2143 val);
2145 if (auto d = toDArrPackedN(arr.m_data.aval)) {
2146 return arrayN_set(arr_packedn(d->type), undisectedKey, val);
2148 auto d = toDArrMapN(arr.m_data.aval);
2149 return arrayN_set(arr_mapn(d.key, d.val), undisectedKey, val);
2152 case DataTag::ArrPacked:
2153 ensure_counted();
2154 if (key.i) {
2155 if (*key.i >= 0 && *key.i < arr.m_data.apacked->elems.size()) {
2156 auto& current = arr.m_data.apacked->elems[*key.i];
2157 if (current.subtypeOf(TInitCell)) {
2158 current = val;
2160 return arr;
2162 if (*key.i == arr.m_data.apacked->elems.size()) {
2163 arr.m_data.apacked->elems.push_back(val);
2164 return arr;
2167 return arr_mapn(union_of(TInt, key.type),
2168 union_of(packed_values(*arr.m_data.apacked), val));
2170 case DataTag::ArrPackedN:
2171 ensure_counted();
2172 return arr_mapn(union_of(TInt, key.type),
2173 union_of(arr.m_data.apackedn->type, val));
2175 case DataTag::ArrStruct:
2176 ensure_counted();
2178 auto& map = arr.m_data.astruct->map;
2179 if (key.s) {
2180 auto it = map.find(*key.s);
2181 if (it == end(map)) {
2182 map[*key.s] = val;
2183 } else {
2184 if (it->second.subtypeOf(TInitCell)) {
2185 it->second = val;
2188 return arr;
2190 return arr_mapn(union_of(key.type, TSStr),
2191 union_of(struct_values(*arr.m_data.astruct), val));
2194 case DataTag::ArrMapN:
2195 ensure_counted();
2196 return arr_mapn(union_of(key.type, arr.m_data.amapn->key),
2197 union_of(val, arr.m_data.amapn->val));
2200 not_reached();
2203 Type array_set(Type arr, const Type& undisectedKey, const Type& val) {
2204 assert(arr.subtypeOf(TArr));
2206 // Unless you know an array can't cow, you don't know if the TRef
2207 // will stay a TRef or turn back into a TInitCell. Generally you
2208 // want a TInitGen.
2209 always_assert(!val.subtypeOf(TRef) &&
2210 "You probably don't want to put Ref types into arrays ...");
2212 auto nonEmptyPart =
2213 arr.couldBe(TArrN) ? arrayN_set(arr, undisectedKey, val)
2214 : TBottom;
2215 if (!arr.couldBe(TArrE)) {
2216 assert(nonEmptyPart != TBottom);
2217 return nonEmptyPart;
2220 // Union in the effects of doing the set if the array was empty.
2221 auto const key = disect_key(undisectedKey);
2222 auto emptyPart = [&] {
2223 if (key.s && !key.i) {
2224 auto map = StructMap{};
2225 map[*key.s] = val;
2226 return arr_struct(std::move(map));
2228 if (key.i && *key.i == 0) return arr_packed({val});
2229 // Keeping the ArrE for now just because we would need to check
2230 // the key.type is not an invalid key (array or object) to prove
2231 // it's non-empty now.
2232 return union_of(arr_mapn(key.type, val), arr);
2233 }();
2234 return union_of(std::move(nonEmptyPart), std::move(emptyPart));
2237 // Do the same as array_newelem_key, but ignoring possible emptiness
2238 // of `arr'.
2239 std::pair<Type,Type> arrayN_newelem_key(const Type& arr, const Type& val) {
2240 switch (arr.m_dataTag) {
2241 case DataTag::Str:
2242 case DataTag::Obj:
2243 case DataTag::Int:
2244 case DataTag::Dbl:
2245 case DataTag::Cls:
2246 case DataTag::RefInner:
2247 not_reached();
2249 case DataTag::None:
2250 return { TArrN, TInt };
2252 case DataTag::ArrVal:
2254 if (auto d = toDArrStruct(arr.m_data.aval)) {
2255 return array_newelem_key(arr_struct(std::move(d->map)), val);
2257 if (auto d = toDArrPacked(arr.m_data.aval)) {
2258 return array_newelem_key(arr_packed(std::move(d->elems)), val);
2260 if (auto d = toDArrPackedN(arr.m_data.aval)) {
2261 return array_newelem_key(arr_packedn(d->type), val);
2263 auto d = toDArrMapN(arr.m_data.aval);
2264 return array_newelem_key(arr_mapn(d.key, d.val), val);
2267 case DataTag::ArrPacked:
2269 auto v = arr.m_data.apacked->elems;
2270 v.push_back(val);
2271 return { arr_packed(std::move(v)), ival(v.size() - 1) };
2274 case DataTag::ArrPackedN:
2275 return { arr_packedn(union_of(arr.m_data.apackedn->type, val)), TInt };
2277 case DataTag::ArrStruct:
2278 return { arr_mapn(union_of(TSStr, TInt),
2279 union_of(struct_values(*arr.m_data.astruct), val)),
2280 TInt };
2282 case DataTag::ArrMapN:
2283 return { arr_mapn(union_of(arr.m_data.amapn->key, TInt),
2284 union_of(arr.m_data.amapn->val, TInitNull)),
2285 TInt };
2288 not_reached();
2291 std::pair<Type,Type> array_newelem_key(const Type& arr, const Type& val) {
2292 assert(arr.subtypeOf(TArr));
2294 // Unless you know an array can't cow, you don't know if the TRef
2295 // will stay a TRef or turn back into a TInitCell. Generally you
2296 // want a TInitGen.
2297 always_assert(!val.subtypeOf(TRef) &&
2298 "You probably don't want to put Ref types into arrays ...");
2300 // Inserting in an empty array creates a packed array of size one.
2301 auto emptyPart = arr.couldBe(TArrE)
2302 ? std::make_pair(arr_packed({val}), ival(0))
2303 : std::make_pair(TBottom, TBottom);
2305 auto nonEmptyPart = arr.couldBe(TArrN)
2306 ? arrayN_newelem_key(arr, val)
2307 : std::make_pair(TBottom, TBottom);
2309 return {
2310 union_of(std::move(emptyPart.first), std::move(nonEmptyPart.first)),
2311 union_of(std::move(emptyPart.second), std::move(nonEmptyPart.second))
2315 Type array_newelem(const Type& arr, const Type& val) {
2316 return array_newelem_key(arr, val).first;
2319 std::pair<Type,Type> iter_types(const Type& iterable) {
2320 if (!iterable.subtypeOf(TArr) || !is_specialized_array(iterable)) {
2321 return { TInitCell, TInitCell };
2324 // Note: we don't need to handle possible emptiness explicitly,
2325 // because if the array was empty we won't ever pull anything out
2326 // while iterating.
2328 switch (iterable.m_dataTag) {
2329 case DataTag::None:
2330 if (iterable.subtypeOf(TSArr)) {
2331 return { TInitUnc, TInitUnc };
2333 return { TInitCell, TInitCell };
2334 case DataTag::Str:
2335 case DataTag::Obj:
2336 case DataTag::Int:
2337 case DataTag::Dbl:
2338 case DataTag::Cls:
2339 case DataTag::RefInner:
2340 always_assert(0);
2341 case DataTag::ArrVal:
2343 auto const mn = toDArrMapN(iterable.m_data.aval);
2344 return { mn.key, mn.val };
2346 case DataTag::ArrPacked:
2347 return { TInt, packed_values(*iterable.m_data.apacked) };
2348 case DataTag::ArrPackedN:
2349 return { TInt, iterable.m_data.apackedn->type };
2350 case DataTag::ArrStruct:
2351 return { TSStr, struct_values(*iterable.m_data.astruct) };
2352 case DataTag::ArrMapN:
2353 return { iterable.m_data.amapn->key, iterable.m_data.amapn->val };
2356 not_reached();
2359 //////////////////////////////////////////////////////////////////////
2361 RepoAuthType make_repo_type_arr(ArrayTypeTable::Builder& arrTable,
2362 const Type& t) {
2363 auto const emptiness = TArrE.couldBe(t) ? RepoAuthType::Array::Empty::Maybe
2364 : RepoAuthType::Array::Empty::No;
2366 auto const arr = [&]() -> const RepoAuthType::Array* {
2367 switch (t.m_dataTag) {
2368 case DataTag::None:
2369 case DataTag::Str:
2370 case DataTag::Obj:
2371 case DataTag::Int:
2372 case DataTag::Dbl:
2373 case DataTag::Cls:
2374 case DataTag::RefInner:
2375 case DataTag::ArrVal:
2376 case DataTag::ArrStruct:
2377 case DataTag::ArrMapN:
2378 return nullptr;
2379 case DataTag::ArrPackedN:
2380 // TODO(#4205897): we need to use this before it's worth putting
2381 // in the repo.
2382 if (false) {
2383 return arrTable.packedn(
2384 emptiness,
2385 make_repo_type(arrTable, t.m_data.apackedn->type)
2388 return nullptr;
2389 case DataTag::ArrPacked:
2391 std::vector<RepoAuthType> repoTypes;
2392 std::transform(
2393 begin(t.m_data.apacked->elems), end(t.m_data.apacked->elems),
2394 std::back_inserter(repoTypes),
2395 [&] (const Type& t) { return make_repo_type(arrTable, t); }
2397 return arrTable.packed(emptiness, repoTypes);
2399 return nullptr;
2401 not_reached();
2402 }();
2404 auto const tag = [&]() -> RepoAuthType::Tag {
2405 if (t.subtypeOf(TSArr)) return RepoAuthType::Tag::SArr;
2406 if (t.subtypeOf(TArr)) return RepoAuthType::Tag::Arr;
2407 if (t.subtypeOf(TOptSArr)) return RepoAuthType::Tag::OptSArr;
2408 if (t.subtypeOf(TOptArr)) return RepoAuthType::Tag::OptArr;
2409 not_reached();
2410 }();
2412 return RepoAuthType { tag, arr };
2415 RepoAuthType make_repo_type(ArrayTypeTable::Builder& arrTable, const Type& t) {
2416 assert(!t.couldBe(TCls));
2417 assert(!t.subtypeOf(TBottom));
2418 using T = RepoAuthType::Tag;
2420 if (t.strictSubtypeOf(TObj) || (is_opt(t) && t.strictSubtypeOf(TOptObj))) {
2421 auto const dobj = dobj_of(t);
2422 auto const tag =
2423 is_opt(t)
2424 ? (dobj.type == DObj::Exact ? T::OptExactObj : T::OptSubObj)
2425 : (dobj.type == DObj::Exact ? T::ExactObj : T::SubObj);
2426 return RepoAuthType { tag, dobj.cls.name() };
2429 if (t.strictSubtypeOf(TArr) ||
2430 // TODO(#4205897): optional array types.
2431 (false && is_opt(t) && t.strictSubtypeOf(TOptArr))) {
2432 return make_repo_type_arr(arrTable, t);
2435 #define X(x) if (t.subtypeOf(T##x)) return RepoAuthType{T::x};
2436 X(Uninit)
2437 X(InitNull)
2438 X(Null)
2439 X(Int)
2440 X(OptInt)
2441 X(Dbl)
2442 X(OptDbl)
2443 X(Res)
2444 X(OptRes)
2445 X(Bool)
2446 X(OptBool)
2447 X(SStr)
2448 X(OptSStr)
2449 X(Str)
2450 X(OptStr)
2451 X(SArr)
2452 X(OptSArr)
2453 X(Arr)
2454 X(OptArr)
2455 X(Obj)
2456 X(OptObj)
2457 X(InitUnc)
2458 X(Unc)
2459 X(InitCell)
2460 X(Cell)
2461 X(Ref)
2462 X(InitGen)
2463 X(Gen)
2464 #undef X
2465 not_reached();
2468 //////////////////////////////////////////////////////////////////////