Skip to content

Commit c61abb7

Browse files
committed
fix scalar pointer constants
1 parent fb708f7 commit c61abb7

3 files changed

Lines changed: 85 additions & 9 deletions

File tree

‎src/lower1/operand/const_eval.rs‎

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use crate::lower1::context::Definitions;
22
use rustc_abi::{BackendRepr, FieldIdx, FieldsShape, Size, TagEncoding, VariantIdx, Variants};
33
use rustc_hash::FxHashMap as HashMap;
44
use rustc_middle::mir::interpret::{
5-
AllocId, AllocRange, Allocation, CtfeProvenance, GlobalAlloc, Pointer, Provenance, Scalar,
5+
AllocId, AllocInit, AllocRange, Allocation, CtfeProvenance, GlobalAlloc, Pointer, Provenance,
6+
Scalar,
67
};
78
use rustc_middle::ty::consts::ConstExt;
89
use rustc_middle::ty::layout::TyAndLayout;
@@ -67,9 +68,7 @@ fn anonymous_memory_identity(
6768
Some(format!("{}::memory::{hash}", tcx.crate_name(LOCAL_CRATE)))
6869
}
6970

70-
/// Decode the optimized `ConstValue::Slice` representation. Rust uses that
71-
/// representation for every reference whose pointee has a slice tail, not
72-
/// merely for `&str` and `&[T]`.
71+
/// Decode a pointer-valued scalar, including aggregates with a scalar ABI.
7372
pub fn read_pointer_constant<'tcx>(
7473
tcx: TyCtxt<'tcx>,
7574
pointer: Pointer<CtfeProvenance>,
@@ -89,10 +88,6 @@ pub fn read_pointer_constant<'tcx>(
8988
if let TyKind::Pat(inner, _) = ty.kind() {
9089
return read_pointer_constant(tcx, pointer, *inner, oomir_data_types, instance);
9190
}
92-
if let Some(field_ty) = scalar_struct_field_ty(tcx, ty)? {
93-
return read_pointer_constant(tcx, pointer, field_ty, oomir_data_types, instance);
94-
}
95-
9691
match ty.kind() {
9792
TyKind::FnPtr(..) => {
9893
read_function_pointer_constant(tcx, pointer, ty, oomir_data_types, instance)
@@ -143,7 +138,39 @@ pub fn read_pointer_constant<'tcx>(
143138
)
144139
}
145140
}
146-
_ => read_pointee_constant(tcx, pointer, ty, oomir_data_types, instance),
141+
_ => {
142+
let layout = tcx
143+
.layout_of(TypingEnv::fully_monomorphized().as_query_input(ty))
144+
.map_err(|error| format!("Could not determine scalar constant layout: {error:?}"))?;
145+
if !matches!(layout.backend_repr, BackendRepr::Scalar(_))
146+
|| layout.size != tcx.data_layout.pointer_size()
147+
{
148+
return Err(format!("Unexpected pointer scalar for constant type {ty:?}"));
149+
}
150+
// This pointer is the aggregate's contents, not its address. Put
151+
// the scalar in temporary storage so the normal decoder preserves
152+
// enum niches, field types, and function-pointer provenance.
153+
let mut allocation =
154+
ConstAllocation::new(layout.size, layout.align.abi, AllocInit::Uninit, ());
155+
allocation
156+
.write_scalar(
157+
&tcx.data_layout,
158+
AllocRange {
159+
start: Size::ZERO,
160+
size: layout.size,
161+
},
162+
Scalar::from_pointer(pointer, &tcx.data_layout),
163+
)
164+
.map_err(|error| format!("Could not store pointer scalar: {error:?}"))?;
165+
read_constant_value_from_memory(
166+
tcx,
167+
&allocation,
168+
Size::ZERO,
169+
ty,
170+
oomir_data_types,
171+
instance,
172+
)
173+
}
147174
}
148175
}
149176

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use std::hint::black_box;
2+
3+
trait Property {
4+
const FOLD: Option<fn(i32, i32) -> i32>;
5+
}
6+
7+
struct Sum;
8+
struct Plain;
9+
10+
impl Property for Sum {
11+
const FOLD: Option<fn(i32, i32) -> i32> = Some(add);
12+
}
13+
14+
impl Property for Plain {
15+
const FOLD: Option<fn(i32, i32) -> i32> = None;
16+
}
17+
18+
fn add(a: i32, b: i32) -> i32 {
19+
a + b
20+
}
21+
22+
fn fold<P: Property>(a: i32, b: i32) -> i32 {
23+
match black_box(P::FOLD) {
24+
Some(f) => f(a, b),
25+
None => a,
26+
}
27+
}
28+
29+
pub fn run() {
30+
assert_eq!(fold::<Sum>(black_box(19), 23), 42);
31+
assert_eq!(fold::<Plain>(black_box(19), 23), 19);
32+
33+
const CLOSURE: Option<fn(i32) -> i32> = Some(|x| x + 1);
34+
assert_eq!(black_box(CLOSURE).unwrap()(41), 42);
35+
36+
// Scalar ABI aggregates retain their nominal type, even when their only
37+
// non-zero-sized field is represented by a pointer during const evaluation.
38+
const TUPLE: (Option<fn(i32, i32) -> i32>, ()) = (Some(add), ());
39+
assert_eq!(black_box(TUPLE).0.unwrap()(19, 23), 42);
40+
41+
struct Wrapped(Option<fn(i32, i32) -> i32>);
42+
const WRAPPED: Wrapped = Wrapped(Some(add));
43+
assert_eq!(black_box(WRAPPED).0.unwrap()(19, 23), 42);
44+
45+
const REFERENCE: Option<&i32> = Some(&42);
46+
assert_eq!(*black_box(REFERENCE).unwrap(), 42);
47+
}

‎tests/binary/fn_pointers/src/main.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod borrowed_enum;
2+
mod constant_options;
23

34
#[derive(Copy, Clone)]
45
enum ComputeStep<T> {
@@ -342,6 +343,7 @@ fn higher_ranked_maybe_uninit_slice() {
342343

343344
fn main() {
344345
borrowed_enum::run();
346+
constant_options::run();
345347
higher_ranked_maybe_uninit_slice();
346348
function_pointer_address_roundtrips();
347349

0 commit comments

Comments
 (0)