Skip to content

Commit 3f5227a

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 fef627b commit 3f5227a

5 files changed

Lines changed: 289 additions & 3 deletions

File tree

compiler/rustc_const_eval/src/const_eval/type_info.rs

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ mod adt;
22

33
use std::borrow::Cow;
44

5-
use rustc_abi::{FieldIdx, VariantIdx};
5+
use rustc_abi::{ExternAbi, FieldIdx, VariantIdx};
66
use rustc_ast::Mutability;
77
use rustc_hir::LangItem;
88
use rustc_middle::span_bug;
99
use rustc_middle::ty::layout::TyAndLayout;
10-
use rustc_middle::ty::{self, Const, ScalarInt, Ty};
10+
use rustc_middle::ty::{self, Const, FnHeader, FnSigTys, ScalarInt, Ty, TyCtxt};
1111
use rustc_span::{Symbol, sym};
1212

1313
use crate::const_eval::CompileTimeMachine;
@@ -188,10 +188,21 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
188188
self.write_dyn_trait_type_info(dyn_place, *predicates, *region)?;
189189
variant
190190
}
191+
ty::FnPtr(sig, fn_header) => {
192+
let (variant, variant_place) =
193+
self.downcast(&field_dest, sym::FnPtr)?;
194+
let fn_ptr_place =
195+
self.project_field(&variant_place, FieldIdx::ZERO)?;
196+
197+
// FIXME: handle lifetime bounds
198+
let sig = sig.skip_binder();
199+
200+
self.write_fn_ptr_type_info(fn_ptr_place, &sig, fn_header)?;
201+
variant
202+
}
191203
ty::Foreign(_)
192204
| ty::Pat(_, _)
193205
| ty::FnDef(..)
194-
| ty::FnPtr(..)
195206
| ty::UnsafeBinder(..)
196207
| ty::Closure(..)
197208
| ty::CoroutineClosure(..)
@@ -402,6 +413,65 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
402413
interp_ok(())
403414
}
404415

416+
pub(crate) fn write_fn_ptr_type_info(
417+
&mut self,
418+
place: impl Writeable<'tcx, CtfeProvenance>,
419+
sig: &FnSigTys<TyCtxt<'tcx>>,
420+
fn_header: &FnHeader<TyCtxt<'tcx>>,
421+
) -> InterpResult<'tcx> {
422+
let FnHeader { safety, c_variadic, abi, .. } = fn_header;
423+
424+
for (field_idx, field) in
425+
place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
426+
{
427+
let field_place = self.project_field(&place, field_idx)?;
428+
429+
match field.name {
430+
sym::unsafety => {
431+
self.write_scalar(Scalar::from_bool(safety.is_unsafe()), &field_place)?;
432+
}
433+
sym::abi => match abi {
434+
ExternAbi::C { .. } => {
435+
let (rust_variant, _rust_place) =
436+
self.downcast(&field_place, sym::ExternC)?;
437+
self.write_discriminant(rust_variant, &field_place)?;
438+
}
439+
ExternAbi::Rust => {
440+
let (rust_variant, _rust_place) =
441+
self.downcast(&field_place, sym::ExternRust)?;
442+
self.write_discriminant(rust_variant, &field_place)?;
443+
}
444+
other_abi => {
445+
let (variant, variant_place) = self.downcast(&field_place, sym::Named)?;
446+
let str_place = self.allocate_str_dedup(other_abi.as_str())?;
447+
let str_ref = self.mplace_to_ref(&str_place)?;
448+
let payload = self.project_field(&variant_place, FieldIdx::ZERO)?;
449+
self.write_immediate(*str_ref, &payload)?;
450+
self.write_discriminant(variant, &field_place)?;
451+
}
452+
},
453+
sym::inputs => {
454+
let inputs = sig.inputs();
455+
self.allocate_fill_and_write_slice_ptr(
456+
field_place,
457+
inputs.len() as _,
458+
|this, i, place| this.write_type_id(inputs[i as usize], &place),
459+
)?;
460+
}
461+
sym::output => {
462+
let output = sig.output();
463+
self.write_type_id(output, &field_place)?;
464+
}
465+
sym::variadic => {
466+
self.write_scalar(Scalar::from_bool(*c_variadic), &field_place)?;
467+
}
468+
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
469+
}
470+
}
471+
472+
interp_ok(())
473+
}
474+
405475
pub(crate) fn write_pointer_type_info(
406476
&mut self,
407477
place: impl Writeable<'tcx, CtfeProvenance>,

compiler/rustc_span/src/symbol.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ symbols! {
242242
Equal,
243243
Err,
244244
Error,
245+
ExternC,
246+
ExternRust,
245247
File,
246248
FileType,
247249
Float,
@@ -250,6 +252,7 @@ symbols! {
250252
Fn,
251253
FnMut,
252254
FnOnce,
255+
FnPtr,
253256
Formatter,
254257
Forward,
255258
From,
@@ -303,6 +306,7 @@ symbols! {
303306
Mutex,
304307
MutexGuard,
305308
N,
309+
Named,
306310
NonNull,
307311
NonZero,
308312
None,
@@ -1290,6 +1294,7 @@ symbols! {
12901294
inline_const,
12911295
inline_const_pat,
12921296
inout,
1297+
inputs,
12931298
instant_now,
12941299
instruction_set,
12951300
integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below
@@ -1660,6 +1665,7 @@ symbols! {
16601665
os_string_as_os_str,
16611666
other,
16621667
out,
1668+
output,
16631669
overflow_checks,
16641670
overlapping_marker_traits,
16651671
owned_box,
@@ -2440,6 +2446,7 @@ symbols! {
24402446
unsafe_no_drop_flag,
24412447
unsafe_pinned,
24422448
unsafe_unpin,
2449+
unsafety,
24432450
unsize,
24442451
unsized_const_param_ty,
24452452
unsized_const_params,
@@ -2484,6 +2491,7 @@ symbols! {
24842491
value,
24852492
values,
24862493
var,
2494+
variadic,
24872495
variant_count,
24882496
variants,
24892497
vec,

library/core/src/mem/type_info.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ pub enum TypeKind {
7575
Reference(Reference),
7676
/// Pointers.
7777
Pointer(Pointer),
78+
/// Function pointers.
79+
FnPtr(FnPtr),
7880
/// FIXME(#146922): add all the common types
7981
Other,
8082
}
@@ -305,3 +307,39 @@ pub struct Pointer {
305307
/// Whether this pointer is mutable or not.
306308
pub mutable: bool,
307309
}
310+
311+
#[derive(Debug)]
312+
#[unstable(feature = "type_info", issue = "146922")]
313+
/// Function pointer, e.g. fn(u8),
314+
pub struct FnPtr {
315+
/// Unsafety, true is unsafe
316+
pub unsafety: bool,
317+
318+
/// Abi, e.g. extern "C"
319+
pub abi: Abi,
320+
321+
/// Function inputs
322+
pub inputs: &'static [TypeId],
323+
324+
/// Function return type, default is TypeId::of::<()>
325+
pub output: TypeId,
326+
327+
/// Vardiadic function, e.g. extern "C" fn add(n: usize, mut args: ...);
328+
pub variadic: bool,
329+
}
330+
331+
#[derive(Debug, Default)]
332+
#[non_exhaustive]
333+
#[unstable(feature = "type_info", issue = "146922")]
334+
/// Abi of [FnPtr]
335+
pub enum Abi {
336+
/// Named abi, e.g. extern "custom", "stdcall" etc.
337+
Named(&'static str),
338+
339+
/// Default
340+
#[default]
341+
ExternRust,
342+
343+
/// C-calling convention
344+
ExternC,
345+
}

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::*;
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
use std::any::TypeId;
2+
use std::mem::type_info::{Abi, FnPtr, Type, TypeKind};
3+
4+
const STRING_TY: TypeId = const { TypeId::of::<String>() };
5+
const U8_TY: TypeId = const { TypeId::of::<u8>() };
6+
const _U8_REF_TY: TypeId = const { TypeId::of::<&u8>() };
7+
const UNIT_TY: TypeId = const { TypeId::of::<()>() };
8+
9+
#[test]
10+
fn test_fn_ptrs() {
11+
let TypeKind::FnPtr(FnPtr {
12+
unsafety: false,
13+
abi: Abi::ExternRust,
14+
inputs: &[],
15+
output,
16+
variadic: false,
17+
}) = (const { Type::of::<fn()>().kind })
18+
else {
19+
panic!();
20+
};
21+
assert_eq!(output, UNIT_TY);
22+
}
23+
#[test]
24+
fn test_ref() {
25+
const {
26+
// references are tricky because the lifetimes give the references different type ids
27+
// so we check the pointees instead
28+
let TypeKind::FnPtr(FnPtr {
29+
unsafety: false,
30+
abi: Abi::ExternRust,
31+
inputs: &[ty1, ty2],
32+
output,
33+
variadic: false,
34+
}) = (const { Type::of::<fn(&u8, &u8)>().kind })
35+
else {
36+
panic!();
37+
};
38+
if output != UNIT_TY {
39+
panic!();
40+
}
41+
let TypeKind::Reference(reference) = ty1.info().kind else {
42+
panic!();
43+
};
44+
if reference.pointee != U8_TY {
45+
panic!();
46+
}
47+
let TypeKind::Reference(reference) = ty2.info().kind else {
48+
panic!();
49+
};
50+
if reference.pointee != U8_TY {
51+
panic!();
52+
}
53+
}
54+
}
55+
56+
#[test]
57+
fn test_unsafe() {
58+
let TypeKind::FnPtr(FnPtr {
59+
unsafety: true,
60+
abi: Abi::ExternRust,
61+
inputs: &[],
62+
output,
63+
variadic: false,
64+
}) = (const { Type::of::<unsafe fn()>().kind })
65+
else {
66+
panic!();
67+
};
68+
assert_eq!(output, UNIT_TY);
69+
}
70+
#[test]
71+
fn test_abi() {
72+
let TypeKind::FnPtr(FnPtr {
73+
unsafety: false,
74+
abi: Abi::ExternRust,
75+
inputs: &[],
76+
output,
77+
variadic: false,
78+
}) = (const { Type::of::<extern "Rust" fn()>().kind })
79+
else {
80+
panic!();
81+
};
82+
assert_eq!(output, UNIT_TY);
83+
84+
let TypeKind::FnPtr(FnPtr {
85+
unsafety: false,
86+
abi: Abi::ExternC,
87+
inputs: &[],
88+
output,
89+
variadic: false,
90+
}) = (const { Type::of::<extern "C" fn()>().kind })
91+
else {
92+
panic!();
93+
};
94+
assert_eq!(output, UNIT_TY);
95+
96+
let TypeKind::FnPtr(FnPtr {
97+
unsafety: true,
98+
abi: Abi::Named("system"),
99+
inputs: &[],
100+
output,
101+
variadic: false,
102+
}) = (const { Type::of::<unsafe extern "system" fn()>().kind })
103+
else {
104+
panic!();
105+
};
106+
assert_eq!(output, UNIT_TY);
107+
}
108+
109+
#[test]
110+
fn test_inputs() {
111+
let TypeKind::FnPtr(FnPtr {
112+
unsafety: false,
113+
abi: Abi::ExternRust,
114+
inputs: &[ty1, ty2],
115+
output,
116+
variadic: false,
117+
}) = (const { Type::of::<fn(String, u8)>().kind })
118+
else {
119+
panic!();
120+
};
121+
assert_eq!(output, UNIT_TY);
122+
assert_eq!(ty1, STRING_TY);
123+
assert_eq!(ty2, U8_TY);
124+
125+
let TypeKind::FnPtr(FnPtr {
126+
unsafety: false,
127+
abi: Abi::ExternRust,
128+
inputs: &[ty1, ty2],
129+
output,
130+
variadic: false,
131+
}) = (const { Type::of::<fn(val: String, p2: u8)>().kind })
132+
else {
133+
panic!();
134+
};
135+
assert_eq!(output, UNIT_TY);
136+
assert_eq!(ty1, STRING_TY);
137+
assert_eq!(ty2, U8_TY);
138+
}
139+
140+
#[test]
141+
fn test_output() {
142+
let TypeKind::FnPtr(FnPtr {
143+
unsafety: false,
144+
abi: Abi::ExternRust,
145+
inputs: &[],
146+
output,
147+
variadic: false,
148+
}) = (const { Type::of::<fn() -> u8>().kind })
149+
else {
150+
panic!();
151+
};
152+
assert_eq!(output, U8_TY);
153+
}
154+
155+
#[test]
156+
fn test_variadic() {
157+
let TypeKind::FnPtr(FnPtr {
158+
unsafety: false,
159+
abi: Abi::ExternC,
160+
inputs: [ty1],
161+
output,
162+
variadic: true,
163+
}) = &(const { Type::of::<extern "C" fn(u8, ...)>().kind })
164+
else {
165+
panic!();
166+
};
167+
assert_eq!(output, &UNIT_TY);
168+
assert_eq!(*ty1, U8_TY);
169+
}

0 commit comments

Comments
 (0)