From c03427b15452e3beef90de10f1f3d05444ffddf8 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Mon, 13 Sep 2021 22:34:10 -0700 Subject: [PATCH] Document TypeResolution and TypeInner::ValuePointer. --- src/lib.rs | 6 +++ src/proc/typifier.rs | 101 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 076a57349e..b8190cf19f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -579,7 +579,13 @@ pub enum TypeInner { base: Handle, class: StorageClass, }, + /// Pointer to a value. + /// + /// A `ValuePointer` type is equivalent to a `Pointer` to a `Scalar` or `Vector`. + /// This is for use in [`TypeResolution::Value`] variants. + /// + /// [`TypeResolution::Value`]: proc::TypeResolution::Value ValuePointer { size: Option, kind: ScalarKind, diff --git a/src/proc/typifier.rs b/src/proc/typifier.rs index e5635f9013..4a38ea6fd1 100644 --- a/src/proc/typifier.rs +++ b/src/proc/typifier.rs @@ -2,11 +2,97 @@ use crate::arena::{Arena, Handle}; use thiserror::Error; +/// The result of computing an expression's type. +/// +/// This is the type returned by [`ResolveContext::resolve`] to represent the type +/// it ascribes to some expression. +/// +/// You might expect such a function to simply return a `Handle`. However, +/// we want type resolution to be a read-only process, and that would limit the +/// possible results to types already present in the expression's associated +/// `Arena`. Naga IR does have certain expressions whose types are not +/// certain to be present. +/// +/// So instead, type resolution returns a `TypeResolution` enum: either a +/// [`Handle`], referencing some type in the arena, or a [`Value`], holding a +/// free-floating [`TypeInner`]. This extends the range to cover anything that +/// can be represented with a `TypeInner` referring to the existing arena. +/// +/// What sorts of expressions can have types not available in the arena? +/// +/// - An [`Access`] or [`AccessIndex`] expression applied to a [`Vector`] or +/// [`Matrix`] must have a [`Scalar`] or [`Vector`] type. But since `Vector` +/// and `Matrix` represent their element and column types implicitly, not +/// via a handle, there may not be a suitable type in the expression's +/// associated arena. Instead, resolving such an expression returns a +/// `TypeResolution::Value(TypeInner::X { ... })`, where `X` is `Scalar` or +/// `Vector`. +/// +/// - Similarly, the type of an [`Access`] or [`AccessIndex`] expression +/// applied to a *pointer to* a vector or matrix must produce a *pointer to* +/// a scalar or vector type. These cannot be represented with a +/// [`TypeInner::Pointer`], since the `Pointer`'s `base` must point into the +/// arena, and as before, we cannot assume that a suitable scalar or vector +/// type is there. So we take things one step further and provide +/// [`TypeInner::ValuePointer`], specifically for the case of pointers to +/// scalars or vectors. This type fits in a `TypeInner` and is exactly +/// equivalent to a `Pointer` to a `Vector` or `Scalar`. +/// +/// So, for example, the type of an `Access` expression applied to a value of type: +/// +/// ```ignore +/// TypeInner::Matrix { columns, rows, width } +/// ``` +/// +/// might be: +/// +/// ```ignore +/// TypeResolution::Value(TypeInner::Vector { +/// size: rows, +/// kind: ScalarKind::Float, +/// width, +/// }) +/// ``` +/// +/// and the type of an access to a pointer of storage class `class` to such a +/// matrix might be: +/// +/// ```ignore +/// TypeResolution::Value(TypeInner::ValuePointer { +/// size: Some(rows), +/// kind: ScalarKind::Float, +/// width, +/// class +/// }) +/// ``` +/// +/// [`Handle`]: TypeResolution::Handle +/// [`Value`]: TypeResolution::Value +/// +/// [`Access`]: crate::Expression::Access +/// [`AccessIndex`]: crate::Expression::AccessIndex +/// +/// [`TypeInner`]: crate::TypeInner +/// [`Matrix`]: crate::TypeInner::Matrix +/// [`Pointer`]: crate::TypeInner::Pointer +/// [`Scalar`]: crate::TypeInner::Scalar +/// [`ValuePointer`]: crate::TypeInner::ValuePointer +/// [`Vector`]: crate::TypeInner::Vector +/// +/// [`TypeInner::Pointer`]: crate::TypeInner::Pointer +/// [`TypeInner::ValuePointer`]: crate::TypeInner::ValuePointer #[derive(Debug, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Serialize))] #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] pub enum TypeResolution { + /// A type stored in the associated arena. Handle(Handle), + + /// A free-floating [`TypeInner`], representing a type that may not be + /// available in the associated arena. However, the `TypeInner` itself may + /// contain `Handle` values referring to types from the arena. + /// + /// [`TypeInner`]: crate::TypeInner Value(crate::TypeInner), } @@ -119,6 +205,21 @@ pub struct ResolveContext<'a> { } impl<'a> ResolveContext<'a> { + /// Determine the type of `expr`. + /// + /// The `past` argument must be a closure that can resolve the types of any + /// expressions that `expr` refers to. These can be gathered by caching the + /// results of prior calls to `resolve`, perhaps as done by the + /// [`front::Typifier`] utility type. + /// + /// Type resolution is a read-only process: this method takes `self` by + /// shared reference. However, this means that we cannot add anything to + /// `self.types` that we might need to describe `expr`. To work around this, + /// this method returns a [`TypeResolution`], rather than simply returning a + /// `Handle`; see the documentation for [`TypeResolution`] for + /// details. + /// + /// [`front::Typifier`]: crate::front::Typifier pub fn resolve( &self, expr: &crate::Expression,