Skip to content

Commit 907a078

Browse files
committed
Add range information to slice metadata for types that are known to not be ZST
1 parent 5a4ee43 commit 907a078

19 files changed

+327
-73
lines changed

Diff for: compiler/rustc_hir/src/hir.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -4076,11 +4076,17 @@ mod size_asserts {
40764076
use rustc_data_structures::static_assert_size;
40774077

40784078
use super::*;
4079-
// tidy-alphabetical-start
40804079
static_assert_size!(Block<'_>, 48);
40814080
static_assert_size!(Body<'_>, 24);
4081+
#[cfg(bootstrap)]
40824082
static_assert_size!(Expr<'_>, 64);
4083+
#[cfg(not(bootstrap))]
4084+
static_assert_size!(Expr<'_>, 56);
4085+
#[cfg(bootstrap)]
40834086
static_assert_size!(ExprKind<'_>, 48);
4087+
#[cfg(not(bootstrap))]
4088+
// tidy-alphabetical-start
4089+
static_assert_size!(ExprKind<'_>, 40);
40844090
static_assert_size!(FnDecl<'_>, 40);
40854091
static_assert_size!(ForeignItem<'_>, 88);
40864092
static_assert_size!(ForeignItemKind<'_>, 56);

Diff for: compiler/rustc_hir_typeck/src/intrinsicck.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use rustc_index::Idx;
66
use rustc_middle::bug;
77
use rustc_middle::ty::layout::{LayoutError, SizeSkeleton};
88
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
9-
use rustc_target::abi::{Pointer, VariantIdx};
9+
use rustc_target::abi::{Pointer, Size, VariantIdx};
1010
use tracing::trace;
1111

1212
use super::FnCtxt;
@@ -87,8 +87,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
8787
}
8888
}
8989

90+
fn size_to_bits(size: Size) -> u128 {
91+
let Some(bits) = u128::from(size.bytes()).checked_mul(8) else {
92+
// `u128` should definitely be able to hold the size of different architectures
93+
// larger sizes should be reported as error `are too big for the current architecture`
94+
// otherwise we have a bug somewhere
95+
bug!("{:?} overflow for u128", size)
96+
};
97+
98+
bits
99+
}
100+
90101
// Try to display a sensible error with as much information as possible.
91102
let skeleton_string = |ty: Ty<'tcx>, sk: Result<_, &_>| match sk {
103+
Ok(SizeSkeleton::Pointer { tail, known_size: Some(size), .. }) => {
104+
format!("{} bits, pointer to `{tail}`", size_to_bits(size))
105+
}
92106
Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"),
93107
Ok(SizeSkeleton::Known(size, _)) => {
94108
if let Some(v) = u128::from(size.bytes()).checked_mul(8) {

Diff for: compiler/rustc_middle/src/ty/layout.rs

+29-6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::num::NonZero;
22
use std::ops::Bound;
33
use std::{cmp, fmt};
44

5+
use rustc_ast::Mutability;
56
use rustc_error_messages::DiagMessage;
67
use rustc_errors::{
78
Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level,
@@ -316,6 +317,8 @@ pub enum SizeSkeleton<'tcx> {
316317
Pointer {
317318
/// If true, this pointer is never null.
318319
non_zero: bool,
320+
/// Available if the width of the pointer is known, i.e. whether it's 1 or 2 usizes
321+
known_size: Option<Size>,
319322
/// The type which determines the unsized metadata, if any,
320323
/// of this pointer. Either a type parameter or a projection
321324
/// depending on one, with regions erased.
@@ -374,7 +377,23 @@ impl<'tcx> SizeSkeleton<'tcx> {
374377
match tail.kind() {
375378
ty::Param(_) | ty::Alias(ty::Projection | ty::Inherent, _) => {
376379
debug_assert!(tail.has_non_region_param());
377-
Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(tail) })
380+
Ok(SizeSkeleton::Pointer {
381+
non_zero,
382+
known_size: None,
383+
tail: tcx.erase_regions(tail),
384+
})
385+
}
386+
ty::Slice(_) => {
387+
debug_assert!(tail.has_non_region_param());
388+
// Assumption: all slice pointers have the same size. At most they differ in niches or or ptr/len ordering
389+
let simple_slice =
390+
Ty::new_ptr(tcx, Ty::new_slice(tcx, tcx.types.unit), Mutability::Not);
391+
let size = tcx.layout_of(param_env.and(simple_slice)).unwrap().size;
392+
Ok(SizeSkeleton::Pointer {
393+
non_zero,
394+
known_size: Some(size),
395+
tail: tcx.erase_regions(tail),
396+
})
378397
}
379398
ty::Error(guar) => {
380399
// Fixes ICE #124031
@@ -452,7 +471,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
452471
let v0 = zero_or_ptr_variant(0)?;
453472
// Newtype.
454473
if def.variants().len() == 1 {
455-
if let Some(SizeSkeleton::Pointer { non_zero, tail }) = v0 {
474+
if let Some(SizeSkeleton::Pointer { non_zero, known_size, tail }) = v0 {
456475
return Ok(SizeSkeleton::Pointer {
457476
non_zero: non_zero
458477
|| match tcx.layout_scalar_valid_range(def.did()) {
@@ -462,6 +481,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
462481
}
463482
_ => false,
464483
},
484+
known_size,
465485
tail,
466486
});
467487
} else {
@@ -472,9 +492,9 @@ impl<'tcx> SizeSkeleton<'tcx> {
472492
let v1 = zero_or_ptr_variant(1)?;
473493
// Nullable pointer enum optimization.
474494
match (v0, v1) {
475-
(Some(SizeSkeleton::Pointer { non_zero: true, tail }), None)
476-
| (None, Some(SizeSkeleton::Pointer { non_zero: true, tail })) => {
477-
Ok(SizeSkeleton::Pointer { non_zero: false, tail })
495+
(Some(SizeSkeleton::Pointer { non_zero: true, known_size, tail }), None)
496+
| (None, Some(SizeSkeleton::Pointer { non_zero: true, known_size, tail })) => {
497+
Ok(SizeSkeleton::Pointer { non_zero: false, known_size, tail })
478498
}
479499
_ => Err(err),
480500
}
@@ -495,7 +515,10 @@ impl<'tcx> SizeSkeleton<'tcx> {
495515

496516
pub fn same_size(self, other: SizeSkeleton<'tcx>) -> bool {
497517
match (self, other) {
498-
(SizeSkeleton::Known(a, _), SizeSkeleton::Known(b, _)) => a == b,
518+
(
519+
SizeSkeleton::Known(a, _) | SizeSkeleton::Pointer { known_size: Some(a), .. },
520+
SizeSkeleton::Known(b, _) | SizeSkeleton::Pointer { known_size: Some(b), .. },
521+
) => a == b,
499522
(SizeSkeleton::Pointer { tail: a, .. }, SizeSkeleton::Pointer { tail: b, .. }) => {
500523
a == b
501524
}

Diff for: compiler/rustc_ty_utils/src/layout.rs

+202-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::fmt::Debug;
2-
use std::iter;
2+
use std::ops::ControlFlow;
3+
use std::{cmp, iter};
34

45
use hir::def_id::DefId;
56
use rustc_hir as hir;
@@ -13,12 +14,14 @@ use rustc_middle::ty::layout::{
1314
};
1415
use rustc_middle::ty::print::with_no_trimmed_paths;
1516
use rustc_middle::ty::{
16-
self, AdtDef, CoroutineArgsExt, EarlyBinder, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt,
17+
self, AdtDef, CoroutineArgsExt, EarlyBinder, GenericArgsRef, ParamEnv, Ty, TyCtxt,
18+
TypeVisitableExt,
1719
};
1820
use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
1921
use rustc_span::sym;
2022
use rustc_span::symbol::Symbol;
2123
use rustc_target::abi::*;
24+
use rustc_type_ir::DynKind;
2225
use tracing::{debug, instrument, trace};
2326

2427
use crate::errors::{
@@ -155,7 +158,7 @@ fn layout_of_uncached<'tcx>(
155158
};
156159
debug_assert!(!ty.has_non_region_infer());
157160

158-
Ok(match *ty.kind() {
161+
let layout = match *ty.kind() {
159162
ty::Pat(ty, pat) => {
160163
let layout = cx.layout_of(ty)?.layout;
161164
let mut layout = LayoutS::clone(&layout.0);
@@ -192,7 +195,6 @@ fn layout_of_uncached<'tcx>(
192195
}
193196
}
194197
}
195-
196198
// Basic scalars.
197199
ty::Bool => tcx.mk_layout(LayoutS::scalar(cx, Scalar::Initialized {
198200
value: Int(I8, false),
@@ -263,10 +265,32 @@ fn layout_of_uncached<'tcx>(
263265
return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
264266
}
265267

266-
let Abi::Scalar(metadata) = metadata_layout.abi else {
268+
let Abi::Scalar(mut metadata) = metadata_layout.abi else {
267269
return Err(error(cx, LayoutError::Unknown(pointee)));
268270
};
269271

272+
if !ty.is_unsafe_ptr() && metadata_ty == tcx.types.usize {
273+
let tail = tcx.struct_tail_for_codegen(pointee, param_env);
274+
// // eprintln!("usize-meta {:?} {}", pointee, pointee_zst);
275+
match tail.kind() {
276+
ty::Slice(element) => match ty_is_non_zst(*element, param_env, tcx) {
277+
NonZst::True => {
278+
metadata.valid_range_mut().end =
279+
dl.ptr_sized_integer().signed_max() as u128
280+
}
281+
NonZst::Unknown => return Err(error(cx, LayoutError::Unknown(ty))),
282+
_ => {}
283+
},
284+
ty::Str => {
285+
metadata.valid_range_mut().end =
286+
dl.ptr_sized_integer().signed_max() as u128;
287+
}
288+
_ => {
289+
eprint!("unexpected tail {:?}", tail);
290+
}
291+
}
292+
}
293+
270294
metadata
271295
} else {
272296
let unsized_part = tcx.struct_tail_for_codegen(pointee, param_env);
@@ -275,7 +299,28 @@ fn layout_of_uncached<'tcx>(
275299
ty::Foreign(..) => {
276300
return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
277301
}
278-
ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)),
302+
ty::Slice(element) => {
303+
let mut metadata = scalar_unit(Int(dl.ptr_sized_integer(), false));
304+
if !ty.is_unsafe_ptr() {
305+
match ty_is_non_zst(*element, param_env, tcx) {
306+
NonZst::True => {
307+
metadata.valid_range_mut().end =
308+
dl.ptr_sized_integer().signed_max() as u128
309+
}
310+
NonZst::Unknown => return Err(error(cx, LayoutError::Unknown(ty))),
311+
_ => {}
312+
}
313+
}
314+
metadata
315+
}
316+
ty::Str => {
317+
let mut metadata = scalar_unit(Int(dl.ptr_sized_integer(), false));
318+
if !ty.is_unsafe_ptr() {
319+
metadata.valid_range_mut().end =
320+
dl.ptr_sized_integer().signed_max() as u128;
321+
}
322+
metadata
323+
}
279324
ty::Dynamic(..) => {
280325
let mut vtable = scalar_unit(Pointer(AddressSpace::DATA));
281326
vtable.valid_range_mut().start = 1;
@@ -667,7 +712,157 @@ fn layout_of_uncached<'tcx>(
667712
ty::Placeholder(..) | ty::Param(_) => {
668713
return Err(error(cx, LayoutError::Unknown(ty)));
669714
}
670-
})
715+
};
716+
717+
#[cfg(debug_assertions)]
718+
if layout.is_sized() && !layout.abi.is_uninhabited() {
719+
match (ty_is_non_zst(ty, param_env, tcx), layout.is_zst()) {
720+
(NonZst::Unknown, _) => {
721+
bug!("ZSTness should not be unknown at this point {:?} {:?}", ty, layout)
722+
}
723+
(n @ (NonZst::False | NonZst::Uninhabited), false) => {
724+
bug!("{:?} is not a ZST but ty_is_non_zst() thinks it is NonZst::{:?}", ty, n)
725+
}
726+
(NonZst::True, true) => bug!("{:?} is a ZST but ty_is_non_zst() thinks it isn't", ty),
727+
_ => {}
728+
}
729+
}
730+
731+
Ok(layout)
732+
}
733+
734+
fn ty_is_non_zst<'tcx>(ty: Ty<'tcx>, param_env: ParamEnv<'tcx>, tcx: TyCtxt<'tcx>) -> NonZst {
735+
fn fold_fields<'tcx>(
736+
mut it: impl Iterator<Item = Ty<'tcx>>,
737+
param_env: ParamEnv<'tcx>,
738+
tcx: TyCtxt<'tcx>,
739+
) -> NonZst {
740+
let (ControlFlow::Break(res) | ControlFlow::Continue(res)) =
741+
it.try_fold(NonZst::False, |acc, ty| {
742+
if acc == NonZst::True {
743+
return ControlFlow::Break(acc);
744+
}
745+
746+
ControlFlow::Continue(cmp::max(acc, ty_is_non_zst(ty, param_env, tcx)))
747+
});
748+
749+
res
750+
}
751+
752+
match ty.kind() {
753+
ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
754+
| ty::Uint(_)
755+
| ty::Int(_)
756+
| ty::Bool
757+
| ty::Float(_)
758+
| ty::FnPtr(_, _)
759+
| ty::RawPtr(..)
760+
| ty::Dynamic(_, _, DynKind::DynStar)
761+
| ty::Char
762+
| ty::Ref(..) => NonZst::True,
763+
764+
ty::Pat(ty, _) => ty_is_non_zst(*ty, param_env, tcx),
765+
ty::Closure(_, args) => fold_fields(args.as_closure().upvar_tys().iter(), param_env, tcx),
766+
ty::Coroutine(_, _) => NonZst::True,
767+
ty::CoroutineClosure(_, args) => {
768+
fold_fields(args.as_coroutine_closure().upvar_tys().iter(), param_env, tcx)
769+
}
770+
ty::Array(ty, len) => {
771+
let len = if len.has_aliases() {
772+
tcx.normalize_erasing_regions(param_env, *len)
773+
} else {
774+
*len
775+
};
776+
777+
if let Some(len) = len.try_to_target_usize(tcx) {
778+
if len == 0 {
779+
return NonZst::False;
780+
}
781+
let element_zst = ty_is_non_zst(*ty, param_env, tcx);
782+
if element_zst != NonZst::Unknown {
783+
return element_zst;
784+
}
785+
}
786+
NonZst::Unknown
787+
}
788+
ty::Tuple(tys) => fold_fields(tys.iter(), param_env, tcx),
789+
ty::Adt(def, args) => {
790+
if ty.is_enum() {
791+
// repr(C) enums can never be ZSTs or uninhabited.
792+
// They must have at least one variant and even if the variant has a payload that is uninhabited,
793+
// the tag is still there.
794+
if def.repr().c() {
795+
return NonZst::True;
796+
}
797+
798+
if def.variants().len() == 0 {
799+
return NonZst::Uninhabited;
800+
}
801+
// An enum is !ZST if
802+
// * it has a repr(int) and at least one non-uninhabited variant
803+
// * it has at least one variant with a !ZST payload
804+
// * it has multiple variants that are not uninhabited
805+
806+
let min_empty_variants = if def.repr().inhibit_enum_layout_opt() { 1 } else { 2 };
807+
808+
// first check without recursing
809+
let simple_variants = def.variants().iter().filter(|v| v.fields.len() == 0).count();
810+
if simple_variants >= min_empty_variants {
811+
return NonZst::True;
812+
}
813+
814+
let mut inhabited_zst_variants = 0;
815+
let mut unknown = false;
816+
817+
for variant in def.variants().iter().filter(|v| v.fields.len() != 0) {
818+
let variant_sized =
819+
fold_fields(variant.fields.iter().map(|f| f.ty(tcx, args)), param_env, tcx);
820+
821+
match variant_sized {
822+
// enum E { A(!, u32) } counts as !ZST for our purposes
823+
NonZst::True => return NonZst::True,
824+
NonZst::False => inhabited_zst_variants += 1,
825+
NonZst::Unknown => unknown = true,
826+
NonZst::Uninhabited => {}
827+
}
828+
}
829+
830+
if simple_variants + inhabited_zst_variants >= min_empty_variants {
831+
return NonZst::True;
832+
}
833+
if unknown {
834+
return NonZst::Unknown;
835+
}
836+
if simple_variants + inhabited_zst_variants == 0 {
837+
return NonZst::Uninhabited;
838+
}
839+
840+
NonZst::False
841+
} else {
842+
fold_fields(def.all_fields().map(|f| f.ty(tcx, args)), param_env, tcx)
843+
}
844+
}
845+
ty::FnDef(..) => NonZst::False,
846+
ty::Never => NonZst::Uninhabited,
847+
ty::Param(..) => NonZst::Unknown,
848+
ty::Str => NonZst::True,
849+
// treat unsized types as potentially-ZST
850+
ty::Dynamic(..) | ty::Slice(..) => NonZst::False,
851+
ty::Alias(..) => match tcx.try_normalize_erasing_regions(param_env, ty) {
852+
Ok(ty) if !matches!(ty.kind(), ty::Alias(..)) => ty_is_non_zst(ty, param_env, tcx),
853+
_ => NonZst::Unknown,
854+
},
855+
ty::Error(_) => NonZst::Unknown,
856+
_ => bug!("is_non_zst not implemented for this kind {:?}", ty),
857+
}
858+
}
859+
860+
#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)]
861+
enum NonZst {
862+
False,
863+
Uninhabited,
864+
Unknown,
865+
True,
671866
}
672867

673868
/// Overlap eligibility and variant assignment for each CoroutineSavedLocal.

0 commit comments

Comments
 (0)