Skip to content

Commit 5d6c830

Browse files
Rollup merge of #152173 - 9SonSteroids:fn_ptr_type_info, r=oli-obk
Reflection TypeKind::FnPtr This is for #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.
2 parents d7ad9f7 + 7287be9 commit 5d6c830

File tree

5 files changed

+289
-3
lines changed

5 files changed

+289
-3
lines changed

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,
@@ -2439,6 +2445,7 @@ symbols! {
24392445
unsafe_no_drop_flag,
24402446
unsafe_pinned,
24412447
unsafe_unpin,
2448+
unsafety,
24422449
unsize,
24432450
unsized_const_param_ty,
24442451
unsized_const_params,
@@ -2483,6 +2490,7 @@ symbols! {
24832490
value,
24842491
values,
24852492
var,
2493+
variadic,
24862494
variant_count,
24872495
variants,
24882496
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)