Add the GPoll evaluation vocabulary and an inert Type::Ref variant

This commit is contained in:
Dennis Kobert
2026-07-25 17:03:32 +00:00
parent eb50be844b
commit 1f9e0cc37b
5 changed files with 233 additions and 0 deletions

View File

@@ -323,6 +323,7 @@ pub(crate) fn property_from_type(
Type::Generic(_) => vec![TextLabel::new("Generic Type (Not Supported)").widget_instance()].into(),
Type::Fn(_, out) => return property_from_type(node_id, index, out, number_options, unit, display_decimal_places, step, context),
Type::Future(out) => return property_from_type(node_id, index, out, number_options, unit, display_decimal_places, step, context),
Type::Ref(inner) => return property_from_type(node_id, index, inner, number_options, unit, display_decimal_places, step, context),
};
extra_widgets.push(widgets);

View File

@@ -309,6 +309,7 @@ macro_rules! tagged_value {
pub fn from_type(input: &Type) -> Option<Self> {
match input {
Type::Generic(_) => None,
Type::Ref(_) => None,
Type::Concrete(concrete_type) => {
let name = concrete_type.name.as_ref();
// TODO: Add default implementations for types such as TaggedValue::Subpaths, and use the defaults here and in document_node_types
@@ -569,6 +570,7 @@ impl TaggedValue {
match ty {
Type::Generic(_) => None,
Type::Ref(_) => None,
Type::Concrete(concrete_type) => {
let ty = concrete_type.id?;
use std::any::TypeId;

View File

@@ -0,0 +1,222 @@
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ErrorKind {
Node(&'static str),
ArenaExhausted,
}
impl PartialEq<&str> for ErrorKind {
fn eq(&self, other: &&str) -> bool {
matches!(self, ErrorKind::Node(kind) if kind == other)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GraphError {
pub kind: ErrorKind,
pub trace: Vec<usize>,
}
impl GraphError {
pub fn new(kind: &'static str) -> Self {
Self {
kind: ErrorKind::Node(kind),
trace: Vec::new(),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum GPoll<T> {
Pending,
Final(T),
Partial(T),
Fallback(Box<(T, GraphError)>),
Error(Box<GraphError>),
}
impl<T> GPoll<T> {
#[inline(always)]
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> GPoll<U> {
match self {
GPoll::Pending => GPoll::Pending,
GPoll::Final(value) => GPoll::Final(f(value)),
GPoll::Partial(value) => GPoll::Partial(f(value)),
GPoll::Fallback(boxed) => {
let (value, e) = *boxed;
GPoll::Fallback(Box::new((f(value), e)))
}
GPoll::Error(e) => GPoll::Error(e),
}
}
#[inline(always)]
pub fn and_then<U>(self, f: impl FnOnce(T) -> GPoll<U>) -> GPoll<U> {
match self {
GPoll::Pending => GPoll::Pending,
GPoll::Final(value) => f(value),
GPoll::Partial(value) => match f(value) {
GPoll::Final(result) => GPoll::Partial(result),
other => other,
},
GPoll::Fallback(boxed) => {
let (value, e) = *boxed;
match f(value) {
GPoll::Pending => GPoll::Pending,
GPoll::Final(result) | GPoll::Partial(result) => GPoll::Fallback(Box::new((result, e))),
GPoll::Fallback(inner) => {
let (result, _) = *inner;
GPoll::Fallback(Box::new((result, e)))
}
GPoll::Error(inner) => GPoll::Error(inner),
}
}
GPoll::Error(e) => GPoll::Error(e),
}
}
#[inline(always)]
pub fn zip<U>(self, other: GPoll<U>) -> GPoll<(T, U)> {
match (self, other) {
(GPoll::Error(e), _) | (_, GPoll::Error(e)) => GPoll::Error(e),
(GPoll::Pending, _) | (_, GPoll::Pending) => GPoll::Pending,
(GPoll::Final(a), GPoll::Final(b)) => GPoll::Final((a, b)),
(GPoll::Fallback(boxed), GPoll::Final(b) | GPoll::Partial(b)) => {
let (a, e) = *boxed;
GPoll::Fallback(Box::new(((a, b), e)))
}
(GPoll::Final(a) | GPoll::Partial(a), GPoll::Fallback(boxed)) => {
let (b, e) = *boxed;
GPoll::Fallback(Box::new(((a, b), e)))
}
(GPoll::Fallback(first), GPoll::Fallback(second)) => {
let (a, e) = *first;
let (b, _) = *second;
GPoll::Fallback(Box::new(((a, b), e)))
}
(GPoll::Partial(a), GPoll::Final(b) | GPoll::Partial(b)) | (GPoll::Final(a), GPoll::Partial(b)) => GPoll::Partial((a, b)),
}
}
#[inline(always)]
pub fn trace(self, input: usize) -> Self {
match self {
GPoll::Fallback(mut boxed) => {
boxed.1.trace.push(input);
GPoll::Fallback(boxed)
}
GPoll::Error(mut e) => {
e.trace.push(input);
GPoll::Error(e)
}
other => other,
}
}
pub fn fallback(value: T, kind: &'static str) -> Self {
GPoll::Fallback(Box::new((value, GraphError::new(kind))))
}
pub fn error(kind: &'static str) -> Self {
GPoll::Error(Box::new(GraphError::new(kind)))
}
pub fn arena_exhausted() -> Self {
GPoll::Error(Box::new(GraphError {
kind: ErrorKind::ArenaExhausted,
trace: Vec::new(),
}))
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Interrupt {
Pending,
Error(Box<GraphError>),
}
impl From<GraphError> for Interrupt {
fn from(error: GraphError) -> Self {
Interrupt::Error(Box::new(error))
}
}
impl<T> From<Interrupt> for GPoll<T> {
fn from(interrupt: Interrupt) -> Self {
match interrupt {
Interrupt::Pending => GPoll::Pending,
Interrupt::Error(e) => GPoll::Error(e),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Extent {
Free,
Exactly(usize),
}
impl Extent {
pub fn meet(a: GPoll<Extent>, b: GPoll<Extent>) -> GPoll<Extent> {
a.zip(b).and_then(|(a, b)| match (a, b) {
(Extent::Free, other) | (other, Extent::Free) => GPoll::Final(other),
(Extent::Exactly(n), Extent::Exactly(m)) if n == m => GPoll::Final(Extent::Exactly(n)),
(Extent::Exactly(n), Extent::Exactly(m)) => GPoll::fallback(Extent::Exactly(n.min(m)), "extent mismatch"),
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Finality {
AllFinal,
Partial,
}
impl Finality {
pub fn meet(self, other: Finality) -> Finality {
match (self, other) {
(Finality::AllFinal, Finality::AllFinal) => Finality::AllFinal,
_ => Finality::Partial,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn free_is_the_meet_identity() {
let meet = Extent::meet(GPoll::Final(Extent::Free), GPoll::Final(Extent::Exactly(4)));
assert_eq!(meet, GPoll::Final(Extent::Exactly(4)));
}
#[test]
fn extent_mismatch_truncates_and_reports() {
let meet = Extent::meet(GPoll::Final(Extent::Exactly(3)), GPoll::Final(Extent::Exactly(5)));
let GPoll::Fallback(boxed) = meet else {
panic!("expected fallback, got {meet:?}");
};
assert_eq!(boxed.0, Extent::Exactly(3));
assert!(boxed.1.kind == "extent mismatch");
}
#[test]
fn error_dominates_pending_in_zip() {
let zipped = GPoll::<u32>::error("boom").zip(GPoll::<u32>::Pending);
assert!(matches!(zipped, GPoll::Error(_)));
}
#[test]
fn trace_builds_root_to_source_path() {
let poll = GPoll::<u32>::error("boom").trace(2).trace(0);
let GPoll::Error(e) = poll else { unreachable!() };
assert_eq!(e.trace, vec![2, 0]);
}
#[test]
fn interrupt_round_trips_to_gpoll() {
assert_eq!(GPoll::<u32>::from(Interrupt::Pending), GPoll::Pending);
let interrupt = Interrupt::from(GraphError::new("boom"));
assert!(matches!(GPoll::<u32>::from(interrupt), GPoll::Error(e) if e.kind == "boom"));
}
}

View File

@@ -4,6 +4,7 @@ pub mod bounds;
pub mod consts;
pub mod context;
pub mod generic;
pub mod gpoll;
pub mod list;
pub mod math;
pub mod memo;

View File

@@ -235,6 +235,7 @@ pub enum Type {
Fn(Box<Type>, Box<Type>),
/// Represents a future which promises to return the inner type.
Future(Box<Type>),
Ref(Box<Type>),
}
impl Default for Type {
@@ -308,6 +309,7 @@ impl Type {
Self::Concrete(ty) => Some(ty.size),
Self::Fn(_, _) => None,
Self::Future(_) => None,
Self::Ref(_) => None,
}
}
@@ -317,6 +319,7 @@ impl Type {
Self::Concrete(ty) => Some(ty.align),
Self::Fn(_, _) => None,
Self::Future(_) => None,
Self::Ref(_) => None,
}
}
@@ -326,6 +329,7 @@ impl Type {
Self::Concrete(_) => self,
Self::Fn(_, output) => output.nested_type(),
Self::Future(output) => output.nested_type(),
Self::Ref(inner) => inner.nested_type(),
}
}
@@ -338,6 +342,7 @@ impl Type {
Self::Concrete(_) => None,
Self::Fn(_, output) => output.replace_nested(f),
Self::Future(output) => output.replace_nested(f),
Self::Ref(inner) => inner.replace_nested(f),
}
}
@@ -347,6 +352,7 @@ impl Type {
Type::Concrete(ty) => simplify_identifier_name(&ty.name),
Type::Fn(call_arg, return_value) => format!("{} called with {}", return_value.identifier_name(), call_arg.identifier_name()),
Type::Future(ty) => ty.identifier_name(),
Type::Ref(ty) => ty.identifier_name(),
}
}
}
@@ -441,6 +447,7 @@ impl std::fmt::Display for Type {
Type::Concrete(ty) => write!(f, "{ty}"),
Type::Fn(_, return_value) => write!(f, "{return_value}"),
Type::Future(ty) => write!(f, "{ty}"),
Type::Ref(ty) => write!(f, "{ty}"),
}
}
}