Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/next_solver/generic_arg.rs')
-rw-r--r--crates/hir-ty/src/next_solver/generic_arg.rs19
1 files changed, 18 insertions, 1 deletions
diff --git a/crates/hir-ty/src/next_solver/generic_arg.rs b/crates/hir-ty/src/next_solver/generic_arg.rs
index f31b487eae..b600f6000d 100644
--- a/crates/hir-ty/src/next_solver/generic_arg.rs
+++ b/crates/hir-ty/src/next_solver/generic_arg.rs
@@ -1,4 +1,10 @@
-//! Things related to generic args in the next-trait-solver.
+//! Things related to generic args in the next-trait-solver (`GenericArg`, `GenericArgs`, `Term`).
+//!
+//! Implementations of `GenericArg` and `Term` are pointer-tagged instead of an enum (rustc does
+//! the same). This is done to save memory (which also helps speed) - one `GenericArg` is a machine
+//! word instead of two, while matching on it is basically as cheap. The implementation for both
+//! `GenericArg` and `Term` is shared in [`GenericArgImpl`]. This both simplifies the implementation,
+//! as well as enables a noop conversion from `Term` to `GenericArg`.
use std::{hint::unreachable_unchecked, marker::PhantomData, ptr::NonNull};
@@ -29,10 +35,14 @@ pub type TermKind<'db> = rustc_type_ir::TermKind<DbInterner<'db>>;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
struct GenericArgImpl<'db> {
+ /// # Invariant
+ ///
+ /// Contains an [`InternedRef`] of a [`Ty`], [`Const`] or [`Region`], bit-tagged as per the consts below.
ptr: NonNull<()>,
_marker: PhantomData<(Ty<'db>, Const<'db>, Region<'db>)>,
}
+// SAFETY: We essentially own the `Ty`, `Const` or `Region`, and they are `Send + Sync`.
unsafe impl Send for GenericArgImpl<'_> {}
unsafe impl Sync for GenericArgImpl<'_> {}
@@ -46,6 +56,7 @@ impl<'db> GenericArgImpl<'db> {
#[inline]
fn new_ty(ty: Ty<'db>) -> Self {
Self {
+ // SAFETY: We create it from an `InternedRef`, and it's never null.
ptr: unsafe {
NonNull::new_unchecked(
ty.interned
@@ -62,6 +73,7 @@ impl<'db> GenericArgImpl<'db> {
#[inline]
fn new_const(ty: Const<'db>) -> Self {
Self {
+ // SAFETY: We create it from an `InternedRef`, and it's never null.
ptr: unsafe {
NonNull::new_unchecked(
ty.interned
@@ -78,6 +90,7 @@ impl<'db> GenericArgImpl<'db> {
#[inline]
fn new_region(ty: Region<'db>) -> Self {
Self {
+ // SAFETY: We create it from an `InternedRef`, and it's never null.
ptr: unsafe {
NonNull::new_unchecked(
ty.interned
@@ -94,6 +107,7 @@ impl<'db> GenericArgImpl<'db> {
#[inline]
fn kind(self) -> GenericArgKind<'db> {
let ptr = self.ptr.as_ptr().map_addr(|addr| addr & Self::PTR_MASK);
+ // SAFETY: We can only be created from a `Ty`, a `Const` or a `Region`, and the tag will match.
unsafe {
match self.ptr.addr().get() & Self::KIND_MASK {
Self::TY_TAG => GenericArgKind::Type(Ty {
@@ -113,6 +127,9 @@ impl<'db> GenericArgImpl<'db> {
#[inline]
fn term_kind(self) -> TermKind<'db> {
let ptr = self.ptr.as_ptr().map_addr(|addr| addr & Self::PTR_MASK);
+ // SAFETY: We can only be created from a `Ty`, a `Const` or a `Region`, and the tag will match.
+ // It is the caller's responsibility (encapsulated within this module) to only call this with
+ // `Term`, which cannot be constructed from a `Region`.
unsafe {
match self.ptr.addr().get() & Self::KIND_MASK {
Self::TY_TAG => {