Skip to content

Create an AllocId for ConstValue::Slice. #116707

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions compiler/rustc_codegen_cranelift/src/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,7 @@ pub(crate) fn codegen_const_value<'tcx>(
.offset_i64(fx, i64::try_from(offset.bytes()).unwrap()),
layout,
),
ConstValue::Slice { data, meta } => {
let alloc_id = fx.tcx.reserve_and_set_memory_alloc(data);
ConstValue::Slice { alloc_id, meta, phantom: _ } => {
let ptr = pointer_for_allocation(fx, alloc_id).get_addr(fx);
let len = fx.bcx.ins().iconst(fx.pointer_type, meta as i64);
CValue::by_val_pair(ptr, len, layout)
Expand Down
7 changes: 2 additions & 5 deletions compiler/rustc_codegen_ssa/src/mir/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,11 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
OperandValue::Immediate(llval)
}
ConstValue::ZeroSized => return OperandRef::zero_sized(layout),
ConstValue::Slice { data, meta } => {
ConstValue::Slice { alloc_id, meta, phantom: _ } => {
let Abi::ScalarPair(a_scalar, _) = layout.abi else {
bug!("from_const: invalid ScalarPair layout: {:#?}", layout);
};
let a = Scalar::from_pointer(
Pointer::new(bx.tcx().reserve_and_set_memory_alloc(data).into(), Size::ZERO),
&bx.tcx(),
);
let a = Scalar::from_pointer(Pointer::new(alloc_id.into(), Size::ZERO), &bx.tcx());
let a_llval = bx.scalar_to_backend(
a,
a_scalar,
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_const_eval/src/const_eval/eval_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,9 @@ pub(super) fn op_to_const<'tcx>(
// We know `offset` is relative to the allocation, so we can use `into_parts`.
let (prov, offset) = a.to_pointer(ecx).expect(msg).into_parts();
let alloc_id = prov.expect(msg).alloc_id();
let data = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
assert!(offset == abi::Size::ZERO, "{}", msg);
let meta = b.to_target_usize(ecx).expect(msg);
ConstValue::Slice { data, meta }
ConstValue::Slice { alloc_id, meta, phantom: std::marker::PhantomData }
}
Immediate::Uninit => bug!("`Uninit` is not a valid value for {}", op.layout.ty),
},
Expand Down
15 changes: 7 additions & 8 deletions compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ use rustc_middle::ty::{Ty, TyCtxt};
use rustc_middle::{
mir::{
self,
interpret::{
Allocation, ConstAllocation, GlobalId, InterpResult, PointerArithmetic, Scalar,
},
interpret::{AllocId, GlobalId, InterpResult, PointerArithmetic, Scalar},
BinOp, ConstValue, NonDivergingIntrinsic,
},
ty::layout::TyAndLayout,
Expand All @@ -28,10 +26,11 @@ use super::{
use crate::fluent_generated as fluent;

/// Directly returns an `Allocation` containing an absolute path representation of the given type.
pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAllocation<'tcx> {
pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId, u64) {
let path = crate::util::type_name(tcx, ty);
let alloc = Allocation::from_bytes_byte_aligned_immutable(path.into_bytes());
tcx.mk_const_alloc(alloc)
let bytes = path.into_bytes();
let len = bytes.len().try_into().unwrap();
(tcx.allocate_bytes(bytes), len)
}

/// The logic for all nullary intrinsics is implemented here. These intrinsics don't get evaluated
Expand All @@ -47,8 +46,8 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>(
Ok(match name {
sym::type_name => {
ensure_monomorphic_enough(tcx, tp_ty)?;
let alloc = alloc_type_name(tcx, tp_ty);
ConstValue::Slice { data: alloc, meta: alloc.inner().size().bytes() }
let (alloc_id, len) = alloc_type_name(tcx, tp_ty);
ConstValue::Slice { alloc_id, meta: len, phantom: std::marker::PhantomData }
}
sym::needs_drop => {
ensure_monomorphic_enough(tcx, tp_ty)?;
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_const_eval/src/interpret/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -780,9 +780,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
mir::ConstValue::Scalar(x) => adjust_scalar(x)?.into(),
mir::ConstValue::ZeroSized => Immediate::Uninit,
mir::ConstValue::Slice { data, meta } => {
mir::ConstValue::Slice { alloc_id, meta, phantom: _ } => {
// This is const data, no mutation allowed.
let alloc_id = self.tcx.reserve_and_set_memory_alloc(data);
let ptr = Pointer::new(CtfeProvenance::from(alloc_id).as_immutable(), Size::ZERO);
Immediate::new_slice(self.global_base_pointer(ptr)?.into(), meta, self)
}
Expand Down
50 changes: 28 additions & 22 deletions compiler/rustc_middle/src/mir/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt};
use rustc_span::{Span, DUMMY_SP};
use rustc_target::abi::{HasDataLayout, Size};

use crate::mir::interpret::{alloc_range, AllocId, ConstAllocation, ErrorHandled, Scalar};
use crate::mir::interpret::{alloc_range, AllocId, ErrorHandled, Scalar};
use crate::mir::{pretty_print_const_value, Promoted};
use crate::ty::print::with_no_trimmed_paths;
use crate::ty::GenericArgsRef;
Expand All @@ -28,8 +28,8 @@ pub struct ConstAlloc<'tcx> {

/// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for
/// array length computations, enum discriminants and the pattern matching logic.
#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
#[derive(HashStable, Lift)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Lift, Hash)]
#[derive(HashStable)]
pub enum ConstValue<'tcx> {
/// Used for types with `layout::abi::Scalar` ABI.
///
Expand All @@ -48,10 +48,11 @@ pub enum ConstValue<'tcx> {
Slice {
/// The allocation storing the slice contents.
/// This always points to the beginning of the allocation.
data: ConstAllocation<'tcx>,
alloc_id: AllocId,
/// The metadata field of the reference.
/// This is a "target usize", so we use `u64` as in the interpreter.
meta: u64,
phantom: std::marker::PhantomData<&'tcx ()>,
},

/// A value not representable by the other variants; needs to be stored in-memory.
Expand All @@ -73,7 +74,7 @@ pub enum ConstValue<'tcx> {
#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))]
static_assert_size!(ConstValue<'_>, 24);

impl<'tcx> ConstValue<'tcx> {
impl ConstValue<'_> {
#[inline]
pub fn try_to_scalar(&self) -> Option<Scalar> {
match *self {
Expand All @@ -94,11 +95,11 @@ impl<'tcx> ConstValue<'tcx> {
self.try_to_scalar_int()?.try_into().ok()
}

pub fn try_to_target_usize(&self, tcx: TyCtxt<'tcx>) -> Option<u64> {
pub fn try_to_target_usize(&self, tcx: TyCtxt<'_>) -> Option<u64> {
self.try_to_scalar_int()?.try_to_target_usize(tcx).ok()
}

pub fn try_to_bits_for_ty(
pub fn try_to_bits_for_ty<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
Expand All @@ -125,12 +126,15 @@ impl<'tcx> ConstValue<'tcx> {
}

/// Must only be called on constants of type `&str` or `&[u8]`!
pub fn try_get_slice_bytes_for_diagnostics(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx [u8]> {
let (data, start, end) = match self {
pub fn try_get_slice_bytes_for_diagnostics<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
) -> Option<&'tcx [u8]> {
let (alloc_id, start, len) = match self {
ConstValue::Scalar(_) | ConstValue::ZeroSized => {
bug!("`try_get_slice_bytes` on non-slice constant")
}
&ConstValue::Slice { data, meta } => (data, 0, meta),
&ConstValue::Slice { alloc_id, meta, phantom: _ } => (alloc_id, 0, meta),
&ConstValue::Indirect { alloc_id, offset } => {
// The reference itself is stored behind an indirection.
// Load the reference, and then load the actual slice contents.
Expand Down Expand Up @@ -162,26 +166,29 @@ impl<'tcx> ConstValue<'tcx> {
}
// Non-empty slice, must have memory. We know this is a relative pointer.
let (inner_prov, offset) = ptr.into_parts();
let data = tcx.global_alloc(inner_prov?.alloc_id()).unwrap_memory();
(data, offset.bytes(), offset.bytes() + len)
(inner_prov?.alloc_id(), offset.bytes(), offset.bytes() + len)
}
};

let data = tcx.global_alloc(alloc_id).unwrap_memory();

// This is for diagnostics only, so we are okay to use `inspect_with_uninit_and_ptr_outside_interpreter`.
let start = start.try_into().unwrap();
let end = end.try_into().unwrap();
let end = start + usize::try_from(len).unwrap();
Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end))
}

/// Check if a constant may contain provenance information. This is used by MIR opts.
/// Can return `true` even if there is no provenance.
pub fn may_have_provenance(&self, tcx: TyCtxt<'tcx>, size: Size) -> bool {
pub fn may_have_provenance(&self, tcx: TyCtxt<'_>, size: Size) -> bool {
match *self {
ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false,
ConstValue::Scalar(Scalar::Ptr(..)) => return true,
// It's hard to find out the part of the allocation we point to;
// just conservatively check everything.
ConstValue::Slice { data, meta: _ } => !data.inner().provenance().ptrs().is_empty(),
ConstValue::Slice { alloc_id, meta: _, phantom: _ } => {
!tcx.global_alloc(alloc_id).unwrap_memory().inner().provenance().ptrs().is_empty()
}
ConstValue::Indirect { alloc_id, offset } => !tcx
.global_alloc(alloc_id)
.unwrap_memory()
Expand Down Expand Up @@ -424,9 +431,8 @@ impl<'tcx> Const<'tcx> {
/// taking into account even pointer identity tests.
pub fn is_deterministic(&self) -> bool {
// Some constants may generate fresh allocations for pointers they contain,
// so using the same constant twice can yield two different results:
// - valtrees purposefully generate new allocations
// - ConstValue::Slice also generate new allocations
// so using the same constant twice can yield two different results.
// Notably, valtrees purposefully generate new allocations.
match self {
Const::Ty(c) => match c.kind() {
ty::ConstKind::Param(..) => true,
Expand All @@ -444,11 +450,11 @@ impl<'tcx> Const<'tcx> {
| ty::ConstKind::Placeholder(..) => bug!(),
},
Const::Unevaluated(..) => false,
// If the same slice appears twice in the MIR, we cannot guarantee that we will
// give the same `AllocId` to the data.
Const::Val(ConstValue::Slice { .. }, _) => false,
Const::Val(
ConstValue::ZeroSized | ConstValue::Scalar(_) | ConstValue::Indirect { .. },
ConstValue::Slice { .. }
| ConstValue::ZeroSized
| ConstValue::Scalar(_)
| ConstValue::Indirect { .. },
_,
) => true,
}
Expand Down
6 changes: 1 addition & 5 deletions compiler/rustc_middle/src/mir/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1360,11 +1360,7 @@ pub fn write_allocations<'tcx>(
}
ConstValue::Scalar(interpret::Scalar::Int { .. }) => Either::Right(std::iter::empty()),
ConstValue::ZeroSized => Either::Right(std::iter::empty()),
ConstValue::Slice { .. } => {
// `u8`/`str` slices, shouldn't contain pointers that we want to print.
Either::Right(std::iter::empty())
}
ConstValue::Indirect { alloc_id, .. } => {
ConstValue::Slice { alloc_id, .. } | ConstValue::Indirect { alloc_id, .. } => {
// FIXME: we don't actually want to print all of these, since some are printed nicely directly as values inline in MIR.
// Really we'd want `pretty_print_const_value` to decide which allocations to print, instead of having a separate visitor.
Either::Left(std::iter::once(alloc_id))
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ use rustc_type_ir::TyKind::*;
use rustc_type_ir::WithCachedTypeInfo;
use rustc_type_ir::{CollectAndApply, Interner, TypeFlags};

use std::borrow::Borrow;
use std::borrow::{Borrow, Cow};
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
Expand Down Expand Up @@ -866,7 +866,7 @@ impl<'tcx> TyCtxt<'tcx> {
}

/// Allocates a read-only byte or string literal for `mir::interpret`.
pub fn allocate_bytes(self, bytes: &[u8]) -> interpret::AllocId {
pub fn allocate_bytes<'a>(self, bytes: impl Into<Cow<'a, [u8]>>) -> interpret::AllocId {
// Create an allocation that just contains these bytes.
let alloc = interpret::Allocation::from_bytes_byte_aligned_immutable(bytes);
let alloc = self.mk_const_alloc(alloc);
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_middle/src/ty/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use rustc_target::abi::TyAndLayout;
use rustc_type_ir::{ConstKind, DebugWithInfcx, InferCtxtLike, WithInfcx};

use std::fmt::{self, Debug};
use std::marker::PhantomData;

use super::print::PrettyPrinter;
use super::{GenericArg, GenericArgKind, Region};
Expand Down Expand Up @@ -459,6 +460,13 @@ TrivialTypeTraversalAndLiftImpls! {
///////////////////////////////////////////////////////////////////////////
// Lift implementations

impl<'tcx> Lift<'tcx> for PhantomData<&()> {
type Lifted = PhantomData<&'tcx ()>;
fn lift_to_tcx(self, _: TyCtxt<'tcx>) -> Option<Self::Lifted> {
Some(PhantomData)
}
}

impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option<T> {
type Lifted = Option<T::Lifted>;
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
Expand Down
37 changes: 25 additions & 12 deletions compiler/rustc_mir_build/src/build/expr/as_constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
use crate::build::{parse_float_into_constval, Builder};
use rustc_ast as ast;
use rustc_middle::mir;
use rustc_middle::mir::interpret::{Allocation, LitToConstError, LitToConstInput, Scalar};
use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput, Scalar};
use rustc_middle::mir::*;
use rustc_middle::thir::*;
use rustc_middle::ty::{
self, CanonicalUserType, CanonicalUserTypeAnnotation, TyCtxt, UserTypeAnnotationIndex,
};
use rustc_target::abi::Size;
use std::marker::PhantomData;

impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Compile `expr`, yielding a compile-time constant. Assumes that
Expand Down Expand Up @@ -124,27 +125,39 @@ fn lit_to_mir_constant<'tcx>(

let value = match (lit, &ty.kind()) {
(ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => {
let s = s.as_str();
let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes());
let allocation = tcx.mk_const_alloc(allocation);
ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() }
let s = s.as_str().as_bytes();
let len = s.len();
let allocation = tcx.allocate_bytes(s);
ConstValue::Slice {
alloc_id: allocation,
meta: len.try_into().unwrap(),
phantom: PhantomData,
}
}
(ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _))
if matches!(inner_ty.kind(), ty::Slice(_)) =>
{
let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]);
let allocation = tcx.mk_const_alloc(allocation);
ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() }
let len = data.len();
let allocation = tcx.allocate_bytes(&**data);
ConstValue::Slice {
alloc_id: allocation,
meta: len.try_into().unwrap(),
phantom: PhantomData,
}
}
(ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => {
let id = tcx.allocate_bytes(data);
let id = tcx.allocate_bytes(&**data);
ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx))
}
(ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().c_str()) =>
{
let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]);
let allocation = tcx.mk_const_alloc(allocation);
ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() }
let len = data.len();
let allocation = tcx.allocate_bytes(&**data);
ConstValue::Slice {
alloc_id: allocation,
meta: len.try_into().unwrap(),
phantom: PhantomData,
}
}
(ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => {
ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1)))
Expand Down
6 changes: 2 additions & 4 deletions compiler/rustc_monomorphize/src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1496,10 +1496,8 @@ fn collect_const_value<'tcx>(
collect_alloc(tcx, ptr.provenance.alloc_id(), output)
}
mir::ConstValue::Indirect { alloc_id, .. } => collect_alloc(tcx, alloc_id, output),
mir::ConstValue::Slice { data, meta: _ } => {
for &prov in data.inner().provenance().ptrs().values() {
collect_alloc(tcx, prov.alloc_id(), output);
}
mir::ConstValue::Slice { alloc_id, meta: _, phantom: _ } => {
collect_alloc(tcx, alloc_id, output);
}
_ => {}
}
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_smir/src/rustc_smir/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ pub fn try_new_allocation<'tcx>(
.align;
new_empty_allocation(align.abi)
}
ConstValue::Slice { data, meta } => {
let alloc_id = tables.tcx.reserve_and_set_memory_alloc(data);
ConstValue::Slice { alloc_id, meta, phantom: _ } => {
let ptr = Pointer::new(alloc_id.into(), rustc_target::abi::Size::ZERO);
let scalar_ptr = rustc_middle::mir::interpret::Scalar::from_pointer(ptr, &tables.tcx);
let scalar_meta =
Expand Down
4 changes: 4 additions & 0 deletions tests/mir-opt/building/issue_101867.main.built.after.mir
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,7 @@ fn main() -> () {
resume;
}
}

ALLOC0 (size: 14, align: 1) {
65 78 70 6c 69 63 69 74 20 70 61 6e 69 63 │ explicit panic
}
4 changes: 2 additions & 2 deletions tests/mir-opt/building/match/sort_candidates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ fn constant_eq(s: &str, b: bool) -> u32 {

// CHECK-LABEL: fn constant_eq(
// CHECK: bb0: {
// CHECK: [[a:_.*]] = const "a";
// CHECK-NOT: {{_.*}} = const "a";
// CHECK: const "a"
// CHECK-NOT: const "a"
match (s, b) {
("a", _) if true => 1,
("b", true) => 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,7 @@ static XXX: &Foo = {
return;
}
}

ALLOC0 (size: 2, align: 1) {
68 69 │ hi
}
Loading
Loading