Skip to content

Commit d04a5a6

Browse files
author
jasper3108
committed
Implement const-eval 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
1 parent 0a13b43 commit d04a5a6

6 files changed

Lines changed: 327 additions & 4 deletions

File tree

compiler/rustc_const_eval/src/const_eval/type_info.rs

Lines changed: 116 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
use rustc_abi::FieldIdx;
1+
use rustc_abi::{FieldIdx, VariantIdx};
22
use rustc_ast::Mutability;
33
use rustc_hir::LangItem;
44
use rustc_middle::span_bug;
55
use rustc_middle::ty::layout::TyAndLayout;
6-
use rustc_middle::ty::{self, Const, ScalarInt, Ty};
6+
use rustc_middle::ty::{self, Const, FnHeader, ScalarInt, Ty};
77
use rustc_span::{Symbol, sym};
88

99
use crate::const_eval::CompileTimeMachine;
@@ -13,11 +13,59 @@ use crate::interpret::{
1313
};
1414

1515
impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
16+
// FIXME: Merge with #151142
17+
fn downcast(
18+
&self,
19+
place: &(impl Writeable<'tcx, CtfeProvenance> + 'tcx),
20+
name: Symbol,
21+
) -> InterpResult<'tcx, (VariantIdx, impl Writeable<'tcx, CtfeProvenance> + 'tcx)> {
22+
let variants = place.layout().ty.ty_adt_def().unwrap().variants();
23+
let variant_id = variants
24+
.iter_enumerated()
25+
.find(|(_idx, var)| var.name == name)
26+
.unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}"))
27+
.0;
28+
29+
interp_ok((variant_id, self.project_downcast(place, variant_id)?))
30+
}
31+
32+
// FIXME: Merge with #151142
33+
// A general method to write an array to a static slice place.
34+
fn allocate_fill_and_write_slice_ptr(
35+
&mut self,
36+
slice_place: impl Writeable<'tcx, CtfeProvenance>,
37+
len: u64,
38+
writer: impl Fn(&mut Self, /* index */ u64, MPlaceTy<'tcx>) -> InterpResult<'tcx>,
39+
) -> InterpResult<'tcx> {
40+
// Array element type
41+
let field_ty = slice_place
42+
.layout()
43+
.ty
44+
.builtin_deref(false)
45+
.unwrap()
46+
.sequence_element_type(self.tcx.tcx);
47+
48+
// Allocate an array
49+
let array_layout = self.layout_of(Ty::new_array(self.tcx.tcx, field_ty, len))?;
50+
let array_place = self.allocate(array_layout, MemoryKind::Stack)?;
51+
52+
// Fill the array fields
53+
let mut field_places = self.project_array_fields(&array_place)?;
54+
while let Some((i, place)) = field_places.next(self)? {
55+
writer(self, i, place)?;
56+
}
57+
58+
// Write the slice pointing to the array
59+
let array_place = array_place.map_provenance(CtfeProvenance::as_immutable);
60+
let ptr = Immediate::new_slice(array_place.ptr(), len, self);
61+
self.write_immediate(ptr, &slice_place)
62+
}
63+
1664
/// Writes a `core::mem::type_info::TypeInfo` for a given type, `ty` to the given place.
1765
pub(crate) fn write_type_info(
1866
&mut self,
1967
ty: Ty<'tcx>,
20-
dest: &impl Writeable<'tcx, CtfeProvenance>,
68+
dest: &(impl Writeable<'tcx, CtfeProvenance> + 'tcx),
2169
) -> InterpResult<'tcx> {
2270
let ty_struct = self.tcx.require_lang_item(LangItem::Type, self.tcx.span);
2371
let ty_struct = self.tcx.type_of(ty_struct).no_bound_vars().unwrap();
@@ -135,11 +183,24 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
135183
self.write_dyn_trait_type_info(dyn_place, *predicates, *region)?;
136184
variant
137185
}
186+
ty::FnPtr(binder, FnHeader { safety, c_variadic, abi }) => {
187+
let (variant, variant_place) = downcast(sym::FnPtr)?;
188+
let fn_ptr_place =
189+
self.project_field(&variant_place, FieldIdx::ZERO)?;
190+
self.write_fn_ptr_type_info(
191+
fn_ptr_place,
192+
binder.skip_binder().output(),
193+
safety.is_unsafe(),
194+
*c_variadic,
195+
(!abi.is_rustic_abi()).then_some(abi.as_str()),
196+
binder.skip_binder().inputs(),
197+
)?;
198+
variant
199+
}
138200
ty::Adt(_, _)
139201
| ty::Foreign(_)
140202
| ty::Pat(_, _)
141203
| ty::FnDef(..)
142-
| ty::FnPtr(..)
143204
| ty::UnsafeBinder(..)
144205
| ty::Closure(..)
145206
| ty::CoroutineClosure(..)
@@ -354,6 +415,57 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
354415
interp_ok(())
355416
}
356417

418+
pub(crate) fn write_fn_ptr_type_info(
419+
&mut self,
420+
place: impl Writeable<'tcx, CtfeProvenance> + 'tcx,
421+
output: Ty<'tcx>,
422+
unsafety: bool,
423+
variadic: bool,
424+
abi: Option<&'static str>,
425+
inputs: &[Ty<'tcx>],
426+
) -> InterpResult<'tcx> {
427+
for (field_idx, field) in
428+
place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
429+
{
430+
let field_place = self.project_field(&place, field_idx)?;
431+
432+
match field.name {
433+
sym::unsafety => {
434+
self.write_scalar(Scalar::from_bool(unsafety), &field_place)?;
435+
}
436+
sym::abi => {
437+
if let Some(abi) = abi {
438+
let (variant, variant_place) = self.downcast(&field_place, sym::Named)?;
439+
let str_place = self.allocate_str_dedup(abi)?;
440+
let str_ref = self.mplace_to_ref(&str_place)?;
441+
let payload = self.project_field(&variant_place, FieldIdx::ZERO)?;
442+
self.write_immediate(*str_ref, &payload)?;
443+
self.write_discriminant(variant, &field_place)?;
444+
} else {
445+
let (rust_variant, _rust_place) = self.downcast(&field_place, sym::Rust)?;
446+
self.write_discriminant(rust_variant, &field_place)?;
447+
}
448+
}
449+
sym::inputs => {
450+
self.allocate_fill_and_write_slice_ptr(
451+
field_place,
452+
inputs.len() as _,
453+
|this, i, place| this.write_type_id(inputs[i as usize], &place),
454+
)?;
455+
}
456+
sym::output => {
457+
self.write_type_id(output, &field_place)?;
458+
}
459+
sym::variadic => {
460+
self.write_scalar(Scalar::from_bool(variadic), &field_place)?;
461+
}
462+
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
463+
}
464+
}
465+
466+
interp_ok(())
467+
}
468+
357469
pub(crate) fn write_pointer_type_info(
358470
&mut self,
359471
place: impl Writeable<'tcx, CtfeProvenance>,

compiler/rustc_span/src/symbol.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ symbols! {
247247
Fn,
248248
FnMut,
249249
FnOnce,
250+
FnPtr,
250251
Formatter,
251252
Forward,
252253
From,
@@ -299,6 +300,7 @@ symbols! {
299300
Mutex,
300301
MutexGuard,
301302
N,
303+
Named,
302304
NonNull,
303305
NonZero,
304306
None,
@@ -1279,6 +1281,7 @@ symbols! {
12791281
inline_const,
12801282
inline_const_pat,
12811283
inout,
1284+
inputs,
12821285
instant_now,
12831286
instruction_set,
12841287
integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below
@@ -1647,6 +1650,7 @@ symbols! {
16471650
os_string_as_os_str,
16481651
other,
16491652
out,
1653+
output,
16501654
overflow_checks,
16511655
overlapping_marker_traits,
16521656
owned_box,
@@ -2427,6 +2431,7 @@ symbols! {
24272431
unsafe_no_drop_flag,
24282432
unsafe_pinned,
24292433
unsafe_unpin,
2434+
unsafety,
24302435
unsize,
24312436
unsized_const_param_ty,
24322437
unsized_const_params,
@@ -2471,6 +2476,7 @@ symbols! {
24712476
value,
24722477
values,
24732478
var,
2479+
variadic,
24742480
variant_count,
24752481
vec,
24762482
vec_as_mut_slice,

library/core/src/mem/type_info.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ pub enum TypeKind {
6363
Reference(Reference),
6464
/// Pointers.
6565
Pointer(Pointer),
66+
/// Function pointers.
67+
FnPtr(FnPtr),
6668
/// FIXME(#146922): add all the common types
6769
Other,
6870
}
@@ -202,3 +204,35 @@ pub struct Pointer {
202204
/// Whether this pointer is mutable or not.
203205
pub mutable: bool,
204206
}
207+
208+
#[derive(Debug)]
209+
#[unstable(feature = "type_info", issue = "146922")]
210+
/// Function pointer, e.g. fn(u8),
211+
pub struct FnPtr {
212+
/// Unsafety, true is unsafe
213+
pub unsafety: bool,
214+
215+
/// Abi, e.g. extern "C"
216+
pub abi: Abi,
217+
218+
/// Function inputs
219+
pub inputs: &'static [TypeId],
220+
221+
/// Function return type, default is TypeId::of::<()>
222+
pub output: TypeId,
223+
224+
/// Vardiadic function, e.g. extern "C" fn add(n: usize, mut args: ...);
225+
pub variadic: bool,
226+
}
227+
228+
#[derive(Debug, Default)]
229+
#[non_exhaustive]
230+
#[unstable(feature = "type_info", issue = "146922")]
231+
/// Abi of [FnPtr]
232+
pub enum Abi {
233+
/// Named abi, e.g. extern "custom", "C" etc.
234+
Named(&'static str),
235+
/// Default
236+
#[default]
237+
Rust,
238+
}

library/coretests/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// tidy-alphabetical-start
22
#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))]
33
#![cfg_attr(test, feature(cfg_select))]
4+
#![feature(abi_custom)]
45
#![feature(array_ptr_get)]
56
#![feature(array_try_from_fn)]
67
#![feature(array_try_map)]

library/coretests/tests/mem.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod fn_ptr;
12
mod type_info;
23

34
use core::mem::*;

0 commit comments

Comments
 (0)