blob: 207fb1999906d29b10860d85f4767ca705abbe16 [file]
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_OBJECTS_TAGGED_H_
#define V8_OBJECTS_TAGGED_H_
#include <type_traits>
#include "include/v8config.h"
#include "src/common/globals.h"
#include "src/objects/tagged-impl.h"
#include "src/objects/union.h"
namespace v8 {
namespace internal {
class BigInt;
class FieldType;
class HeapObject;
class HeapNumber;
class TrustedObject;
class ExposedTrustedObject;
class Object;
class TaggedIndex;
class Smi;
// Tagged<T> represents an uncompressed V8 tagged pointer.
//
// The tagged pointer is a pointer-sized value with a tag in the LSB. The value
// is either:
//
// * A small integer (Smi), shifted right, with the tag set to 0
// * A strong pointer to an object on the V8 heap, with the tag set to 01
// * A weak pointer to an object on the V8 heap, with the tag set to 11
// * A cleared weak pointer, with the value 11
//
// The exact encoding differs depending on 32- vs 64-bit architectures, and in
// the latter case, whether or not pointer compression is enabled.
//
// On 32-bit architectures, this is:
// |----- 32 bits -----|
// Pointer: |______address____w1|
// Smi: |____int31_value___0|
//
// On 64-bit architectures with pointer compression:
// |----- 32 bits -----|----- 32 bits -----|
// Pointer: |________base_______|______offset_____w1|
// Smi: |......garbage......|____int31_value___0|
//
// On 64-bit architectures without pointer compression:
// |----- 32 bits -----|----- 32 bits -----|
// Pointer: |________________address______________w1|
// Smi: |____int32_value____|00...............00|
//
// where `w` is the "weak" bit.
//
// We specialise Tagged separately for Object, Smi and HeapObject, and then all
// other types T, so that:
//
// Tagged<Object> -> StrongTaggedBase
// Tagged<Smi> -> StrongTaggedBase
// Tagged<T> -> Tagged<HeapObject> -> StrongTaggedBase
//
// We also specialize it separately for Weak types, with a parallel
// hierarchy:
//
// Tagged<Weak<Object>> -> WeakTaggedBase
// Tagged<Weak<Smi>> -> WeakTaggedBase
// Tagged<Weak<T>> -> Tagged<Weak<HeapObject>> -> WeakTaggedBase
// Tagged<Weak<T>> -> Tagged<Weak<HeapObject>> -> WeakTaggedBase
template <typename T>
class Tagged;
// Weak<T> represents a reference to T that is weak.
//
// Weak doesn't really exist by itself, but is rather a sentinel type for
// templates on tagged interfaces (like Tagged). For example, where Tagged<T>
// represents a strong reference to T, Tagged<Weak<T>> represents a
// potentially weak reference to T, and it is the responsibility of the Tagged
// interface to provide some mechanism (likely template specialization) to
// distinguish between the two and provide accessors to the T reference itself
// (which will always be strong).
template <typename T>
class Weak {
public:
// Smis can't be weak.
static_assert(!std::is_same_v<T, Smi>);
// Generic Objects can't be weak, use a Union with a Smi instead.
static_assert(!std::is_same_v<T, Object>);
// "Weak" should be inside unions, not outside of them.
static_assert(!is_union_v<T>);
using strong_type = T;
};
template <typename T>
struct is_weak : public std::false_type {};
template <typename T>
struct is_weak<Weak<T>> : public std::true_type {};
template <typename... T>
struct is_weak<Union<T...>> : public std::conjunction<is_weak<T>...> {};
template <typename T>
static constexpr bool is_weak_v = is_weak<T>::value;
template <typename T>
struct is_maybe_weak : public std::false_type {};
template <typename T>
struct is_maybe_weak<Weak<T>> : public std::true_type {};
template <typename... T>
struct is_maybe_weak<Union<T...>>
: public std::disjunction<is_maybe_weak<T>...> {};
template <typename T>
static constexpr bool is_maybe_weak_v = is_maybe_weak<T>::value;
namespace detail {
template <typename T>
struct weak_of_helper {
using type = Weak<T>;
};
template <>
struct weak_of_helper<Smi> {
using type = Smi;
};
template <>
struct weak_of_helper<Object> {
using type = UnionOf<Smi, HeapObject, Weak<HeapObject>>;
};
template <typename T>
struct weak_of_helper<Weak<T>> {
using type = Weak<T>;
};
template <typename... Ts>
struct weak_of_helper<Union<Ts...>> {
using type = UnionOf<typename weak_of_helper<Ts>::type...>;
};
} // namespace detail
template <typename T>
using WeakOf = typename detail::weak_of_helper<T>::type;
namespace detail {
template <typename T>
struct strong_of_helper {
using type = T;
};
template <>
struct strong_of_helper<Smi> {
using type = Smi;
};
template <typename T>
struct strong_of_helper<Weak<T>> {
using type = T;
};
template <>
struct strong_of_helper<MaybeObject> {
using type = Object;
};
template <typename... Ts>
struct strong_of_helper<Union<Ts...>> {
using type = UnionOf<typename strong_of_helper<Ts>::type...>;
};
} // namespace detail
template <typename T>
using StrongOf = typename detail::strong_of_helper<T>::type;
// ClearedWeakValue is a sentinel type for cleared weak values.
class ClearedWeakValue {};
// Convert a reference to T into a definitely weak reference to T.
template <typename T>
inline Tagged<WeakOf<T>> MakeWeak(Tagged<T> value);
// Convert a Smi or reference to T into a Smi or weak reference to T.
template <typename T>
inline Tagged<WeakOf<T>> MakeWeakOrSmi(Tagged<T> value);
// Convert a (maybe) weak reference to T into a strong reference to T.
template <typename T>
inline Tagged<StrongOf<T>> MakeStrong(Tagged<T> value);
// Base class for all Tagged<T> classes.
using StrongTaggedBase = TaggedImpl<HeapObjectReferenceType::STRONG, Address>;
using WeakTaggedBase = TaggedImpl<HeapObjectReferenceType::WEAK, Address>;
namespace detail {
template <typename Derived, typename Base>
consteval bool is_subtype_helper();
}
// `is_subtype<Derived, Base>::value` is true when Derived is a subtype of Base
// according to our object hierarchy. In particular, Smi is considered a
// subtype of Object.
template <typename Derived, typename Base>
concept SubtypeOf = detail::is_subtype_helper<Derived, Base>();
template <typename Derived, typename Base>
static constexpr bool is_subtype_v = detail::is_subtype_helper<Derived, Base>();
template <typename Derived, typename Base>
struct is_subtype : public std::integral_constant<
bool, detail::is_subtype_helper<Derived, Base>()> {};
namespace detail {
template <typename From, typename To>
concept is_pointer_convertible = requires(From* from, To* to) { to = from; };
template <typename T>
concept is_complete = requires { sizeof(T); };
template <typename T>
struct normalize_type {
using type = T;
};
template <>
struct normalize_type<Object> {
using type = Union<Smi, HeapObject>;
};
template <>
struct normalize_type<FieldType> {
using type = Union<Smi, Map>;
};
template <typename DerivedUnion, typename Base>
struct are_all_subtype_of;
template <typename... Ts, typename Base>
struct are_all_subtype_of<Union<Ts...>, Base> {
static constexpr bool value = (is_subtype_helper<Ts, Base>() && ...);
};
template <typename Derived, typename BaseUnion>
struct is_subtype_of_any;
template <typename Derived, typename... Ts>
struct is_subtype_of_any<Derived, Union<Ts...>> {
static constexpr bool value = v8::base::has_type_v<Derived, Ts...> ||
(is_subtype_helper<Derived, Ts>() || ...);
};
template <typename D, typename B>
consteval bool is_subtype_helper() {
using std::is_base_of_v;
using std::is_same_v;
// Bailout for identical types before normalization.
if constexpr (is_same_v<D, B>) return true;
// Normalize Union-like types and layout types to a canonical representation.
using Derived = typename normalize_type<D>::type;
using Base = typename normalize_type<B>::type;
if constexpr (is_same_v<Derived, Base>) {
// Exact Identity Match
return true;
} else if constexpr (is_same_v<Derived, TaggedIndex> &&
(is_same_v<Base,
typename normalize_type<Object>::type> ||
is_same_v<Base, typename normalize_type<
MaybeObject>::type>)) {
// Extra special handling of TaggedIndex as a subclass of (normalized)
// Object and MaybeObject. Has to be checked before the unions are checked.
// TODO(leszeks): Make this either part of those unions, or fix the uses
// which try to cast this.
return true;
} else if constexpr (is_union_v<Derived>) {
// If Derived is a union, ALL of its members must be a subtype of Base.
return are_all_subtype_of<Derived, Base>::value;
} else if constexpr (is_union_v<Base>) {
// If Base is a union, Derived must be a subtype of AT LEAST ONE member.
return is_subtype_of_any<Derived, Base>::value;
} else if constexpr (is_same_v<Derived, Smi> || is_same_v<Base, Smi> ||
is_same_v<Derived, TaggedIndex> ||
is_same_v<Base, TaggedIndex>) {
// Smi and TaggedIndex must match on exact identity or union.
return false;
} else if constexpr (is_weak_v<Base>) {
// Base = Weak<T>
if constexpr (is_same_v<Derived, ClearedWeakValue>) {
// ClearedValue is always a subtype of Weak<T>
return true;
} else if constexpr (is_weak_v<Derived>) {
// Weak<T> < Weak<U> <=> T < U
return is_subtype_helper<typename Derived::strong_type,
typename Base::strong_type>();
} else {
// non-weak < Weak<U> is always false.
return false;
}
} else if constexpr (is_weak_v<Derived>) {
// Weak<T> cannot be a subtype of a non-weak Base (unless
// matched exactly earlier by a union)
return false;
} else {
// Fallback to base_of.
return is_base_of_v<Base, Derived>;
}
}
} // namespace detail
static_assert(is_subtype_v<Smi, Object>);
static_assert(is_subtype_v<HeapObject, Object>);
static_assert(is_subtype_v<HeapObject, HeapObject>);
static_assert(is_subtype_v<Smi, MaybeObject>);
static_assert(
is_subtype_v<Union<HeapObject, Weak<HeapObject>, Smi>, MaybeObject>);
static_assert(!is_subtype_v<WeakOf<Object>, Object>);
static_assert(is_subtype_v<
Object, Union<Smi, HeapObject, Weak<HeapObject>, TaggedIndex>>);
static_assert(is_subtype_v<Object, MaybeObject>);
static_assert(is_subtype_v<TaggedIndex, Object>);
static_assert(is_subtype_v<Union<HeapObject, TaggedIndex>, Object>);
// `is_taggable<T>::value` is true when T is a valid type for Tagged. This means
// being a subtype of Smi, TaggedIndex, or a weak/strong HeapObject. Include
// Object in the list because it's special.
template <typename T>
using is_taggable =
is_subtype<T,
Union<Smi, TaggedIndex, Object, HeapObject, Weak<HeapObject>>>;
template <typename T>
static constexpr bool is_taggable_v = is_taggable<T>::value;
// `is_castable<From, To>::value` is true when you can use `::cast` to cast from
// From to To. This means an upcast or downcast, which in practice means
// checking `is_subtype` symmetrically.
template <typename From, typename To>
using is_castable =
std::disjunction<is_subtype<To, From>, is_subtype<From, To>>;
template <typename From, typename To>
static constexpr bool is_castable_v = is_castable<From, To>::value;
// TODO(leszeks): Remove this once there are no more conversions between
// Tagged<Foo> and Foo.
static constexpr bool kTaggedCanConvertToRawObjects = true;
namespace detail {
template <typename T>
struct BaseForTagged {
using type = Tagged<HeapObject>;
};
template <typename T>
struct BaseForTagged<Weak<T>> {
using type = Tagged<Weak<HeapObject>>;
};
template <typename... T>
struct BaseForTagged<Union<T...>> {
template <typename U>
using is_non_heap_object =
std::disjunction<std::is_same<U, Smi>, std::is_same<U, Object>,
std::is_same<U, TaggedIndex>,
std::is_same<U, FieldType>>;
using type = std::conditional_t<
std::disjunction_v<is_maybe_weak<T>...>, WeakTaggedBase,
std::conditional_t<std::disjunction_v<is_non_heap_object<T>...>,
Tagged<Object>, Tagged<HeapObject>>>;
};
// FieldType is special, since it can be Smi or Map. It could probably even be
// its own specialization, to avoid exposing an operator->.
template <>
struct BaseForTagged<FieldType> {
using type = Tagged<Object>;
};
} // namespace detail
// Specialization for Object, where it's unknown whether this is a Smi or a
// HeapObject.
template <>
class V8_GSL_POINTER Tagged<Object> : public StrongTaggedBase {
public:
// Allow Tagged<Object> to be created from any address.
V8_INLINE constexpr explicit Tagged(Address o) : StrongTaggedBase(o) {}
// Allow explicit uninitialized initialization.
// TODO(leszeks): Consider zapping this instead, since it's odd that
// Tagged<Object> implicitly initialises to Smi::zero().
V8_INLINE constexpr Tagged() : StrongTaggedBase(kNullAddress) {}
// Allow implicit conversion from const HeapObject* to Tagged<Object>.
// TODO(leszeks): Make this more const-correct.
// TODO(leszeks): Consider making this an explicit conversion.
// NOLINTNEXTLINE
V8_INLINE Tagged(const HeapObject* ptr)
: Tagged(reinterpret_cast<Address>(ptr) + kHeapObjectTag) {}
// Implicit conversion for subclasses -- all classes are subclasses of Object,
// so allow all tagged pointers.
// NOLINTNEXTLINE
V8_INLINE constexpr Tagged(StrongTaggedBase other)
: StrongTaggedBase(other.ptr()) {}
V8_INLINE constexpr Tagged& operator=(StrongTaggedBase other) {
return *this = Tagged(other);
}
};
// Specialization for Smi disallowing any implicit creation or access via ->,
// but offering instead a cast from Object and an int32_t value() method.
template <>
class V8_GSL_POINTER Tagged<Smi> : public StrongTaggedBase {
public:
V8_INLINE constexpr Tagged() = default;
V8_INLINE constexpr explicit Tagged(Address ptr) : StrongTaggedBase(ptr) {}
// No implicit conversions from other tagged pointers.
V8_INLINE constexpr bool IsHeapObject() const { return false; }
V8_INLINE constexpr bool IsSmi() const { return true; }
V8_INLINE constexpr int32_t value() const {
return Internals::SmiValue(ptr());
}
};
// Specialization for TaggedIndex disallowing any implicit creation or access
// via ->, but offering instead a cast from Object and an intptr_t value()
// method.
template <>
class V8_GSL_POINTER Tagged<TaggedIndex> : public StrongTaggedBase {
public:
V8_INLINE constexpr Tagged() = default;
V8_INLINE constexpr explicit Tagged(Address ptr) : StrongTaggedBase(ptr) {}
// No implicit conversions from other tagged pointers.
V8_INLINE constexpr bool IsHeapObject() const { return false; }
V8_INLINE constexpr bool IsSmi() const { return true; }
// Returns the integer value.
V8_INLINE constexpr intptr_t value() const {
// Truncate and shift down (requires >> to be sign extending).
return static_cast<intptr_t>(ptr()) >> kSmiTagSize;
}
// Implicit conversions to/from raw pointers
// TODO(leszeks): Remove once we're using Tagged everywhere.
// NOLINTNEXTLINE
V8_INLINE constexpr Tagged(TaggedIndex raw);
private:
// Handles of the same type are allowed to access the Address constructor.
friend class Handle<TaggedIndex>;
#ifdef V8_ENABLE_DIRECT_HANDLE
friend class DirectHandle<TaggedIndex>;
#endif
template <typename TFieldType, int kFieldOffset, typename CompressionScheme>
friend class TaggedField;
};
// Specialization for HeapObject, to group together functions shared between all
// HeapObjects
template <>
class V8_GSL_POINTER Tagged<HeapObject> : public StrongTaggedBase {
using Base = StrongTaggedBase;
public:
V8_INLINE constexpr Tagged() = default;
// Allow implicit conversion from const HeapObject* to
// Tagged<HeapObject>.
// TODO(leszeks): Make this more const-correct.
// TODO(leszeks): Consider making this an explicit conversion.
// NOLINTNEXTLINE
V8_INLINE Tagged(const HeapObject* ptr)
: Tagged(reinterpret_cast<Address>(ptr) + kHeapObjectTag) {}
// Implicit conversion for subclasses.
template <typename U>
V8_INLINE constexpr Tagged& operator=(Tagged<U> other)
requires(!std::is_base_of_v<Tagged<HeapObject>, Tagged<U>> &&
is_subtype_v<U, HeapObject>)
{
return *this = Tagged(other);
}
// Implicit conversion for subclasses.
template <typename U>
// NOLINTNEXTLINE
V8_INLINE constexpr Tagged(Tagged<U> other)
requires(!std::is_base_of_v<Tagged<HeapObject>, Tagged<U>> &&
is_subtype_v<U, HeapObject>)
: Base(other) {}
V8_INLINE HeapObject& operator*() const;
V8_INLINE HeapObject* operator->() const;
V8_INLINE constexpr bool is_null() const {
return static_cast<Tagged_t>(this->ptr()) ==
static_cast<Tagged_t>(kNullAddress);
}
constexpr V8_INLINE bool IsHeapObject() const {
DCHECK_IMPLIES(!is_null(), TaggedImpl::IsHeapObject());
return !is_null();
}
constexpr V8_INLINE bool IsSmi() const {
// Null values overlap with Smi zero, so return "true" here for now.
// TODO(leszeks): Consider either making callers check for nullness instead
// of Sminess, or even introducing a "nullable" concept.
DCHECK_IMPLIES(!is_null(), !TaggedImpl::IsSmi());
return is_null();
}
Address address() const { return this->ptr() - kHeapObjectTag; }
protected:
V8_INLINE constexpr explicit Tagged(Address ptr) : Base(ptr) {}
private:
friend class HeapObject;
// Handles of the same type are allowed to access the Address constructor.
friend class Handle<HeapObject>;
#ifdef V8_ENABLE_DIRECT_HANDLE
friend class DirectHandle<HeapObject>;
#endif
template <typename TFieldType, int kFieldOffset, typename CompressionScheme>
friend class TaggedField;
template <typename TFieldType, typename CompressionScheme>
friend class TaggedMember;
template <typename To, typename From>
friend inline Tagged<To> UncheckedCast(Tagged<From> value);
template <typename U>
friend Tagged<StrongOf<U>> MakeStrong(Tagged<U> value);
V8_INLINE HeapObject* ToRawPtr() const;
};
static_assert(Tagged<HeapObject>().is_null());
// Specialization for Weak<HeapObject>, to group together functions shared
// between all HeapObjects
template <>
class V8_GSL_POINTER Tagged<Weak<HeapObject>> : public WeakTaggedBase {
using Base = WeakTaggedBase;
public:
V8_INLINE constexpr Tagged() = default;
// Allow implicit conversion from const HeapObject* to
// Tagged<HeapObject>.
// TODO(leszeks): Make this more const-correct.
// TODO(leszeks): Consider making this an explicit conversion.
// NOLINTNEXTLINE
V8_INLINE Tagged(const HeapObject* ptr)
: Tagged(reinterpret_cast<Address>(ptr) + kHeapObjectTag) {}
// Implicit conversion for subclasses.
template <typename U>
V8_INLINE constexpr Tagged& operator=(Tagged<U> other)
requires(is_subtype_v<U, Weak<HeapObject>>)
{
return *this = Tagged(other);
}
// Implicit conversion for subclasses.
template <typename U>
// NOLINTNEXTLINE
V8_INLINE constexpr Tagged(Tagged<U> other)
requires(is_subtype_v<U, Weak<HeapObject>>)
: Base(other.ptr()) {}
V8_INLINE constexpr bool is_null() const {
return static_cast<Tagged_t>(this->ptr()) ==
static_cast<Tagged_t>(kNullAddress);
}
constexpr V8_INLINE bool IsSmi() const { return false; }
protected:
V8_INLINE constexpr explicit Tagged(Address ptr) : Base(ptr) {}
private:
// Handles of the same type are allowed to access the Address constructor.
friend class Handle<Weak<HeapObject>>;
#ifdef V8_ENABLE_DIRECT_HANDLE
friend class DirectHandle<Weak<HeapObject>>;
#endif
template <typename TFieldType, int kFieldOffset, typename CompressionScheme>
friend class TaggedField;
template <typename TFieldType, typename CompressionScheme>
friend class TaggedMember;
template <typename To, typename From>
friend inline Tagged<To> UncheckedCast(Tagged<From> value);
template <typename U>
friend Tagged<WeakOf<U>> MakeWeak(Tagged<U> value);
template <typename U>
friend Tagged<WeakOf<U>> MakeWeakOrSmi(Tagged<U> value);
};
// Generic Tagged<T> for Unions. This doesn't allow direct access to the object,
// aside from casting.
template <typename... Ts>
class V8_GSL_POINTER Tagged<Union<Ts...>>
: public detail::BaseForTagged<Union<Ts...>>::type {
using This = Union<Ts...>;
using Base = typename detail::BaseForTagged<This>::type;
public:
V8_INLINE constexpr Tagged() = default;
// Implicit conversion for subclasses.
template <typename U>
V8_INLINE constexpr Tagged& operator=(Tagged<U> other)
requires(is_subtype_v<U, This>)
{
*this = Tagged(other);
return *this;
}
// Implicit conversion for subclasses.
template <typename U>
// NOLINTNEXTLINE
V8_INLINE constexpr Tagged(Tagged<U> other)
requires(is_subtype_v<U, This>)
: Base(other.ptr()) {}
// Implicit conversions and explicit casts to/from raw pointers
// TODO(leszeks): Remove once we're using Tagged everywhere.
template <typename U>
// NOLINTNEXTLINE
V8_INLINE constexpr Tagged(U raw)
requires(is_subtype_v<U, This> && std::is_base_of_v<HeapObject, U>)
: Base(raw.ptr()) {
static_assert(kTaggedCanConvertToRawObjects);
}
template <typename U>
requires(is_maybe_weak_v<This> && is_subtype_v<U, This>)
V8_INLINE explicit constexpr Tagged(Tagged<U> other,
HeapObjectReferenceType type)
: Base(type == HeapObjectReferenceType::WEAK ? MakeWeak(other).ptr()
: MakeStrong(other).ptr()) {}
// For the very specific MaybeObject case, allow the public constructor.
V8_INLINE constexpr explicit Tagged(Address ptr)
requires std::is_same_v<This, MaybeObject>
: Base(ptr) {}
private:
// Handles of the same type are allowed to access the Address constructor.
friend class Handle<This>;
#ifdef V8_ENABLE_DIRECT_HANDLE
friend class DirectHandle<This>;
#endif
template <typename TFieldType, int kFieldOffset, typename CompressionScheme>
friend class TaggedField;
template <typename TFieldType, typename CompressionScheme>
friend class TaggedMember;
friend class CompressedHeapObjectSlot;
friend class CompressedMaybeObjectSlot;
friend class FullMaybeObjectSlot;
friend class FullHeapObjectSlot;
template <typename THeapObjectSlot>
friend void UpdateHeapObjectReferenceSlot(THeapObjectSlot slot,
Tagged<HeapObject> value);
friend V8_EXPORT_PRIVATE Address CheckObjectType(Address raw_value,
Address raw_type,
Address raw_location);
template <typename U>
friend Tagged<WeakOf<U>> MakeWeak(Tagged<U> value);
template <typename U>
friend Tagged<WeakOf<U>> MakeWeakOrSmi(Tagged<U> value);
template <typename U>
friend Tagged<StrongOf<U>> MakeStrong(Tagged<U> value);
template <typename To, typename From>
friend inline Tagged<To> UncheckedCast(Tagged<From> value);
V8_INLINE constexpr explicit Tagged(Address ptr) : Base(ptr) {}
};
namespace detail {
template <typename From, typename To>
concept is_tagged_convertible =
is_pointer_convertible<From, To> ||
(is_complete<From> && is_complete<To> && SubtypeOf<From, To>);
} // namespace detail
// Generic Tagged<T> for any T that is a subclass of HeapObject. There are
// separate Tagged<T> specialaizations for T==Smi and T==Object, so we know that
// all other Tagged<T> are definitely pointers and not Smis.
template <typename T>
class V8_GSL_POINTER Tagged : public detail::BaseForTagged<T>::type {
using Base = typename detail::BaseForTagged<T>::type;
public:
V8_INLINE constexpr Tagged() = default;
// Allow implicit conversion from const T* to Tagged<T>.
// TODO(leszeks): Make this more const-correct.
// TODO(leszeks): Consider making this an explicit conversion.
// NOLINTNEXTLINE
V8_INLINE Tagged(const T* ptr)
: Base(reinterpret_cast<Address>(ptr) + kHeapObjectTag) {}
// Implicit conversion for subclasses.
template <typename U>
V8_INLINE constexpr Tagged& operator=(Tagged<U> other)
requires(detail::is_tagged_convertible<U, T>)
{
*this = Tagged(other);
return *this;
}
// Implicit conversion for subclasses.
template <typename U>
// NOLINTNEXTLINE
V8_INLINE constexpr Tagged(Tagged<U> other)
requires(detail::is_tagged_convertible<U, T>)
: Base(other) {}
V8_INLINE T& operator*() const { return *ToRawPtr(); }
V8_INLINE T* operator->() const { return ToRawPtr(); }
private:
friend T;
// Handles of the same type are allowed to access the Address constructor.
friend class Handle<T>;
#ifdef V8_ENABLE_DIRECT_HANDLE
friend class DirectHandle<T>;
#endif
template <typename TFieldType, int kFieldOffset, typename CompressionScheme>
friend class TaggedField;
template <typename TFieldType, typename CompressionScheme>
friend class TaggedMember;
template <typename To, typename From>
friend inline Tagged<To> UncheckedCast(Tagged<From> value);
template <typename U>
friend Tagged<StrongOf<U>> MakeStrong(Tagged<U> value);
V8_INLINE constexpr explicit Tagged(Address ptr) : Base(ptr) {}
V8_INLINE T* ToRawPtr() const {
// Check whether T is taggable on raw ptr access rather than top-level, to
// allow forward declarations.
static_assert(is_taggable_v<T>);
return reinterpret_cast<T*>(this->ptr() - kHeapObjectTag);
}
};
// Specialized Tagged<T> for cleared weak values. This is only used, in
// practice, for conversions from Tagged<ClearedWeakValue> to a
// Tagged<MaybeWeak<T>>, where subtyping rules mean that this works for
// aribitrary T.
template <>
class V8_GSL_POINTER Tagged<ClearedWeakValue> : public WeakTaggedBase {
public:
V8_INLINE constexpr explicit Tagged(Address ptr) : WeakTaggedBase(ptr) {}
};
// Specialized Tagged<T> for weak references to T, which are known to be
// subclasses of HeapObject (Smis can't be weak).
template <typename T>
class V8_GSL_POINTER Tagged<Weak<T>>
: public detail::BaseForTagged<Weak<T>>::type {
using Base = typename detail::BaseForTagged<Weak<T>>::type;
public:
V8_INLINE constexpr Tagged() = default;
// Allow implicit conversion from const T* to Tagged<Weak<T>>.
// TODO(leszeks): Make this more const-correct.
// TODO(leszeks): Consider making this an explicit conversion.
// NOLINTNEXTLINE
V8_INLINE Tagged(const T* ptr)
: Tagged(reinterpret_cast<Address>(ptr) + kHeapObjectTag) {}
// Implicit conversion for subclasses.
template <typename U>
V8_INLINE constexpr Tagged& operator=(Tagged<U> other)
requires(is_subtype_v<U, T>)
{
*this = Tagged(other);
return *this;
}
// Implicit conversion for subclasses.
template <typename U>
// NOLINTNEXTLINE
V8_INLINE constexpr Tagged(Tagged<U> other)
requires(is_subtype_v<U, Weak<T>>)
: Base(other) {}
private:
V8_INLINE constexpr explicit Tagged(Address ptr) : Base(ptr) {}
friend T;
// Handles of the same type are allowed to access the Address constructor.
friend class Handle<Weak<T>>;
#ifdef V8_ENABLE_DIRECT_HANDLE
friend class DirectHandle<Weak<T>>;
#endif
template <typename U>
friend Tagged<WeakOf<U>> MakeWeak(Tagged<U> value);
template <typename U>
friend Tagged<WeakOf<U>> MakeWeakOrSmi(Tagged<U> value);
template <typename To, typename From>
friend inline Tagged<To> UncheckedCast(Tagged<From> value);
};
template <typename T>
inline Tagged<WeakOf<T>> MakeWeak(Tagged<T> value) {
static_assert(!is_subtype_v<Smi, T>, "Not allowed to make Smis weak.");
return Tagged<WeakOf<T>>(value.ptr() | kWeakHeapObjectTag);
}
template <typename T>
inline Tagged<WeakOf<T>> MakeWeakOrSmi(Tagged<T> value) {
static_assert(is_subtype_v<Smi, T>,
"Use MakeWeak if this is known to not be a Smi.");
if (value.IsSmi()) return Tagged<WeakOf<T>>(value.ptr());
return Tagged<WeakOf<T>>(value.ptr() | kWeakHeapObjectTag);
}
template <typename T>
inline Tagged<StrongOf<T>> MakeStrong(Tagged<T> value) {
// This works with Smis.
return Tagged<StrongOf<T>>(value.ptr() &
(~kWeakHeapObjectTag | kHeapObjectTag));
}
Tagged(const HeapObject* object) -> Tagged<HeapObject>;
template <class T>
Tagged(const T* object) -> Tagged<T>;
template <class T>
Tagged(T* object) -> Tagged<T>;
template <typename T>
struct RemoveTagged {
using type = T;
};
template <typename T>
struct RemoveTagged<Tagged<T>> {
using type = T;
};
} // namespace internal
} // namespace v8
namespace std {
// Template specialize std::common_type to always return Object when compared
// against a subtype of Object.
//
// This is an incomplete specialization for objects and common_type, but
// sufficient for existing use-cases. A proper specialization would need to be
// conditionally enabled via `requires`, which is C++20, or with `enable_if`,
// which would require a custom common_type implementation.
template <class T>
struct common_type<T, i::Object> {
static_assert(i::is_subtype_v<T, i::Object>,
"common_type with Object is only partially specialized.");
using type = i::Object;
};
} // namespace std
#endif // V8_OBJECTS_TAGGED_H_