Skip to content
Open
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
51 changes: 1 addition & 50 deletions compiler/rustc_abi/src/layout.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::BTreeSet;
use std::fmt::{self, Write};
use std::ops::{Bound, Deref};
use std::ops::Deref;
use std::{cmp, iter};

use rustc_hashes::Hash64;
Expand Down Expand Up @@ -348,7 +348,6 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
is_enum: bool,
is_special_no_niche: bool,
scalar_valid_range: (Bound<u128>, Bound<u128>),
discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool),
discriminants: impl Iterator<Item = (VariantIdx, i128)>,
always_sized: bool,
Expand Down Expand Up @@ -380,7 +379,6 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
variants,
is_enum,
is_special_no_niche,
scalar_valid_range,
always_sized,
present_first,
)
Expand Down Expand Up @@ -530,7 +528,6 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
is_enum: bool,
is_special_no_niche: bool,
scalar_valid_range: (Bound<u128>, Bound<u128>),
always_sized: bool,
present_first: VariantIdx,
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
Expand Down Expand Up @@ -570,52 +567,6 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
return Ok(st);
}

let (start, end) = scalar_valid_range;
match st.backend_repr {
BackendRepr::Scalar(ref mut scalar) | BackendRepr::ScalarPair(ref mut scalar, _) => {
// Enlarging validity ranges would result in missed
// optimizations, *not* wrongly assuming the inner
// value is valid. e.g. unions already enlarge validity ranges,
// because the values may be uninitialized.
//
// Because of that we only check that the start and end
// of the range is representable with this scalar type.

let max_value = scalar.size(dl).unsigned_int_max();
if let Bound::Included(start) = start {
// FIXME(eddyb) this might be incorrect - it doesn't
// account for wrap-around (end < start) ranges.
assert!(start <= max_value, "{start} > {max_value}");
scalar.valid_range_mut().start = start;
}
if let Bound::Included(end) = end {
// FIXME(eddyb) this might be incorrect - it doesn't
// account for wrap-around (end < start) ranges.
assert!(end <= max_value, "{end} > {max_value}");
scalar.valid_range_mut().end = end;
}

// Update `largest_niche` if we have introduced a larger niche.
let niche = Niche::from_scalar(dl, Size::ZERO, *scalar);
if let Some(niche) = niche {
match st.largest_niche {
Some(largest_niche) => {
// Replace the existing niche even if they're equal,
// because this one is at a lower offset.
if largest_niche.available(dl) <= niche.available(dl) {
st.largest_niche = Some(niche);
}
}
None => st.largest_niche = Some(niche),
}
}
}
_ => assert!(
start == Bound::Unbounded && end == Bound::Unbounded,
"nonscalar layout for layout_scalar_valid_range type: {st:#?}",
),
}

Ok(st)
}

Expand Down
26 changes: 0 additions & 26 deletions compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,32 +92,6 @@ impl NoArgsAttributeParser for RustcNoImplicitAutorefsParser {
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoImplicitAutorefs;
}

pub(crate) struct RustcLayoutScalarValidRangeStartParser;

impl SingleAttributeParser for RustcLayoutScalarValidRangeStartParser {
const PATH: &[Symbol] = &[sym::rustc_layout_scalar_valid_range_start];
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
const TEMPLATE: AttributeTemplate = template!(List: &["start"]);

fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
parse_single_integer(cx, args)
.map(|n| AttributeKind::RustcLayoutScalarValidRangeStart(Box::new(n), cx.attr_span))
}
}

pub(crate) struct RustcLayoutScalarValidRangeEndParser;

impl SingleAttributeParser for RustcLayoutScalarValidRangeEndParser {
const PATH: &[Symbol] = &[sym::rustc_layout_scalar_valid_range_end];
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
const TEMPLATE: AttributeTemplate = template!(List: &["end"]);

fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
parse_single_integer(cx, args)
.map(|n| AttributeKind::RustcLayoutScalarValidRangeEnd(Box::new(n), cx.attr_span))
}
}

pub(crate) struct RustcLegacyConstGenericsParser;

impl SingleAttributeParser for RustcLegacyConstGenericsParser {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_attr_parsing/src/attributes/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub fn is_builtin_attr(attr: &ast::Attribute) -> bool {
/// Parse a single integer.
///
/// Used by attributes that take a single integer as argument, such as
/// `#[link_ordinal]` and `#[rustc_layout_scalar_valid_range_start]`.
/// `#[link_ordinal]`.
/// `cx` is the context given to the attribute.
/// `args` is the parser for the attribute arguments.
pub(crate) fn parse_single_integer(
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,6 @@ attribute_parsers!(
Single<RustcDumpSymbolNameParser>,
Single<RustcForceInlineParser>,
Single<RustcIfThisChangedParser>,
Single<RustcLayoutScalarValidRangeEndParser>,
Single<RustcLayoutScalarValidRangeStartParser>,
Single<RustcLegacyConstGenericsParser>,
Single<RustcLintOptDenyFieldAccessParser>,
Single<RustcMacroTransparencyParser>,
Expand Down
100 changes: 66 additions & 34 deletions compiler/rustc_const_eval/src/interpret/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1442,7 +1442,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
// First check that the base type is valid
self.visit_value(&val.transmute(self.ecx.layout_of(*base)?, self.ecx)?)?;
// When you extend this match, make sure to also add tests to
// tests/ui/type/pattern_types/validity.rs((
// tests/ui/type/pattern_types/validity.rs
match **pat {
// Range and non-null patterns are precisely reflected into `valid_range` and thus
// handled fully by `visit_scalar` (called below).
Expand All @@ -1457,6 +1457,34 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
// we won't see optimizations actually breaking such programs.
ty::PatternKind::Or(_patterns) => {}
}
// FIXME(pattern_types): handle everything based on the pattern, not on the layout.
// it's ok to run scalar validation even if the pattern type is `u8 is 0..=255` and thus
// allows uninit values, because that's rare and so not a perf issue.
match val.layout.backend_repr {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
match val.layout.backend_repr {
// FIXME(pattern_types): Instead of checking the resulting layout, we should be checking
// the pattern directly.
match val.layout.backend_repr {

Fine for now but structurally it'd make a lot more sense to check the pattern rather than the layout (and then somehow have a sanity check that the layout does not add extra restrictions not covered by the pattern).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it does, but I haven't figured out how to do that without just repeating a lot of logic. Tho I guess we can just not look at whether is_uninit_valid is set, because if your pattern type is the full range of what the base type allows, that's so rare, that the small perf hit is fine to take

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_uninit_valid is only set for unions. I hope those aren't allowed in pattern types.^^

BackendRepr::Scalar(scalar_layout) => {
if !scalar_layout.is_uninit_valid() {
// There is something to check here.
// We read directly via `ecx` since the read cannot fail -- we already read
// this field above when recursing into the field.
let scalar = self.ecx.read_scalar(val)?;
self.visit_scalar(scalar, scalar_layout)?;
}
}
BackendRepr::ScalarPair(a_layout, b_layout) => {
// We can only proceed if *both* scalars need to be initialized.
// FIXME: find a way to also check ScalarPair when one side can be uninit but
// the other must be init.
if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() {
// We read directly via `ecx` since the read cannot fail -- we already read
// this field above when recursing into the field.
let (a, b) = self.ecx.read_immediate(val)?.to_scalar_pair();
self.visit_scalar(a, a_layout)?;
self.visit_scalar(b, b_layout)?;
}
}
BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } => unreachable!(),
BackendRepr::Memory { .. } => unreachable!()
}
}
ty::Adt(adt, _) if adt.is_maybe_dangling() => {
let old_may_dangle = mem::replace(&mut self.may_dangle, true);
Expand All @@ -1479,51 +1507,55 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
}
}

// *After* all of this, check further information stored in the layout. We need to check
// this to handle types like `NonNull` where the `Scalar` info is more restrictive than what
// the fields say (`rustc_layout_scalar_valid_range_start`). But in most cases, this will
// just propagate what the fields say, and then we want the error to point at the field --
// so, we first recurse, then we do this check.
// *After* all of this, check further information stored in the layout.
// On leaf types like `!` or empty enums, this will raise the error.
// This means that for types wrapping such a type, we won't ever get here, but it's
// just the simplest way to check for this case.
//
// FIXME: We could avoid some redundant checks here. For newtypes wrapping
// scalars, we do the same check on every "level" (e.g., first we check
// MyNewtype and then the scalar in there).
// the fields of MyNewtype, and then we check MyNewType again).
Comment on lines +1510 to +1517
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems mostly outdated now, and the FIXME is resolved because there are no longer any redundant checks.

The only question that remains is why we need an is_uninhabited check here. Can this even be reached? Maybe it can be turned into an assertion that just double-checks that indeed we caught all uninhabited types above?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the uninhabited check is still reachable and it's sufficiently unrelated to pattern types and layout attributes that I've decided to leave the fixme and try it in a separate PR once this one lands

if val.layout.is_uninhabited() {
let ty = val.layout.ty;
throw_validation_failure!(
self.path,
format!("encountered a value of uninhabited type `{ty}`")
);
}
match val.layout.backend_repr {
BackendRepr::Scalar(scalar_layout) => {
if !scalar_layout.is_uninit_valid() {
// There is something to check here.
// We read directly via `ecx` since the read cannot fail -- we already read
// this field above when recursing into the field.
let scalar = self.ecx.read_scalar(val)?;
self.visit_scalar(scalar, scalar_layout)?;
if cfg!(debug_assertions) {
// Check that we don't miss any new changes to layout computation in our checks above.
match val.layout.backend_repr {
BackendRepr::Scalar(scalar_layout) => {
if !scalar_layout.is_uninit_valid() {
// There is something to check here.
// We read directly via `ecx` since the read cannot fail -- we already read
// this field above when recursing into the field.
let scalar = self
.ecx
.read_scalar(val)
.expect("the above checks should have fully handled this situation");
self.visit_scalar(scalar, scalar_layout)
.expect("the above checks should have fully handled this situation");
}
}
}
BackendRepr::ScalarPair(a_layout, b_layout) => {
// We can only proceed if *both* scalars need to be initialized.
// FIXME: find a way to also check ScalarPair when one side can be uninit but
// the other must be init.
if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() {
// We read directly via `ecx` since the read cannot fail -- we already read
// this field above when recursing into the field.
let (a, b) = self.ecx.read_immediate(val)?.to_scalar_pair();
self.visit_scalar(a, a_layout)?;
self.visit_scalar(b, b_layout)?;
BackendRepr::ScalarPair(a_layout, b_layout) => {
// We can only proceed if *both* scalars need to be initialized.
// FIXME: find a way to also check ScalarPair when one side can be uninit but
// the other must be init.
if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() {
let (a, b) = self
.ecx
.read_immediate(val)
.expect("the above checks should have fully handled this situation")
.to_scalar_pair();
self.visit_scalar(a, a_layout)
.expect("the above checks should have fully handled this situation");
self.visit_scalar(b, b_layout)
.expect("the above checks should have fully handled this situation");
}
}
}
BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } => {
// No checks here, we assume layout computation gets this right.
// (This is harder to check since Miri does not represent these as `Immediate`. We
// also cannot use field projections since this might be a newtype around a vector.)
}
BackendRepr::Memory { .. } => {
// Nothing to do.
BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } => {}
BackendRepr::Memory { .. } => {}
}
}

Expand Down
10 changes: 0 additions & 10 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -573,16 +573,6 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
// Internal attributes, Layout related:
// ==========================================================================

rustc_attr!(
rustc_layout_scalar_valid_range_start,
"the `#[rustc_layout_scalar_valid_range_start]` attribute is just used to enable \
niche optimizations in the standard library",
),
rustc_attr!(
rustc_layout_scalar_valid_range_end,
"the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \
niche optimizations in the standard library",
),
rustc_attr!(
rustc_simd_monomorphize_lane_limit,
"the `#[rustc_simd_monomorphize_lane_limit]` attribute is just used by std::simd \
Expand Down
6 changes: 0 additions & 6 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1435,12 +1435,6 @@ pub enum AttributeKind {
/// Represents `#[rustc_intrinsic_const_stable_indirect]`
RustcIntrinsicConstStableIndirect,

/// Represents `#[rustc_layout_scalar_valid_range_end]`.
RustcLayoutScalarValidRangeEnd(Box<u128>, Span),

/// Represents `#[rustc_layout_scalar_valid_range_start]`.
RustcLayoutScalarValidRangeStart(Box<u128>, Span),

/// Represents `#[rustc_legacy_const_generics]`
RustcLegacyConstGenerics {
fn_indexes: ThinVec<(usize, Span)>,
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,6 @@ impl AttributeKind {
RustcInsignificantDtor => Yes,
RustcIntrinsic => Yes,
RustcIntrinsicConstStableIndirect => No,
RustcLayoutScalarValidRangeEnd(..) => Yes,
RustcLayoutScalarValidRangeStart(..) => Yes,
RustcLegacyConstGenerics { .. } => Yes,
RustcLintOptDenyFieldAccess { .. } => Yes,
RustcLintOptTy => Yes,
Expand Down
9 changes: 2 additions & 7 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
//! crate as a kind of pass. This should eventually be factored away.

use std::cell::Cell;
use std::ops::{Bound, ControlFlow};
use std::ops::ControlFlow;
use std::{assert_matches, iter};

use rustc_abi::{ExternAbi, Size};
Expand Down Expand Up @@ -1039,12 +1039,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
.fields()
.iter()
.map(|f| tcx.type_of(f.def_id).instantiate_identity().skip_norm_wip());
// constructors for structs with `layout_scalar_valid_range` are unsafe to call
let safety = match tcx.layout_scalar_valid_range(adt_def_id) {
(Bound::Unbounded, Bound::Unbounded) => hir::Safety::Safe,
_ => hir::Safety::Unsafe,
};
ty::Binder::dummy(tcx.mk_fn_sig_rust_abi(inputs, ty, safety))
ty::Binder::dummy(tcx.mk_fn_sig_rust_abi(inputs, ty, hir::Safety::Safe))
}

Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => {
Expand Down
13 changes: 1 addition & 12 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::env::VarError;
use std::ffi::OsStr;
use std::hash::{Hash, Hasher};
use std::marker::{PhantomData, PointeeSized};
use std::ops::{Bound, Deref};
use std::ops::Deref;
use std::sync::{Arc, OnceLock};
use std::{fmt, iter, mem};

Expand Down Expand Up @@ -947,17 +947,6 @@ impl<'tcx> TyCtxt<'tcx> {
matches!(self.as_lang_item(def_id), Some(LangItem::Sized | LangItem::MetaSized))
}

/// Returns a range of the start/end indices specified with the
/// `rustc_layout_scalar_valid_range` attribute.
// FIXME(eddyb) this is an awkward spot for this method, maybe move it?
pub fn layout_scalar_valid_range(self, def_id: DefId) -> (Bound<u128>, Bound<u128>) {
let start = find_attr!(self, def_id, RustcLayoutScalarValidRangeStart(n, _) => Bound::Included(**n)).unwrap_or(Bound::Unbounded);
let end =
find_attr!(self, def_id, RustcLayoutScalarValidRangeEnd(n, _) => Bound::Included(**n))
.unwrap_or(Bound::Unbounded);
(start, end)
}

pub fn lift<T: Lift<TyCtxt<'tcx>>>(self, value: T) -> Option<T::Lifted> {
value.lift_to_interner(self)
}
Expand Down
13 changes: 1 addition & 12 deletions compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::ops::Bound;
use std::{cmp, fmt};

use rustc_abi as abi;
Expand Down Expand Up @@ -465,17 +464,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
// Newtype.
if def.variants().len() == 1 {
if let Some(SizeSkeleton::Pointer { non_zero, tail }) = v0 {
return Ok(SizeSkeleton::Pointer {
non_zero: non_zero
|| match tcx.layout_scalar_valid_range(def.did()) {
(Bound::Included(start), Bound::Unbounded) => start > 0,
(Bound::Included(start), Bound::Included(end)) => {
0 < start && start < end
}
_ => false,
},
tail,
});
return Ok(SizeSkeleton::Pointer { non_zero, tail });
} else {
return Err(err);
}
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_mir_build/src/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1276,5 +1276,3 @@ mod expr;
mod matches;
mod misc;
mod scope;

pub(crate) use expr::category::Category as ExprCategory;
Loading