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
120 changes: 116 additions & 4 deletions compiler/rustc_const_eval/src/const_eval/type_info.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use rustc_abi::FieldIdx;
use rustc_abi::{FieldIdx, VariantIdx};
use rustc_ast::Mutability;
use rustc_hir::LangItem;
use rustc_middle::span_bug;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{self, Const, ScalarInt, Ty};
use rustc_middle::ty::{self, Const, FnHeader, ScalarInt, Ty};
use rustc_span::{Symbol, sym};

use crate::const_eval::CompileTimeMachine;
Expand All @@ -13,11 +13,59 @@ use crate::interpret::{
};

impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
// FIXME: Merge with #151142
fn downcast(
&self,
place: &(impl Writeable<'tcx, CtfeProvenance> + 'tcx),
name: Symbol,
) -> InterpResult<'tcx, (VariantIdx, impl Writeable<'tcx, CtfeProvenance> + 'tcx)> {
let variants = place.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(place, variant_id)?))
}

// FIXME: Merge with #151142
// A general method to write an array to a static slice place.
fn allocate_fill_and_write_slice_ptr(
&mut self,
slice_place: impl Writeable<'tcx, CtfeProvenance>,
len: u64,
writer: impl Fn(&mut Self, /* index */ u64, MPlaceTy<'tcx>) -> InterpResult<'tcx>,
) -> InterpResult<'tcx> {
// Array element type
let field_ty = slice_place
.layout()
.ty
.builtin_deref(false)
.unwrap()
.sequence_element_type(self.tcx.tcx);

// Allocate an array
let array_layout = self.layout_of(Ty::new_array(self.tcx.tcx, field_ty, len))?;
let array_place = self.allocate(array_layout, MemoryKind::Stack)?;

// Fill the array fields
let mut field_places = self.project_array_fields(&array_place)?;
while let Some((i, place)) = field_places.next(self)? {
writer(self, i, place)?;
}

// Write the slice pointing to the array
let array_place = array_place.map_provenance(CtfeProvenance::as_immutable);
let ptr = Immediate::new_slice(array_place.ptr(), len, self);
self.write_immediate(ptr, &slice_place)
}

/// 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>,
dest: &(impl Writeable<'tcx, CtfeProvenance> + 'tcx),
) -> InterpResult<'tcx> {
let ty_struct = self.tcx.require_lang_item(LangItem::Type, self.tcx.span);
let ty_struct = self.tcx.type_of(ty_struct).no_bound_vars().unwrap();
Expand Down Expand Up @@ -135,11 +183,24 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
self.write_dyn_trait_type_info(dyn_place, *predicates, *region)?;
variant
}
ty::FnPtr(binder, FnHeader { safety, c_variadic, abi }) => {
let (variant, variant_place) = downcast(sym::FnPtr)?;
let fn_ptr_place =
self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_fn_ptr_type_info(
fn_ptr_place,
binder.skip_binder().output(),
safety.is_unsafe(),
*c_variadic,
(!abi.is_rustic_abi()).then_some(abi.as_str()),
binder.skip_binder().inputs(),
)?;
variant
}
ty::Adt(_, _)
| ty::Foreign(_)
| ty::Pat(_, _)
| ty::FnDef(..)
| ty::FnPtr(..)
| ty::UnsafeBinder(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
Expand Down Expand Up @@ -354,6 +415,57 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
interp_ok(())
}

pub(crate) fn write_fn_ptr_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance> + 'tcx,
output: Ty<'tcx>,
unsafety: bool,
variadic: bool,
abi: Option<&'static str>,
inputs: &[Ty<'tcx>],
) -> InterpResult<'tcx> {
for (field_idx, 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.name {
sym::unsafety => {
self.write_scalar(Scalar::from_bool(unsafety), &field_place)?;
}
sym::abi => {
if let Some(abi) = abi {
let (variant, variant_place) = self.downcast(&field_place, sym::Named)?;
let str_place = self.allocate_str_dedup(abi)?;
let str_ref = self.mplace_to_ref(&str_place)?;
let payload = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_immediate(*str_ref, &payload)?;
self.write_discriminant(variant, &field_place)?;
} else {
let (rust_variant, _rust_place) = self.downcast(&field_place, sym::Rust)?;
self.write_discriminant(rust_variant, &field_place)?;
}
}
sym::inputs => {
self.allocate_fill_and_write_slice_ptr(
field_place,
inputs.len() as _,
|this, i, place| this.write_type_id(inputs[i as usize], &place),
)?;
}
sym::output => {
self.write_type_id(output, &field_place)?;
}
sym::variadic => {
self.write_scalar(Scalar::from_bool(variadic), &field_place)?;
}
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
}
}

interp_ok(())
}

pub(crate) fn write_pointer_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ symbols! {
Fn,
FnMut,
FnOnce,
FnPtr,
Formatter,
Forward,
From,
Expand Down Expand Up @@ -299,6 +300,7 @@ symbols! {
Mutex,
MutexGuard,
N,
Named,
NonNull,
NonZero,
None,
Expand Down Expand Up @@ -1279,6 +1281,7 @@ symbols! {
inline_const,
inline_const_pat,
inout,
inputs,
instant_now,
instruction_set,
integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below
Expand Down Expand Up @@ -1647,6 +1650,7 @@ symbols! {
os_string_as_os_str,
other,
out,
output,
overflow_checks,
overlapping_marker_traits,
owned_box,
Expand Down Expand Up @@ -2427,6 +2431,7 @@ symbols! {
unsafe_no_drop_flag,
unsafe_pinned,
unsafe_unpin,
unsafety,
unsize,
unsized_const_param_ty,
unsized_const_params,
Expand Down Expand Up @@ -2471,6 +2476,7 @@ symbols! {
value,
values,
var,
variadic,
variant_count,
vec,
vec_as_mut_slice,
Expand Down
34 changes: 34 additions & 0 deletions library/core/src/mem/type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ pub enum TypeKind {
Reference(Reference),
/// Pointers.
Pointer(Pointer),
/// Function pointers.
FnPtr(FnPtr),
/// FIXME(#146922): add all the common types
Other,
}
Expand Down Expand Up @@ -202,3 +204,35 @@ pub struct Pointer {
/// Whether this pointer is mutable or not.
pub mutable: bool,
}

#[derive(Debug)]
#[unstable(feature = "type_info", issue = "146922")]
/// Function pointer, e.g. fn(u8),
pub struct FnPtr {
/// Unsafety, true is unsafe
pub unsafety: bool,

/// Abi, e.g. extern "C"
pub abi: Abi,

/// Function inputs
pub inputs: &'static [TypeId],

/// Function return type, default is TypeId::of::<()>
pub output: TypeId,

/// Vardiadic function, e.g. extern "C" fn add(n: usize, mut args: ...);
pub variadic: bool,
}

#[derive(Debug, Default)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
/// Abi of [FnPtr]
pub enum Abi {
/// Named abi, e.g. extern "custom", "C" etc.
Named(&'static str),
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like "C" should get its own variant, given how important it is

/// Default
#[default]
Rust,
}
1 change: 1 addition & 0 deletions library/coretests/tests/mem.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod fn_ptr;
mod type_info;

use core::mem::*;
Expand Down
Loading
Loading