Implement reflection support for function pointer types and add tests
- Implement handling of FnPtr TypeKind in const-eval, including: - Unsafety flag (safe vs unsafe fn) - ABI variants (Rust, Named(C), Named(custom)) - Input and output types - Variadic function pointers - Add const-eval tests covering: - Basic Rust fn() pointers - Unsafe fn() pointers - Extern C and custom ABI pointers - Functions with multiple inputs and output types - Variadic functions - Use const TypeId checks to verify correctness of inputs, outputs, and payloads
This commit is contained in:
parent
fef627b1eb
commit
7287be9006
5 changed files with 289 additions and 3 deletions
|
|
@ -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;
|
||||
|
|
@ -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(..)
|
||||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -242,6 +242,8 @@ symbols! {
|
|||
Equal,
|
||||
Err,
|
||||
Error,
|
||||
ExternC,
|
||||
ExternRust,
|
||||
File,
|
||||
FileType,
|
||||
Float,
|
||||
|
|
@ -250,6 +252,7 @@ symbols! {
|
|||
Fn,
|
||||
FnMut,
|
||||
FnOnce,
|
||||
FnPtr,
|
||||
Formatter,
|
||||
Forward,
|
||||
From,
|
||||
|
|
@ -303,6 +306,7 @@ symbols! {
|
|||
Mutex,
|
||||
MutexGuard,
|
||||
N,
|
||||
Named,
|
||||
NonNull,
|
||||
NonZero,
|
||||
None,
|
||||
|
|
@ -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
|
||||
|
|
@ -1660,6 +1665,7 @@ symbols! {
|
|||
os_string_as_os_str,
|
||||
other,
|
||||
out,
|
||||
output,
|
||||
overflow_checks,
|
||||
overlapping_marker_traits,
|
||||
owned_box,
|
||||
|
|
@ -2440,6 +2446,7 @@ symbols! {
|
|||
unsafe_no_drop_flag,
|
||||
unsafe_pinned,
|
||||
unsafe_unpin,
|
||||
unsafety,
|
||||
unsize,
|
||||
unsized_const_param_ty,
|
||||
unsized_const_params,
|
||||
|
|
@ -2484,6 +2491,7 @@ symbols! {
|
|||
value,
|
||||
values,
|
||||
var,
|
||||
variadic,
|
||||
variant_count,
|
||||
variants,
|
||||
vec,
|
||||
|
|
|
|||
|
|
@ -75,6 +75,8 @@ pub enum TypeKind {
|
|||
Reference(Reference),
|
||||
/// Pointers.
|
||||
Pointer(Pointer),
|
||||
/// Function pointers.
|
||||
FnPtr(FnPtr),
|
||||
/// FIXME(#146922): add all the common types
|
||||
Other,
|
||||
}
|
||||
|
|
@ -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,3 +1,4 @@
|
|||
mod fn_ptr;
|
||||
mod type_info;
|
||||
|
||||
use core::mem::*;
|
||||
|
|
|
|||
169
library/coretests/tests/mem/fn_ptr.rs
Normal file
169
library/coretests/tests/mem/fn_ptr.rs
Normal file
|
|
@ -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);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue