Skip to content

Commit ea42d81

Browse files
committed
decode constant raw pointers canonically
1 parent ae3c61c commit ea42d81

3 files changed

Lines changed: 163 additions & 44 deletions

File tree

‎src/lower1/operand/const_eval.rs‎

Lines changed: 85 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_abi::{FieldIdx, FieldsShape, Size, TagEncoding, VariantIdx, Variants};
1+
use rustc_abi::{BackendRepr, FieldIdx, FieldsShape, Size, TagEncoding, VariantIdx, Variants};
22
use rustc_middle::mir::interpret::{
33
AllocId, AllocRange, Allocation, CtfeProvenance, GlobalAlloc, Pointer, Provenance, Scalar,
44
};
@@ -259,7 +259,7 @@ pub fn read_scalar_int_constant<'tcx>(
259259
});
260260
}
261261

262-
if let TyKind::Adt(adt_def, substs) = ty.kind() {
262+
if let TyKind::Adt(adt_def, _) = ty.kind() {
263263
if adt_def.is_enum() {
264264
// A scalar enum constant is the enum's physical ABI carrier, not
265265
// necessarily its source-level discriminant. Niche-encoded enums
@@ -281,44 +281,8 @@ pub fn read_scalar_int_constant<'tcx>(
281281
return scalar_int_to_oomir_constant(tcx, scalar_int, carrier_ty);
282282
}
283283

284-
let variant = adt_def
285-
.variants()
286-
.iter()
287-
.next()
288-
.ok_or_else(|| format!("Transparent ADT {:?} has no variants", ty))?;
289-
let non_zst_fields = variant
290-
.fields
291-
.iter()
292-
.filter(|field_def| {
293-
!tcx.layout_of(PseudoCanonicalInput {
294-
typing_env: TypingEnv::post_analysis(tcx, field_def.did),
295-
value: field_def.ty(tcx, substs).skip_norm_wip(),
296-
})
297-
.map(|layout| layout.is_zst())
298-
.unwrap_or(false)
299-
})
300-
.collect::<Vec<_>>();
301-
302-
if non_zst_fields.len() != 1 {
303-
return Err(format!(
304-
"Transparent ADT {:?} has {} non-ZST fields, expected exactly one",
305-
ty,
306-
non_zst_fields.len()
307-
));
308-
}
309-
310-
let field_def = non_zst_fields[0];
311-
let unnormalized_field_ty = field_def.ty(tcx, substs);
312-
let field_ty = tcx
313-
.try_normalize_erasing_regions(TypingEnv::fully_monomorphized(), unnormalized_field_ty)
314-
.map_err(|error| {
315-
format!(
316-
"Could not normalize constant field {} of type {:?}: {:?}",
317-
field_def.ident(tcx),
318-
unnormalized_field_ty,
319-
error
320-
)
321-
})?;
284+
let field_ty = scalar_struct_field_ty(tcx, ty)?
285+
.ok_or_else(|| format!("Scalar constant ADT {ty:?} did not have one non-ZST field"))?;
322286
// A scalar ADT is carried using the bits of its one non-ZST field.
323287
// Keep that physical carrier here and let value-representation
324288
// adaptation reconstruct the nominal JVM object at the use site.
@@ -332,6 +296,57 @@ pub fn read_scalar_int_constant<'tcx>(
332296
scalar_int_to_oomir_constant(tcx, scalar_int, ty)
333297
}
334298

299+
fn scalar_struct_field_ty<'tcx>(
300+
tcx: TyCtxt<'tcx>,
301+
ty: Ty<'tcx>,
302+
) -> Result<Option<Ty<'tcx>>, String> {
303+
let TyKind::Adt(adt_def, substs) = ty.kind() else {
304+
return Ok(None);
305+
};
306+
if !adt_def.is_struct() {
307+
return Ok(None);
308+
}
309+
let layout = tcx
310+
.layout_of(TypingEnv::fully_monomorphized().as_query_input(ty))
311+
.map_err(|error| format!("Could not determine constant layout for {ty:?}: {error:?}"))?;
312+
if !matches!(layout.backend_repr, BackendRepr::Scalar(_)) {
313+
return Ok(None);
314+
}
315+
316+
let variant = adt_def
317+
.variants()
318+
.iter()
319+
.next()
320+
.ok_or_else(|| format!("Scalar ADT {ty:?} has no variants"))?;
321+
let non_zst_fields = variant
322+
.fields
323+
.iter()
324+
.filter(|field_def| {
325+
!tcx.layout_of(PseudoCanonicalInput {
326+
typing_env: TypingEnv::post_analysis(tcx, field_def.did),
327+
value: field_def.ty(tcx, substs).skip_norm_wip(),
328+
})
329+
.map(|layout| layout.is_zst())
330+
.unwrap_or(false)
331+
})
332+
.collect::<Vec<_>>();
333+
let [field_def] = non_zst_fields.as_slice() else {
334+
return Err(format!(
335+
"Scalar ADT {ty:?} has {} non-ZST fields, expected exactly one",
336+
non_zst_fields.len()
337+
));
338+
};
339+
let field_ty = field_def.ty(tcx, substs);
340+
tcx.try_normalize_erasing_regions(TypingEnv::fully_monomorphized(), field_ty)
341+
.map(Some)
342+
.map_err(|error| {
343+
format!(
344+
"Could not normalize constant field {} of type {field_ty:?}: {error:?}",
345+
field_def.ident(tcx)
346+
)
347+
})
348+
}
349+
335350
pub fn read_zero_sized_constant<'tcx>(
336351
tcx: TyCtxt<'tcx>,
337352
ty: Ty<'tcx>,
@@ -513,6 +528,22 @@ pub fn read_pointer_constant<'tcx>(
513528
oomir_data_types: &mut HashMap<String, oomir::DataType>,
514529
instance: Instance<'tcx>,
515530
) -> Result<oomir::Constant, String> {
531+
let ty = EarlyBinder::bind(tcx, ty)
532+
.instantiate(tcx, instance.args)
533+
.skip_norm_wip();
534+
let ty = tcx
535+
.try_normalize_erasing_regions(
536+
TypingEnv::fully_monomorphized(),
537+
rustc_middle::ty::Unnormalized::new_wip(ty),
538+
)
539+
.unwrap_or(ty);
540+
if let TyKind::Pat(inner, _) = ty.kind() {
541+
return read_pointer_constant(tcx, pointer, *inner, oomir_data_types, instance);
542+
}
543+
if let Some(field_ty) = scalar_struct_field_ty(tcx, ty)? {
544+
return read_pointer_constant(tcx, pointer, field_ty, oomir_data_types, instance);
545+
}
546+
516547
match ty.kind() {
517548
TyKind::FnPtr(..) => {
518549
read_function_pointer_constant(tcx, pointer, ty, oomir_data_types, instance)
@@ -1183,9 +1214,20 @@ pub fn read_constant_value_from_memory<'tcx>(
11831214
oomir_data_types,
11841215
instance,
11851216
)?;
1186-
let pointee_type =
1187-
ty_to_oomir_type(*inner_ty, tcx, oomir_data_types, instance);
1188-
Ok(oomir::Constant::Array(Box::new(pointee_type), vec![value]))
1217+
if matches!(oomir::Type::from_constant(&value), oomir::Type::Pointer(_))
1218+
{
1219+
// A static reference is already its stable address. Keep that
1220+
// Pointer carrier rather than adding a pointer-to-pointer cell.
1221+
Ok(value)
1222+
} else {
1223+
pointer_constant_for_pointee(
1224+
tcx,
1225+
*inner_ty,
1226+
value,
1227+
oomir_data_types,
1228+
instance,
1229+
)
1230+
}
11891231
}
11901232
}
11911233
}

‎src/lower1/value_repr.rs‎

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,8 +690,50 @@ fn adapt_mutable_reference_carrier<'tcx>(
690690
return operand_var(dest, target_jvm_ty.clone());
691691
}
692692

693+
let target_pointer_ty = match target_rust_ty.kind() {
694+
TyKind::Pat(inner, _) => resolved_ty(*inner, tcx, instance),
695+
_ => target_rust_ty,
696+
};
697+
if let (Some(oomir::Type::Pointer(source_inner)), oomir::Type::Pointer(_)) =
698+
(source.get_type(), target_jvm_ty)
699+
&& let TyKind::Ref(_, pointee_ty, _) | TyKind::RawPtr(pointee_ty, _) =
700+
target_pointer_ty.kind()
701+
{
702+
let pointee_ty = resolved_ty(*pointee_ty, tcx, instance);
703+
let source_ty = oomir::Type::Pointer(source_inner);
704+
let dest = format!("{temp_prefix}_retyped_pointer");
705+
instructions.push(oomir::Instruction::InvokeVirtual {
706+
dest: Some(dest.clone()),
707+
class_name: oomir::POINTER_CLASS.to_string(),
708+
method_name: "retype".to_string(),
709+
method_ty: oomir::Signature {
710+
params: vec![
711+
("self".to_string(), source_ty),
712+
("view_size".to_string(), oomir::Type::U64),
713+
("view_codec".to_string(), oomir::Type::java_string()),
714+
],
715+
ret: Box::new(target_jvm_ty.clone()),
716+
is_static: false,
717+
},
718+
args: vec![
719+
oomir::Operand::Constant(oomir::Constant::U64(
720+
u64::try_from(
721+
super::types::layout_size_bytes(tcx, pointee_ty).unwrap_or_else(|error| {
722+
panic!("could not determine pointer view layout: {error}")
723+
}),
724+
)
725+
.expect("pointer view layout exceeds u64"),
726+
)),
727+
super::types::pointer_view_codec_operand(pointee_ty, tcx, data_types, instance),
728+
],
729+
operand: source,
730+
});
731+
return operand_var(dest, target_jvm_ty.clone());
732+
}
733+
693734
if let oomir::Type::Pointer(target_inner) = target_jvm_ty
694-
&& let TyKind::Ref(_, pointee_ty, _) | TyKind::RawPtr(pointee_ty, _) = target_rust_ty.kind()
735+
&& let TyKind::Ref(_, pointee_ty, _) | TyKind::RawPtr(pointee_ty, _) =
736+
target_pointer_ty.kind()
695737
{
696738
let mut pointee = adapt_operand_to_rust_type(
697739
source,
@@ -760,8 +802,13 @@ fn adapt_mutable_reference_carrier<'tcx>(
760802
return operand_var(dest, target_jvm_ty.clone());
761803
}
762804

805+
let target_is_scalar_struct = matches!(target_rust_ty.kind(), TyKind::Adt(adt_def, _) if adt_def.is_struct())
806+
&& tcx
807+
.layout_of(TypingEnv::fully_monomorphized().as_query_input(target_rust_ty))
808+
.is_ok_and(|layout| matches!(layout.backend_repr, BackendRepr::Scalar(_)));
763809
if let Some(oomir::Type::Pointer(inner)) = source.get_type()
764810
&& !matches!(target_rust_ty.kind(), TyKind::RawPtr(..) | TyKind::Ref(..))
811+
&& !target_is_scalar_struct
765812
{
766813
return super::place::emit_pointer_read(
767814
source,

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1718,11 +1718,41 @@ const CONST_PTR: *const i32 = CONST_ARRAY.as_ptr();
17181718
const OFFSET_PTR: *const i32 = unsafe { CONST_PTR.add(2) };
17191719
const PTR_ADDR_IS_NULL: bool = CONST_PTR.is_null();
17201720

1721+
struct ConstMessage {
1722+
code: u32,
1723+
}
1724+
1725+
static CONST_MESSAGE: ConstMessage = ConstMessage { code: 42 };
1726+
1727+
const CONST_NON_NULL_UNIT: core::ptr::NonNull<()> = unsafe {
1728+
core::ptr::NonNull::new_unchecked(&CONST_MESSAGE as *const ConstMessage as *mut ())
1729+
};
1730+
1731+
#[derive(Copy, Clone)]
1732+
struct ConstPointerRepr(core::ptr::NonNull<()>, core::marker::PhantomData<()>);
1733+
1734+
#[derive(Copy, Clone)]
1735+
struct ConstPointerError {
1736+
repr: ConstPointerRepr,
1737+
}
1738+
1739+
const CONST_POINTER_ERROR: ConstPointerError = ConstPointerError {
1740+
repr: ConstPointerRepr(CONST_NON_NULL_UNIT, core::marker::PhantomData),
1741+
};
1742+
17211743
fn const_pointer_operations() {
17221744
unsafe {
17231745
assert_eq!(*OFFSET_PTR, 30);
17241746
}
17251747
assert!(!PTR_ADDR_IS_NULL);
1748+
assert_eq!(
1749+
CONST_NON_NULL_UNIT.as_ptr(),
1750+
&CONST_MESSAGE as *const ConstMessage as *mut (),
1751+
);
1752+
assert_eq!(
1753+
CONST_POINTER_ERROR.repr.0.as_ptr(),
1754+
&CONST_MESSAGE as *const ConstMessage as *mut (),
1755+
);
17261756
}
17271757

17281758
#[derive(Copy, Clone, PartialEq, Debug)]

0 commit comments

Comments
 (0)