Skip to content

Commit df04d28

Browse files
committed
Auto merge of #103917 - oli-obk:layout_math, r=RalfJung,lcnr
Various cleanups around scalar layout restrictions Pulled out of #103724
2 parents 5ac7e08 + 208bb93 commit df04d28

File tree

16 files changed

+261
-129
lines changed

16 files changed

+261
-129
lines changed

compiler/rustc_abi/src/layout.rs

+11-13
Original file line numberDiff line numberDiff line change
@@ -382,28 +382,26 @@ pub trait LayoutCalculator {
382382
let (start, end) = scalar_valid_range;
383383
match st.abi {
384384
Abi::Scalar(ref mut scalar) | Abi::ScalarPair(ref mut scalar, _) => {
385-
// the asserts ensure that we are not using the
386-
// `#[rustc_layout_scalar_valid_range(n)]`
387-
// attribute to widen the range of anything as that would probably
388-
// result in UB somewhere
389-
// FIXME(eddyb) the asserts are probably not needed,
390-
// as larger validity ranges would result in missed
385+
// Enlarging validity ranges would result in missed
391386
// optimizations, *not* wrongly assuming the inner
392-
// value is valid. e.g. unions enlarge validity ranges,
387+
// value is valid. e.g. unions already enlarge validity ranges,
393388
// because the values may be uninitialized.
389+
//
390+
// Because of that we only check that the start and end
391+
// of the range is representable with this scalar type.
392+
393+
let max_value = scalar.size(dl).unsigned_int_max();
394394
if let Bound::Included(start) = start {
395395
// FIXME(eddyb) this might be incorrect - it doesn't
396396
// account for wrap-around (end < start) ranges.
397-
let valid_range = scalar.valid_range_mut();
398-
assert!(valid_range.start <= start);
399-
valid_range.start = start;
397+
assert!(start <= max_value, "{start} > {max_value}");
398+
scalar.valid_range_mut().start = start;
400399
}
401400
if let Bound::Included(end) = end {
402401
// FIXME(eddyb) this might be incorrect - it doesn't
403402
// account for wrap-around (end < start) ranges.
404-
let valid_range = scalar.valid_range_mut();
405-
assert!(valid_range.end >= end);
406-
valid_range.end = end;
403+
assert!(end <= max_value, "{end} > {max_value}");
404+
scalar.valid_range_mut().end = end;
407405
}
408406

409407
// Update `largest_niche` if we have introduced a larger niche.

compiler/rustc_const_eval/src/interpret/validity.rs

+4-12
Original file line numberDiff line numberDiff line change
@@ -785,18 +785,10 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
785785
}
786786
}
787787
Abi::ScalarPair(a_layout, b_layout) => {
788-
// There is no `rustc_layout_scalar_valid_range_start` for pairs, so
789-
// we would validate these things as we descend into the fields,
790-
// but that can miss bugs in layout computation. Layout computation
791-
// is subtle due to enums having ScalarPair layout, where one field
792-
// is the discriminant.
793-
if cfg!(debug_assertions)
794-
&& !a_layout.is_uninit_valid()
795-
&& !b_layout.is_uninit_valid()
796-
{
797-
// We can only proceed if *both* scalars need to be initialized.
798-
// FIXME: find a way to also check ScalarPair when one side can be uninit but
799-
// the other must be init.
788+
// We can only proceed if *both* scalars need to be initialized.
789+
// FIXME: find a way to also check ScalarPair when one side can be uninit but
790+
// the other must be init.
791+
if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() {
800792
let (a, b) =
801793
self.read_immediate(op, "initiailized scalar value")?.to_scalar_pair();
802794
self.visit_scalar(a, a_layout)?;

compiler/rustc_const_eval/src/transform/validate.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ struct TypeChecker<'a, 'tcx> {
8181
}
8282

8383
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
84+
#[track_caller]
8485
fn fail(&self, location: Location, msg: impl AsRef<str>) {
8586
let span = self.body.source_info(location).span;
8687
// We use `delay_span_bug` as we might see broken MIR when other errors have already
@@ -226,12 +227,12 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
226227
let check_equal = |this: &Self, location, f_ty| {
227228
if !this.mir_assign_valid_types(ty, f_ty) {
228229
this.fail(
229-
location,
230-
format!(
231-
"Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is `{:?}`",
232-
parent, f, ty, f_ty
230+
location,
231+
format!(
232+
"Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is `{:?}`",
233+
parent, f, ty, f_ty
234+
)
233235
)
234-
)
235236
}
236237
};
237238

compiler/rustc_lint/src/builtin.rs

+97-63
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ use rustc_span::edition::Edition;
5252
use rustc_span::source_map::Spanned;
5353
use rustc_span::symbol::{kw, sym, Ident, Symbol};
5454
use rustc_span::{BytePos, InnerSpan, Span};
55-
use rustc_target::abi::VariantIdx;
55+
use rustc_target::abi::{Abi, VariantIdx};
5656
use rustc_trait_selection::traits::{self, misc::can_type_implement_copy};
5757

5858
use crate::nonstandard_style::{method_context, MethodLateContext};
@@ -2413,8 +2413,34 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
24132413
}
24142414

24152415
/// Information about why a type cannot be initialized this way.
2416-
/// Contains an error message and optionally a span to point at.
2417-
type InitError = (String, Option<Span>);
2416+
struct InitError {
2417+
message: String,
2418+
/// Spans from struct fields and similar that can be obtained from just the type.
2419+
span: Option<Span>,
2420+
/// Used to report a trace through adts.
2421+
nested: Option<Box<InitError>>,
2422+
}
2423+
impl InitError {
2424+
fn spanned(self, span: Span) -> InitError {
2425+
Self { span: Some(span), ..self }
2426+
}
2427+
2428+
fn nested(self, nested: impl Into<Option<InitError>>) -> InitError {
2429+
assert!(self.nested.is_none());
2430+
Self { nested: nested.into().map(Box::new), ..self }
2431+
}
2432+
}
2433+
2434+
impl<'a> From<&'a str> for InitError {
2435+
fn from(s: &'a str) -> Self {
2436+
s.to_owned().into()
2437+
}
2438+
}
2439+
impl From<String> for InitError {
2440+
fn from(message: String) -> Self {
2441+
Self { message, span: None, nested: None }
2442+
}
2443+
}
24182444

24192445
/// Test if this constant is all-0.
24202446
fn is_zero(expr: &hir::Expr<'_>) -> bool {
@@ -2470,25 +2496,54 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
24702496

24712497
fn variant_find_init_error<'tcx>(
24722498
cx: &LateContext<'tcx>,
2499+
ty: Ty<'tcx>,
24732500
variant: &VariantDef,
24742501
substs: ty::SubstsRef<'tcx>,
24752502
descr: &str,
24762503
init: InitKind,
24772504
) -> Option<InitError> {
2478-
variant.fields.iter().find_map(|field| {
2479-
ty_find_init_error(cx, field.ty(cx.tcx, substs), init).map(|(mut msg, span)| {
2480-
if span.is_none() {
2481-
// Point to this field, should be helpful for figuring
2482-
// out where the source of the error is.
2483-
let span = cx.tcx.def_span(field.did);
2484-
write!(&mut msg, " (in this {descr})").unwrap();
2485-
(msg, Some(span))
2505+
let mut field_err = variant.fields.iter().find_map(|field| {
2506+
ty_find_init_error(cx, field.ty(cx.tcx, substs), init).map(|mut err| {
2507+
if !field.did.is_local() {
2508+
err
2509+
} else if err.span.is_none() {
2510+
err.span = Some(cx.tcx.def_span(field.did));
2511+
write!(&mut err.message, " (in this {descr})").unwrap();
2512+
err
24862513
} else {
2487-
// Just forward.
2488-
(msg, span)
2514+
InitError::from(format!("in this {descr}"))
2515+
.spanned(cx.tcx.def_span(field.did))
2516+
.nested(err)
24892517
}
24902518
})
2491-
})
2519+
});
2520+
2521+
// Check if this ADT has a constrained layout (like `NonNull` and friends).
2522+
if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)) {
2523+
if let Abi::Scalar(scalar) | Abi::ScalarPair(scalar, _) = &layout.abi {
2524+
let range = scalar.valid_range(cx);
2525+
let msg = if !range.contains(0) {
2526+
"must be non-null"
2527+
} else if init == InitKind::Uninit && !scalar.is_always_valid(cx) {
2528+
// Prefer reporting on the fields over the entire struct for uninit,
2529+
// as the information bubbles out and it may be unclear why the type can't
2530+
// be null from just its outside signature.
2531+
2532+
"must be initialized inside its custom valid range"
2533+
} else {
2534+
return field_err;
2535+
};
2536+
if let Some(field_err) = &mut field_err {
2537+
// Most of the time, if the field error is the same as the struct error,
2538+
// the struct error only happens because of the field error.
2539+
if field_err.message.contains(msg) {
2540+
field_err.message = format!("because {}", field_err.message);
2541+
}
2542+
}
2543+
return Some(InitError::from(format!("`{ty}` {msg}")).nested(field_err));
2544+
}
2545+
}
2546+
field_err
24922547
}
24932548

24942549
/// Return `Some` only if we are sure this type does *not*
@@ -2501,63 +2556,36 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
25012556
use rustc_type_ir::sty::TyKind::*;
25022557
match ty.kind() {
25032558
// Primitive types that don't like 0 as a value.
2504-
Ref(..) => Some(("references must be non-null".to_string(), None)),
2505-
Adt(..) if ty.is_box() => Some(("`Box` must be non-null".to_string(), None)),
2506-
FnPtr(..) => Some(("function pointers must be non-null".to_string(), None)),
2507-
Never => Some(("the `!` type has no valid value".to_string(), None)),
2559+
Ref(..) => Some("references must be non-null".into()),
2560+
Adt(..) if ty.is_box() => Some("`Box` must be non-null".into()),
2561+
FnPtr(..) => Some("function pointers must be non-null".into()),
2562+
Never => Some("the `!` type has no valid value".into()),
25082563
RawPtr(tm) if matches!(tm.ty.kind(), Dynamic(..)) =>
25092564
// raw ptr to dyn Trait
25102565
{
2511-
Some(("the vtable of a wide raw pointer must be non-null".to_string(), None))
2566+
Some("the vtable of a wide raw pointer must be non-null".into())
25122567
}
25132568
// Primitive types with other constraints.
25142569
Bool if init == InitKind::Uninit => {
2515-
Some(("booleans must be either `true` or `false`".to_string(), None))
2570+
Some("booleans must be either `true` or `false`".into())
25162571
}
25172572
Char if init == InitKind::Uninit => {
2518-
Some(("characters must be a valid Unicode codepoint".to_string(), None))
2573+
Some("characters must be a valid Unicode codepoint".into())
25192574
}
25202575
Int(_) | Uint(_) if init == InitKind::Uninit => {
2521-
Some(("integers must not be uninitialized".to_string(), None))
2522-
}
2523-
Float(_) if init == InitKind::Uninit => {
2524-
Some(("floats must not be uninitialized".to_string(), None))
2576+
Some("integers must be initialized".into())
25252577
}
2578+
Float(_) if init == InitKind::Uninit => Some("floats must be initialized".into()),
25262579
RawPtr(_) if init == InitKind::Uninit => {
2527-
Some(("raw pointers must not be uninitialized".to_string(), None))
2580+
Some("raw pointers must be initialized".into())
25282581
}
25292582
// Recurse and checks for some compound types. (but not unions)
25302583
Adt(adt_def, substs) if !adt_def.is_union() => {
2531-
// First check if this ADT has a layout attribute (like `NonNull` and friends).
2532-
use std::ops::Bound;
2533-
match cx.tcx.layout_scalar_valid_range(adt_def.did()) {
2534-
// We exploit here that `layout_scalar_valid_range` will never
2535-
// return `Bound::Excluded`. (And we have tests checking that we
2536-
// handle the attribute correctly.)
2537-
// We don't add a span since users cannot declare such types anyway.
2538-
(Bound::Included(lo), Bound::Included(hi)) if 0 < lo && lo < hi => {
2539-
return Some((format!("`{}` must be non-null", ty), None));
2540-
}
2541-
(Bound::Included(lo), Bound::Unbounded) if 0 < lo => {
2542-
return Some((format!("`{}` must be non-null", ty), None));
2543-
}
2544-
(Bound::Included(_), _) | (_, Bound::Included(_))
2545-
if init == InitKind::Uninit =>
2546-
{
2547-
return Some((
2548-
format!(
2549-
"`{}` must be initialized inside its custom valid range",
2550-
ty,
2551-
),
2552-
None,
2553-
));
2554-
}
2555-
_ => {}
2556-
}
25572584
// Handle structs.
25582585
if adt_def.is_struct() {
25592586
return variant_find_init_error(
25602587
cx,
2588+
ty,
25612589
adt_def.non_enum_variant(),
25622590
substs,
25632591
"struct field",
@@ -2581,13 +2609,14 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
25812609
Some((variant, definitely_inhabited))
25822610
});
25832611
let Some(first_variant) = potential_variants.next() else {
2584-
return Some(("enums with no inhabited variants have no valid value".to_string(), Some(span)));
2612+
return Some(InitError::from("enums with no inhabited variants have no valid value").spanned(span));
25852613
};
25862614
// So we have at least one potentially inhabited variant. Might we have two?
25872615
let Some(second_variant) = potential_variants.next() else {
25882616
// There is only one potentially inhabited variant. So we can recursively check that variant!
25892617
return variant_find_init_error(
25902618
cx,
2619+
ty,
25912620
&first_variant.0,
25922621
substs,
25932622
"field of the only potentially inhabited enum variant",
@@ -2605,10 +2634,9 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
26052634
.filter(|(_variant, definitely_inhabited)| *definitely_inhabited)
26062635
.count();
26072636
if definitely_inhabited > 1 {
2608-
return Some((
2609-
"enums with multiple inhabited variants have to be initialized to a variant".to_string(),
2610-
Some(span),
2611-
));
2637+
return Some(InitError::from(
2638+
"enums with multiple inhabited variants have to be initialized to a variant",
2639+
).spanned(span));
26122640
}
26132641
}
26142642
// We couldn't find anything wrong here.
@@ -2637,8 +2665,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
26372665
// using zeroed or uninitialized memory.
26382666
// We are extremely conservative with what we warn about.
26392667
let conjured_ty = cx.typeck_results().expr_ty(expr);
2640-
if let Some((msg, span)) =
2641-
with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init))
2668+
if let Some(mut err) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init))
26422669
{
26432670
// FIXME(davidtwco): make translatable
26442671
cx.struct_span_lint(
@@ -2664,10 +2691,17 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
26642691
"help: use `MaybeUninit<T>` instead, \
26652692
and only call `assume_init` after initialization is done",
26662693
);
2667-
if let Some(span) = span {
2668-
lint.span_note(span, &msg);
2669-
} else {
2670-
lint.note(&msg);
2694+
loop {
2695+
if let Some(span) = err.span {
2696+
lint.span_note(span, &err.message);
2697+
} else {
2698+
lint.note(&err.message);
2699+
}
2700+
if let Some(e) = err.nested {
2701+
err = *e;
2702+
} else {
2703+
break;
2704+
}
26712705
}
26722706
lint
26732707
},

compiler/rustc_middle/src/ty/consts/int.rs

+12
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,18 @@ impl ScalarInt {
245245
self.to_bits(size)
246246
}
247247

248+
// Tries to convert the `ScalarInt` to `bool`. Fails if the `size` of the `ScalarInt`
249+
// in not equal to `Size { raw: 1 }` or if the value is not 0 or 1 and returns the `size`
250+
// value of the `ScalarInt` in that case.
251+
#[inline]
252+
pub fn try_to_bool(self) -> Result<bool, Size> {
253+
match self.try_to_u8()? {
254+
0 => Ok(false),
255+
1 => Ok(true),
256+
_ => Err(self.size()),
257+
}
258+
}
259+
248260
// Tries to convert the `ScalarInt` to `u8`. Fails if the `size` of the `ScalarInt`
249261
// in not equal to `Size { raw: 1 }` and returns the `size` value of the `ScalarInt` in
250262
// that case.

compiler/rustc_span/src/def_id.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ impl CrateNum {
3434

3535
impl fmt::Display for CrateNum {
3636
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37-
fmt::Display::fmt(&self.private, f)
37+
fmt::Display::fmt(&self.as_u32(), f)
3838
}
3939
}
4040

compiler/rustc_span/src/hygiene.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ pub struct ExpnId {
7676
impl fmt::Debug for ExpnId {
7777
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7878
// Generate crate_::{{expn_}}.
79-
write!(f, "{:?}::{{{{expn{}}}}}", self.krate, self.local_id.private)
79+
write!(f, "{:?}::{{{{expn{}}}}}", self.krate, self.local_id.as_u32())
8080
}
8181
}
8282

0 commit comments

Comments
 (0)