Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ pub enum TypeInner<A> {
/// Nominal enum type, represented as a balanced sum of its variants'
/// payload types
Enum(EnumInfo),
/// Type of a recovered subtree. Compatible with every type; never re-reported.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fdfae8b:

What is a "recovered subtree" what does it mean to "re-report" a type?

/// A diagnostic was already emitted for its span.
Never,
}

/// One variant of a nominal enum type: its name and payload types.
Expand Down Expand Up @@ -201,6 +204,7 @@ impl<A> TypeInner<A> {
}
},
TypeInner::Enum(info) => write!(f, "{}", info.name()),
TypeInner::Never => write!(f, "<never>"),
}
}
}
Expand Down Expand Up @@ -459,6 +463,14 @@ impl ResolvedType {
pub fn as_inner(&self) -> &TypeInner<Arc<Self>> {
&self.0
}

pub const fn never() -> Self {
Self(TypeInner::Never)
}

pub fn is_never(&self) -> bool {
matches!(self.0, TypeInner::Never)
}
}

/// Nominal enum types.
Expand Down Expand Up @@ -571,7 +583,9 @@ impl TypeDeconstructible for ResolvedType {
impl TreeLike for &ResolvedType {
fn as_node(&self) -> Tree<Self> {
match &self.0 {
TypeInner::Boolean | TypeInner::UInt(..) | TypeInner::Enum(..) => Tree::Nullary,
TypeInner::Boolean | TypeInner::UInt(..) | TypeInner::Enum(..) | TypeInner::Never => {
Tree::Nullary
}
TypeInner::Option(l) | TypeInner::Array(l, _) | TypeInner::List(l, _) => Tree::Unary(l),
TypeInner::Either(l, r) => Tree::Binary(l, r),
TypeInner::Tuple(elements) => Tree::Nary(elements.iter().map(Arc::as_ref).collect()),
Expand Down Expand Up @@ -729,6 +743,10 @@ impl AliasedType {
Self(AliasedInner::Builtin(builtin))
}

pub const fn error() -> Self {
Self(AliasedInner::Inner(TypeInner::Never))
}

/// Resolve all aliases in the type based on the given map of `aliases` to types.
pub fn resolve<F, E>(&self, mut get_alias: F) -> Result<ResolvedType, E>
where
Expand Down Expand Up @@ -775,6 +793,7 @@ impl AliasedType {
TypeInner::Enum(info) => {
output.push(ResolvedType::enumeration(info.clone()));
}
TypeInner::Never => output.push(ResolvedType::never()),
},
}
}
Expand Down Expand Up @@ -809,6 +828,7 @@ impl_require_feature!(TypeInner<Arc<AliasedType>> {
Array(element, _),
List(element, _),
Enum(_),
Never,
});

impl TypeConstructible for AliasedType {
Expand Down Expand Up @@ -901,7 +921,10 @@ impl TreeLike for &AliasedType {
match &self.0 {
AliasedInner::Alias(_) | AliasedInner::Builtin(_) => Tree::Nullary,
AliasedInner::Inner(inner) => match inner {
TypeInner::Boolean | TypeInner::UInt(..) | TypeInner::Enum(..) => Tree::Nullary,
TypeInner::Boolean
| TypeInner::UInt(..)
| TypeInner::Enum(..)
| TypeInner::Never => Tree::Nullary,
TypeInner::Option(l) | TypeInner::Array(l, _) | TypeInner::List(l, _) => {
Tree::Unary(l)
}
Expand Down Expand Up @@ -1205,6 +1228,9 @@ impl From<&ResolvedType> for StructuralType {
TypeInner::Enum(info) => {
output.push(StructuralType::balanced_sum(info.structural_variants()));
}
TypeInner::Never => unreachable!(
"poisoned type reached codegen; compilation must be gated on zero errors"
),
}
}
debug_assert_eq!(output.len(), 1);
Expand Down
4 changes: 3 additions & 1 deletion src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,7 @@ impl Value {
}
}
}
TypeInner::Never => unreachable!("poisoned type in value reconstruction; values exist only for error-free programs"),
}
}
debug_assert_eq!(output.len(), 1);
Expand Down Expand Up @@ -937,6 +938,7 @@ impl crate::ArbitraryOfType for Value {
.collect::<arbitrary::Result<Vec<Self>>>()?;
Ok(Self::list(elements, ty.as_ref().clone(), *bound))
}
TypeInner::Never => unreachable!("cannot generate a value of a poisoned type"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fdfae8b:

This one is not unreachable. We should return an Arbitrary error here indicating that there is no value of the never type.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed with @KyrylR that this PR is too large to review as one unit. I'm splitting it into a sequence:

(Rough split, not yet validated)

  1. Add the uninhabited Never type to TypeInner (+ is_never,
    ResolvedType::compatible, and the Arbitrary impl returning an error
    instead of unreachable!). Standalone and useful on its own.
  2. Error variants in the parse tree + parser recovery (Implement error states in parser #205).
  3. Diagnostics sink in Scope; containers collect instead of aborting (Error recovery in analysis #207).
  4. Split parse_from_str / parse_from_content by error policy.

@apoelstra, I plan to address your comment about the Arbitrary branch in (1). I'll also rewrite the doc comment "recovered subtree" and "re-report" are not defined anywhere. Sorry for the churn.

Keeping this open as a draft for the overall design; I'll close it once (4)
is merged.

}
}
}
Expand Down Expand Up @@ -1257,7 +1259,7 @@ impl TreeLike for Destructor<'_> {
Self::WrongType => return Tree::Nullary,
};
match ty.as_inner() {
TypeInner::Boolean | TypeInner::UInt(..) => Tree::Nullary,
TypeInner::Boolean | TypeInner::UInt(..) | TypeInner::Never => Tree::Nullary,
TypeInner::Enum(info) => match destruct::as_enum_leaf(value, info.variants().len()) {
Some((index, leaf)) => {
Tree::Unary(Self::new(leaf, info.variants()[index].payload_type()))
Expand Down