summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_transmute
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:57:19 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:57:19 +0000
commita0b8f38ab54ac451646aa00cd5e91b6c76f22a84 (patch)
treefc451898ccaf445814e26b46664d78702178101d /compiler/rustc_transmute
parentAdding debian version 1.71.1+dfsg1-2. (diff)
downloadrustc-a0b8f38ab54ac451646aa00cd5e91b6c76f22a84.tar.xz
rustc-a0b8f38ab54ac451646aa00cd5e91b6c76f22a84.zip
Merging upstream version 1.72.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_transmute')
-rw-r--r--compiler/rustc_transmute/src/layout/mod.rs42
-rw-r--r--compiler/rustc_transmute/src/layout/tree.rs23
-rw-r--r--compiler/rustc_transmute/src/lib.rs36
-rw-r--r--compiler/rustc_transmute/src/maybe_transmutable/mod.rs330
-rw-r--r--compiler/rustc_transmute/src/maybe_transmutable/tests.rs4
5 files changed, 294 insertions, 141 deletions
diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs
index f8d05bc89..76d97e0e6 100644
--- a/compiler/rustc_transmute/src/layout/mod.rs
+++ b/compiler/rustc_transmute/src/layout/mod.rs
@@ -30,33 +30,49 @@ impl fmt::Debug for Byte {
}
pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone {}
-pub trait Ref: Debug + Hash + Eq + PartialEq + Copy + Clone {}
+pub trait Ref: Debug + Hash + Eq + PartialEq + Copy + Clone {
+ fn min_align(&self) -> usize;
+
+ fn is_mutable(&self) -> bool;
+}
impl Def for ! {}
-impl Ref for ! {}
+impl Ref for ! {
+ fn min_align(&self) -> usize {
+ unreachable!()
+ }
+ fn is_mutable(&self) -> bool {
+ unreachable!()
+ }
+}
#[cfg(feature = "rustc")]
-pub(crate) mod rustc {
+pub mod rustc {
use rustc_middle::mir::Mutability;
- use rustc_middle::ty;
- use rustc_middle::ty::Region;
- use rustc_middle::ty::Ty;
+ use rustc_middle::ty::{self, Ty};
/// A reference in the layout.
#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)]
pub struct Ref<'tcx> {
- lifetime: Region<'tcx>,
- ty: Ty<'tcx>,
- mutability: Mutability,
+ pub lifetime: ty::Region<'tcx>,
+ pub ty: Ty<'tcx>,
+ pub mutability: Mutability,
+ pub align: usize,
}
- impl<'tcx> super::Ref for Ref<'tcx> {}
+ impl<'tcx> super::Ref for Ref<'tcx> {
+ fn min_align(&self) -> usize {
+ self.align
+ }
- impl<'tcx> Ref<'tcx> {
- pub fn min_align(&self) -> usize {
- todo!()
+ fn is_mutable(&self) -> bool {
+ match self.mutability {
+ Mutability::Mut => true,
+ Mutability::Not => false,
+ }
}
}
+ impl<'tcx> Ref<'tcx> {}
/// A visibility node in the layout.
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs
index a6d88b134..be434eb7d 100644
--- a/compiler/rustc_transmute/src/layout/tree.rs
+++ b/compiler/rustc_transmute/src/layout/tree.rs
@@ -188,14 +188,14 @@ pub(crate) mod rustc {
/// The layout of the type is unspecified.
Unspecified,
/// This error will be surfaced elsewhere by rustc, so don't surface it.
- Unknown,
+ UnknownLayout,
TypeError(ErrorGuaranteed),
}
- impl<'tcx> From<LayoutError<'tcx>> for Err {
- fn from(err: LayoutError<'tcx>) -> Self {
+ impl<'tcx> From<&LayoutError<'tcx>> for Err {
+ fn from(err: &LayoutError<'tcx>) -> Self {
match err {
- LayoutError::Unknown(..) => Self::Unknown,
+ LayoutError::Unknown(..) => Self::UnknownLayout,
err => unimplemented!("{:?}", err),
}
}
@@ -221,7 +221,7 @@ pub(crate) mod rustc {
}
impl LayoutSummary {
- fn from_ty<'tcx>(ty: Ty<'tcx>, ctx: TyCtxt<'tcx>) -> Result<Self, LayoutError<'tcx>> {
+ fn from_ty<'tcx>(ty: Ty<'tcx>, ctx: TyCtxt<'tcx>) -> Result<Self, &'tcx LayoutError<'tcx>> {
use rustc_middle::ty::ParamEnvAnd;
use rustc_target::abi::{TyAndLayout, Variants};
@@ -365,6 +365,17 @@ pub(crate) mod rustc {
}
}))
}
+
+ ty::Ref(lifetime, ty, mutability) => {
+ let align = layout_of(tcx, *ty)?.align();
+ Ok(Tree::Ref(Ref {
+ lifetime: *lifetime,
+ ty: *ty,
+ mutability: *mutability,
+ align,
+ }))
+ }
+
_ => Err(Err::Unspecified),
}
}
@@ -471,7 +482,7 @@ pub(crate) mod rustc {
fn layout_of<'tcx>(
ctx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
- ) -> Result<alloc::Layout, LayoutError<'tcx>> {
+ ) -> Result<alloc::Layout, &'tcx LayoutError<'tcx>> {
use rustc_middle::ty::ParamEnvAnd;
use rustc_target::abi::TyAndLayout;
diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs
index 77c0526e3..34ad6bd8c 100644
--- a/compiler/rustc_transmute/src/lib.rs
+++ b/compiler/rustc_transmute/src/lib.rs
@@ -8,7 +8,7 @@ extern crate tracing;
pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set};
-pub(crate) mod layout;
+pub mod layout;
pub(crate) mod maybe_transmutable;
#[derive(Default)]
@@ -19,29 +19,29 @@ pub struct Assume {
pub validity: bool,
}
-/// The type encodes answers to the question: "Are these types transmutable?"
-#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)]
-pub enum Answer<R>
-where
- R: layout::Ref,
-{
- /// `Src` is transmutable into `Dst`.
+/// Either we have an error, transmutation is allowed, or we have an optional
+/// Condition that must hold.
+#[derive(Debug, Hash, Eq, PartialEq, Clone)]
+pub enum Answer<R> {
Yes,
-
- /// `Src` is NOT transmutable into `Dst`.
No(Reason),
+ If(Condition<R>),
+}
+/// A condition which must hold for safe transmutation to be possible.
+#[derive(Debug, Hash, Eq, PartialEq, Clone)]
+pub enum Condition<R> {
/// `Src` is transmutable into `Dst`, if `src` is transmutable into `dst`.
IfTransmutable { src: R, dst: R },
/// `Src` is transmutable into `Dst`, if all of the enclosed requirements are met.
- IfAll(Vec<Answer<R>>),
+ IfAll(Vec<Condition<R>>),
/// `Src` is transmutable into `Dst` if any of the enclosed requirements are met.
- IfAny(Vec<Answer<R>>),
+ IfAny(Vec<Condition<R>>),
}
-/// Answers: Why wasn't the source type transmutable into the destination type?
+/// Answers "why wasn't the source type transmutable into the destination type?"
#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)]
pub enum Reason {
/// The layout of the source type is unspecified.
@@ -54,6 +54,16 @@ pub enum Reason {
DstIsPrivate,
/// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized.
DstIsTooBig,
+ /// Src should have a stricter alignment than Dst, but it does not.
+ DstHasStricterAlignment { src_min_align: usize, dst_min_align: usize },
+ /// Can't go from shared pointer to unique pointer
+ DstIsMoreUnique,
+ /// Encountered a type error
+ TypeError,
+ /// The layout of src is unknown
+ SrcLayoutUnknown,
+ /// The layout of dst is unknown
+ DstLayoutUnknown,
}
#[cfg(feature = "rustc")]
diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
index 2e2fb90e7..b223a90f7 100644
--- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
+++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
@@ -1,13 +1,13 @@
-use crate::Map;
-use crate::{Answer, Reason};
-
+pub(crate) mod query_context;
#[cfg(test)]
mod tests;
-mod query_context;
-use query_context::QueryContext;
+use crate::{
+ layout::{self, dfa, Byte, Dfa, Nfa, Ref, Tree, Uninhabited},
+ maybe_transmutable::query_context::QueryContext,
+ Answer, Condition, Map, Reason,
+};
-use crate::layout::{self, dfa, Byte, Dfa, Nfa, Tree, Uninhabited};
pub(crate) struct MaybeTransmutableQuery<L, C>
where
C: QueryContext,
@@ -33,6 +33,7 @@ where
Self { src, dst, scope, assume, context }
}
+ // FIXME(bryangarza): Delete this when all usages are removed
pub(crate) fn map_layouts<F, M>(
self,
f: F,
@@ -53,6 +54,7 @@ where
}
}
+// FIXME: Nix this cfg, so we can write unit tests independently of rustc
#[cfg(feature = "rustc")]
mod rustc {
use super::*;
@@ -66,30 +68,26 @@ mod rustc {
/// then computes an answer using those trees.
#[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))]
pub fn answer(self) -> Answer<<TyCtxt<'tcx> as QueryContext>::Ref> {
- let query_or_answer = self.map_layouts(|src, dst, scope, &context| {
- // Convert `src` and `dst` from their rustc representations, to `Tree`-based
- // representations. If these conversions fail, conclude that the transmutation is
- // unacceptable; the layouts of both the source and destination types must be
- // well-defined.
- let src = Tree::from_ty(src, context);
- let dst = Tree::from_ty(dst, context);
-
- match (src, dst) {
- // Answer `Yes` here, because 'unknown layout' and type errors will already
- // be reported by rustc. No need to spam the user with more errors.
- (Err(Err::TypeError(_)), _) => Err(Answer::Yes),
- (_, Err(Err::TypeError(_))) => Err(Answer::Yes),
- (Err(Err::Unknown), _) => Err(Answer::Yes),
- (_, Err(Err::Unknown)) => Err(Answer::Yes),
- (Err(Err::Unspecified), _) => Err(Answer::No(Reason::SrcIsUnspecified)),
- (_, Err(Err::Unspecified)) => Err(Answer::No(Reason::DstIsUnspecified)),
- (Ok(src), Ok(dst)) => Ok((src, dst)),
- }
- });
+ let Self { src, dst, scope, assume, context } = self;
+
+ // Convert `src` and `dst` from their rustc representations, to `Tree`-based
+ // representations. If these conversions fail, conclude that the transmutation is
+ // unacceptable; the layouts of both the source and destination types must be
+ // well-defined.
+ let src = Tree::from_ty(src, context);
+ let dst = Tree::from_ty(dst, context);
- match query_or_answer {
- Ok(query) => query.answer(),
- Err(answer) => answer,
+ match (src, dst) {
+ (Err(Err::TypeError(_)), _) | (_, Err(Err::TypeError(_))) => {
+ Answer::No(Reason::TypeError)
+ }
+ (Err(Err::UnknownLayout), _) => Answer::No(Reason::SrcLayoutUnknown),
+ (_, Err(Err::UnknownLayout)) => Answer::No(Reason::DstLayoutUnknown),
+ (Err(Err::Unspecified), _) => Answer::No(Reason::SrcIsUnspecified),
+ (_, Err(Err::Unspecified)) => Answer::No(Reason::DstIsUnspecified),
+ (Ok(src), Ok(dst)) => {
+ MaybeTransmutableQuery { src, dst, scope, assume, context }.answer()
+ }
}
}
}
@@ -107,6 +105,7 @@ where
#[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))]
pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
let assume_visibility = self.assume.safety;
+ // FIXME(bryangarza): Refactor this code to get rid of `map_layouts`
let query_or_answer = self.map_layouts(|src, dst, scope, context| {
// Remove all `Def` nodes from `src`, without checking their visibility.
let src = src.prune(&|def| true);
@@ -155,6 +154,7 @@ where
#[inline(always)]
#[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))]
pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
+ // FIXME(bryangarza): Refactor this code to get rid of `map_layouts`
let query_or_answer = self
.map_layouts(|src, dst, scope, context| Ok((Dfa::from_nfa(src), Dfa::from_nfa(dst))));
@@ -203,8 +203,29 @@ where
if let Some(answer) = cache.get(&(src_state, dst_state)) {
answer.clone()
} else {
+ debug!(?src_state, ?dst_state);
+ debug!(src = ?self.src);
+ debug!(dst = ?self.dst);
+ debug!(
+ src_transitions_len = self.src.transitions.len(),
+ dst_transitions_len = self.dst.transitions.len()
+ );
let answer = if dst_state == self.dst.accepting {
// truncation: `size_of(Src) >= size_of(Dst)`
+ //
+ // Why is truncation OK to do? Because even though the Src is bigger, all we care about
+ // is whether we have enough data for the Dst to be valid in accordance with what its
+ // type dictates.
+ // For example, in a u8 to `()` transmutation, we have enough data available from the u8
+ // to transmute it to a `()` (though in this case does `()` really need any data to
+ // begin with? It doesn't). Same thing with u8 to fieldless struct.
+ // Now then, why is something like u8 to bool not allowed? That is not because the bool
+ // is smaller in size, but rather because those 2 bits that we are re-interpreting from
+ // the u8 could introduce invalid states for the bool type.
+ //
+ // So, if it's possible to transmute to a smaller Dst by truncating, and we can guarantee
+ // that none of the actually-used data can introduce an invalid state for Dst's type, we
+ // are able to safely transmute, even with truncation.
Answer::Yes
} else if src_state == self.src.accepting {
// extension: `size_of(Src) >= size_of(Dst)`
@@ -214,108 +235,201 @@ where
Answer::No(Reason::DstIsTooBig)
}
} else {
- let src_quantification = if self.assume.validity {
+ let src_quantifier = if self.assume.validity {
// if the compiler may assume that the programmer is doing additional validity checks,
// (e.g.: that `src != 3u8` when the destination type is `bool`)
// then there must exist at least one transition out of `src_state` such that the transmute is viable...
- there_exists
+ Quantifier::ThereExists
} else {
// if the compiler cannot assume that the programmer is doing additional validity checks,
// then for all transitions out of `src_state`, such that the transmute is viable...
- // then there must exist at least one transition out of `src_state` such that the transmute is viable...
- for_all
+ // then there must exist at least one transition out of `dst_state` such that the transmute is viable...
+ Quantifier::ForAll
+ };
+
+ let bytes_answer = src_quantifier.apply(
+ // for each of the byte transitions out of the `src_state`...
+ self.src.bytes_from(src_state).unwrap_or(&Map::default()).into_iter().map(
+ |(&src_validity, &src_state_prime)| {
+ // ...try to find a matching transition out of `dst_state`.
+ if let Some(dst_state_prime) =
+ self.dst.byte_from(dst_state, src_validity)
+ {
+ self.answer_memo(cache, src_state_prime, dst_state_prime)
+ } else if let Some(dst_state_prime) =
+ // otherwise, see if `dst_state` has any outgoing `Uninit` transitions
+ // (any init byte is a valid uninit byte)
+ self.dst.byte_from(dst_state, Byte::Uninit)
+ {
+ self.answer_memo(cache, src_state_prime, dst_state_prime)
+ } else {
+ // otherwise, we've exhausted our options.
+ // the DFAs, from this point onwards, are bit-incompatible.
+ Answer::No(Reason::DstIsBitIncompatible)
+ }
+ },
+ ),
+ );
+
+ // The below early returns reflect how this code would behave:
+ // if self.assume.validity {
+ // or(bytes_answer, refs_answer)
+ // } else {
+ // and(bytes_answer, refs_answer)
+ // }
+ // ...if `refs_answer` was computed lazily. The below early
+ // returns can be deleted without impacting the correctness of
+ // the algoritm; only its performance.
+ debug!(?bytes_answer);
+ match bytes_answer {
+ Answer::No(_) if !self.assume.validity => return bytes_answer,
+ Answer::Yes if self.assume.validity => return bytes_answer,
+ _ => {}
};
- src_quantification(
- self.src.bytes_from(src_state).unwrap_or(&Map::default()),
- |(&src_validity, &src_state_prime)| {
- if let Some(dst_state_prime) = self.dst.byte_from(dst_state, src_validity) {
- self.answer_memo(cache, src_state_prime, dst_state_prime)
- } else if let Some(dst_state_prime) =
- self.dst.byte_from(dst_state, Byte::Uninit)
- {
- self.answer_memo(cache, src_state_prime, dst_state_prime)
- } else {
- Answer::No(Reason::DstIsBitIncompatible)
- }
- },
- )
+ let refs_answer = src_quantifier.apply(
+ // for each reference transition out of `src_state`...
+ self.src.refs_from(src_state).unwrap_or(&Map::default()).into_iter().map(
+ |(&src_ref, &src_state_prime)| {
+ // ...there exists a reference transition out of `dst_state`...
+ Quantifier::ThereExists.apply(
+ self.dst
+ .refs_from(dst_state)
+ .unwrap_or(&Map::default())
+ .into_iter()
+ .map(|(&dst_ref, &dst_state_prime)| {
+ if !src_ref.is_mutable() && dst_ref.is_mutable() {
+ Answer::No(Reason::DstIsMoreUnique)
+ } else if !self.assume.alignment
+ && src_ref.min_align() < dst_ref.min_align()
+ {
+ Answer::No(Reason::DstHasStricterAlignment {
+ src_min_align: src_ref.min_align(),
+ dst_min_align: dst_ref.min_align(),
+ })
+ } else {
+ // ...such that `src` is transmutable into `dst`, if
+ // `src_ref` is transmutability into `dst_ref`.
+ and(
+ Answer::If(Condition::IfTransmutable {
+ src: src_ref,
+ dst: dst_ref,
+ }),
+ self.answer_memo(
+ cache,
+ src_state_prime,
+ dst_state_prime,
+ ),
+ )
+ }
+ }),
+ )
+ },
+ ),
+ );
+
+ if self.assume.validity {
+ or(bytes_answer, refs_answer)
+ } else {
+ and(bytes_answer, refs_answer)
+ }
};
- cache.insert((src_state, dst_state), answer.clone());
+ if let Some(..) = cache.insert((src_state, dst_state), answer.clone()) {
+ panic!("failed to correctly cache transmutability")
+ }
answer
}
}
}
-impl<R> Answer<R>
+fn and<R>(lhs: Answer<R>, rhs: Answer<R>) -> Answer<R>
where
- R: layout::Ref,
+ R: PartialEq,
{
- pub(crate) fn and(self, rhs: Self) -> Self {
- match (self, rhs) {
- (Self::No(reason), _) | (_, Self::No(reason)) => Self::No(reason),
- (Self::Yes, Self::Yes) => Self::Yes,
- (Self::IfAll(mut lhs), Self::IfAll(ref mut rhs)) => {
- lhs.append(rhs);
- Self::IfAll(lhs)
- }
- (constraint, Self::IfAll(mut constraints))
- | (Self::IfAll(mut constraints), constraint) => {
- constraints.push(constraint);
- Self::IfAll(constraints)
- }
- (lhs, rhs) => Self::IfAll(vec![lhs, rhs]),
+ match (lhs, rhs) {
+ // If both are errors, then we should return the more specific one
+ (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason))
+ | (Answer::No(reason), Answer::No(_))
+ // If either is an error, return it
+ | (Answer::No(reason), _) | (_, Answer::No(reason)) => Answer::No(reason),
+ // If only one side has a condition, pass it along
+ | (Answer::Yes, other) | (other, Answer::Yes) => other,
+ // If both sides have IfAll conditions, merge them
+ (Answer::If(Condition::IfAll(mut lhs)), Answer::If(Condition::IfAll(ref mut rhs))) => {
+ lhs.append(rhs);
+ Answer::If(Condition::IfAll(lhs))
}
- }
-
- pub(crate) fn or(self, rhs: Self) -> Self {
- match (self, rhs) {
- (Self::Yes, _) | (_, Self::Yes) => Self::Yes,
- (Self::No(lhr), Self::No(rhr)) => Self::No(lhr),
- (Self::IfAny(mut lhs), Self::IfAny(ref mut rhs)) => {
- lhs.append(rhs);
- Self::IfAny(lhs)
- }
- (constraint, Self::IfAny(mut constraints))
- | (Self::IfAny(mut constraints), constraint) => {
- constraints.push(constraint);
- Self::IfAny(constraints)
- }
- (lhs, rhs) => Self::IfAny(vec![lhs, rhs]),
+ // If only one side is an IfAll, add the other Condition to it
+ (Answer::If(cond), Answer::If(Condition::IfAll(mut conds)))
+ | (Answer::If(Condition::IfAll(mut conds)), Answer::If(cond)) => {
+ conds.push(cond);
+ Answer::If(Condition::IfAll(conds))
}
+ // Otherwise, both lhs and rhs conditions can be combined in a parent IfAll
+ (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAll(vec![lhs, rhs])),
}
}
-pub fn for_all<R, I, F>(iter: I, f: F) -> Answer<R>
+fn or<R>(lhs: Answer<R>, rhs: Answer<R>) -> Answer<R>
where
- R: layout::Ref,
- I: IntoIterator,
- F: FnMut(<I as IntoIterator>::Item) -> Answer<R>,
+ R: PartialEq,
{
- use std::ops::ControlFlow::{Break, Continue};
- let (Continue(result) | Break(result)) =
- iter.into_iter().map(f).try_fold(Answer::Yes, |constraints, constraint| {
- match constraint.and(constraints) {
- Answer::No(reason) => Break(Answer::No(reason)),
- maybe => Continue(maybe),
- }
- });
- result
+ match (lhs, rhs) {
+ // If both are errors, then we should return the more specific one
+ (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason))
+ | (Answer::No(reason), Answer::No(_)) => Answer::No(reason),
+ // Otherwise, errors can be ignored for the rest of the pattern matching
+ (Answer::No(_), other) | (other, Answer::No(_)) => or(other, Answer::Yes),
+ // If only one side has a condition, pass it along
+ (Answer::Yes, other) | (other, Answer::Yes) => other,
+ // If both sides have IfAny conditions, merge them
+ (Answer::If(Condition::IfAny(mut lhs)), Answer::If(Condition::IfAny(ref mut rhs))) => {
+ lhs.append(rhs);
+ Answer::If(Condition::IfAny(lhs))
+ }
+ // If only one side is an IfAny, add the other Condition to it
+ (Answer::If(cond), Answer::If(Condition::IfAny(mut conds)))
+ | (Answer::If(Condition::IfAny(mut conds)), Answer::If(cond)) => {
+ conds.push(cond);
+ Answer::If(Condition::IfAny(conds))
+ }
+ // Otherwise, both lhs and rhs conditions can be combined in a parent IfAny
+ (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAny(vec![lhs, rhs])),
+ }
}
-pub fn there_exists<R, I, F>(iter: I, f: F) -> Answer<R>
-where
- R: layout::Ref,
- I: IntoIterator,
- F: FnMut(<I as IntoIterator>::Item) -> Answer<R>,
-{
- use std::ops::ControlFlow::{Break, Continue};
- let (Continue(result) | Break(result)) = iter.into_iter().map(f).try_fold(
- Answer::No(Reason::DstIsBitIncompatible),
- |constraints, constraint| match constraint.or(constraints) {
- Answer::Yes => Break(Answer::Yes),
- maybe => Continue(maybe),
- },
- );
- result
+pub enum Quantifier {
+ ThereExists,
+ ForAll,
+}
+
+impl Quantifier {
+ pub fn apply<R, I>(&self, iter: I) -> Answer<R>
+ where
+ R: layout::Ref,
+ I: IntoIterator<Item = Answer<R>>,
+ {
+ use std::ops::ControlFlow::{Break, Continue};
+
+ let (init, try_fold_f): (_, fn(_, _) -> _) = match self {
+ Self::ThereExists => {
+ (Answer::No(Reason::DstIsBitIncompatible), |accum: Answer<R>, next| {
+ match or(accum, next) {
+ Answer::Yes => Break(Answer::Yes),
+ maybe => Continue(maybe),
+ }
+ })
+ }
+ Self::ForAll => (Answer::Yes, |accum: Answer<R>, next| {
+ let answer = and(accum, next);
+ match answer {
+ Answer::No(_) => Break(answer),
+ maybe => Continue(maybe),
+ }
+ }),
+ };
+
+ let (Continue(result) | Break(result)) = iter.into_iter().try_fold(init, try_fold_f);
+ result
+ }
}
diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs
index a8675f4ae..e49bebf57 100644
--- a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs
+++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs
@@ -1,9 +1,11 @@
use super::query_context::test::{Def, UltraMinimal};
use crate::maybe_transmutable::MaybeTransmutableQuery;
-use crate::{layout, Answer, Reason};
+use crate::{layout, Reason};
use itertools::Itertools;
mod bool {
+ use crate::Answer;
+
use super::*;
#[test]