2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
19 #include "hphp/runtime/base/array-data.h"
20 #include "hphp/runtime/base/record-data.h"
21 #include "hphp/runtime/base/tv-conversions.h"
22 #include "hphp/runtime/base/tv-mutate.h"
23 #include "hphp/runtime/base/tv-refcount.h"
24 #include "hphp/runtime/base/tv-val.h"
25 #include "hphp/runtime/base/tv-variant.h"
26 #include "hphp/runtime/base/type-object.h"
27 #include "hphp/runtime/base/type-resource.h"
28 #include "hphp/runtime/base/type-string.h"
29 #include "hphp/runtime/base/typed-value.h"
30 #include "hphp/runtime/vm/class-meth-data-ref.h"
31 #include "hphp/runtime/vm/rclass-meth-data.h"
32 #include "hphp/runtime/vm/rfunc-data.h"
35 #include <type_traits>
38 ///////////////////////////////////////////////////////////////////////////////
40 // Forward declare these to avoid including tv-conversions.h which has a
41 // circular dependency with this file.
43 struct OptionalVariant
;
46 * These structs are substitutes for Variant& or const Variant&, when there's
47 * no actual Variant in memory to take a reference to (when working with a
48 * tv_lval from an Array, for example). They should be treated with the same
49 * care as normal references: when copying or storing a variant_ref or
50 * const_variant_ref, think carefully about the lifetime of the underlying
53 namespace variant_ref_detail
{
54 template<bool is_const
>
57 using tv_val_t
= typename
std::conditional
<is_const
, tv_rval
, tv_lval
>::type
;
60 explicit base(tv_val_t val
) : m_val
{val
} {}
62 DataType
getType() const {
65 bool isNull() const { return isNullType(getType()); }
66 bool isBoolean() const { return isBooleanType(getType()); }
67 bool isInteger() const { return isIntType(getType()); }
68 bool isDouble() const { return isDoubleType(getType()); }
69 bool isString() const { return isStringType(getType()); }
70 bool isArray() const { return isArrayLikeType(getType()); }
71 bool isVec() const { return isVecType(getType()); }
72 bool isDict() const { return isDictType(getType()); }
73 bool isKeyset() const { return isKeysetType(getType()); }
74 bool isObject() const { return isObjectType(getType()); }
75 bool isResource() const { return isResourceType(getType()); }
76 bool isFunc() const { return isFuncType(getType()); }
77 bool isClass() const { return isClassType(getType()); }
78 bool isLazyClass() const { return isLazyClassType(getType()); }
79 bool isClsMeth() const { return isClsMethType(getType()); }
81 bool isPrimitive() const { return !isRefcountedType(type(m_val
)); }
83 auto toBoolean() const { return tvCastToBoolean(*m_val
); }
84 auto toInt64() const { return tvCastToInt64(*m_val
); }
85 auto toDouble() const { return tvCastToDouble(*m_val
); }
86 auto toString(ConvNoticeLevel level
= ConvNoticeLevel::None
,
87 const StringData
* notice_reason
= nullptr) const {
88 if (isStringType(type(m_val
))) return String
{val(m_val
).pstr
};
89 return String::attach(tvCastToStringData(*m_val
, level
, notice_reason
));
91 auto toArray() const {
92 if (isArrayLikeType(type(m_val
))) return Array
{val(m_val
).parr
};
93 return Array::attach(tvCastToArrayLikeData
<IntishCast::None
>(*m_val
));
95 auto toObject() const {
96 if (isObjectType(type(m_val
))) return Object
{val(m_val
).pobj
};
97 return Object::attach(tvCastToObjectData(*m_val
));
100 auto& asCStrRef() const { return HPHP::asCStrRef(m_val
); }
101 auto& asCArrRef() const { return HPHP::asCArrRef(m_val
); }
102 auto& asCObjRef() const { return HPHP::asCObjRef(m_val
); }
104 tv_rval
asTypedValue() const { return m_val
; }
106 ArrayData
*getArrayData() const {
108 return val(m_val
).parr
;
111 auto toFuncVal() const {
113 return val(m_val
).pfunc
;
116 auto toClassVal() const {
118 return val(m_val
).pclass
;
121 auto toLazyClassVal() const {
122 assertx(isLazyClass());
123 return val(m_val
).plazyclass
;
126 ClsMethDataRef
toClsMethVal() const {
127 assertx(isClsMeth());
128 return val(m_val
).pclsmeth
;
131 RefCount
getRefCount() const noexcept
{
132 return isRefcountedType(type(m_val
)) || hasPersistentFlavor(type(m_val
))
133 ? val(m_val
).pcnt
->count()
138 const tv_val_t m_val
;
142 struct const_variant_ref
;
144 struct variant_ref
: variant_ref_detail::base
<false> {
145 using variant_ref_detail::base
<false>::base
;
147 /* implicit */ variant_ref(Variant
& v
);
149 /* implicit */ operator const_variant_ref() const;
151 tv_lval
lval() const { return m_val
; }
154 tvMove(make_tv
<KindOfUninit
>(), m_val
);
157 void setNull() noexcept
{
161 variant_ref
& operator=(const Variant
& v
) noexcept
;
163 variant_ref
& assign(const Variant
& v
) noexcept
;
165 variant_ref
& operator=(Variant
&&rhs
) noexcept
;
167 // Generic assignment operator. Forward argument (preserving rvalue-ness and
168 // lvalue-ness) to the appropriate set function, as long as its not a Variant.
169 template <typename T
>
170 typename
std::enable_if
<
173 typename
std::remove_reference
<typename
std::remove_cv
<T
>::type
>::type
178 typename
std::remove_reference
<typename
std::remove_cv
<T
>::type
>::type
181 >::type
operator=(T
&& v
) {
182 set(std::forward
<T
>(v
));
186 void set(bool v
) noexcept
;
187 void set(int v
) noexcept
;
188 void set(int64_t v
) noexcept
;
189 void set(double v
) noexcept
;
190 void set(const char* v
) = delete;
191 void set(const std::string
& v
) {
192 return set(String(v
));
194 void set(StringData
*v
) noexcept
;
195 void set(ArrayData
*v
) noexcept
;
196 void set(ObjectData
*v
) noexcept
;
197 void set(ResourceHdr
*v
) noexcept
;
198 void set(ResourceData
*v
) noexcept
{ set(v
->hdr()); }
199 void set(const StringData
*v
) = delete;
200 void set(const ArrayData
*v
) = delete;
201 void set(const ObjectData
*v
) = delete;
202 void set(const ResourceData
*v
) = delete;
203 void set(const ResourceHdr
*v
) = delete;
205 void set(const String
& v
) noexcept
{ set(v
.get()); }
206 void set(const StaticString
& v
) noexcept
;
207 void set(const Array
& v
) noexcept
{ set(v
.get()); }
208 void set(const Object
& v
) noexcept
{ set(v
.get()); }
209 void set(const Resource
& v
) noexcept
{ set(v
.hdr()); }
211 void set(String
&& v
) noexcept
{ steal(v
.detach()); }
212 void set(Array
&& v
) noexcept
{ steal(v
.detach()); }
213 void set(Object
&& v
) noexcept
{ steal(v
.detach()); }
214 void set(Resource
&& v
) noexcept
{ steal(v
.detachHdr()); }
217 void set(const req::ptr
<T
> &v
) noexcept
{
221 template <typename T
>
222 void set(req::ptr
<T
>&& v
) noexcept
{
223 return steal(v
.detach());
226 void steal(StringData
* v
) noexcept
;
227 void steal(ArrayData
* v
) noexcept
;
228 void steal(ObjectData
* v
) noexcept
;
229 void steal(ResourceHdr
* v
) noexcept
;
230 void steal(ResourceData
* v
) noexcept
{ steal(v
->hdr()); }
233 struct const_variant_ref
: variant_ref_detail::base
<true> {
234 using variant_ref_detail::base
<true>::base
;
236 /* implicit */ const_variant_ref(const Variant
& v
);
238 tv_rval
rval() const { return m_val
; }
241 inline variant_ref::operator const_variant_ref() const {
242 return const_variant_ref
{m_val
};
246 * This class predates HHVM.
248 * In hphpc, when type inference failed to know type of a variable, we
249 * would use Variant to represent the php variable in generated C++.
251 * Now Variant is only used in C++ extensions, and the API is mostly
252 * legacy stuff. If you're writing a C++ extension, try to avoid
253 * Variant when you can (but you often can't, and we don't really have
254 * a replacement yet, sorry).
256 * In C++ extensions, this class can be used as a generic handle to
257 * one of our other data types (e.g. StringData, ArrayData)
261 * For historical reasons, this class does a lot of things you
262 * don't really expect in a well-behaved C++ class.
264 * For example, the copy constructor is not a copy constructor (it converts
265 * KindOfUninit to KindOfNull). A similar story applies to the move
266 * constructor. (And this means we may actually rely on whether copy
267 * elision (NRVO etc) is happening in some places for correctness.)
273 struct Variant
: private TypedValue
{
276 // Used by VariantTraits to create a folly::Optional-like
277 // optional Variant which fits in 16 bytes.
278 using Optional
= OptionalVariant
;
280 enum class NullInit
{};
281 enum class NoInit
{};
282 enum class TVCopy
{};
284 enum class ArrayInitCtor
{};
285 enum class Attach
{};
288 Variant() noexcept
{ m_type
= KindOfUninit
; }
289 explicit Variant(NullInit
) noexcept
{ m_type
= KindOfNull
; }
290 explicit Variant(NoInit
) noexcept
{}
292 /* implicit */ Variant(bool v
) noexcept
{
293 m_type
= KindOfBoolean
; m_data
.num
= v
;
295 /* implicit */ Variant(int v
) noexcept
{
296 m_type
= KindOfInt64
; m_data
.num
= v
;
298 // The following two overloads will accept int64_t whether it's
299 // implemented as long or long long.
300 /* implicit */ Variant(long v
) noexcept
{
301 m_type
= KindOfInt64
; m_data
.num
= v
;
303 /* implicit */ Variant(long long v
) noexcept
{
304 m_type
= KindOfInt64
; m_data
.num
= v
;
306 /* implicit */ Variant(unsigned v
) noexcept
{
307 m_type
= KindOfInt64
; m_data
.num
= v
;
309 /* implicit */ Variant(unsigned long v
) noexcept
{
310 m_type
= KindOfInt64
; m_data
.num
= v
;
312 /* implicit */ Variant(unsigned long long v
) noexcept
{
313 m_type
= KindOfInt64
; m_data
.num
= v
;
315 /* implicit */ Variant(double v
) noexcept
{
316 m_type
= KindOfDouble
; m_data
.dbl
= v
;
319 /* implicit */ Variant(const char* v
) {
320 m_type
= KindOfString
;
321 m_data
.pstr
= StringData::Make(v
);
323 /* implicit */ Variant(const std::string
&v
) {
324 m_type
= KindOfString
;
325 StringData
*s
= StringData::Make(v
.c_str(), v
.size(), CopyString
);
329 /* implicit */ Variant(const StaticString
&v
) noexcept
{
330 assertx(v
.get() && !v
.get()->isRefCounted());
331 m_type
= KindOfPersistentString
;
332 m_data
.pstr
= v
.get();
335 /* implicit */ Variant(const String
& v
) noexcept
: Variant(v
.get()) {}
336 /* implicit */ Variant(const Array
& v
) noexcept
: Variant(v
.get()) { }
337 /* implicit */ Variant(const Object
& v
) noexcept
: Variant(v
.get()) {}
338 /* implicit */ Variant(const Resource
& v
) noexcept
339 : Variant(v
.hdr()) {}
341 /* implicit */ Variant(Class
* v
) {
342 m_type
= KindOfClass
;
346 /* implicit */ Variant(LazyClassData v
) {
347 m_type
= KindOfLazyClass
;
348 m_data
.plazyclass
= v
;
351 /* implicit */ Variant(const ClsMethDataRef v
) {
352 m_type
= KindOfClsMeth
;
358 * Explicit conversion constructors. These all manipulate ref-counts of bare
359 * pointers as a side-effect, so we want to be explicit when its happening.
361 explicit Variant(StringData
* v
) noexcept
;
362 explicit Variant(ArrayData
* v
) noexcept
{
365 if (v
->isRefCounted()) {
366 m_type
= v
->toDataType();
369 m_type
= v
->toPersistentDataType();
375 explicit Variant(ObjectData
* v
) noexcept
{
377 m_type
= KindOfObject
;
385 explicit Variant(const Func
* f
) noexcept
{
391 explicit Variant(RFuncData
* v
) noexcept
{
394 m_type
= KindOfRFunc
;
402 explicit Variant(RClsMethData
* v
) noexcept
{
405 m_type
= KindOfRClsMeth
;
406 m_data
.prclsmeth
= v
;
413 template <typename T
>
414 explicit Variant(const req::ptr
<T
>& ptr
) : Variant(ptr
.get()) { }
415 template <typename T
>
416 explicit Variant(req::ptr
<T
>&& ptr
) noexcept
417 : Variant(ptr
.detach(), Attach
{}) { }
420 * Creation constructor from ArrayInit that avoids a null check and an
423 explicit Variant(ArrayData
* ad
, DataType dt
, ArrayInitCtor
) noexcept
{
424 assertx(ad
->toDataType() == dt
);
429 enum class PersistentArrInit
{};
430 Variant(const ArrayData
* ad
, DataType dt
, PersistentArrInit
) noexcept
{
431 // TODO(T58820726): Switch back to trusting caller and strict equality.
432 assertx(equivDataTypes(ad
->toPersistentDataType(), dt
));
433 assertx(!ad
->isRefCounted());
434 m_data
.parr
= const_cast<ArrayData
*>(ad
);
438 enum class PersistentStrInit
{};
439 explicit Variant(const StringData
*s
, PersistentStrInit
) noexcept
{
440 assertx(!s
->isRefCounted());
441 m_data
.pstr
= const_cast<StringData
*>(s
);
442 m_type
= KindOfPersistentString
;
445 // These are prohibited, but declared just to prevent accidentally
446 // calling the bool constructor just because we had a pointer to
448 /* implicit */ Variant(const void*) = delete;
449 template<typename Ret
, typename
... Args
>
450 /* implicit */ Variant(Ret (*)(Args
...)) = delete;
451 template<class Class
, typename Ret
, typename
... Args
>
452 /* implicit */ Variant(Ret (Class::*)(Args
...)) = delete;
453 template<class Class
, typename Ret
, typename
... Args
>
454 /* implicit */ Variant(Ret (Class::*)(Args
...) const) = delete;
456 //////////////////////////////////////////////////////////////////////
459 * Copy constructor and copy assignment do not semantically make
460 * copies: they turn uninits to null.
463 Variant(const Variant
& v
) noexcept
;
464 explicit Variant(const_variant_ref v
) noexcept
;
466 Variant(const Variant
& v
, TVCopy
) noexcept
{
471 Variant(const Variant
& v
, TVDup
) noexcept
{
474 tvIncRefGen(*asTypedValue());
477 Variant
& operator=(const Variant
& v
) noexcept
{
484 * Note: not semantically moves. Like our "copy constructor", these
485 * turn uninits to null.
488 Variant(Variant
&& v
) noexcept
{
490 if (v
.m_type
!= KindOfUninit
) {
493 v
.m_type
= KindOfNull
;
499 // Move ctor for strings
500 /* implicit */ Variant(String
&& v
) noexcept
{
501 StringData
*s
= v
.get();
502 if (LIKELY(s
!= nullptr)) {
504 m_type
= s
->isRefCounted() ? KindOfString
: KindOfPersistentString
;
511 // Move ctor for arrays
512 /* implicit */ Variant(Array
&& v
) noexcept
{
513 ArrayData
*a
= v
.get();
514 if (LIKELY(a
!= nullptr)) {
516 m_type
= a
->isRefCounted() ? a
->toDataType() : a
->toPersistentDataType();
523 // Move ctor for objects
524 /* implicit */ Variant(Object
&& v
) noexcept
{
525 ObjectData
*pobj
= v
.get();
527 m_type
= KindOfObject
;
535 // Move ctor for resources
536 /* implicit */ Variant(Resource
&& v
) noexcept
{
539 m_type
= KindOfResource
;
550 * Note: not semantically moves. Like our "copies", these turn uninits
553 Variant
& operator=(Variant
&&rhs
) noexcept
{
554 assertx(this != &rhs
); // we end up as null on a self move-assign.
555 Variant
& lhs
= *this;
557 Variant
goner((NoInit()));
558 goner
.m_data
= lhs
.m_data
;
559 goner
.m_type
= lhs
.m_type
;
561 if (rhs
.m_type
== KindOfUninit
) {
562 lhs
.m_type
= KindOfNull
;
564 lhs
.m_type
= rhs
.m_type
;
565 lhs
.m_data
= rhs
.m_data
;
566 rhs
.m_type
= KindOfNull
;
572 ALWAYS_INLINE
~Variant() noexcept
{
573 tvDecRefGen(asTypedValue());
575 memset(this, kTVTrashFill2
, sizeof(*this));
580 //////////////////////////////////////////////////////////////////////
583 * During sweeping, request-allocated things are not allowed to be decref'd
584 * or manipulated. This function is used to cause a Variant to go
585 * into a state where its destructor will have no effects on the
586 * request local heap, in cases where sweepable objects can't
587 * organize things to avoid running Variant destructors.
589 void releaseForSweep() { m_type
= KindOfNull
; }
591 //////////////////////////////////////////////////////////////////////
594 * Break bindings and set to uninit.
597 auto const old
= *asTypedValue();
598 m_type
= KindOfUninit
;
603 * set to null without breaking bindings (if any), faster than v_a = null;
605 void setNull() noexcept
{
606 tvSetNull(*asTypedValue());
609 static Variant
attach(TypedValue tv
) noexcept
{
610 return Variant
{tv
, Attach
{}};
612 static Variant
attach(StringData
* var
) noexcept
{
613 return Variant
{var
, Attach
{}};
615 static Variant
attach(ArrayData
* var
) noexcept
{
616 return Variant
{var
, Attach
{}};
618 static Variant
attach(ObjectData
* var
) noexcept
{
619 return Variant
{var
, Attach
{}};
621 static Variant
attach(ResourceData
* var
) noexcept
{
622 return Variant
{var
, Attach
{}};
624 static Variant
attach(ResourceHdr
* var
) noexcept
{
625 return Variant
{var
, Attach
{}};
628 static Variant
wrap(TypedValue tv
) noexcept
{
629 return Variant
{tv
, Wrap
{}};
632 ///////////////////////////////////////////////////////////////////////////////
635 ALWAYS_INLINE
int64_t asInt64Val() const {
636 assertx(m_type
== KindOfInt64
);
640 ALWAYS_INLINE
int64_t toInt64Val() const {
641 assertx(is(KindOfInt64
));
645 ///////////////////////////////////////////////////////////////////////////////
648 ALWAYS_INLINE
double asDoubleVal() const {
649 assertx(m_type
== KindOfDouble
);
653 ALWAYS_INLINE
double toDoubleVal() const {
654 assertx(is(KindOfDouble
));
658 ///////////////////////////////////////////////////////////////////////////////
661 ALWAYS_INLINE
bool asBooleanVal() const {
662 assertx(m_type
== KindOfBoolean
);
666 ALWAYS_INLINE
bool toBooleanVal() const {
667 assertx(is(KindOfBoolean
));
671 ///////////////////////////////////////////////////////////////////////////////
674 ALWAYS_INLINE
const String
& asCStrRef() const {
675 assertx(isStringType(m_type
) && m_data
.pstr
);
676 return *reinterpret_cast<const String
*>(&m_data
.pstr
);
679 ALWAYS_INLINE String
& asStrRef() {
680 assertx(isStringType(m_type
) && m_data
.pstr
);
681 // The caller is likely going to modify the string, so we have to eagerly
682 // promote KindOfPersistentString -> KindOfString.
683 m_type
= KindOfString
;
684 return *reinterpret_cast<String
*>(&m_data
.pstr
);
687 ///////////////////////////////////////////////////////////////////////////////
690 ALWAYS_INLINE
const Array
& asCArrRef() const {
691 assertx(isArrayLikeType(m_type
) && m_data
.parr
);
692 return *reinterpret_cast<const Array
*>(&m_data
.parr
);
695 ALWAYS_INLINE Array
& asArrRef() {
696 assertx(isArrayLikeType(m_type
) && m_data
.parr
);
697 m_type
= m_data
.parr
->toDataType();
698 return *reinterpret_cast<Array
*>(&m_data
.parr
);
701 ///////////////////////////////////////////////////////////////////////////////
704 ALWAYS_INLINE
const Object
& asCObjRef() const {
705 assertx(m_type
== KindOfObject
&& m_data
.pobj
);
706 return *reinterpret_cast<const Object
*>(&m_data
.pobj
);
709 ALWAYS_INLINE Object
& asObjRef() {
710 assertx(m_type
== KindOfObject
&& m_data
.pobj
);
711 return *reinterpret_cast<Object
*>(&m_data
.pobj
);
714 ALWAYS_INLINE
const Resource
& asCResRef() const {
715 assertx(m_type
== KindOfResource
&& m_data
.pres
);
716 return *reinterpret_cast<const Resource
*>(&m_data
.pres
);
719 ALWAYS_INLINE
const Resource
& toCResRef() const {
720 assertx(is(KindOfResource
));
721 assertx(m_data
.pres
);
722 return *reinterpret_cast<const Resource
*>(&m_data
.pres
);
725 ALWAYS_INLINE Resource
& asResRef() {
726 assertx(m_type
== KindOfResource
&& m_data
.pres
);
727 return *reinterpret_cast<Resource
*>(&m_data
.pres
);
731 * Type testing functions
733 DataType
getType() const {
736 bool is(DataType type
) const {
737 return getType() == type
;
739 bool isInitialized() const {
740 return m_type
!= KindOfUninit
;
742 bool isNull() const {
743 return isNullType(getType());
745 bool isBoolean() const {
746 return getType() == KindOfBoolean
;
748 bool isInteger() const {
749 return getType() == KindOfInt64
;
751 bool isDouble() const {
752 return getType() == KindOfDouble
;
754 bool isString() const {
755 return isStringType(getType());
757 bool isArray() const {
758 return isArrayLikeType(getType());
761 return isVecType(getType());
763 bool isDict() const {
764 return isDictType(getType());
766 bool isKeyset() const {
767 return isKeysetType(getType());
769 bool isObject() const {
770 return getType() == KindOfObject
;
772 bool isResource() const {
773 return getType() == KindOfResource
;
775 bool isFunc() const {
776 return isFuncType(getType());
778 bool isClass() const {
779 return isClassType(getType());
781 bool isLazyClass() const {
782 return isLazyClassType(getType());
784 bool isClsMeth() const {
785 return isClsMethType(getType());
788 bool isNumeric(bool checkString
= false) const noexcept
;
789 DataType
toNumeric(int64_t &ival
, double &dval
, bool checkString
= false)
791 bool isScalar() const noexcept
;
792 bool isIntVal() const {
802 case KindOfPersistentString
:
804 case KindOfPersistentVec
:
806 case KindOfPersistentDict
:
808 case KindOfPersistentKeyset
:
813 case KindOfLazyClass
:
822 // Is "define('CONSTANT', <this value>)" legal?
823 enum class AllowedAsConstantValue
{
826 , ContainsObject
// Allowed if constant of an "enum class".
828 AllowedAsConstantValue
isAllowedAsConstantValue() const;
831 * Get reference count of weak or strong binding. For debugging purpose.
833 int getRefCount() const noexcept
{
834 return const_variant_ref
{*this}.getRefCount();
837 bool getBoolean() const {
838 assertx(getType() == KindOfBoolean
);
841 int64_t getInt64() const {
842 assertx(getType() == KindOfInt64
);
845 double getDouble() const {
846 assertx(getType() == KindOfDouble
);
853 Variant
& assign(const Variant
& v
) noexcept
{
854 tvSet(tvToInit(*v
.asTypedValue()), *asTypedValue());
858 // Generic assignment operator. Forward argument (preserving rvalue-ness and
859 // lvalue-ness) to the appropriate set function, as long as its not a Variant.
860 template <typename T
>
861 typename
std::enable_if
<
864 typename
std::remove_reference
<typename
std::remove_cv
<T
>::type
>::type
869 typename
std::remove_reference
<typename
std::remove_cv
<T
>::type
>::type
872 >::type
operator=(T
&& v
) {
873 set(std::forward
<T
>(v
));
877 Variant
operator + () const = delete;
878 Variant
&operator += (const Variant
& v
) = delete;
879 Variant
&operator += (int n
) = delete;
880 Variant
&operator += (int64_t n
) = delete;
881 Variant
&operator += (double n
) = delete;
883 Variant
operator - () const = delete;
884 Variant
operator - (const Variant
& v
) const = delete;
885 Variant
&operator -= (const Variant
& v
) = delete;
886 Variant
&operator -= (int n
) = delete;
887 Variant
&operator -= (int64_t n
) = delete;
888 Variant
&operator -= (double n
) = delete;
890 Variant
operator * (const Variant
& v
) const = delete;
891 Variant
&operator *= (const Variant
& v
) = delete;
892 Variant
&operator *= (int n
) = delete;
893 Variant
&operator *= (int64_t n
) = delete;
894 Variant
&operator *= (double n
) = delete;
896 Variant
operator / (const Variant
& v
) const = delete;
897 Variant
&operator /= (const Variant
& v
) = delete;
898 Variant
&operator /= (int n
) = delete;
899 Variant
&operator /= (int64_t n
) = delete;
900 Variant
&operator /= (double n
) = delete;
902 int64_t operator % (const Variant
& v
) const = delete;
903 Variant
&operator %= (const Variant
& v
) = delete;
904 Variant
&operator %= (int n
) = delete;
905 Variant
&operator %= (int64_t n
) = delete;
906 Variant
&operator %= (double n
) = delete;
908 Variant
operator | (const Variant
& v
) const = delete;
909 Variant
&operator |= (const Variant
& v
) = delete;
910 Variant
operator & (const Variant
& v
) const = delete;
911 Variant
&operator &= (const Variant
& v
) = delete;
912 Variant
operator ^ (const Variant
& v
) const = delete;
913 Variant
&operator ^= (const Variant
& v
) = delete;
914 Variant
&operator <<=(int64_t n
) = delete;
915 Variant
&operator >>=(int64_t n
) = delete;
917 Variant
&operator ++ () = delete;
918 Variant
operator ++ (int) = delete;
919 Variant
&operator -- () = delete;
920 Variant
operator -- (int) = delete;
923 * Explicit type conversions
925 bool toBoolean() const {
926 if (isNullType(m_type
)) return false;
927 if (hasNumData(m_type
)) return m_data
.num
;
928 return toBooleanHelper();
930 char toByte() const { return (char)toInt64();}
931 short toInt16(int base
= 10) const { return (short)toInt64(base
);}
932 int toInt32(int base
= 10) const { return (int)toInt64(base
);}
933 int64_t toInt64() const {
934 if (isNullType(m_type
)) return 0;
935 if (hasNumData(m_type
)) return m_data
.num
;
936 return toInt64Helper(10);
938 int64_t toInt64(int base
) const {
939 if (isNullType(m_type
)) return 0;
940 if (hasNumData(m_type
)) return m_data
.num
;
941 return toInt64Helper(base
);
943 double toDouble() const {
944 if (m_type
== KindOfDouble
) return m_data
.dbl
;
945 return tvCastToDouble(*asTypedValue());
948 String
toString(ConvNoticeLevel level
= ConvNoticeLevel::None
,
949 const StringData
* notice_reason
= nullptr) const& {
950 if (isStringType(m_type
)) return String
{m_data
.pstr
};
951 return String::attach(tvCastToStringData(*this, level
, notice_reason
));
954 String
toString(ConvNoticeLevel level
= ConvNoticeLevel::None
,
955 const StringData
* notice_reason
= nullptr) && {
956 if (isStringType(m_type
)) {
958 return String::attach(m_data
.pstr
);
960 return toString(level
, notice_reason
);
963 // Convert a non-array-like type to a PHP array, leaving PHP arrays and Hack
964 // arrays unchanged. Use toPHPArray() if you want the result to always be a
966 template <IntishCast IC
= IntishCast::None
>
967 Array
toArray() const {
968 if (isArrayLikeType(m_type
)) return Array
{m_data
.parr
};
969 return Array::attach(tvCastToArrayLikeData
<IC
>(*this));
971 Array
toPHPArray() const {
972 return toPHPArrayHelper();
974 Object
toObject() const {
975 if (isObjectType(m_type
)) return Object
{m_data
.pobj
};
976 return Object::attach(tvCastToObjectData(*this));
978 Resource
toResource() const {
979 if (m_type
== KindOfResource
) return Resource
{m_data
.pres
};
980 return toResourceHelper();
983 Array
toVec() const {
984 if (isVecType(m_type
)) return Array
{m_data
.parr
};
986 tvCastToVecInPlace(copy
.asTypedValue());
987 assertx(copy
.isVec());
988 return Array::attach(copy
.detach().m_data
.parr
);
991 Array
toDict() const {
992 if (isDictType(m_type
)) return Array
{m_data
.parr
};
994 tvCastToDictInPlace(copy
.asTypedValue());
995 assertx(copy
.isDict());
996 return Array::attach(copy
.detach().m_data
.parr
);
999 Array
toKeyset() const {
1000 if (isKeysetType(m_type
)) return Array
{m_data
.parr
};
1002 tvCastToKeysetInPlace(copy
.asTypedValue());
1003 assertx(copy
.isKeyset());
1004 return Array::attach(copy
.detach().m_data
.parr
);
1007 Array
toVArray() const { return toVec(); }
1008 Array
toDArray() const { return toDict(); }
1010 template <typename T
>
1011 typename
std::enable_if
<std::is_base_of
<ResourceData
,T
>::value
, bool>::type
1013 if (m_type
== KindOfResource
) {
1014 return m_data
.pres
->data()->instanceof
<T
>();
1019 template <typename T
>
1020 typename
std::enable_if
<std::is_base_of
<ObjectData
,T
>::value
, bool>::type
1022 if (m_type
== KindOfObject
) {
1023 return m_data
.pobj
->instanceof
<T
>();
1029 * Convert to a valid key or throw an exception. If convertStrKeys is true
1030 * int-like string keys will be converted to int keys.
1032 template <IntishCast IC
= IntishCast::None
>
1033 VarNR
toKey(const ArrayData
*) const;
1035 /* Creating a temporary Array, String, or Object with no ref-counting and
1036 * no type checking, use it only when we have checked the variant type and
1037 * we are sure the internal data will have a reference until the temporary
1038 * one gets out-of-scope.
1040 StrNR
toStrNR() const {
1041 return StrNR(getStringData());
1043 ArrNR
toArrNR() const {
1044 return ArrNR(getArrayData());
1046 ObjNR
toObjNR() const {
1047 return ObjNR(getObjectData());
1050 auto toFuncVal() const {
1051 return const_variant_ref
{*this}.toFuncVal();
1053 auto toClassVal() const {
1054 return const_variant_ref
{*this}.toClassVal();
1056 auto toLazyClassVal() const {
1057 return const_variant_ref
{*this}.toLazyClassVal();
1059 ClsMethDataRef
toClsMethVal() const {
1060 return const_variant_ref
{*this}.toClsMethVal();
1064 * Low level access that should be restricted to internal use.
1066 int64_t *getInt64Data() const {
1067 assertx(getType() == KindOfInt64
);
1068 return const_cast<int64_t*>(&m_data
.num
);
1070 double *getDoubleData() const {
1071 assertx(getType() == KindOfDouble
);
1072 return const_cast<double*>(&m_data
.dbl
);
1074 StringData
*getStringData() const {
1075 assertx(isStringType(getType()));
1078 StringData
*getStringDataOrNull() const {
1079 // This is a necessary evil because getStringData() returns
1080 // an undefined result if this is a null variant
1081 assertx(isNull() || isString());
1082 return isNullType(m_type
) ? nullptr : m_data
.pstr
;
1084 ArrayData
*getArrayData() const {
1088 ArrayData
*getArrayDataOrNull() const {
1089 // This is a necessary evil because getArrayData() returns
1090 // an undefined result if this is a null variant
1091 assertx(isNull() || isArray());
1092 return isNullType(m_type
) ? nullptr : m_data
.parr
;
1094 ObjectData
* getObjectData() const {
1095 assertx(is(KindOfObject
));
1098 ObjectData
*getObjectDataOrNull() const {
1099 // This is a necessary evil because getObjectData() returns
1100 // an undefined result if this is a null variant
1101 assertx(isNull() || is(KindOfObject
));
1102 return isNullType(m_type
) ? nullptr : m_data
.pobj
;
1105 int64_t getNumData() const { return m_data
.num
; }
1108 * Make any Variant strings and arrays not ref counted (e.g., static).
1109 * Use it, for example, if you need a long-lived Variant before the Memory
1110 * Manager has been initialized.
1111 * You will still get an assertion if the Variant is an object, resource, etc.
1113 void setEvalScalar();
1116 * Access this Variant as a TypedValue.
1118 const TypedValue
* asTypedValue() const { return this; }
1119 TypedValue
* asTypedValue() { return this; }
1122 * Read this Variant as an InitCell, without incrementing the
1123 * reference count. I.e. turn KindOfUninit into KindOfNull.
1125 TypedValue
asInitTVTmp() const {
1126 if (m_type
== KindOfUninit
) return make_tv
<KindOfNull
>();
1130 TypedValue
detach() noexcept
{
1131 auto tv
= *asTypedValue();
1132 m_type
= KindOfNull
;
1137 ResourceData
* getResourceData() const {
1138 assertx(is(KindOfResource
));
1139 return m_data
.pres
->data();
1142 ResourceData
* detachResourceData() {
1143 assertx(is(KindOfResource
));
1144 assertx(m_type
== KindOfResource
);
1145 m_type
= KindOfNull
;
1146 return m_data
.pres
->data();
1149 ObjectData
* detachObjectData() {
1150 assertx(is(KindOfObject
));
1151 assertx(m_type
== KindOfObject
);
1152 m_type
= KindOfNull
;
1156 template <typename T
>
1157 friend typename
std::enable_if
<
1158 std::is_base_of
<ResourceData
,T
>::value
,
1160 >::type
deref(const Variant
& v
) { return v
.getResourceData(); }
1162 template <typename T
>
1163 friend typename
std::enable_if
<
1164 std::is_base_of
<ObjectData
,T
>::value
,
1166 >::type
deref(const Variant
& v
) { return v
.getObjectData(); }
1168 template <typename T
>
1169 friend typename
std::enable_if
<
1170 std::is_base_of
<ResourceData
,T
>::value
,
1172 >::type
detach(Variant
&& v
) { return v
.detachResourceData(); }
1174 template <typename T
>
1175 friend typename
std::enable_if
<
1176 std::is_base_of
<ObjectData
,T
>::value
,
1178 >::type
detach(Variant
&& v
) { return v
.detachObjectData(); }
1180 explicit Variant(ResourceData
* v
) noexcept
{
1182 m_type
= KindOfResource
;
1183 m_data
.pres
= v
->hdr();
1186 m_type
= KindOfNull
;
1189 explicit Variant(ResourceHdr
* v
) noexcept
{
1191 m_type
= KindOfResource
;
1195 m_type
= KindOfNull
;
1200 * This set of constructors act like the normal constructors for the
1201 * given types except that they do not increment the reference count
1202 * of the passed value. They are used for the req::ptr move constructor.
1204 Variant(StringData
* var
, Attach
) noexcept
{
1206 m_type
= var
->isRefCounted() ? KindOfString
: KindOfPersistentString
;
1209 m_type
= KindOfNull
;
1212 Variant(ArrayData
* var
, Attach
) noexcept
{
1215 var
->isRefCounted() ? var
->toDataType() : var
->toPersistentDataType();
1218 m_type
= KindOfNull
;
1221 Variant(ObjectData
* var
, Attach
) noexcept
{
1223 m_type
= KindOfObject
;
1226 m_type
= KindOfNull
;
1229 Variant(ResourceData
* var
, Attach
) noexcept
{
1231 m_type
= KindOfResource
;
1232 m_data
.pres
= var
->hdr();
1234 m_type
= KindOfNull
;
1237 Variant(ResourceHdr
* var
, Attach
) noexcept
{
1239 m_type
= KindOfResource
;
1242 m_type
= KindOfNull
;
1245 Variant(TypedValue tv
, Attach
) noexcept
: TypedValue(tv
) {}
1246 Variant(TypedValue tv
, Wrap
) noexcept
: TypedValue(tv
) {
1247 tvIncRefGen(*asTypedValue());
1250 bool isPrimitive() const { return !isRefcountedType(m_type
); }
1251 bool isObjectConvertable() {
1252 return isNullType(m_type
) ||
1253 (m_type
== KindOfBoolean
&& !m_data
.num
) ||
1254 (isStringType(m_type
) && m_data
.pstr
->empty());
1257 void set(bool v
) noexcept
;
1258 void set(int v
) noexcept
;
1259 void set(int64_t v
) noexcept
;
1260 void set(double v
) noexcept
;
1261 void set(const char* v
) = delete;
1262 void set(const std::string
& v
) {
1263 return set(String(v
));
1265 void set(StringData
*v
) noexcept
;
1266 void set(ArrayData
*v
) noexcept
;
1267 void set(ObjectData
*v
) noexcept
;
1268 void set(ResourceHdr
*v
) noexcept
;
1269 void set(ResourceData
*v
) noexcept
{ set(v
->hdr()); }
1270 void set(const StringData
*v
) = delete;
1271 void set(const ArrayData
*v
) = delete;
1272 void set(const ObjectData
*v
) = delete;
1273 void set(const ResourceData
*v
) = delete;
1274 void set(const ResourceHdr
*v
) = delete;
1276 void set(const String
& v
) noexcept
{ set(v
.get()); }
1277 void set(const StaticString
& v
) noexcept
;
1278 void set(const Array
& v
) noexcept
{ set(v
.get()); }
1279 void set(const Object
& v
) noexcept
{ set(v
.get()); }
1280 void set(const Resource
& v
) noexcept
{ set(v
.hdr()); }
1282 void set(String
&& v
) noexcept
{ steal(v
.detach()); }
1283 void set(Array
&& v
) noexcept
{ steal(v
.detach()); }
1284 void set(Object
&& v
) noexcept
{ steal(v
.detach()); }
1285 void set(Resource
&& v
) noexcept
{ steal(v
.detachHdr()); }
1287 template<typename T
>
1288 void set(const req::ptr
<T
> &v
) noexcept
{
1289 return set(v
.get());
1292 template <typename T
>
1293 void set(req::ptr
<T
>&& v
) noexcept
{
1294 return steal(v
.detach());
1297 void steal(StringData
* v
) noexcept
;
1298 void steal(ArrayData
* v
) noexcept
;
1299 void steal(ObjectData
* v
) noexcept
;
1300 void steal(ResourceHdr
* v
) noexcept
;
1301 void steal(ResourceData
* v
) noexcept
{ steal(v
->hdr()); }
1304 bool toBooleanHelper() const;
1305 int64_t toInt64Helper(int base
= 10) const;
1306 Array
toPHPArrayHelper() const;
1307 Resource
toResourceHelper() const;
1309 DataType
convertToNumeric(int64_t *lval
, double *dval
) const;
1312 Variant
operator+(const Variant
& lhs
, const Variant
& rhs
) = delete;
1314 ///////////////////////////////////////////////////////////////////////////////
1317 * Definitions for some members of variant_ref et al. that use Variant.
1320 inline variant_ref::variant_ref(Variant
& v
)
1321 : variant_ref_detail::base
<false>{v
.asTypedValue()}
1324 inline const_variant_ref::const_variant_ref(const Variant
& v
)
1325 : variant_ref_detail::base
<true>{v
.asTypedValue()}
1328 inline variant_ref
& variant_ref::operator=(const Variant
& v
) noexcept
{
1332 inline variant_ref
& variant_ref::assign(const Variant
& v
) noexcept
{
1333 tvSet(tvToInit(*v
.asTypedValue()), m_val
);
1337 inline variant_ref
& variant_ref::operator=(Variant
&&rhs
) noexcept
{
1338 variant_ref lhs
= *this;
1340 Variant
goner((Variant::NoInit()));
1341 goner
.m_data
= val(lhs
.m_val
);
1342 goner
.m_type
= type(lhs
.m_val
);
1344 if (rhs
.m_type
== KindOfUninit
) {
1345 type(lhs
.m_val
) = KindOfNull
;
1347 type(lhs
.m_val
) = rhs
.m_type
;
1348 val(lhs
.m_val
) = rhs
.m_data
;
1349 rhs
.m_type
= KindOfNull
;
1355 ///////////////////////////////////////////////////////////////////////////////
1358 struct VarNR
: private TypedValue
{
1359 static VarNR
MakeKey(const String
& s
) {
1360 if (s
.empty()) return VarNR(staticEmptyString());
1364 // Use to hold variant that do not need ref-counting
1365 explicit VarNR(bool v
) { m_type
= KindOfBoolean
; m_data
.num
= (v
?1:0);}
1366 explicit VarNR(int v
) { m_type
= KindOfInt64
; m_data
.num
= v
;}
1367 // The following two overloads will accept int64_t whether it's
1368 // implemented as long or long long.
1369 explicit VarNR(long v
) { m_type
= KindOfInt64
; m_data
.num
= v
;}
1370 explicit VarNR(long long v
) { m_type
= KindOfInt64
; m_data
.num
= v
;}
1371 explicit VarNR(uint64_t v
) { m_type
= KindOfInt64
; m_data
.num
= v
;}
1372 explicit VarNR(double v
) { m_type
= KindOfDouble
; m_data
.dbl
= v
;}
1374 explicit VarNR(const StaticString
&v
) {
1375 assertx(v
.get() && !v
.get()->isRefCounted());
1376 m_type
= KindOfPersistentString
;
1377 m_data
.pstr
= v
.get();
1380 explicit VarNR(const String
& v
) : VarNR(v
.get()) {}
1381 explicit VarNR(const Array
& v
) : VarNR(v
.get()) {}
1382 explicit VarNR(const Object
& v
) : VarNR(v
.get()) {}
1384 explicit VarNR(StringData
*v
);
1385 explicit VarNR(const StringData
*v
) {
1386 assertx(v
&& !v
->isRefCounted());
1387 m_type
= KindOfPersistentString
;
1388 m_data
.pstr
= const_cast<StringData
*>(v
);
1390 explicit VarNR(ArrayData
*v
);
1391 explicit VarNR(const ArrayData
*) = delete;
1392 explicit VarNR(ObjectData
*v
);
1393 explicit VarNR(const ObjectData
*) = delete;
1395 explicit VarNR(TypedValue tv
) { m_type
= tv
.m_type
; m_data
= tv
.m_data
; }
1396 explicit VarNR(const Variant
& v
) : VarNR
{*v
.asTypedValue()} {}
1398 VarNR(const VarNR
&) = delete;
1399 VarNR
& operator=(const VarNR
&) = delete;
1401 explicit VarNR() { asVariant()->asTypedValue()->m_type
= KindOfUninit
; }
1406 memset(this, kTVTrashFill2
, sizeof(*this));
1410 TypedValue
tv() const { return *this; }
1412 operator const Variant
&() const { return *asVariant(); }
1414 bool isNull() const {
1415 return asVariant()->isNull();
1418 /* implicit */ VarNR(const char* v
) = delete;
1419 /* implicit */ VarNR(const std::string
& v
) = delete;
1421 const Variant
*asVariant() const {
1422 return &tvAsCVarRef(static_cast<const TypedValue
*>(this));
1424 Variant
* asVariant() {
1425 return &tvAsVariant(static_cast<TypedValue
*>(this));
1427 void checkRefCount() {
1428 assertx(isRealType(m_type
));
1434 assertx(m_data
.pstr
->checkCount());
1439 assertx(m_data
.parr
->checkCount());
1442 assertx(checkCountClsMeth(m_data
.pclsmeth
));
1444 case KindOfRClsMeth
:
1445 assertx(m_data
.prclsmeth
->checkCount());
1447 assertx(m_data
.pobj
->checkCount());
1449 case KindOfResource
:
1450 assertx(m_data
.pres
->checkCount());
1453 assertx(m_data
.prec
->checkCount());
1456 assertx(m_data
.prfunc
->checkCount());
1463 //////////////////////////////////////////////////////////////////////
1466 * The lvalBlackHole is used in array operations when a NewElem can't
1467 * create a new slot. (Basically if the next integer key in an array
1468 * is already at the maximum integer.)
1470 Variant
& lvalBlackHole();
1473 * The lvalBlackHole has request lifetime.
1475 void initBlackHole();
1476 void clearBlackHole();
1478 ///////////////////////////////////////////////////////////////////////////////
1479 // breaking circular dependencies
1481 inline Variant
Array::operator[](TypedValue key
) const {
1482 return Variant::wrap(lookup(key
));
1484 inline Variant
Array::operator[](int key
) const {
1485 return Variant::wrap(lookup(key
));
1487 inline Variant
Array::operator[](int64_t key
) const {
1488 return Variant::wrap(lookup(key
));
1490 inline Variant
Array::operator[](const String
& key
) const {
1491 return Variant::wrap(lookup(key
));
1493 inline Variant
Array::operator[](const Variant
& key
) const {
1494 return Variant::wrap(lookup(key
));
1497 inline void Array::append(const Variant
& v
) {
1498 append(*v
.asTypedValue());
1501 ALWAYS_INLINE Variant
uninit_null() {
1505 ALWAYS_INLINE Variant
init_null() {
1506 return Variant(Variant::NullInit());
1509 inline void concat_assign(Variant
&v1
, const char* s2
) = delete;
1511 inline void concat_assign(tv_lval lhs
, const String
& s2
) {
1512 if (!isStringType(type(lhs
))) tvCastToStringInPlace(lhs
);
1513 asStrRef(lhs
) += s2
;
1516 //////////////////////////////////////////////////////////////////////
1518 inline Array
& forceToArray(Variant
& var
) {
1519 if (!var
.isArray()) var
= Variant(Array::CreateDict());
1520 return var
.asArrRef();
1523 inline Array
& forceToArray(tv_lval lval
) {
1524 if (!isArrayLikeType(lval
.type())) {
1525 tvMove(make_array_like_tv(ArrayData::CreateDict()), lval
);
1527 return asArrRef(lval
);
1530 inline Array
& forceToDict(Variant
& var
) {
1531 if (!var
.isDict()) var
= Variant(Array::CreateDict());
1532 return var
.asArrRef();
1535 inline Array
& forceToDict(tv_lval lval
) {
1536 if (!isDictType(lval
.type())) {
1537 tvSet(make_tv
<KindOfDict
>(ArrayData::CreateDict()), lval
);
1539 return asArrRef(lval
);
1542 //////////////////////////////////////////////////////////////////////
1544 ALWAYS_INLINE Variant
empty_string_variant() {
1545 return Variant(staticEmptyString(), Variant::PersistentStrInit
{});
1548 template <typename T
>
1549 inline Variant
toVariant(const req::ptr
<T
>& p
) {
1550 return p
? Variant(p
) : Variant(false);
1553 template <typename T
>
1554 inline Variant
toVariant(req::ptr
<T
>&& p
) {
1555 return p
? Variant(std::move(p
)) : Variant(false);
1559 inline bool ptr_is_null(const Variant
& v
) {
1563 template <typename T
>
1564 inline bool isa_non_null(const Variant
& v
) {
1568 // Defined here to avoid introducing a dependency cycle between type-variant
1570 template <IntishCast IC
>
1571 ALWAYS_INLINE TypedValue
Array::convertKey(TypedValue k
) const {
1572 return tvToKey
<IC
>(k
, m_arr
? m_arr
.get() : ArrayData::CreateDict());
1574 template <IntishCast IC
>
1575 ALWAYS_INLINE TypedValue
Array::convertKey(const Variant
& k
) const {
1576 return convertKey
<IC
>(*k
.asTypedValue());
1579 template <IntishCast IC
>
1580 inline VarNR
Variant::toKey(const ArrayData
* ad
) const {
1581 return VarNR(tvToKey
<IC
>(*this, ad
));
1584 struct alignas(16) OptionalVariant
{
1586 m_tv
.m_type
= kInvalidDataType
;
1588 ~OptionalVariant() {
1591 OptionalVariant(const OptionalVariant
& other
) {
1592 if (other
.hasValue()) {
1593 construct(other
.value());
1596 m_tv
.m_type
= kInvalidDataType
;
1598 OptionalVariant(OptionalVariant
&& other
) noexcept
{
1599 if (other
.hasValue()) {
1600 construct(std::move(other
.value()));
1601 other
.m_tv
.m_type
= kInvalidDataType
;
1604 m_tv
.m_type
= kInvalidDataType
;
1607 template<typename Arg
>
1608 OptionalVariant
& operator=(Arg
&& arg
) {
1609 assign(std::forward
<Arg
>(arg
));
1612 OptionalVariant
& operator=(const OptionalVariant
& arg
) {
1616 OptionalVariant
& operator=(OptionalVariant
&& arg
) {
1617 assign(std::move(arg
));
1621 bool hasValue() const {
1622 return m_tv
.m_type
!= kInvalidDataType
;
1624 bool has_value() const {
1628 assertx(hasValue());
1629 return tvAsVariant(&m_tv
);
1631 const Variant
& value() const {
1632 assertx(hasValue());
1633 return tvAsCVarRef(&m_tv
);
1637 auto const old
= m_tv
;
1638 m_tv
.m_type
= kInvalidDataType
;
1642 template <class... Args
>
1643 Variant
& emplace(Args
&&... args
) {
1645 construct(std::forward
<Args
>(args
)...);
1648 void assign(const Variant
& other
) {
1655 void assign(Variant
&& other
) {
1657 value() = std::move(other
);
1660 construct(std::move(other
));
1662 void assign(const OptionalVariant
& other
) {
1663 if (other
.hasValue()) return assign(other
.value());
1666 void assign(OptionalVariant
&& other
) {
1667 if (other
.hasValue()) {
1668 assign(std::move(other
.value()));
1669 other
.m_tv
.m_type
= kInvalidDataType
;
1675 operator bool() const { return hasValue(); }
1677 const Variant
* operator->() const { return &value(); }
1678 Variant
* operator->() { return &value(); }
1680 const Variant
& operator*() const & { return value(); }
1681 Variant
& operator*() & { return value(); }
1682 Variant
&& operator*() && { return std::move(value()); }
1684 template<typename
... Args
>
1685 void construct(Args
&&... args
) {
1686 new (&m_tv
) Variant(std::forward
<Args
>(args
)...);
1691 //////////////////////////////////////////////////////////////////////