Rollup merge of #152173 - 9SonSteroids:fn_ptr_type_info, r=oli-obk
Reflection TypeKind::FnPtr This is for https://github.com/rust-lang/rust/issues/146922. Const-eval currently lacks full support for function pointer (fn) types. We should implement handling of FnPtr TypeKind, covering safe and unsafe functions, Rust and custom ABIs, input and output types, higher-ranked lifetimes, and variadic functions.
This commit is contained in:
commit
b1880bfbfb
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,
|
||||
|
|
@ -2439,6 +2445,7 @@ symbols! {
|
|||
unsafe_no_drop_flag,
|
||||
unsafe_pinned,
|
||||
unsafe_unpin,
|
||||
unsafety,
|
||||
unsize,
|
||||
unsized_const_param_ty,
|
||||
unsized_const_params,
|
||||
|
|
@ -2483,6 +2490,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