tamer: Newtypes for all Infallible ParseState errors
More information will be presented in the commit that follows to generalize these, but this sets the stage. The recently-introduced pipeline macro takes care of most of the job of a declarative pipeline, but it's still leaky, since it requires that the _caller_ create error sum types. This not only exposes implementation details and so undermines the goal of making pipelines easy to declare and compose, but it's also one of the last major components of boilerplate for the lowering pipeline. My previous attempts at generating error sum types automatically for pipelines ran into a problem because of overlapping `impl`s for the various `<S as ParseState>::Error` types; this resolves that issue via newtypes. I had considered other approaches, including explicitly generating code to `map_err` as part of the lowering pipeline, but in the end this is the easier way to reason about things that also keeps manual `Lower` pipelines on the same level of expressiveness as the pipeline macro; I want to restrict its unique capabilities as much as possible to elimination of boilerplate and nothing more. DEV-13162main
parent
672cc54c14
commit
1bb25b05b3
|
@ -54,7 +54,7 @@ use crate::{
|
|||
},
|
||||
};
|
||||
use arrayvec::ArrayVec;
|
||||
use std::{convert::Infallible, fmt::Display, marker::PhantomData};
|
||||
use std::{fmt::Display, marker::PhantomData};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum AsgTreeToXirf<'a> {
|
||||
|
@ -75,10 +75,14 @@ impl<'a> Display for AsgTreeToXirf<'a> {
|
|||
|
||||
type Xirf = XirfToken<Text>;
|
||||
|
||||
diagnostic_infallible! {
|
||||
pub enum AsgTreeToXirfError {}
|
||||
}
|
||||
|
||||
impl<'a> ParseState for AsgTreeToXirf<'a> {
|
||||
type Token = TreeWalkRel;
|
||||
type Object = Xirf;
|
||||
type Error = Infallible;
|
||||
type Error = AsgTreeToXirfError;
|
||||
type Context = TreeContext<'a>;
|
||||
|
||||
fn parse_token(
|
||||
|
|
|
@ -74,7 +74,7 @@ pub use graph::{
|
|||
ObjectKind,
|
||||
},
|
||||
visit,
|
||||
xmli::AsgTreeToXirf,
|
||||
xmli::{AsgTreeToXirf, AsgTreeToXirfError},
|
||||
Asg, AsgResult, IndexType,
|
||||
};
|
||||
|
||||
|
|
|
@ -39,16 +39,21 @@ use std::{
|
|||
path::Path,
|
||||
};
|
||||
use tamer::{
|
||||
asg::{air::Air, AsgError, DefaultAsg},
|
||||
asg::{
|
||||
air::Air, visit::TreeWalkRel, AsgError, AsgTreeToXirfError, DefaultAsg,
|
||||
},
|
||||
diagnose::{
|
||||
AnnotatedSpan, Diagnostic, FsSpanResolver, Reporter, VisualReporter,
|
||||
},
|
||||
nir::{InterpError, Nir, NirToAirError, XirfToNirError},
|
||||
nir::{
|
||||
InterpError, Nir, NirToAirError, TplShortDesugarError, XirfToNirError,
|
||||
},
|
||||
parse::{lowerable, FinalizeError, ParseError, Token, UnknownToken},
|
||||
pipeline::parse_package_xml,
|
||||
xir::{
|
||||
self,
|
||||
flat::{RefinedText, XirToXirfError, XirfToken},
|
||||
autoclose::XirfAutoCloseError,
|
||||
flat::{RefinedText, Text, XirToXirfError, XirfToXirError, XirfToken},
|
||||
reader::XmlXirReader,
|
||||
DefaultEscaper, Token as XirToken,
|
||||
},
|
||||
|
@ -317,6 +322,9 @@ pub enum UnrecoverableError {
|
|||
Io(io::Error),
|
||||
Fmt(fmt::Error),
|
||||
XirWriterError(xir::writer::Error),
|
||||
AsgTreeToXirfError(ParseError<TreeWalkRel, AsgTreeToXirfError>),
|
||||
XirfAutoCloseError(ParseError<XirfToken<Text>, XirfAutoCloseError>),
|
||||
XirfToXirError(ParseError<XirfToken<Text>, XirfToXirError>),
|
||||
ErrorsDuringLowering(ErrorCount),
|
||||
FinalizeError(FinalizeError),
|
||||
}
|
||||
|
@ -353,6 +361,7 @@ pub enum RecoverableError {
|
|||
XirParseError(ParseError<UnknownToken, xir::Error>),
|
||||
XirfParseError(ParseError<XirToken, XirToXirfError>),
|
||||
NirParseError(ParseError<XirfToken<RefinedText>, XirfToNirError>),
|
||||
TplShortDesugarError(ParseError<Nir, TplShortDesugarError>),
|
||||
InterpError(ParseError<Nir, InterpError>),
|
||||
NirToAirError(ParseError<Nir, NirToAirError>),
|
||||
AirAggregateError(ParseError<Air, AsgError>),
|
||||
|
@ -376,15 +385,29 @@ impl From<xir::writer::Error> for UnrecoverableError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<FinalizeError> for UnrecoverableError {
|
||||
fn from(e: FinalizeError) -> Self {
|
||||
Self::FinalizeError(e)
|
||||
impl From<ParseError<TreeWalkRel, AsgTreeToXirfError>> for UnrecoverableError {
|
||||
fn from(e: ParseError<TreeWalkRel, AsgTreeToXirfError>) -> Self {
|
||||
Self::AsgTreeToXirfError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Infallible> for UnrecoverableError {
|
||||
fn from(_: Infallible) -> Self {
|
||||
unreachable!("<UnrecoverableError as From<Infallible>>::from")
|
||||
impl From<ParseError<XirfToken<Text>, XirfToXirError>> for UnrecoverableError {
|
||||
fn from(e: ParseError<XirfToken<Text>, XirfToXirError>) -> Self {
|
||||
Self::XirfToXirError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseError<XirfToken<Text>, XirfAutoCloseError>>
|
||||
for UnrecoverableError
|
||||
{
|
||||
fn from(e: ParseError<XirfToken<Text>, XirfAutoCloseError>) -> Self {
|
||||
Self::XirfAutoCloseError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FinalizeError> for UnrecoverableError {
|
||||
fn from(e: FinalizeError) -> Self {
|
||||
Self::FinalizeError(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -396,14 +419,6 @@ impl<T: Token> From<ParseError<T, Infallible>> for UnrecoverableError {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Token> From<ParseError<T, Infallible>> for RecoverableError {
|
||||
fn from(_: ParseError<T, Infallible>) -> Self {
|
||||
unreachable!(
|
||||
"<RecoverableError as From<ParseError<T, Infallible>>>::from"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseError<UnknownToken, xir::Error>> for RecoverableError {
|
||||
fn from(e: ParseError<UnknownToken, xir::Error>) -> Self {
|
||||
Self::XirParseError(e)
|
||||
|
@ -424,6 +439,12 @@ impl From<ParseError<XirfToken<RefinedText>, XirfToNirError>>
|
|||
}
|
||||
}
|
||||
|
||||
impl From<ParseError<Nir, TplShortDesugarError>> for RecoverableError {
|
||||
fn from(e: ParseError<Nir, TplShortDesugarError>) -> Self {
|
||||
Self::TplShortDesugarError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseError<Nir, InterpError>> for RecoverableError {
|
||||
fn from(e: ParseError<Nir, InterpError>) -> Self {
|
||||
Self::InterpError(e)
|
||||
|
@ -450,6 +471,9 @@ impl Display for UnrecoverableError {
|
|||
Io(e) => Display::fmt(e, f),
|
||||
Fmt(e) => Display::fmt(e, f),
|
||||
XirWriterError(e) => Display::fmt(e, f),
|
||||
AsgTreeToXirfError(e) => Display::fmt(e, f),
|
||||
XirfToXirError(e) => Display::fmt(e, f),
|
||||
XirfAutoCloseError(e) => Display::fmt(e, f),
|
||||
FinalizeError(e) => Display::fmt(e, f),
|
||||
|
||||
// TODO: Use formatter for dynamic "error(s)"
|
||||
|
@ -468,6 +492,7 @@ impl Display for RecoverableError {
|
|||
XirParseError(e) => Display::fmt(e, f),
|
||||
XirfParseError(e) => Display::fmt(e, f),
|
||||
NirParseError(e) => Display::fmt(e, f),
|
||||
TplShortDesugarError(e) => Display::fmt(e, f),
|
||||
InterpError(e) => Display::fmt(e, f),
|
||||
NirToAirError(e) => Display::fmt(e, f),
|
||||
AirAggregateError(e) => Display::fmt(e, f),
|
||||
|
@ -475,40 +500,16 @@ impl Display for RecoverableError {
|
|||
}
|
||||
}
|
||||
|
||||
impl Error for UnrecoverableError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
use UnrecoverableError::*;
|
||||
|
||||
match self {
|
||||
Io(e) => Some(e),
|
||||
Fmt(e) => Some(e),
|
||||
XirWriterError(e) => Some(e),
|
||||
ErrorsDuringLowering(_) => None,
|
||||
FinalizeError(e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for RecoverableError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
use RecoverableError::*;
|
||||
|
||||
match self {
|
||||
XirParseError(e) => Some(e),
|
||||
XirfParseError(e) => Some(e),
|
||||
NirParseError(e) => Some(e),
|
||||
InterpError(e) => Some(e),
|
||||
NirToAirError(e) => Some(e),
|
||||
AirAggregateError(e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Error for UnrecoverableError {}
|
||||
|
||||
impl Diagnostic for UnrecoverableError {
|
||||
fn describe(&self) -> Vec<AnnotatedSpan> {
|
||||
use UnrecoverableError::*;
|
||||
|
||||
match self {
|
||||
AsgTreeToXirfError(e) => e.describe(),
|
||||
XirfToXirError(e) => e.describe(),
|
||||
XirfAutoCloseError(e) => e.describe(),
|
||||
FinalizeError(e) => e.describe(),
|
||||
|
||||
// Fall back to `Display`
|
||||
|
@ -527,6 +528,7 @@ impl Diagnostic for RecoverableError {
|
|||
XirParseError(e) => e.describe(),
|
||||
XirfParseError(e) => e.describe(),
|
||||
NirParseError(e) => e.describe(),
|
||||
TplShortDesugarError(e) => e.describe(),
|
||||
InterpError(e) => e.describe(),
|
||||
NirToAirError(e) => e.describe(),
|
||||
AirAggregateError(e) => e.describe(),
|
||||
|
|
|
@ -332,3 +332,55 @@ impl<S: Into<Span>> Annotate for S {
|
|||
AnnotatedSpan(self.into(), level, label)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a variant-less error enum akin to [`Infallible`].
|
||||
///
|
||||
/// This is used to create [`Infallible`]-like newtypes where unique error
|
||||
/// types are beneficial.
|
||||
/// For example,
|
||||
/// this can be used so that [`From`] implementations can be exclusively
|
||||
/// used to widen errors
|
||||
/// (or lack thereof)
|
||||
/// into error sum variants,
|
||||
/// and is especially useful when code generation is involved to avoid
|
||||
/// generation of overlapping [`From`] `impl`s.
|
||||
///
|
||||
/// The generated enum is convertable [`Into`] and [`From`] [`Infallible`].
|
||||
macro_rules! diagnostic_infallible {
|
||||
($vis:vis enum $name:ident {}) => {
|
||||
/// A unique [`Infallible`](std::convert::Infallible) type.
|
||||
#[derive(Debug, PartialEq)]
|
||||
$vis enum $name {}
|
||||
|
||||
impl std::fmt::Display for $name {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, stringify!($name))
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::diagnose::Diagnostic for $name {
|
||||
fn describe(&self) -> Vec<$crate::diagnose::AnnotatedSpan> {
|
||||
// This is a unit struct and should not be able to be
|
||||
// instantiated!
|
||||
unreachable!(
|
||||
concat!(
|
||||
stringify!($name),
|
||||
" should be unreachable!"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::convert::Infallible> for $name {
|
||||
fn from(_: std::convert::Infallible) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$name> for std::convert::Infallible {
|
||||
fn from(_: $name) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -181,12 +181,13 @@ pub mod global;
|
|||
#[macro_use]
|
||||
extern crate static_assertions;
|
||||
|
||||
#[macro_use]
|
||||
pub mod diagnose;
|
||||
#[macro_use]
|
||||
pub mod xir;
|
||||
|
||||
pub mod asg;
|
||||
pub mod convert;
|
||||
pub mod diagnose;
|
||||
pub mod f;
|
||||
pub mod fmt;
|
||||
pub mod fs;
|
||||
|
|
|
@ -77,7 +77,7 @@ pub use interp::{InterpError, InterpState as InterpolateNir};
|
|||
pub use parse::{
|
||||
NirParseState as XirfToNir, NirParseStateError_ as XirfToNirError,
|
||||
};
|
||||
pub use tplshort::TplShortDesugar;
|
||||
pub use tplshort::{TplShortDesugar, TplShortDesugarError};
|
||||
|
||||
/// IR that is "near" the source code.
|
||||
///
|
||||
|
|
|
@ -108,8 +108,6 @@ impl ParseState for NirToAir {
|
|||
use NirToAir::*;
|
||||
use NirToAirError::*;
|
||||
|
||||
use crate::diagnostic_panic;
|
||||
|
||||
if let Some(obj) = stack.pop() {
|
||||
return Transition(Ready).ok(obj).with_lookahead(tok);
|
||||
}
|
||||
|
|
|
@ -88,7 +88,6 @@ use crate::{
|
|||
SymbolId,
|
||||
},
|
||||
};
|
||||
use std::convert::Infallible;
|
||||
|
||||
use Nir::*;
|
||||
use NirEntity::*;
|
||||
|
@ -130,10 +129,14 @@ impl Display for TplShortDesugar {
|
|||
}
|
||||
}
|
||||
|
||||
diagnostic_infallible! {
|
||||
pub enum TplShortDesugarError {}
|
||||
}
|
||||
|
||||
impl ParseState for TplShortDesugar {
|
||||
type Token = Nir;
|
||||
type Object = Nir;
|
||||
type Error = Infallible;
|
||||
type Error = TplShortDesugarError;
|
||||
type Context = Stack;
|
||||
|
||||
fn parse_token(
|
||||
|
|
|
@ -39,6 +39,18 @@
|
|||
//! The module is responsible for pipeline composition.
|
||||
//! For information on the lowering pipeline as an abstraction,
|
||||
//! see [`Lower`].
|
||||
//!
|
||||
//! Error Widening
|
||||
//! ==============
|
||||
//! Each [`ParseState`] in the pipeline is expected to have its own unique
|
||||
//! error type,
|
||||
//! utilizing newtypes if necessary;
|
||||
//! this ensures that errors are able to be uniquely paired with each
|
||||
//! [`ParseState`] that produced it without having to perform an
|
||||
//! explicit mapping at the call site.
|
||||
//! To facilitate that automatic mapping/aggregation,
|
||||
//! this uniqueness property also allows for generation of [`From`]
|
||||
//! implementations that will not overlap.
|
||||
|
||||
use crate::{
|
||||
asg::{air::AirAggregate, AsgTreeToXirf},
|
||||
|
|
|
@ -106,7 +106,7 @@ use crate::{
|
|||
span::{Span, UNKNOWN_SPAN},
|
||||
xir::EleSpan,
|
||||
};
|
||||
use std::{convert::Infallible, fmt::Display};
|
||||
use std::fmt::Display;
|
||||
|
||||
use XirfAutoClose::*;
|
||||
|
||||
|
@ -138,10 +138,14 @@ impl Display for XirfAutoClose {
|
|||
}
|
||||
}
|
||||
|
||||
diagnostic_infallible! {
|
||||
pub enum XirfAutoCloseError {}
|
||||
}
|
||||
|
||||
impl ParseState for XirfAutoClose {
|
||||
type Token = XirfToken<Text>;
|
||||
type Object = XirfToken<Text>;
|
||||
type Error = Infallible;
|
||||
type Error = XirfAutoCloseError;
|
||||
type Context = AutoCloseStack;
|
||||
|
||||
fn parse_token(
|
||||
|
|
|
@ -55,7 +55,6 @@ use crate::{
|
|||
};
|
||||
use arrayvec::ArrayVec;
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
error::Error,
|
||||
fmt::{Debug, Display},
|
||||
marker::PhantomData,
|
||||
|
@ -926,10 +925,14 @@ impl<T: TextType> Display for XirfToXir<T> {
|
|||
}
|
||||
}
|
||||
|
||||
diagnostic_infallible! {
|
||||
pub enum XirfToXirError {}
|
||||
}
|
||||
|
||||
impl<T: TextType> ParseState for XirfToXir<T> {
|
||||
type Token = XirfToken<T>;
|
||||
type Object = XirToken;
|
||||
type Error = Infallible;
|
||||
type Error = XirfToXirError;
|
||||
|
||||
fn parse_token(
|
||||
self,
|
||||
|
|
|
@ -619,10 +619,10 @@ fn xirf_to_xir() {
|
|||
);
|
||||
|
||||
// The lowering pipeline above requires compatible errors.
|
||||
impl From<ParseError<XirfToken<Text>, Infallible>>
|
||||
impl From<ParseError<XirfToken<Text>, XirfToXirError>>
|
||||
for ParseError<XirToken, XirToXirfError>
|
||||
{
|
||||
fn from(_value: ParseError<XirfToken<Text>, Infallible>) -> Self {
|
||||
fn from(_value: ParseError<XirfToken<Text>, XirfToXirError>) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue