about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-ty/src/consteval/tests/intrinsics.rs134
-rw-r--r--crates/hir-ty/src/mir/eval.rs107
-rw-r--r--crates/hir-ty/src/mir/eval/shim.rs167
-rw-r--r--crates/hir-ty/src/mir/eval/shim/simd.rs124
-rw-r--r--crates/ide/src/inlay_hints/chaining.rs12
-rw-r--r--crates/test-utils/src/minicore.rs9
6 files changed, 493 insertions, 60 deletions
diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs
index 24a463d714c..ad64a455872 100644
--- a/crates/hir-ty/src/consteval/tests/intrinsics.rs
+++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs
@@ -15,6 +15,66 @@ fn size_of() {
 }
 
 #[test]
+fn size_of_val() {
+    check_number(
+        r#"
+        //- minicore: coerce_unsized
+        extern "rust-intrinsic" {
+            pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
+        }
+
+        struct X(i32, u8);
+
+        const GOAL: usize = size_of_val(&X(1, 2));
+        "#,
+        8,
+    );
+    check_number(
+        r#"
+        //- minicore: coerce_unsized
+        extern "rust-intrinsic" {
+            pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
+        }
+
+        const GOAL: usize = {
+            let x: &[i32] = &[1, 2, 3];
+            size_of_val(x)
+        };
+        "#,
+        12,
+    );
+    check_number(
+        r#"
+        //- minicore: coerce_unsized, fmt, builtin_impls
+        extern "rust-intrinsic" {
+            pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
+        }
+
+        const GOAL: usize = {
+            let x: &i16 = &5;
+            let y: &dyn core::fmt::Debug = x;
+            let z: &dyn core::fmt::Debug = &y;
+            size_of_val(x) + size_of_val(y) * 10 + size_of_val(z) * 100
+        };
+        "#,
+        1622,
+    );
+    check_number(
+        r#"
+        //- minicore: coerce_unsized
+        extern "rust-intrinsic" {
+            pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
+        }
+
+        const GOAL: usize = {
+            size_of_val("salam")
+        };
+        "#,
+        5,
+    );
+}
+
+#[test]
 fn transmute() {
     check_number(
         r#"
@@ -69,7 +129,7 @@ fn wrapping_add() {
 }
 
 #[test]
-fn saturating_add() {
+fn saturating() {
     check_number(
         r#"
         extern "rust-intrinsic" {
@@ -83,6 +143,16 @@ fn saturating_add() {
     check_number(
         r#"
         extern "rust-intrinsic" {
+            pub fn saturating_sub<T>(a: T, b: T) -> T;
+        }
+
+        const GOAL: bool = saturating_sub(5u8, 7) == 0 && saturating_sub(8u8, 4) == 4;
+        "#,
+        1,
+    );
+    check_number(
+        r#"
+        extern "rust-intrinsic" {
             pub fn saturating_add<T>(a: T, b: T) -> T;
         }
 
@@ -161,6 +231,24 @@ fn needs_drop() {
 }
 
 #[test]
+fn discriminant_value() {
+    check_number(
+        r#"
+        //- minicore: discriminant, option
+        use core::marker::DiscriminantKind;
+        extern "rust-intrinsic" {
+            pub fn discriminant_value<T>(v: &T) -> <T as DiscriminantKind>::Discriminant;
+        }
+        const GOAL: bool = {
+            discriminant_value(&Some(2i32)) == discriminant_value(&Some(5i32))
+                && discriminant_value(&Some(2i32)) != discriminant_value(&None::<i32>)
+        };
+        "#,
+        1,
+    );
+}
+
+#[test]
 fn likely() {
     check_number(
         r#"
@@ -376,3 +464,47 @@ fn cttz() {
         3,
     );
 }
+
+#[test]
+fn rotate() {
+    check_number(
+        r#"
+        extern "rust-intrinsic" {
+            pub fn rotate_left<T: Copy>(x: T, y: T) -> T;
+        }
+
+        const GOAL: i64 = rotate_left(0xaa00000000006e1i64, 12);
+        "#,
+        0x6e10aa,
+    );
+    check_number(
+        r#"
+        extern "rust-intrinsic" {
+            pub fn rotate_right<T: Copy>(x: T, y: T) -> T;
+        }
+
+        const GOAL: i64 = rotate_right(0x6e10aa, 12);
+        "#,
+        0xaa00000000006e1,
+    );
+    check_number(
+        r#"
+        extern "rust-intrinsic" {
+            pub fn rotate_left<T: Copy>(x: T, y: T) -> T;
+        }
+
+        const GOAL: i8 = rotate_left(129, 2);
+        "#,
+        6,
+    );
+    check_number(
+        r#"
+        extern "rust-intrinsic" {
+            pub fn rotate_right<T: Copy>(x: T, y: T) -> T;
+        }
+
+        const GOAL: i32 = rotate_right(10006016, 1020315);
+        "#,
+        320192512,
+    );
+}
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 0c7669cd54d..78961e19828 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -1049,57 +1049,8 @@ impl Evaluator<'_> {
             Rvalue::Discriminant(p) => {
                 let ty = self.place_ty(p, locals)?;
                 let bytes = self.eval_place(p, locals)?.get(&self)?;
-                let layout = self.layout(&ty)?;
-                let enum_id = 'b: {
-                    match ty.kind(Interner) {
-                        TyKind::Adt(e, _) => match e.0 {
-                            AdtId::EnumId(e) => break 'b e,
-                            _ => (),
-                        },
-                        _ => (),
-                    }
-                    return Ok(Owned(0u128.to_le_bytes().to_vec()));
-                };
-                match &layout.variants {
-                    Variants::Single { index } => {
-                        let r = self.const_eval_discriminant(EnumVariantId {
-                            parent: enum_id,
-                            local_id: index.0,
-                        })?;
-                        Owned(r.to_le_bytes().to_vec())
-                    }
-                    Variants::Multiple { tag, tag_encoding, variants, .. } => {
-                        let Some(target_data_layout) = self.db.target_data_layout(self.crate_id) else {
-                            not_supported!("missing target data layout");
-                        };
-                        let size = tag.size(&*target_data_layout).bytes_usize();
-                        let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field
-                        match tag_encoding {
-                            TagEncoding::Direct => {
-                                let tag = &bytes[offset..offset + size];
-                                Owned(pad16(tag, false).to_vec())
-                            }
-                            TagEncoding::Niche { untagged_variant, niche_start, .. } => {
-                                let tag = &bytes[offset..offset + size];
-                                let candidate_tag = i128::from_le_bytes(pad16(tag, false))
-                                    .wrapping_sub(*niche_start as i128)
-                                    as usize;
-                                let variant = variants
-                                    .iter_enumerated()
-                                    .map(|(x, _)| x)
-                                    .filter(|x| x != untagged_variant)
-                                    .nth(candidate_tag)
-                                    .unwrap_or(*untagged_variant)
-                                    .0;
-                                let result = self.const_eval_discriminant(EnumVariantId {
-                                    parent: enum_id,
-                                    local_id: variant,
-                                })?;
-                                Owned(result.to_le_bytes().to_vec())
-                            }
-                        }
-                    }
-                }
+                let result = self.compute_discriminant(ty, bytes)?;
+                Owned(result.to_le_bytes().to_vec())
             }
             Rvalue::Repeat(x, len) => {
                 let len = match try_const_usize(self.db, &len) {
@@ -1229,6 +1180,60 @@ impl Evaluator<'_> {
         })
     }
 
+    fn compute_discriminant(&self, ty: Ty, bytes: &[u8]) -> Result<i128> {
+        let layout = self.layout(&ty)?;
+        let enum_id = 'b: {
+            match ty.kind(Interner) {
+                TyKind::Adt(e, _) => match e.0 {
+                    AdtId::EnumId(e) => break 'b e,
+                    _ => (),
+                },
+                _ => (),
+            }
+            return Ok(0);
+        };
+        match &layout.variants {
+            Variants::Single { index } => {
+                let r = self.const_eval_discriminant(EnumVariantId {
+                    parent: enum_id,
+                    local_id: index.0,
+                })?;
+                Ok(r)
+            }
+            Variants::Multiple { tag, tag_encoding, variants, .. } => {
+                let Some(target_data_layout) = self.db.target_data_layout(self.crate_id) else {
+                            not_supported!("missing target data layout");
+                        };
+                let size = tag.size(&*target_data_layout).bytes_usize();
+                let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field
+                match tag_encoding {
+                    TagEncoding::Direct => {
+                        let tag = &bytes[offset..offset + size];
+                        Ok(i128::from_le_bytes(pad16(tag, false)))
+                    }
+                    TagEncoding::Niche { untagged_variant, niche_start, .. } => {
+                        let tag = &bytes[offset..offset + size];
+                        let candidate_tag = i128::from_le_bytes(pad16(tag, false))
+                            .wrapping_sub(*niche_start as i128)
+                            as usize;
+                        let variant = variants
+                            .iter_enumerated()
+                            .map(|(x, _)| x)
+                            .filter(|x| x != untagged_variant)
+                            .nth(candidate_tag)
+                            .unwrap_or(*untagged_variant)
+                            .0;
+                        let result = self.const_eval_discriminant(EnumVariantId {
+                            parent: enum_id,
+                            local_id: variant,
+                        })?;
+                        Ok(result)
+                    }
+                }
+            }
+        }
+    }
+
     fn coerce_unsized_look_through_fields<T>(
         &self,
         ty: &Ty,
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index 3ed26d0e9ba..cab8f267b1e 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -5,6 +5,8 @@ use std::cmp;
 
 use super::*;
 
+mod simd;
+
 macro_rules! from_bytes {
     ($ty:tt, $value:expr) => {
         ($ty::from_le_bytes(match ($value).try_into() {
@@ -53,6 +55,28 @@ impl Evaluator<'_> {
             )?;
             return Ok(true);
         }
+        let is_platform_intrinsic = match &function_data.abi {
+            Some(abi) => *abi == Interned::new_str("platform-intrinsic"),
+            None => match def.lookup(self.db.upcast()).container {
+                hir_def::ItemContainerId::ExternBlockId(block) => {
+                    let id = block.lookup(self.db.upcast()).id;
+                    id.item_tree(self.db.upcast())[id.value].abi.as_deref()
+                        == Some("platform-intrinsic")
+                }
+                _ => false,
+            },
+        };
+        if is_platform_intrinsic {
+            self.exec_platform_intrinsic(
+                function_data.name.as_text().unwrap_or_default().as_str(),
+                args,
+                generic_args,
+                destination,
+                &locals,
+                span,
+            )?;
+            return Ok(true);
+        }
         let is_extern_c = match def.lookup(self.db.upcast()).container {
             hir_def::ItemContainerId::ExternBlockId(block) => {
                 let id = block.lookup(self.db.upcast()).id;
@@ -330,6 +354,21 @@ impl Evaluator<'_> {
         }
     }
 
+    fn exec_platform_intrinsic(
+        &mut self,
+        name: &str,
+        args: &[IntervalAndTy],
+        generic_args: &Substitution,
+        destination: Interval,
+        locals: &Locals<'_>,
+        span: MirSpan,
+    ) -> Result<()> {
+        if let Some(name) = name.strip_prefix("simd_") {
+            return self.exec_simd_intrinsic(name, args, generic_args, destination, locals, span);
+        }
+        not_supported!("unknown platform intrinsic {name}");
+    }
+
     fn exec_intrinsic(
         &mut self,
         name: &str,
@@ -478,6 +517,33 @@ impl Evaluator<'_> {
                 let size = self.size_of_sized(ty, locals, "size_of arg")?;
                 destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size])
             }
+            "size_of_val" => {
+                let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
+                    return Err(MirEvalError::TypeError("size_of_val generic arg is not provided"));
+                };
+                let [arg] = args else {
+                    return Err(MirEvalError::TypeError("size_of_val args are not provided"));
+                };
+                let metadata = arg.interval.slice(self.ptr_size()..self.ptr_size() * 2);
+                let size = match ty.kind(Interner) {
+                    TyKind::Str => return destination.write_from_interval(self, metadata),
+                    TyKind::Slice(inner) => {
+                        let len = from_bytes!(usize, metadata.get(self)?);
+                        len * self.size_of_sized(inner, locals, "slice inner type")?
+                    }
+                    TyKind::Dyn(_) => self.size_of_sized(
+                        self.vtable_map.ty_of_bytes(metadata.get(self)?)?,
+                        locals,
+                        "dyn concrete type",
+                    )?,
+                    _ => self.size_of_sized(
+                        ty,
+                        locals,
+                        "unsized type other than str, slice, and dyn",
+                    )?,
+                };
+                destination.write_from_bytes(self, &size.to_le_bytes())
+            }
             "min_align_of" | "pref_align_of" => {
                 let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
                     return Err(MirEvalError::TypeError("align_of generic arg is not provided"));
@@ -501,13 +567,17 @@ impl Evaluator<'_> {
                 let ans = lhs.get(self)? == rhs.get(self)?;
                 destination.write_from_bytes(self, &[u8::from(ans)])
             }
-            "saturating_add" => {
+            "saturating_add" | "saturating_sub" => {
                 let [lhs, rhs] = args else {
                     return Err(MirEvalError::TypeError("saturating_add args are not provided"));
                 };
                 let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
                 let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
-                let ans = lhs.saturating_add(rhs);
+                let ans = match name {
+                    "saturating_add" => lhs.saturating_add(rhs),
+                    "saturating_sub" => lhs.saturating_sub(rhs),
+                    _ => unreachable!(),
+                };
                 let bits = destination.size * 8;
                 // FIXME: signed
                 let is_signed = false;
@@ -544,6 +614,26 @@ impl Evaluator<'_> {
                 let ans = lhs.wrapping_mul(rhs);
                 destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
             }
+            "wrapping_shl" | "unchecked_shl" => {
+                // FIXME: signed
+                let [lhs, rhs] = args else {
+                    return Err(MirEvalError::TypeError("unchecked_shl args are not provided"));
+                };
+                let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+                let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+                let ans = lhs.wrapping_shl(rhs as u32);
+                destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+            }
+            "wrapping_shr" | "unchecked_shr" => {
+                // FIXME: signed
+                let [lhs, rhs] = args else {
+                    return Err(MirEvalError::TypeError("unchecked_shr args are not provided"));
+                };
+                let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+                let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+                let ans = lhs.wrapping_shr(rhs as u32);
+                destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+            }
             "unchecked_rem" => {
                 // FIXME: signed
                 let [lhs, rhs] = args else {
@@ -666,6 +756,79 @@ impl Evaluator<'_> {
                 destination
                     .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size])
             }
+            "rotate_left" => {
+                let [lhs, rhs] = args else {
+                    return Err(MirEvalError::TypeError("rotate_left args are not provided"));
+                };
+                let lhs = &lhs.get(self)?[0..destination.size];
+                let rhs = rhs.get(self)?[0] as u32;
+                match destination.size {
+                    1 => {
+                        let r = from_bytes!(u8, lhs).rotate_left(rhs);
+                        destination.write_from_bytes(self, &r.to_le_bytes())
+                    }
+                    2 => {
+                        let r = from_bytes!(u16, lhs).rotate_left(rhs);
+                        destination.write_from_bytes(self, &r.to_le_bytes())
+                    }
+                    4 => {
+                        let r = from_bytes!(u32, lhs).rotate_left(rhs);
+                        destination.write_from_bytes(self, &r.to_le_bytes())
+                    }
+                    8 => {
+                        let r = from_bytes!(u64, lhs).rotate_left(rhs);
+                        destination.write_from_bytes(self, &r.to_le_bytes())
+                    }
+                    16 => {
+                        let r = from_bytes!(u128, lhs).rotate_left(rhs);
+                        destination.write_from_bytes(self, &r.to_le_bytes())
+                    }
+                    s => not_supported!("destination with size {s} for rotate_left"),
+                }
+            }
+            "rotate_right" => {
+                let [lhs, rhs] = args else {
+                    return Err(MirEvalError::TypeError("rotate_right args are not provided"));
+                };
+                let lhs = &lhs.get(self)?[0..destination.size];
+                let rhs = rhs.get(self)?[0] as u32;
+                match destination.size {
+                    1 => {
+                        let r = from_bytes!(u8, lhs).rotate_right(rhs);
+                        destination.write_from_bytes(self, &r.to_le_bytes())
+                    }
+                    2 => {
+                        let r = from_bytes!(u16, lhs).rotate_right(rhs);
+                        destination.write_from_bytes(self, &r.to_le_bytes())
+                    }
+                    4 => {
+                        let r = from_bytes!(u32, lhs).rotate_right(rhs);
+                        destination.write_from_bytes(self, &r.to_le_bytes())
+                    }
+                    8 => {
+                        let r = from_bytes!(u64, lhs).rotate_right(rhs);
+                        destination.write_from_bytes(self, &r.to_le_bytes())
+                    }
+                    16 => {
+                        let r = from_bytes!(u128, lhs).rotate_right(rhs);
+                        destination.write_from_bytes(self, &r.to_le_bytes())
+                    }
+                    s => not_supported!("destination with size {s} for rotate_right"),
+                }
+            }
+            "discriminant_value" => {
+                let [arg] = args else {
+                    return Err(MirEvalError::TypeError("discriminant_value arg is not provided"));
+                };
+                let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
+                    return Err(MirEvalError::TypeError("discriminant_value generic arg is not provided"));
+                };
+                let addr = Address::from_bytes(arg.get(self)?)?;
+                let size = self.size_of_sized(ty, locals, "discriminant_value ptr type")?;
+                let interval = Interval { addr, size };
+                let r = self.compute_discriminant(ty.clone(), interval.get(self)?)?;
+                destination.write_from_bytes(self, &r.to_le_bytes()[0..destination.size])
+            }
             "const_eval_select" => {
                 let [tuple, const_fn, _] = args else {
                     return Err(MirEvalError::TypeError("const_eval_select args are not provided"));
diff --git a/crates/hir-ty/src/mir/eval/shim/simd.rs b/crates/hir-ty/src/mir/eval/shim/simd.rs
new file mode 100644
index 00000000000..c41e2790c1b
--- /dev/null
+++ b/crates/hir-ty/src/mir/eval/shim/simd.rs
@@ -0,0 +1,124 @@
+//! Shim implementation for simd intrinsics
+
+use crate::TyKind;
+
+use super::*;
+
+macro_rules! from_bytes {
+    ($ty:tt, $value:expr) => {
+        ($ty::from_le_bytes(match ($value).try_into() {
+            Ok(x) => x,
+            Err(_) => return Err(MirEvalError::TypeError("mismatched size")),
+        }))
+    };
+}
+
+macro_rules! not_supported {
+    ($x: expr) => {
+        return Err(MirEvalError::NotSupported(format!($x)))
+    };
+}
+
+impl Evaluator<'_> {
+    fn detect_simd_ty(&self, ty: &Ty) -> Result<usize> {
+        match ty.kind(Interner) {
+            TyKind::Adt(_, subst) => {
+                let Some(len) = subst.as_slice(Interner).get(1).and_then(|x| x.constant(Interner)) else {
+                    return Err(MirEvalError::TypeError("simd type without len param"));
+                };
+                match try_const_usize(self.db, len) {
+                    Some(x) => Ok(x as usize),
+                    None => Err(MirEvalError::TypeError("simd type with unevaluatable len param")),
+                }
+            }
+            _ => Err(MirEvalError::TypeError("simd type which is not a struct")),
+        }
+    }
+
+    pub(super) fn exec_simd_intrinsic(
+        &mut self,
+        name: &str,
+        args: &[IntervalAndTy],
+        _generic_args: &Substitution,
+        destination: Interval,
+        _locals: &Locals<'_>,
+        _span: MirSpan,
+    ) -> Result<()> {
+        match name {
+            "and" | "or" | "xor" => {
+                let [left, right] = args else {
+                    return Err(MirEvalError::TypeError("simd bit op args are not provided"));
+                };
+                let result = left
+                    .get(self)?
+                    .iter()
+                    .zip(right.get(self)?)
+                    .map(|(&x, &y)| match name {
+                        "and" => x & y,
+                        "or" => x | y,
+                        "xor" => x ^ y,
+                        _ => unreachable!(),
+                    })
+                    .collect::<Vec<_>>();
+                destination.write_from_bytes(self, &result)
+            }
+            "eq" | "ne" => {
+                let [left, right] = args else {
+                    return Err(MirEvalError::TypeError("simd_eq args are not provided"));
+                };
+                let result = left.get(self)? == right.get(self)?;
+                let result = result ^ (name == "ne");
+                destination.write_from_bytes(self, &[u8::from(result)])
+            }
+            "bitmask" => {
+                let [op] = args else {
+                    return Err(MirEvalError::TypeError("simd_shuffle args are not provided"));
+                };
+                let op_len = self.detect_simd_ty(&op.ty)?;
+                let op_count = op.interval.size / op_len;
+                let mut result: u64 = 0;
+                for (i, val) in op.get(self)?.chunks(op_count).enumerate() {
+                    if !val.iter().all(|&x| x == 0) {
+                        result |= 1 << i;
+                    }
+                }
+                destination.write_from_bytes(self, &result.to_le_bytes()[0..destination.size])
+            }
+            "shuffle" => {
+                let [left, right, index] = args else {
+                    return Err(MirEvalError::TypeError("simd_shuffle args are not provided"));
+                };
+                let TyKind::Array(_, index_len) = index.ty.kind(Interner) else {
+                    return Err(MirEvalError::TypeError("simd_shuffle index argument has non-array type"));
+                };
+                let index_len = match try_const_usize(self.db, index_len) {
+                    Some(x) => x as usize,
+                    None => {
+                        return Err(MirEvalError::TypeError(
+                            "simd type with unevaluatable len param",
+                        ))
+                    }
+                };
+                let left_len = self.detect_simd_ty(&left.ty)?;
+                let left_count = left.interval.size / left_len;
+                let vector =
+                    left.get(self)?.chunks(left_count).chain(right.get(self)?.chunks(left_count));
+                let mut result = vec![];
+                for index in index.get(self)?.chunks(index.interval.size / index_len) {
+                    let index = from_bytes!(u32, index) as usize;
+                    let val = match vector.clone().nth(index) {
+                        Some(x) => x,
+                        None => {
+                            return Err(MirEvalError::TypeError(
+                                "out of bound access in simd shuffle",
+                            ))
+                        }
+                    };
+                    result.extend(val);
+                }
+                destination.write_from_bytes(self, &result)
+            }
+            _ => not_supported!("unknown simd intrinsic {name}"),
+        }
+    }
+}
diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs
index 84eac16b9f9..774383d50d6 100644
--- a/crates/ide/src/inlay_hints/chaining.rs
+++ b/crates/ide/src/inlay_hints/chaining.rs
@@ -474,7 +474,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 9287..9295,
+                                        range: 9288..9296,
                                     },
                                 ),
                                 tooltip: "",
@@ -487,7 +487,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 9319..9323,
+                                        range: 9320..9324,
                                     },
                                 ),
                                 tooltip: "",
@@ -511,7 +511,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 9287..9295,
+                                        range: 9288..9296,
                                     },
                                 ),
                                 tooltip: "",
@@ -524,7 +524,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 9319..9323,
+                                        range: 9320..9324,
                                     },
                                 ),
                                 tooltip: "",
@@ -548,7 +548,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 9287..9295,
+                                        range: 9288..9296,
                                     },
                                 ),
                                 tooltip: "",
@@ -561,7 +561,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 9319..9323,
+                                        range: 9320..9324,
                                     },
                                 ),
                                 tooltip: "",
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 266bc2391f1..0d6e4b98d8c 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -20,6 +20,7 @@
 //!     deref_mut: deref
 //!     deref: sized
 //!     derive:
+//!     discriminant:
 //!     drop:
 //!     eq: sized
 //!     error: fmt
@@ -129,6 +130,14 @@ pub mod marker {
     #[lang = "phantom_data"]
     pub struct PhantomData<T: ?Sized>;
     // endregion:phantom_data
+
+    // region:discriminant
+    #[lang = "discriminant_kind"]
+    pub trait DiscriminantKind {
+        #[lang = "discriminant_type"]
+        type Discriminant;
+    }
+    // endregion:discriminant
 }
 
 // region:default