Skip to content
Merged
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
76 changes: 73 additions & 3 deletions compiler/rustc_const_eval/src/const_eval/type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ mod adt;

use std::borrow::Cow;

use rustc_abi::{FieldIdx, VariantIdx};
use rustc_abi::{ExternAbi, 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, FnSigTys, ScalarInt, Ty, TyCtxt};
use rustc_span::{Symbol, sym};

use crate::const_eval::CompileTimeMachine;
Expand Down Expand Up @@ -188,10 +188,21 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
self.write_dyn_trait_type_info(dyn_place, *predicates, *region)?;
variant
}
ty::FnPtr(sig, fn_header) => {
let (variant, variant_place) =
self.downcast(&field_dest, sym::FnPtr)?;
let fn_ptr_place =
self.project_field(&variant_place, FieldIdx::ZERO)?;

// FIXME: handle lifetime bounds
let sig = sig.skip_binder();

self.write_fn_ptr_type_info(fn_ptr_place, &sig, fn_header)?;
variant
}
ty::Foreign(_)
| ty::Pat(_, _)
| ty::FnDef(..)
| ty::FnPtr(..)
| ty::UnsafeBinder(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
Expand Down Expand Up @@ -402,6 +413,65 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
interp_ok(())
}

pub(crate) fn write_fn_ptr_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,
sig: &FnSigTys<TyCtxt<'tcx>>,
fn_header: &FnHeader<TyCtxt<'tcx>>,
) -> InterpResult<'tcx> {
let FnHeader { safety, c_variadic, abi } = fn_header;

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(safety.is_unsafe()), &field_place)?;
}
sym::abi => match abi {
ExternAbi::C { .. } => {
let (rust_variant, _rust_place) =
self.downcast(&field_place, sym::ExternC)?;
self.write_discriminant(rust_variant, &field_place)?;
}
ExternAbi::Rust => {
let (rust_variant, _rust_place) =
self.downcast(&field_place, sym::ExternRust)?;
self.write_discriminant(rust_variant, &field_place)?;
}
other_abi => {
let (variant, variant_place) = self.downcast(&field_place, sym::Named)?;
let str_place = self.allocate_str_dedup(other_abi.as_str())?;
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)?;
}
},
sym::inputs => {
let inputs = sig.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 => {
let output = sig.output();
self.write_type_id(output, &field_place)?;
}
sym::variadic => {
self.write_scalar(Scalar::from_bool(*c_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
8 changes: 8 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ symbols! {
Equal,
Err,
Error,
ExternC,
ExternRust,
File,
FileType,
Float,
Expand All @@ -250,6 +252,7 @@ symbols! {
Fn,
FnMut,
FnOnce,
FnPtr,
Formatter,
Forward,
From,
Expand Down Expand Up @@ -303,6 +306,7 @@ symbols! {
Mutex,
MutexGuard,
N,
Named,
NonNull,
NonZero,
None,
Expand Down Expand Up @@ -1290,6 +1294,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 @@ -1660,6 +1665,7 @@ symbols! {
os_string_as_os_str,
other,
out,
output,
overflow_checks,
overlapping_marker_traits,
owned_box,
Expand Down Expand Up @@ -2440,6 +2446,7 @@ symbols! {
unsafe_no_drop_flag,
unsafe_pinned,
unsafe_unpin,
unsafety,
unsize,
unsized_const_param_ty,
unsized_const_params,
Expand Down Expand Up @@ -2484,6 +2491,7 @@ symbols! {
value,
values,
var,
variadic,
variant_count,
variants,
vec,
Expand Down
38 changes: 38 additions & 0 deletions library/core/src/mem/type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,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 @@ -305,3 +307,39 @@ 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", "stdcall" etc.
Named(&'static str),

/// Default
#[default]
ExternRust,

/// C-calling convention
ExternC,
}
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
169 changes: 169 additions & 0 deletions library/coretests/tests/mem/fn_ptr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
use std::any::TypeId;
use std::mem::type_info::{Abi, FnPtr, Type, TypeKind};

const STRING_TY: TypeId = const { TypeId::of::<String>() };
const U8_TY: TypeId = const { TypeId::of::<u8>() };
const _U8_REF_TY: TypeId = const { TypeId::of::<&u8>() };
const UNIT_TY: TypeId = const { TypeId::of::<()>() };

#[test]
fn test_fn_ptrs() {
let TypeKind::FnPtr(FnPtr {
unsafety: false,
abi: Abi::ExternRust,
inputs: &[],
output,
variadic: false,
}) = (const { Type::of::<fn()>().kind })
else {
panic!();
};
assert_eq!(output, UNIT_TY);
}
#[test]
fn test_ref() {
const {
// references are tricky because the lifetimes give the references different type ids
// so we check the pointees instead
let TypeKind::FnPtr(FnPtr {
unsafety: false,
abi: Abi::ExternRust,
inputs: &[ty1, ty2],
output,
variadic: false,
}) = (const { Type::of::<fn(&u8, &u8)>().kind })
else {
panic!();
};
if output != UNIT_TY {
panic!();
}
let TypeKind::Reference(reference) = ty1.info().kind else {
panic!();
};
if reference.pointee != U8_TY {
panic!();
}
let TypeKind::Reference(reference) = ty2.info().kind else {
panic!();
};
if reference.pointee != U8_TY {
panic!();
}
}
}

#[test]
fn test_unsafe() {
let TypeKind::FnPtr(FnPtr {
unsafety: true,
abi: Abi::ExternRust,
inputs: &[],
output,
variadic: false,
}) = (const { Type::of::<unsafe fn()>().kind })
else {
panic!();
};
assert_eq!(output, UNIT_TY);
}
#[test]
fn test_abi() {
let TypeKind::FnPtr(FnPtr {
unsafety: false,
abi: Abi::ExternRust,
inputs: &[],
output,
variadic: false,
}) = (const { Type::of::<extern "Rust" fn()>().kind })
else {
panic!();
};
assert_eq!(output, UNIT_TY);

let TypeKind::FnPtr(FnPtr {
unsafety: false,
abi: Abi::ExternC,
inputs: &[],
output,
variadic: false,
}) = (const { Type::of::<extern "C" fn()>().kind })
else {
panic!();
};
assert_eq!(output, UNIT_TY);

let TypeKind::FnPtr(FnPtr {
unsafety: true,
abi: Abi::Named("system"),
inputs: &[],
output,
variadic: false,
}) = (const { Type::of::<unsafe extern "system" fn()>().kind })
else {
panic!();
};
assert_eq!(output, UNIT_TY);
}

#[test]
fn test_inputs() {
let TypeKind::FnPtr(FnPtr {
unsafety: false,
abi: Abi::ExternRust,
inputs: &[ty1, ty2],
output,
variadic: false,
}) = (const { Type::of::<fn(String, u8)>().kind })
else {
panic!();
};
assert_eq!(output, UNIT_TY);
assert_eq!(ty1, STRING_TY);
assert_eq!(ty2, U8_TY);

let TypeKind::FnPtr(FnPtr {
unsafety: false,
abi: Abi::ExternRust,
inputs: &[ty1, ty2],
output,
variadic: false,
}) = (const { Type::of::<fn(val: String, p2: u8)>().kind })
else {
panic!();
};
assert_eq!(output, UNIT_TY);
assert_eq!(ty1, STRING_TY);
assert_eq!(ty2, U8_TY);
}

#[test]
fn test_output() {
let TypeKind::FnPtr(FnPtr {
unsafety: false,
abi: Abi::ExternRust,
inputs: &[],
output,
variadic: false,
}) = (const { Type::of::<fn() -> u8>().kind })
else {
panic!();
};
assert_eq!(output, U8_TY);
}

#[test]
fn test_variadic() {
let TypeKind::FnPtr(FnPtr {
unsafety: false,
abi: Abi::ExternC,
inputs: [ty1],
output,
variadic: true,
}) = &(const { Type::of::<extern "C" fn(u8, ...)>().kind })
else {
panic!();
};
assert_eq!(output, &UNIT_TY);
assert_eq!(*ty1, U8_TY);
}