use smallvec::SmallVec;
use crate::{AttributeRef, ValueRef};
#[must_use]
pub enum FoldResult<T = ()> {
Ok(T),
InPlace,
Failed,
}
impl<T> FoldResult<T> {
#[inline]
pub fn is_ok(&self) -> bool {
matches!(self, Self::Ok(_) | Self::InPlace)
}
#[inline]
pub fn is_failed(&self) -> bool {
matches!(self, Self::Failed)
}
#[inline]
#[track_caller]
pub fn expect(self, message: &'static str) -> Option<T> {
match self {
Self::Ok(out) => Some(out),
Self::InPlace => None,
Self::Failed => unwrap_failed_fold_result(message),
}
}
#[inline]
#[track_caller]
pub fn unwrap_or_else<F>(self, callback: F) -> Option<T>
where
F: FnOnce() -> !,
{
match self {
Self::Ok(out) => Some(out),
Self::InPlace => None,
Self::Failed => callback(),
}
}
}
impl<T> From<Option<T>> for FoldResult<T> {
fn from(value: Option<T>) -> Self {
match value {
None => FoldResult::Failed,
Some(value) => FoldResult::Ok(value),
}
}
}
impl<T> core::ops::FromResidual for FoldResult<T> {
fn from_residual(residual: <Self as core::ops::Try>::Residual) -> Self {
match residual {
FoldResult::Failed => FoldResult::Failed,
_ => unreachable!(),
}
}
}
impl<T> core::ops::Residual<T> for FoldResult<core::convert::Infallible> {
type TryType = FoldResult<T>;
}
impl<T> core::ops::Try for FoldResult<T> {
type Output = T;
type Residual = FoldResult<core::convert::Infallible>;
#[inline]
fn from_output(output: Self::Output) -> Self {
FoldResult::Ok(output)
}
#[inline]
fn branch(self) -> core::ops::ControlFlow<Self::Residual, Self::Output> {
use core::ops::ControlFlow;
match self {
FoldResult::Ok(c) => ControlFlow::Continue(c),
FoldResult::InPlace => ControlFlow::Break(FoldResult::InPlace),
FoldResult::Failed => ControlFlow::Break(FoldResult::Failed),
}
}
}
#[cold]
#[track_caller]
#[inline(never)]
fn unwrap_failed_fold_result(message: &'static str) -> ! {
panic!("tried to unwrap failed fold result as successful: {message}")
}
pub enum OpFoldResult {
Attribute(AttributeRef),
Value(ValueRef),
}
impl OpFoldResult {
#[inline]
pub fn is_constant(&self) -> bool {
matches!(self, Self::Attribute(_))
}
}
impl Eq for OpFoldResult {}
impl PartialEq for OpFoldResult {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Attribute(lhs), Self::Attribute(rhs)) => lhs.borrow().dyn_eq(&rhs.borrow()),
(Self::Value(lhs), Self::Value(rhs)) => ValueRef::ptr_eq(lhs, rhs),
_ => false,
}
}
}
impl core::fmt::Debug for OpFoldResult {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Attribute(attr) => core::fmt::Debug::fmt(attr, f),
Self::Value(value) => write!(f, "{}", value.borrow().id()),
}
}
}
impl core::fmt::Display for OpFoldResult {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
use crate::{OpPrintingFlags, print::AsmPrinter};
let flags = OpPrintingFlags::default();
match self {
OpFoldResult::Attribute(attr) => {
let attr = attr.borrow();
let mut printer = AsmPrinter::new(attr.context_rc(), &flags);
printer.print_attribute_value(&*attr);
write!(f, "{}", printer.finish())
}
OpFoldResult::Value(v) => core::fmt::Display::fmt(&v, f),
}
}
}
pub trait Foldable {
fn fold(&self, results: &mut SmallVec<[OpFoldResult; 1]>) -> FoldResult;
fn fold_with(
&self,
operands: &[Option<AttributeRef>],
results: &mut SmallVec<[OpFoldResult; 1]>,
) -> FoldResult;
}