Skip to content

Commit feb20f2

Browse files
committed
Track ABI info. in NaiveLayout, and use it for PointerLike checks
THis significantly complicates `NaiveLayout` logic, but is necessary to ensure that bounds like `NonNull<T>: PointerLike` hold in generic contexts. Also implement exact layout computation for structs.
1 parent c30fbb9 commit feb20f2

File tree

5 files changed

+231
-93
lines changed

5 files changed

+231
-93
lines changed

compiler/rustc_middle/src/ty/layout.rs

Lines changed: 126 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_target::abi::call::FnAbi;
1515
use rustc_target::abi::*;
1616
use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy, Target};
1717

18-
use std::cmp::{self, Ordering};
18+
use std::cmp;
1919
use std::fmt;
2020
use std::num::NonZeroUsize;
2121
use std::ops::Bound;
@@ -316,8 +316,8 @@ impl<'tcx> SizeSkeleton<'tcx> {
316316
// First, try computing an exact naive layout (this covers simple types with generic
317317
// references, where a full static layout would fail).
318318
if let Ok(layout) = tcx.naive_layout_of(param_env.and(ty)) {
319-
if layout.is_exact {
320-
return Ok(SizeSkeleton::Known(layout.min_size));
319+
if layout.exact {
320+
return Ok(SizeSkeleton::Known(layout.size));
321321
}
322322
}
323323

@@ -650,51 +650,146 @@ impl std::ops::DerefMut for TyAndNaiveLayout<'_> {
650650
}
651651
}
652652

653-
/// A naive underestimation of the layout of a type.
653+
/// Extremely simplified representation of a type's layout.
654+
///
655+
///
654656
#[derive(Copy, Clone, Debug, HashStable)]
655657
pub struct NaiveLayout {
656-
pub min_size: Size,
657-
pub min_align: Align,
658-
// If `true`, `min_size` and `min_align` are guaranteed to be exact.
659-
pub is_exact: bool,
658+
pub abi: NaiveAbi,
659+
pub size: Size,
660+
pub align: Align,
661+
/// If `true`, `size` and `align` are exact.
662+
pub exact: bool,
663+
}
664+
665+
#[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable)]
666+
pub enum NaiveAbi {
667+
/// A scalar layout, always implies `exact`.
668+
Scalar(Primitive),
669+
/// An uninhabited layout. (needed to properly track `Scalar`)
670+
Uninhabited,
671+
/// An unsized aggregate. (needed to properly track `Scalar`)
672+
Unsized,
673+
Any,
674+
}
675+
676+
impl NaiveAbi {
677+
#[inline]
678+
pub fn as_aggregate(self) -> Self {
679+
match self {
680+
NaiveAbi::Scalar(_) => NaiveAbi::Any,
681+
_ => self,
682+
}
683+
}
660684
}
661685

662686
impl NaiveLayout {
663-
pub const UNKNOWN: Self = Self { min_size: Size::ZERO, min_align: Align::ONE, is_exact: false };
664-
pub const EMPTY: Self = Self { min_size: Size::ZERO, min_align: Align::ONE, is_exact: true };
665-
666-
pub fn is_compatible_with(&self, layout: Layout<'_>) -> bool {
667-
let cmp = |cmp: Ordering| match (cmp, self.is_exact) {
668-
(Ordering::Less | Ordering::Equal, false) => true,
669-
(Ordering::Equal, true) => true,
670-
(_, _) => false,
671-
};
687+
pub const EMPTY: Self =
688+
Self { size: Size::ZERO, align: Align::ONE, exact: true, abi: NaiveAbi::Any };
689+
690+
pub fn is_refined_by(&self, layout: Layout<'_>) -> bool {
691+
if self.size > layout.size() || self.align > layout.align().abi {
692+
return false;
693+
}
672694

673-
cmp(self.min_size.cmp(&layout.size())) && cmp(self.min_align.cmp(&layout.align().abi))
695+
if let NaiveAbi::Scalar(prim) = self.abi {
696+
assert!(self.exact);
697+
if !matches!(layout.abi(), Abi::Scalar(s) if s.primitive() == prim) {
698+
return false;
699+
}
700+
}
701+
702+
!self.exact || (self.size, self.align) == (layout.size(), layout.align().abi)
703+
}
704+
705+
/// Returns if this layout is known to be pointer-like (`None` if uncertain)
706+
///
707+
/// See the corresponding `Layout::is_pointer_like` method.
708+
pub fn is_pointer_like(&self, dl: &TargetDataLayout) -> Option<bool> {
709+
match self.abi {
710+
NaiveAbi::Scalar(_) => {
711+
assert!(self.exact);
712+
Some(self.size == dl.pointer_size && self.align == dl.pointer_align.abi)
713+
}
714+
NaiveAbi::Uninhabited | NaiveAbi::Unsized => Some(false),
715+
NaiveAbi::Any if self.exact => Some(false),
716+
NaiveAbi::Any => None,
717+
}
674718
}
675719

676720
#[must_use]
677-
pub fn pad_to_align(mut self) -> Self {
678-
self.min_size = self.min_size.align_to(self.min_align);
721+
#[inline]
722+
pub fn packed(mut self, align: Align) -> Self {
723+
if self.align > align {
724+
self.align = align;
725+
self.abi = self.abi.as_aggregate();
726+
}
679727
self
680728
}
681729

682730
#[must_use]
683-
pub fn concat<C: HasDataLayout>(&self, other: &Self, cx: &C) -> Option<Self> {
684-
Some(Self {
685-
min_size: self.min_size.checked_add(other.min_size, cx)?,
686-
min_align: std::cmp::max(self.min_align, other.min_align),
687-
is_exact: self.is_exact && other.is_exact,
688-
})
731+
#[inline]
732+
pub fn align_to(mut self, align: Align) -> Self {
733+
if align > self.align {
734+
self.align = align;
735+
self.abi = self.abi.as_aggregate();
736+
}
737+
self
689738
}
690739

691740
#[must_use]
692-
pub fn union(&self, other: &Self) -> Self {
693-
Self {
694-
min_size: std::cmp::max(self.min_size, other.min_size),
695-
min_align: std::cmp::max(self.min_align, other.min_align),
696-
is_exact: self.is_exact && other.is_exact,
741+
#[inline]
742+
pub fn pad_to_align(mut self, align: Align) -> Self {
743+
let new_size = self.size.align_to(align);
744+
if new_size > self.size {
745+
self.abi = self.abi.as_aggregate();
746+
self.size = new_size;
697747
}
748+
self
749+
}
750+
751+
#[must_use]
752+
#[inline]
753+
pub fn concat(&self, other: &Self, dl: &TargetDataLayout) -> Option<Self> {
754+
use NaiveAbi::*;
755+
756+
let size = self.size.checked_add(other.size, dl)?;
757+
let align = cmp::max(self.align, other.align);
758+
let exact = self.exact && other.exact;
759+
let abi = match (self.abi, other.abi) {
760+
// The uninhabited and unsized ABIs override everything.
761+
(Uninhabited, _) | (_, Uninhabited) => Uninhabited,
762+
(Unsized, _) | (_, Unsized) => Unsized,
763+
// A scalar struct must have a single non ZST-field.
764+
(_, s @ Scalar(_)) if exact && self.size == Size::ZERO => s,
765+
(s @ Scalar(_), _) if exact && other.size == Size::ZERO => s,
766+
// Default case.
767+
(_, _) => Any,
768+
};
769+
Some(Self { abi, size, align, exact })
770+
}
771+
772+
#[must_use]
773+
#[inline]
774+
pub fn union(&self, other: &Self) -> Self {
775+
use NaiveAbi::*;
776+
777+
let size = cmp::max(self.size, other.size);
778+
let align = cmp::max(self.align, other.align);
779+
let exact = self.exact && other.exact;
780+
let abi = match (self.abi, other.abi) {
781+
// The unsized ABI overrides everything.
782+
(Unsized, _) | (_, Unsized) => Unsized,
783+
// A scalar union must have a single non ZST-field.
784+
(_, s @ Scalar(_)) if exact && self.size == Size::ZERO => s,
785+
(s @ Scalar(_), _) if exact && other.size == Size::ZERO => s,
786+
// ...or identical scalar fields.
787+
(Scalar(s1), Scalar(s2)) if s1 == s2 => Scalar(s1),
788+
// Default cases.
789+
(Uninhabited, Uninhabited) => Uninhabited,
790+
(_, _) => Any,
791+
};
792+
Self { abi, size, align, exact }
698793
}
699794
}
700795

compiler/rustc_trait_selection/src/solve/trait_goals.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,20 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
223223
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
224224
}
225225

226-
if let Ok(layout) = tcx.layout_of(key)
227-
&& layout.layout.is_pointer_like(&tcx.data_layout)
228-
{
226+
// First, try computing an exact naive layout in case the type is generic.
227+
let is_pointer_like = if let Ok(layout) = tcx.naive_layout_of(key) {
228+
layout.is_pointer_like(&tcx.data_layout).unwrap_or_else(|| {
229+
// Second, we fall back to full layout computation.
230+
tcx.layout_of(key)
231+
.ok()
232+
.filter(|l| l.layout.is_pointer_like(&tcx.data_layout))
233+
.is_some()
234+
})
235+
} else {
236+
false
237+
};
238+
239+
if is_pointer_like {
229240
// FIXME: We could make this faster by making a no-constraints response
230241
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
231242
} else {

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -979,9 +979,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
979979
return;
980980
}
981981

982-
if let Ok(layout) = tcx.layout_of(key)
983-
&& layout.layout.is_pointer_like(&tcx.data_layout)
984-
{
982+
// First, try computing an exact naive layout in case the type is generic.
983+
let is_pointer_like = if let Ok(layout) = tcx.naive_layout_of(key) {
984+
layout.is_pointer_like(&tcx.data_layout).unwrap_or_else(|| {
985+
// Second, we fall back to full layout computation.
986+
tcx.layout_of(key)
987+
.ok()
988+
.filter(|l| l.layout.is_pointer_like(&tcx.data_layout))
989+
.is_some()
990+
})
991+
} else {
992+
false
993+
};
994+
995+
if is_pointer_like {
985996
candidates.vec.push(BuiltinCandidate { has_nested: false });
986997
}
987998
}

0 commit comments

Comments
 (0)