Safe Transmute: Fix propagation of errors

- Make sure that the most specific Reason is the one that bubbles up when we
  are folding over the `Answer` tree. `Reason::DstIsBitIncompatible` is the
  least specific, so that should be used only when there isn't anything else
  available.
- Small fixes where we used the wrong Reason variant.
- Tiny cleanups
This commit is contained in:
Bryan Garza 2023-04-27 17:19:16 -07:00
parent 263a4f2cb6
commit 94ad084ac6
12 changed files with 67 additions and 52 deletions

View file

@ -1,4 +1,4 @@
#![feature(alloc_layout_extra, decl_macro, iterator_try_reduce, never_type)]
#![feature(alloc_layout_extra, decl_macro, iterator_try_reduce, never_type, let_chains)]
#![allow(dead_code, unused_variables)]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]

View file

@ -78,11 +78,12 @@ mod rustc {
match (src, dst) {
// Answer `Ok(None)` 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(Err::TypeError(_))) => Err(Ok(None)),
(Err(Err::Unknown), _) | (_, Err(Err::Unknown)) => Err(Ok(None)),
(Err(Err::Unspecified), _) | (_, Err(Err::Unspecified)) => {
Err(Err(Reason::SrcIsUnspecified))
}
(Err(Err::TypeError(_)), _)
| (_, Err(Err::TypeError(_)))
| (Err(Err::Unknown), _)
| (_, Err(Err::Unknown)) => Err(Ok(None)),
(Err(Err::Unspecified), _) => Err(Err(Reason::SrcIsUnspecified)),
(_, Err(Err::Unspecified)) => Err(Err(Reason::DstIsUnspecified)),
(Ok(src), Ok(dst)) => Ok((src, dst)),
}
});
@ -316,12 +317,19 @@ where
}
}
fn and<R>(lhs: Answer<R>, rhs: Answer<R>) -> Answer<R> {
// Should propagate errors on the right side, because the initial value
// used in `apply` is on the left side.
let rhs = rhs?;
let lhs = lhs?;
Ok(match (lhs, rhs) {
fn and<R>(lhs: Answer<R>, rhs: Answer<R>) -> Answer<R>
where
R: PartialEq,
{
// If both are errors, then we should return the more specific one
if lhs.is_err() && rhs.is_err() {
if lhs == Err(Reason::DstIsBitIncompatible) {
return rhs;
} else {
return lhs;
}
}
Ok(match (lhs?, rhs?) {
// If only one side has a condition, pass it along
(None, other) | (other, None) => other,
// If both sides have IfAll conditions, merge them
@ -340,10 +348,17 @@ fn and<R>(lhs: Answer<R>, rhs: Answer<R>) -> Answer<R> {
})
}
fn or<R>(lhs: Answer<R>, rhs: Answer<R>) -> Answer<R> {
// If both are errors, then we should return the one on the right
fn or<R>(lhs: Answer<R>, rhs: Answer<R>) -> Answer<R>
where
R: PartialEq,
{
// If both are errors, then we should return the more specific one
if lhs.is_err() && rhs.is_err() {
return rhs;
if lhs == Err(Reason::DstIsBitIncompatible) {
return rhs;
} else {
return lhs;
}
}
// Otherwise, errors can be ignored for the rest of the pattern matching
let lhs = lhs.unwrap_or(None);