Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion compiler/rustc_const_eval/src/const_eval/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use std::{fmt, mem};

use rustc_abi::{Align, FIRST_VARIANT, FieldIdx, Size, VariantIdx};
use rustc_ast::Mutability;
use rustc_attr_ir::find_attr;
use rustc_attr_ir::lang_items::LangItem;
use rustc_attr_ir::{HasAttrs, find_attr};
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{CRATE_HIR_ID, HirId};
Expand Down Expand Up @@ -796,6 +796,54 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
)?;
}

sym::type_id_docs => {

@RalfJung RalfJung Oct 28, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The list of intrinsics here is growing so much that I think it'd make sense to move it to a separate file. (In a separate PR.)

View changes since the review

let ty = ecx.read_type_id(&args[0])?;
let attrs = match ty.kind() {
ty::Adt(def, args)
if let Some(FieldInfo { base, variant_idx, field_idx, .. }) =
def.field_representing_type_info(ecx.tcx(), *args) =>
{
match base.kind() {
ty::Adt(base_def, _) => {
let field = &base_def.variants()[variant_idx].fields[field_idx];
field.did.get_attrs(&ecx.tcx)
}
ty::Tuple(_) => &[],
_ => {
// only adts and tuples have fields
span_bug!(ecx.cur_span(), "expected adt or tuple type, got {ty}");
}
}
}
ty::Adt(def, _) => def.did().get_attrs(&ecx.tcx),
_ => &[],
};

ecx.write_doc_attributes(dest, attrs)?;
}

sym::type_id_variant_docs => {

@oli-obk oli-obk Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should add a variant-pattern type (similar to what we do with FRTs instead of also having a field variant of many intrinsics) sooner rather than later instead of having to have all these intrinsics duplicated for variants and for normal types. Not for this PR, but just adding them to the type system without exposing them at the AST level shouldn't be too hard (famous last words)

View changes since the review

let base = ecx.read_type_id(&args[0])?;

if let ty::Adt(def, _) = base.kind() {
let variant_idx = ecx.read_target_usize(&args[1])? as usize;
if variant_idx >= def.variants().len() {
throw_ub!(BoundsCheckFailed {
len: def.variants().len() as u64,
index: variant_idx as u64
});
}
let variant_idx = VariantIdx::from_usize(variant_idx);

ecx.write_doc_attributes(
dest,
def.variant(variant_idx).def_id.get_attrs(&ecx.tcx),
)?;
} else {
span_bug!(ecx.cur_span(), "expected enum type, got {base}")
};
}

sym::type_id_variant_non_exhaustive => {
let base = ecx.read_type_id(&args[0])?;

Expand Down
31 changes: 30 additions & 1 deletion compiler/rustc_const_eval/src/const_eval/type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::borrow::Cow;

use rustc_abi::{ExternAbi, FieldIdx};
use rustc_attr_ir::lang_items::LangItem;
use rustc_attr_ir::{Attribute, AttributeKind};
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{self, FnHeader, FnSigKind, FnSigTys, ScalarInt, Ty, TyCtxt};
use rustc_span::{Symbol, span_bug, sym};
Expand All @@ -16,7 +17,7 @@ use crate::interpret::{

impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
// A general method to write an array to a static slice place.
fn allocate_fill_and_write_slice_ptr(
pub(crate) fn allocate_fill_and_write_slice_ptr(
&mut self,
slice_place: &impl Writeable<'tcx, CtfeProvenance>,
len: u64,
Expand Down Expand Up @@ -346,4 +347,32 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {

interp_ok(())
}

pub(crate) fn write_doc_attributes(
&mut self,
place: &impl Writeable<'tcx, CtfeProvenance>,
attrs: &'tcx [rustc_attr_ir::Attribute],
) -> InterpResult<'tcx> {
let docs = attrs
.iter()
.filter_map(|x| match x {
Attribute::Parsed(AttributeKind::DocComment { comment, .. }) => Some(*comment),
_ => None,
})
.collect::<Vec<Symbol>>();

self.allocate_fill_and_write_slice_ptr(place, docs.len() as _, |this, i, place| {
let doc = docs[i as usize];

let ptr = this.allocate_bytes_dedup(doc.as_str().as_bytes())?;

this.write_immediate(
Immediate::ScalarPair(
Scalar::from_pointer(ptr, this),
Scalar::from_target_usize(doc.as_str().len() as u64, this),
),
&place,
)
})
}
}
22 changes: 22 additions & 0 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
| sym::trunc
| sym::type_id
| sym::type_id_array_len
| sym::type_id_docs
| sym::type_id_element_ty
| sym::type_id_eq
| sym::type_id_field_representing_type
Expand All @@ -183,6 +184,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
| sym::type_id_points_to
| sym::type_id_size_of
| sym::type_id_type_of
| sym::type_id_variant_docs
| sym::type_id_variant_name
| sym::type_id_variant_non_exhaustive
| sym::type_id_variants
Expand Down Expand Up @@ -316,6 +318,26 @@ pub(crate) fn check_intrinsic_type(
sym::type_id_variant_name => {
(0, 0, vec![type_id_ty(), tcx.types.usize], Ty::new_static_str(tcx))
}
sym::type_id_variant_docs => (
0,
0,
vec![type_id_ty(), tcx.types.usize],
Ty::new_imm_ref(
tcx,
tcx.lifetimes.re_static,
Ty::new_slice(tcx, Ty::new_static_str(tcx)),
),
),
sym::type_id_docs => (
0,
0,
vec![type_id_ty()],
Ty::new_imm_ref(
tcx,
tcx.lifetimes.re_static,
Ty::new_slice(tcx, Ty::new_static_str(tcx)),
),
),
sym::type_id_variant_non_exhaustive => {
(0, 0, vec![type_id_ty(), tcx.types.usize], tcx.types.bool)
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -952,7 +952,7 @@ fn analyze_attr(attr: &rustc_attr_ir::Attribute, state: &mut AnalyzeAttrState) -
} else if let rustc_attr_ir::Attribute::Parsed(AttributeKind::DocComment { .. }) = attr {
// We keep all doc comments reachable to rustdoc because they might be "imported" into
// downstream crates if they use `#[doc(inline)]` to copy an item's documentation into
// their own.
// their own. Using reflection, they can also be queried from another crate.
if state.is_exported {
should_encode = true;
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2206,6 +2206,7 @@ symbols! {
type_changing_struct_update,
type_id,
type_id_array_len,
type_id_docs,
type_id_element_ty,
type_id_eq,
type_id_field_representing_type,
Expand All @@ -2218,6 +2219,7 @@ symbols! {
type_id_points_to,
type_id_size_of,
type_id_type_of,
type_id_variant_docs,
type_id_variant_name,
type_id_variant_non_exhaustive,
type_id_variants,
Expand Down
21 changes: 21 additions & 0 deletions library/core/src/intrinsics/reflection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,19 @@ pub fn type_id_variants(_id: crate::any::TypeId) -> usize;
#[rustc_comptime]
pub fn type_id_variant_name(_base: crate::any::TypeId, _variant_index: usize) -> &'static str;

/// Gets the raw documentation attributes of the variant represented by the base `TypeId` and variant_idx.
///
/// The more user-friendly version of this intrinsic is [`core::mem::type_info::VariantId::docs`].
///
/// [`TypeId`]: crate::any::TypeId
#[rustc_intrinsic]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_comptime]
pub fn type_id_variant_docs(
_base: crate::any::TypeId,
_variant_index: usize,
) -> &'static [&'static str];

/// Returns true when the variant represented by the base `TypeId` and variant_idx is non
/// exhaustive.
///
Expand Down Expand Up @@ -192,3 +205,11 @@ pub fn type_id_points_mutably(_id: crate::any::TypeId) -> bool;
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_comptime]
pub fn type_id_function_ptr(_type_id: crate::any::TypeId) -> Option<crate::mem::type_info::FnPtr>;

/// Returns the raw documentation attributes given a `TypeId` which represents a structure, enumeration, union or `FieldId`.
///
/// The more user-friendly version of this intrinsic is [`core::any::TypeId::docs`].
#[rustc_intrinsic]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_comptime]
pub fn type_id_docs(_type_id: crate::any::TypeId) -> &'static [&'static str];
63 changes: 63 additions & 0 deletions library/core/src/mem/type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,26 @@ impl TypeId {
pub fn function_ptr(self) -> Option<FnPtr> {
intrinsics::reflection::type_id_function_ptr(self)
}

/// Returns the raw documentation attributes when given a `TypeId` which represents a structure, enumeration or union.
/// ```
/// #![feature(type_info)]
/// # use std::any::TypeId;
///
/// #[doc = "first"]
/// /** second
/// * third
/// */
/// struct A;
///
/// assert_eq!(const { TypeId::of::<A>().docs() }, ["first", " second\n* third\n"]);
/// ```
#[unstable(feature = "type_info", issue = "146922")]
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
#[rustc_comptime]
pub fn docs(self) -> &'static [&'static str] {
intrinsics::reflection::type_id_docs(self)
}
}

/// Variant representing type ID. Representing a variant of an enum.
Expand Down Expand Up @@ -698,6 +718,28 @@ impl VariantId {
intrinsics::reflection::type_id_variant_name(self.base, self.variant)
}

/// Returns the raw documentation attributes when given a `VariantId`.
/// ```
/// #![feature(type_info)]
/// # use std::any::TypeId;
///
/// enum A {
/// #[doc = "first"]
/// /** second
/// * third*/
/// B,
/// C,
/// }
///
/// assert_eq!(const { TypeId::of::<A>().variant(0).docs() }, ["first", " second\n * third"]);
/// ```
#[unstable(feature = "type_info", issue = "146922")]
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
#[rustc_comptime]
pub fn docs(self) -> &'static [&'static str] {
intrinsics::reflection::type_id_variant_docs(self.base, self.variant)
}

/// Returns whether this variant is marked with `#[non_exhaustive]`.
#[unstable(feature = "type_info", issue = "146922")]
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
Expand Down Expand Up @@ -766,6 +808,27 @@ impl FieldId {
pub fn name(self) -> &'static str {
intrinsics::field_representing_type_name(self.frt_type_id)
}

/// Returns the raw documentation attributes when given a `FieldId`.
/// ```
/// #![feature(type_info)]
/// # use std::any::TypeId;
///
/// struct A {
/// #[doc = "first"]
/// /// second
/// first: u8,
/// }
///
/// assert_eq!(const { TypeId::of::<A>().field(0, 0).docs() }, ["first", " second"]);
/// ```
#[unstable(feature = "type_info", issue = "146922")]
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
#[rustc_comptime]
pub fn docs(self) -> &'static [&'static str] {
intrinsics::reflection::type_id_docs(self.frt_type_id)
}

/// Returns the offset of the field wrt to its containing type.
///
/// ```
Expand Down
Loading
Loading