Skip to content
Open
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
14 changes: 11 additions & 3 deletions compiler/rustc_const_eval/src/const_eval/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ use std::borrow::{Borrow, Cow};
use std::fmt;
use std::hash::Hash;

use rustc_abi::{Align, Size};
use rustc_abi::{Align, FieldIdx, Size};
use rustc_ast::Mutability;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem};
use rustc_middle::mir::AssertMessage;
use rustc_middle::mir::interpret::ReportedErrorInfo;
use rustc_middle::mir::interpret::{Pointer, ReportedErrorInfo};
use rustc_middle::query::TyCtxtAt;
use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout, ValidityRequirement};
use rustc_middle::ty::{self, Ty, TyCtxt};
Expand All @@ -22,7 +22,7 @@ use crate::errors::{LongRunning, LongRunningWarn};
use crate::fluent_generated as fluent;
use crate::interpret::{
self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame,
GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar,
GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, RangeSet, Scalar,
compile_time_machine, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub,
throw_ub_custom, throw_unsup, throw_unsup_format,
};
Expand Down Expand Up @@ -591,6 +591,14 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
}
}

sym::type_of => {
let op = ecx.project_field(&args[0], FieldIdx::ZERO)?;
let op = ecx.project_index(&op, 0)?;
let id = ecx.read_scalar(&op)?.to_pointer(ecx)?;
let (ty, _offset) = ecx.get_ptr_type_id(id)?;
Copy link
Member

Choose a reason for hiding this comment

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

Can you use read_type_id instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yea, this code predates that

ecx.write_type_info(ty, dest)?;
}

_ => {
// We haven't handled the intrinsic, let's see if we can use a fallback body.
if ecx.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_const_eval/src/const_eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod error;
mod eval_queries;
mod fn_queries;
mod machine;
mod type_info;
mod valtrees;

pub use self::dummy_machine::*;
Expand Down
175 changes: 175 additions & 0 deletions compiler/rustc_const_eval/src/const_eval/type_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
use rustc_abi::FieldIdx;
use rustc_hir::LangItem;
use rustc_middle::mir::interpret::CtfeProvenance;
use rustc_middle::span_bug;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{self, ScalarInt, Ty};
use rustc_span::{Symbol, sym};

use crate::const_eval::CompileTimeMachine;
use crate::interpret::{
Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Writeable, interp_ok,
};

impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
/// Writes a `core::mem::type_info::TypeInfo` for a given type, `ty` to the given place.
pub(crate) fn write_type_info(
&mut self,
ty: Ty<'tcx>,
dest: &impl Writeable<'tcx, CtfeProvenance>,
) -> InterpResult<'tcx> {
let ty_struct = self.tcx.require_lang_item(LangItem::Type, self.tcx.span);
let ty_struct = self.tcx.type_of(ty_struct).instantiate_identity();
assert_eq!(ty_struct, dest.layout().ty);
let ty_struct = ty_struct.ty_adt_def().unwrap().non_enum_variant();
// Fill all fields of the `TypeInfo` struct.
for (idx, field) in ty_struct.fields.iter_enumerated() {
let field_dest = self.project_field(dest, idx)?;
let downcast = |name: Symbol| {
let variants = field_dest.layout().ty.ty_adt_def().unwrap().variants();
let variant_id = variants
.iter_enumerated()
.find(|(_idx, var)| var.name == name)
.unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}"))
.0;

interp_ok((variant_id, self.project_downcast(&field_dest, variant_id)?))
};
match field.name {
sym::kind => {
let variant_index = match ty.kind() {
ty::Tuple(fields) => {
let (variant, variant_place) = downcast(sym::Tuple)?;
// project to the single tuple variant field of `type_info::Tuple` struct type
let tuple_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
assert_eq!(
1,
tuple_place
.layout()
.ty
.ty_adt_def()
.unwrap()
.non_enum_variant()
.fields
.len()
);
self.write_tuple_fields(tuple_place, fields, ty)?;
variant
}
// For now just merge all primitives into one `Leaf` variant with no data
ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Char | ty::Bool => {
downcast(sym::Leaf)?.0
}
ty::Adt(_, _)
| ty::Foreign(_)
| ty::Str
| ty::Array(_, _)
| ty::Pat(_, _)
| ty::Slice(_)
| ty::RawPtr(..)
| ty::Ref(..)
| ty::FnDef(..)
| ty::FnPtr(..)
| ty::UnsafeBinder(..)
| ty::Dynamic(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::CoroutineWitness(..)
| ty::Never
| ty::Alias(..)
| ty::Param(_)
| ty::Bound(..)
| ty::Placeholder(_)
| ty::Infer(..)
| ty::Error(_) => downcast(sym::Other)?.0,
};
self.write_discriminant(variant_index, &field_dest)?
}
sym::size => {
let layout = self.layout_of(ty)?;
let variant_index = if layout.is_sized() {
let (variant, variant_place) = downcast(sym::Some)?;
let size_field_place =
self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_scalar(
ScalarInt::try_from_target_usize(layout.size.bytes(), self.tcx.tcx)
.unwrap(),
&size_field_place,
)?;
variant
} else {
downcast(sym::None)?.0
};
self.write_discriminant(variant_index, &field_dest)?;
}
other => span_bug!(self.tcx.span, "unknown `Type` field {other}"),
}
}

interp_ok(())
}

pub(crate) fn write_tuple_fields(
&mut self,
tuple_place: impl Writeable<'tcx, CtfeProvenance>,
fields: &[Ty<'tcx>],
tuple_ty: Ty<'tcx>,
) -> InterpResult<'tcx> {
// project into the `type_info::Tuple::fields` field
let fields_slice_place = self.project_field(&tuple_place, FieldIdx::ZERO)?;
// get the `type_info::Field` type from `fields: &[Field]`
let field_type = fields_slice_place
.layout()
.ty
.builtin_deref(false)
.unwrap()
.sequence_element_type(self.tcx.tcx);
// Create an array with as many elements as the number of fields in the inspected tuple
let fields_layout =
self.layout_of(Ty::new_array(self.tcx.tcx, field_type, fields.len() as u64))?;
let fields_place = self.allocate(fields_layout, MemoryKind::Stack)?;
let mut fields_places = self.project_array_fields(&fields_place)?;

let tuple_layout = self.layout_of(tuple_ty)?;

while let Some((i, place)) = fields_places.next(self)? {
let field_ty = fields[i as usize];
self.write_field(field_ty, place, tuple_layout, i)?;
}

let fields_place = fields_place.map_provenance(CtfeProvenance::as_immutable);

let ptr = Immediate::new_slice(fields_place.ptr(), fields.len() as u64, self);

self.write_immediate(ptr, &fields_slice_place)
}

fn write_field(
&mut self,
field_ty: Ty<'tcx>,
place: MPlaceTy<'tcx>,
layout: TyAndLayout<'tcx>,
idx: u64,
) -> InterpResult<'tcx> {
for (field_idx, field_ty_field) in
place.layout.ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
{
let field_place = self.project_field(&place, field_idx)?;
match field_ty_field.name {
sym::ty => self.write_type_id(field_ty, &field_place)?,
sym::offset => {
let offset = layout.fields.offset(idx as usize);
self.write_scalar(
ScalarInt::try_from_target_usize(offset.bytes(), self.tcx.tcx).unwrap(),
&field_place,
)?;
}
other => {
span_bug!(self.tcx.def_span(field_ty_field.did), "unimplemented field {other}")
}
}
}
interp_ok(())
}
}
5 changes: 3 additions & 2 deletions compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use super::{
throw_ub_custom, throw_ub_format,
};
use crate::fluent_generated as fluent;
use crate::interpret::Writeable;

/// Directly returns an `Allocation` containing an absolute path representation of the given type.
pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId, u64) {
Expand All @@ -32,10 +33,10 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId
}
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
/// Generates a value of `TypeId` for `ty` in-place.
fn write_type_id(
pub(crate) fn write_type_id(
&mut self,
ty: Ty<'tcx>,
dest: &PlaceTy<'tcx, M::Provenance>,
dest: &impl Writeable<'tcx, M::Provenance>,
) -> InterpResult<'tcx, ()> {
let tcx = self.tcx;
let type_id_hash = tcx.type_id_hash(ty).as_u128();
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ language_item_table! {
PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1);
CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None;

Type, sym::type_info, type_struct, Target::Struct, GenericRequirement::None;
TypeId, sym::type_id, type_id, Target::Struct, GenericRequirement::None;

// A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
| sym::type_id
| sym::type_id_eq
| sym::type_name
| sym::type_of
| sym::ub_checks
| sym::variant_count
| sym::wrapping_add
Expand Down Expand Up @@ -310,6 +311,12 @@ pub(crate) fn check_intrinsic_type(
let type_id = tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity();
(0, 0, vec![type_id, type_id], tcx.types.bool)
}
sym::type_of => (
0,
0,
vec![tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity()],
tcx.type_of(tcx.lang_items().type_struct().unwrap()).instantiate_identity(),
),
sym::offset => (2, 0, vec![param(0), param(1)], param(0)),
sym::arith_offset => (
1,
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ symbols! {
IteratorItem,
IteratorMap,
Layout,
Leaf,
Left,
LinkedList,
LintDiagnostic,
Expand All @@ -299,6 +300,7 @@ symbols! {
Ordering,
OsStr,
OsString,
Other,
Output,
Param,
ParamSet,
Expand Down Expand Up @@ -375,6 +377,7 @@ symbols! {
TryCapturePrintable,
TryFrom,
TryInto,
Tuple,
Ty,
TyCtxt,
TyKind,
Expand Down Expand Up @@ -2250,13 +2253,15 @@ symbols! {
type_const,
type_id,
type_id_eq,
type_info,
type_ir,
type_ir_infer_ctxt_like,
type_ir_inherent,
type_ir_interner,
type_length_limit,
type_macros,
type_name,
type_of,
type_privacy_lints,
typed_swap_nonoverlapping,
u8,
Expand Down
1 change: 1 addition & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
#![feature(str_internals)]
#![feature(str_split_inclusive_remainder)]
#![feature(str_split_remainder)]
#![feature(type_info)]
#![feature(ub_checks)]
#![feature(unchecked_neg)]
#![feature(unchecked_shifts)]
Expand Down
3 changes: 3 additions & 0 deletions library/core/src/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ pub use drop_guard::DropGuard;
#[doc(inline)]
pub use crate::intrinsics::transmute;

#[unstable(feature = "type_info", issue = "146922")]
pub mod type_info;
Comment on lines 33 to +35
Copy link
Contributor

Choose a reason for hiding this comment

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

This might be on purpose, but did you intend to use the same re-export pattern as above?

Otherwise, wouldn't this make a guarantee that everything in this module would become unstable/stable at the same time? (and prevent making changes to those internals..?)

if other MVPs tend to ignore this stuff, please ignore this comment :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

if I make the module stable, its internals would not become stable. The module may also stay unstable forever. For easier usage while entirely unstable I'm just making the module publicly available so all internals can be used and played with


/// Takes ownership and "forgets" about the value **without running its destructor**.
///
/// Any resources the value manages, such as heap memory or a file handle, will linger
Expand Down
Loading
Loading