2 +----------------------------------------------------------------------+
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>
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
{
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
) {
48 case BSStr
: case BObj
: case BInt
: case BDbl
:
49 case BOptSStr
: case BOptObj
: case BOptInt
: case BOptDbl
:
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
:
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
114 bool isPredefined(trep bits
) {
176 // Pre: isPredefined(bits)
177 bool canBeOptional(trep bits
) {
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
;
262 if (arr_part
== (BSArrN
|BCArrE
)) return static_cast<trep
>(combined
|BArr
);
263 if (arr_part
== (BSArrE
|BCArrN
)) return static_cast<trep
>(combined
|BArr
);
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
));
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
;
294 from_cell(*iter
.secondRef().asTypedValue())
298 return DArrPacked
{ std::move(elems
) };
301 folly::Optional
<DArrPackedN
> toDArrPackedN(SArray ar
) {
302 assert(!ar
->empty());
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());
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
])) {
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;
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
])) {
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;
391 Type
struct_values(const DArrStruct
& a
) {
393 for (auto& kv
: a
.map
) ret
= union_of(ret
, kv
.second
);
397 Type
packed_values(const DArrPacked
& a
) {
399 for (auto& e
: a
.elems
) ret
= union_of(ret
, e
);
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
410 template<class InnerFn
>
411 struct Commute
: InnerFn
{
412 using result_type
= typename
InnerFn::result_type
;
414 using InnerFn::operator();
417 result_type
operator()(SArray a
, const B
& b
) const {
418 return InnerFn::operator()(b
, a
);
422 result_type
operator()(const DArrStruct
& a
, const B
& b
) const {
423 return InnerFn::operator()(b
, a
);
427 result_type
operator()(const DArrPackedN
& a
, const B
& b
) const {
428 return InnerFn::operator()(b
, a
);
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 {
457 bool operator()(const DArrMapN
& a
, SArray b
) const {
460 bool operator()(const DArrPacked
& a
, const DArrPackedN
& b
) const {
463 bool operator()(const DArrPacked
& a
, const DArrStruct
& b
) const {
466 bool operator()(const DArrPacked
&, const DArrMapN
&) const {
469 bool operator()(const DArrPackedN
& a
, const DArrStruct
& b
) const {
472 bool operator()(const DArrPackedN
&, const DArrMapN
&) const {
475 bool operator()(const DArrStruct
&, const DArrMapN
&) const {
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;
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;
526 bool operator()(const DArrPacked
&, const DArrStruct
&) const {
529 bool operator()(const DArrPacked
&, const DArrMapN
&) const {
532 bool operator()(const DArrPackedN
&, const DArrStruct
&) const {
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
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
)));
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 {
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
);
592 return (*this)(*p1
, *p2
);
595 if (auto const p2n
= toDArrPackedN(b
)) {
596 return (*this)(*p1
, *p2n
);
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
);
616 return (*this)(a
, *p
);
619 Type
operator()(const DArrPackedN
& a
, SArray b
) const {
620 auto const p
= toDArrPackedN(b
);
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
);
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;
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;
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;
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 {
735 bool operator()(const DArrPackedN
& a
, SArray b
) const {
738 bool operator()(const DArrStruct
& a
, const DArrPacked
& b
) const {
741 bool operator()(const DArrStruct
& a
, const DArrPackedN
& b
) const {
744 bool operator()(const DArrPacked
& a
, const DArrStruct
& b
) const {
747 bool operator()(const DArrPackedN
& a
, const DArrStruct
& b
) const {
750 bool operator()(const DArrMapN
&, const DArrPackedN
&) const {
753 bool operator()(const DArrMapN
&, const DArrPacked
&) const {
756 bool operator()(const DArrMapN
&, const DArrStruct
&) const {
759 bool operator()(const DArrMapN
&, SArray
) const {
764 using DisjointEq
= Commute
<DisjointEqImpl
>;
765 using DisjointCouldBe
= Commute
<DisjointCouldBeImpl
>;
766 using ArrUnion
= Commute
<ArrUnionImpl
>;
768 //////////////////////////////////////////////////////////////////////
772 //////////////////////////////////////////////////////////////////////
774 Type::Type(const Type
& o
) noexcept
776 , m_dataTag(o
.m_dataTag
)
778 SCOPE_EXIT
{ assert(checkInvariants()); };
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
);
790 case DataTag::ArrPacked
:
791 new (&m_data
.apacked
) copy_ptr
<DArrPacked
>(o
.m_data
.apacked
);
793 case DataTag::ArrPackedN
:
794 new (&m_data
.apackedn
) copy_ptr
<DArrPackedN
>(o
.m_data
.apackedn
);
796 case DataTag::ArrStruct
:
797 new (&m_data
.astruct
) copy_ptr
<DArrStruct
>(o
.m_data
.astruct
);
799 case DataTag::ArrMapN
:
800 new (&m_data
.amapn
) copy_ptr
<DArrMapN
>(o
.m_data
.amapn
);
806 Type::Type(Type
&& o
) noexcept
808 , m_dataTag(o
.m_dataTag
)
810 SCOPE_EXIT
{ assert(checkInvariants());
811 assert(o
.checkInvariants()); };
813 o
.m_dataTag
= DataTag::None
;
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
));
825 case DataTag::ArrPacked
:
826 new (&m_data
.apacked
) copy_ptr
<DArrPacked
>(move(o
.m_data
.apacked
));
828 case DataTag::ArrPackedN
:
829 new (&m_data
.apackedn
) copy_ptr
<DArrPackedN
>(move(o
.m_data
.apackedn
));
831 case DataTag::ArrStruct
:
832 new (&m_data
.astruct
) copy_ptr
<DArrStruct
>(move(o
.m_data
.astruct
));
834 case DataTag::ArrMapN
:
835 new (&m_data
.amapn
) copy_ptr
<DArrMapN
>(move(o
.m_data
.amapn
));
841 Type
& Type::operator=(const Type
& o
) noexcept
{
842 SCOPE_EXIT
{ assert(checkInvariants()); };
848 Type
& Type::operator=(Type
&& o
) noexcept
{
849 SCOPE_EXIT
{ assert(checkInvariants());
850 assert(o
.checkInvariants()); };
852 new (this) Type(std::move(o
));
856 Type::~Type() noexcept
{
857 assert(checkInvariants());
862 case DataTag::ArrVal
:
866 case DataTag::RefInner
:
867 m_data
.inner
.~copy_ptr
<Type
>();
875 case DataTag::ArrPacked
:
876 m_data
.apacked
.~copy_ptr
<DArrPacked
>();
878 case DataTag::ArrPackedN
:
879 m_data
.apackedn
.~copy_ptr
<DArrPackedN
>();
881 case DataTag::ArrStruct
:
882 m_data
.astruct
.~copy_ptr
<DArrStruct
>();
884 case DataTag::ArrMapN
:
885 m_data
.amapn
.~copy_ptr
<DArrMapN
>();
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(); }
902 template<class Ret
, class T
, class Function
>
903 Type::DJHelperFn
<Ret
,T
,Function
> Type::djbind(const Function
& f
,
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
);
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
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
;
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
));
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
{});
973 return m_data
.sval
== o
.m_data
.sval
;
974 case DataTag::ArrVal
:
975 return m_data
.aval
== o
.m_data
.aval
;
977 return m_data
.ival
== o
.m_data
.ival
;
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
));
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
);
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
;
1005 bool Type::subtypeData(const Type
& o
) const {
1006 if (m_dataTag
!= o
.m_dataTag
) {
1007 return disjointDataFn(o
, DisjointSubtype
{});
1010 switch (m_dataTag
) {
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
)) {
1018 if (o
.m_data
.dobj
.type
== DObj::Sub
) {
1019 return m_data
.dobj
.cls
.subtypeOf(o
.m_data
.dobj
.cls
);
1023 if (m_data
.dcls
.type
== o
.m_data
.dcls
.type
&&
1024 m_data
.dcls
.cls
.same(o
.m_data
.dcls
.cls
)) {
1027 if (o
.m_data
.dcls
.type
== DCls::Sub
) {
1028 return m_data
.dcls
.cls
.subtypeOf(o
.m_data
.dcls
.cls
);
1032 case DataTag::ArrVal
:
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
);
1052 bool Type::couldBeData(const Type
& o
) const {
1053 if (m_dataTag
!= o
.m_dataTag
) {
1054 return disjointDataFn(o
, DisjointCouldBe
{});
1057 switch (m_dataTag
) {
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
)) {
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
);
1072 if (m_data
.dcls
.type
== o
.m_data
.dcls
.type
&&
1073 m_data
.dcls
.cls
.same(o
.m_data
.dcls
.cls
)) {
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
);
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
;
1085 return m_data
.sval
== o
.m_data
.sval
;
1087 return m_data
.ival
== o
.m_data
.ival
;
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
);
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
);
1117 if (is_specialized_wait_handle(o
)) {
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
)) {
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;
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
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
);
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
));
1238 case DataTag::Cls
: break;
1240 if (auto t
= m_data
.dobj
.whType
.get()) {
1241 t
->checkInvariants();
1244 case DataTag::ArrVal
:
1245 assert(m_data
.aval
->isStatic());
1246 assert(!m_data
.aval
->empty());
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
));
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
));
1260 case DataTag::ArrPackedN
:
1261 assert(m_data
.apackedn
->type
.subtypeOf(TInitGen
));
1263 case DataTag::ArrMapN
:
1264 assert(m_data
.amapn
->key
.subtypeOf(TInitCell
));
1265 assert(m_data
.amapn
->val
.subtypeOf(TInitGen
));
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
));
1280 bool is_specialized_wait_handle(const Type
& t
) {
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
);
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
;
1306 Type
ival(int64_t val
) {
1307 auto r
= Type
{ BInt
};
1308 r
.m_data
.ival
= val
;
1309 r
.m_dataTag
= DataTag::Int
;
1313 Type
dval(double val
) {
1314 auto r
= Type
{ BDbl
};
1315 r
.m_data
.dval
= val
;
1316 r
.m_dataTag
= DataTag::Dbl
;
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
;
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
;
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
;
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
;
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
;
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
;
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
) {
1389 case DataTag::RefInner
:
1391 case DataTag::ArrVal
:
1392 case DataTag::ArrPacked
:
1393 case DataTag::ArrPackedN
:
1394 case DataTag::ArrStruct
:
1395 case DataTag::ArrMapN
:
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
;
1411 Type
sarr_packed(std::vector
<Type
> elems
) {
1412 auto t
= arr_packed(std::move(elems
));
1417 Type
carr_packed(std::vector
<Type
> elems
) {
1418 auto t
= arr_packed(std::move(elems
));
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
;
1432 Type
sarr_packedn(Type t
) {
1433 auto r
= arr_packedn(std::move(t
));
1438 Type
carr_packedn(Type t
) {
1439 auto r
= arr_packedn(std::move(t
));
1444 Type
arr_struct(StructMap m
) {
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
;
1454 Type
sarr_struct(StructMap m
) {
1455 auto r
= arr_struct(std::move(m
));
1460 Type
carr_struct(StructMap m
) {
1461 auto r
= arr_struct(std::move(m
));
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
;
1475 Type
sarr_mapn(Type k
, Type v
) {
1476 auto r
= arr_mapn(k
, v
);
1481 Type
carr_mapn(Type k
, Type v
) {
1482 auto r
= arr_mapn(std::move(k
), std::move(v
));
1488 assert(canBeOptional(t
.m_bits
));
1490 ret
.m_bits
= static_cast<trep
>(ret
.m_bits
| BInitNull
);
1494 Type
unopt(Type t
) {
1496 t
.m_bits
= static_cast<trep
>(t
.m_bits
& ~BInitNull
);
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
);
1524 //////////////////////////////////////////////////////////////////////
1526 folly::Optional
<Cell
> tv(Type t
) {
1527 assert(t
.checkInvariants());
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());
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
));
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
1555 case DataTag::RefInner
:
1556 case DataTag::ArrPackedN
:
1557 case DataTag::ArrMapN
:
1568 Type
type_of_istype(IsTypeOp 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);
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
:
1606 always_assert(cell
.m_data
.pstr
->isStatic());
1607 return sval(cell
.m_data
.pstr
);
1610 always_assert(cell
.m_data
.parr
->isStatic());
1611 return aval(cell
.m_data
.parr
);
1615 case KindOfResource
:
1619 always_assert(0 && "reference counted type in from_cell");
1622 Type
from_DataType(DataType 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
;
1638 always_assert(0 && "dt in from_DataType didn't satisfy preconditions");
1641 Type
from_hni_constraint(SString s
) {
1642 if (!s
) return TGen
;
1648 ret
= union_of(ret
, TInitNull
);
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.
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
));
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
;
1681 switch (a
.m_dataTag
) {
1688 case DataTag::RefInner
:
1690 case DataTag::ArrVal
:
1692 ret
= ArrUnion
{}(a
.m_data
.aval
, b
.m_data
.aval
);
1693 ret
.m_bits
= newBits
;
1696 case DataTag::ArrPacked
:
1698 ret
= ArrUnion
{}(*a
.m_data
.apacked
, *b
.m_data
.apacked
);
1699 ret
.m_bits
= newBits
;
1702 case DataTag::ArrPackedN
:
1705 ret
.m_bits
= newBits
;
1706 ret
.m_data
.apackedn
->type
= union_of(a
.m_data
.apackedn
->type
,
1707 b
.m_data
.apackedn
->type
);
1710 case DataTag::ArrStruct
:
1712 ret
= ArrUnion
{}(*a
.m_data
.astruct
, *b
.m_data
.astruct
);
1713 ret
.m_bits
= newBits
;
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
;
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
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
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
));
1775 if (b
.subtypeOf(TOptArrE
)) {
1777 a
.m_bits
= combine_arr_bits(a
.m_bits
, b
.m_bits
);
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
) };
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;
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
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).
1851 Type
promote_emptyish(Type a
, Type b
) {
1852 if (is_opt(a
)) a
= unopt(a
);
1853 if (a
.subtypeOf(sempty())) {
1856 auto t
= trep(a
.m_bits
& ~(BNull
| BFalse
));
1857 if (!isPredefined(t
)) {
1858 if (trep(t
& BInitPrim
) == t
) {
1860 } else if (trep(t
& BInitUnc
) == t
) {
1862 } else if (trep(t
& BInitCell
) == t
) {
1867 return union_of(Type
{ t
}, b
);
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
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
);
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
;
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'.
1938 folly::Optional
<int64_t> i
;
1939 folly::Optional
<SString
> s
;
1943 ArrKey
disect_key(const Type
& keyTy
) {
1944 auto ret
= ArrKey
{};
1946 if (keyTy
.strictSubtypeOf(TInt
)) {
1947 ret
.i
= keyTy
.m_data
.ival
;
1951 if (keyTy
.strictSubtypeOf(TStr
) && keyTy
.m_dataTag
== DataTag::Str
) {
1952 ret
.s
= keyTy
.m_data
.sval
;
1955 if (keyTy
.m_data
.sval
->isStrictlyInteger(i
)) {
1961 if (keyTy
.strictSubtypeOf(TDbl
)) {
1962 ret
.i
= static_cast<int64_t>(keyTy
.m_data
.dval
);
1966 if (keyTy
.subtypeOf(TNum
)) {
1971 if (keyTy
.subtypeOf(TNull
)) {
1972 ret
.s
= s_empty
.get();
1973 ret
.type
= sempty();
1976 if (keyTy
.subtypeOf(TRes
)) {
1981 if (keyTy
.subtypeOf(TTrue
)) {
1986 if (keyTy
.subtypeOf(TFalse
)) {
1991 if (keyTy
.subtypeOf(TBool
)) {
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
) {
2004 if (!keyTy
.m_data
.sval
->isStrictlyInteger(ignore
)) {
2005 ret
.type
= keyTy
.strictSubtypeOf(TOptSStr
) ? TSStr
: TStr
;
2010 // Nothing we can do in other cases that could be strings (without
2011 // statically known values)---they may behave like integers at
2014 // TODO(#3774082): We should be able to set this to a Str|Int type.
2015 ret
.type
= TInitCell
;
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
) {
2030 case DataTag::RefInner
:
2034 return arr
.subtypeOf(TSArr
) ? TInitUnc
: TInitCell
;
2036 case DataTag::ArrVal
:
2038 if (auto const r
= arr
.m_data
.aval
->nvGet(*key
.i
)) {
2039 return from_cell(*r
);
2044 if (auto const r
= arr
.m_data
.aval
->nvGet(*key
.s
)) {
2045 return from_cell(*r
);
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
:
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
];
2067 case DataTag::ArrPackedN
:
2068 return union_of(arr
.m_data
.apackedn
->type
, TInitNull
);
2070 case DataTag::ArrStruct
:
2072 auto it
= arr
.m_data
.astruct
->map
.find(*key
.s
);
2073 return it
!= end(arr
.m_data
.astruct
->map
)
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
);
2085 if (!ty
.subtypeOf(TInitCell
)) {
2087 } else if (arr
.couldBe(TArrE
)) {
2088 ty
= union_of(std::move(ty
), TInitNull
);
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
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
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
);
2119 arr
.m_bits
= combine_arr_bits(arr
.m_bits
, BCArr
);
2123 switch (arr
.m_dataTag
) {
2129 case DataTag::RefInner
:
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
,
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
:
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
)) {
2162 if (*key
.i
== arr
.m_data
.apacked
->elems
.size()) {
2163 arr
.m_data
.apacked
->elems
.push_back(val
);
2167 return arr_mapn(union_of(TInt
, key
.type
),
2168 union_of(packed_values(*arr
.m_data
.apacked
), val
));
2170 case DataTag::ArrPackedN
:
2172 return arr_mapn(union_of(TInt
, key
.type
),
2173 union_of(arr
.m_data
.apackedn
->type
, val
));
2175 case DataTag::ArrStruct
:
2178 auto& map
= arr
.m_data
.astruct
->map
;
2180 auto it
= map
.find(*key
.s
);
2181 if (it
== end(map
)) {
2184 if (it
->second
.subtypeOf(TInitCell
)) {
2190 return arr_mapn(union_of(key
.type
, TSStr
),
2191 union_of(struct_values(*arr
.m_data
.astruct
), val
));
2194 case DataTag::ArrMapN
:
2196 return arr_mapn(union_of(key
.type
, arr
.m_data
.amapn
->key
),
2197 union_of(val
, arr
.m_data
.amapn
->val
));
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
2209 always_assert(!val
.subtypeOf(TRef
) &&
2210 "You probably don't want to put Ref types into arrays ...");
2213 arr
.couldBe(TArrN
) ? arrayN_set(arr
, undisectedKey
, val
)
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
{};
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
);
2234 return union_of(std::move(nonEmptyPart
), std::move(emptyPart
));
2237 // Do the same as array_newelem_key, but ignoring possible emptiness
2239 std::pair
<Type
,Type
> arrayN_newelem_key(const Type
& arr
, const Type
& val
) {
2240 switch (arr
.m_dataTag
) {
2246 case DataTag::RefInner
:
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
;
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
)),
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
)),
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
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
);
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
2328 switch (iterable
.m_dataTag
) {
2330 if (iterable
.subtypeOf(TSArr
)) {
2331 return { TInitUnc
, TInitUnc
};
2333 return { TInitCell
, TInitCell
};
2339 case DataTag::RefInner
:
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
};
2359 //////////////////////////////////////////////////////////////////////
2361 RepoAuthType
make_repo_type_arr(ArrayTypeTable::Builder
& arrTable
,
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
) {
2374 case DataTag::RefInner
:
2375 case DataTag::ArrVal
:
2376 case DataTag::ArrStruct
:
2377 case DataTag::ArrMapN
:
2379 case DataTag::ArrPackedN
:
2380 // TODO(#4205897): we need to use this before it's worth putting
2383 return arrTable
.packedn(
2385 make_repo_type(arrTable
, t
.m_data
.apackedn
->type
)
2389 case DataTag::ArrPacked
:
2391 std::vector
<RepoAuthType
> repoTypes
;
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
);
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
;
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
);
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};
2468 //////////////////////////////////////////////////////////////////////