Skip to main content

naga/proc/
index.rs

1/*!
2Definitions for index bounds checking.
3*/
4
5use core::iter::{self, zip};
6
7use crate::arena::{Handle, HandleSet, UniqueArena};
8use crate::{valid, FastHashSet};
9
10/// How should code generated by Naga do bounds checks?
11///
12/// When a vector, matrix, or array index is out of bounds—either negative, or
13/// greater than or equal to the number of elements in the type—WGSL requires
14/// that some other index of the implementation's choice that is in bounds is
15/// used instead. (There are no types with zero elements.)
16///
17/// Similarly, when out-of-bounds coordinates, array indices, or sample indices
18/// are presented to the WGSL `textureLoad` and `textureStore` operations, the
19/// operation is redirected to do something safe.
20///
21/// Different users of Naga will prefer different defaults:
22///
23/// -   When used as part of a WebGPU implementation, the WGSL specification
24///     requires the `Restrict` behavior for array, vector, and matrix accesses,
25///     and either the `Restrict` or `ReadZeroSkipWrite` behaviors for texture
26///     accesses.
27///
28/// -   When used by the `wgpu` crate for native development, `wgpu` selects
29///     `ReadZeroSkipWrite` as its default.
30///
31/// -   Naga's own default is `Unchecked`, so that shader translations
32///     are as faithful to the original as possible.
33///
34/// Sometimes the underlying hardware and drivers can perform bounds checks
35/// themselves, in a way that performs better than the checks Naga would inject.
36/// If you're using native checks like this, then having Naga inject its own
37/// checks as well would be redundant, and the `Unchecked` policy is
38/// appropriate.
39#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
40#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
41#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
42pub enum BoundsCheckPolicy {
43    /// Replace out-of-bounds indexes with some arbitrary in-bounds index.
44    ///
45    /// (This does not necessarily mean clamping. For example, interpreting the
46    /// index as unsigned and taking the minimum with the largest valid index
47    /// would also be a valid implementation. That would map negative indices to
48    /// the last element, not the first.)
49    Restrict,
50
51    /// Out-of-bounds reads return zero, and writes have no effect.
52    ///
53    /// When applied to a chain of accesses, like `a[i][j].b[k]`, all index
54    /// expressions are evaluated, regardless of whether prior or later index
55    /// expressions were in bounds. But all the accesses per se are skipped
56    /// if any index is out of bounds.
57    ReadZeroSkipWrite,
58
59    /// Naga adds no checks to indexing operations. Generate the fastest code
60    /// possible. This is the default for Naga, as a translator, but consumers
61    /// should consider defaulting to a safer behavior.
62    Unchecked,
63}
64
65/// Policies for injecting bounds checks during code generation.
66#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
67#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
68#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
69#[cfg_attr(feature = "deserialize", serde(default))]
70pub struct BoundsCheckPolicies {
71    /// How should the generated code handle array, vector, or matrix indices
72    /// that are out of range?
73    pub index: BoundsCheckPolicy,
74
75    /// How should the generated code handle array, vector, or matrix indices
76    /// that are out of range, when those values live in a [`GlobalVariable`] in
77    /// the [`Storage`] or [`Uniform`] address spaces?
78    ///
79    /// Some graphics hardware provides "robust buffer access", a feature that
80    /// ensures that using a pointer cannot access memory outside the 'buffer'
81    /// that it was derived from. In Naga terms, this means that the hardware
82    /// ensures that pointers computed by applying [`Access`] and
83    /// [`AccessIndex`] expressions to a [`GlobalVariable`] whose [`space`] is
84    /// [`Storage`] or [`Uniform`] will never read or write memory outside that
85    /// global variable.
86    ///
87    /// When hardware offers such a feature, it is probably undesirable to have
88    /// Naga inject bounds checking code for such accesses, since the hardware
89    /// can probably provide the same protection more efficiently. However,
90    /// bounds checks are still needed on accesses to indexable values that do
91    /// not live in buffers, like local variables.
92    ///
93    /// So, this option provides a separate policy that applies only to accesses
94    /// to storage and uniform globals. When depending on hardware bounds
95    /// checking, this policy can be `Unchecked` to avoid unnecessary overhead.
96    ///
97    /// When special hardware support is not available, this should probably be
98    /// the same as `index_bounds_check_policy`.
99    ///
100    /// [`GlobalVariable`]: crate::GlobalVariable
101    /// [`space`]: crate::GlobalVariable::space
102    /// [`Restrict`]: crate::proc::BoundsCheckPolicy::Restrict
103    /// [`ReadZeroSkipWrite`]: crate::proc::BoundsCheckPolicy::ReadZeroSkipWrite
104    /// [`Access`]: crate::Expression::Access
105    /// [`AccessIndex`]: crate::Expression::AccessIndex
106    /// [`Storage`]: crate::AddressSpace::Storage
107    /// [`Uniform`]: crate::AddressSpace::Uniform
108    pub buffer: BoundsCheckPolicy,
109
110    /// How should the generated code handle image texel loads that are out
111    /// of range?
112    ///
113    /// This controls the behavior of [`ImageLoad`] expressions when a coordinate,
114    /// texture array index, level of detail, or multisampled sample number is out of range.
115    ///
116    /// There is no corresponding policy for [`ImageStore`] statements. All the
117    /// platforms we support already discard out-of-bounds image stores,
118    /// effectively implementing the "skip write" part of [`ReadZeroSkipWrite`].
119    ///
120    /// [`ImageLoad`]: crate::Expression::ImageLoad
121    /// [`ImageStore`]: crate::Statement::ImageStore
122    /// [`ReadZeroSkipWrite`]: BoundsCheckPolicy::ReadZeroSkipWrite
123    pub image_load: BoundsCheckPolicy,
124
125    /// How should the generated code handle binding array indexes that are out of bounds.
126    pub binding_array: BoundsCheckPolicy,
127}
128
129/// The default `BoundsCheckPolicy` is `Unchecked`.
130impl Default for BoundsCheckPolicy {
131    fn default() -> Self {
132        BoundsCheckPolicy::Unchecked
133    }
134}
135
136impl BoundsCheckPolicies {
137    /// Determine which policy applies to `base`.
138    ///
139    /// `base` is the "base" expression (the expression being indexed) of a `Access`
140    /// and `AccessIndex` expression. This is either a pointer, a value, being directly
141    /// indexed, or a binding array.
142    ///
143    /// See the documentation for [`BoundsCheckPolicy`] for details about
144    /// when each policy applies.
145    pub fn choose_policy(
146        &self,
147        base: Handle<crate::Expression>,
148        types: &UniqueArena<crate::Type>,
149        info: &valid::FunctionInfo,
150    ) -> BoundsCheckPolicy {
151        let ty = info[base].ty.inner_with(types);
152
153        if let crate::TypeInner::BindingArray { .. } = *ty {
154            return self.binding_array;
155        }
156
157        match ty.pointer_space() {
158            Some(crate::AddressSpace::Storage { access: _ } | crate::AddressSpace::Uniform) => {
159                self.buffer
160            }
161            // This covers other address spaces, but also accessing vectors and
162            // matrices by value, where no pointer is involved.
163            _ => self.index,
164        }
165    }
166
167    /// Return `true` if any of `self`'s policies are `policy`.
168    pub fn contains(&self, policy: BoundsCheckPolicy) -> bool {
169        self.index == policy || self.buffer == policy || self.image_load == policy
170    }
171}
172
173/// An index that may be statically known, or may need to be computed at runtime.
174///
175/// This enum lets us handle both [`Access`] and [`AccessIndex`] expressions
176/// with the same code.
177///
178/// [`Access`]: crate::Expression::Access
179/// [`AccessIndex`]: crate::Expression::AccessIndex
180#[derive(Clone, Copy, Debug)]
181pub enum GuardedIndex {
182    Known(u32),
183    Expression(Handle<crate::Expression>),
184}
185
186/// Build a set of expressions used as indices, to cache in temporary variables when
187/// emitted.
188///
189/// Given the bounds-check policies `policies`, construct a `HandleSet` containing the handle
190/// indices of all the expressions in `function` that are ever used as guarded indices
191/// under the [`ReadZeroSkipWrite`] policy. The `module` argument must be the module to
192/// which `function` belongs, and `info` should be that function's analysis results.
193///
194/// Such index expressions will be used twice in the generated code: first for the
195/// comparison to see if the index is in bounds, and then for the access itself, should
196/// the comparison succeed. To avoid computing the expressions twice, the generated code
197/// should cache them in temporary variables.
198///
199/// Why do we need to build such a set in advance, instead of just processing access
200/// expressions as we encounter them? Whether an expression needs to be cached depends on
201/// whether it appears as something like the [`index`] operand of an [`Access`] expression
202/// or the [`level`] operand of an [`ImageLoad`] expression, and on the index bounds check
203/// policies that apply to those accesses. But [`Emit`] statements just identify a range
204/// of expressions by index; there's no good way to tell what an expression is used
205/// for. The only way to do it is to just iterate over all the expressions looking for
206/// relevant `Access` expressions --- which is what this function does.
207///
208/// Simple expressions like variable loads and constants don't make sense to cache: it's
209/// no better than just re-evaluating them. But constants are not covered by `Emit`
210/// statements, and `Load`s are always cached to ensure they occur at the right time, so
211/// we don't bother filtering them out from this set.
212///
213/// Fortunately, we don't need to deal with [`ImageStore`] statements here. When we emit
214/// code for a statement, the writer isn't in the middle of an expression, so we can just
215/// emit declarations for temporaries, initialized appropriately.
216///
217/// None of these concerns apply for SPIR-V output, since it's easy to just reuse an
218/// instruction ID in two places; that has the same semantics as a temporary variable, and
219/// it's inherent in the design of SPIR-V. This function is more useful for text-based
220/// back ends.
221///
222/// [`ReadZeroSkipWrite`]: BoundsCheckPolicy::ReadZeroSkipWrite
223/// [`index`]: crate::Expression::Access::index
224/// [`Access`]: crate::Expression::Access
225/// [`level`]: crate::Expression::ImageLoad::level
226/// [`ImageLoad`]: crate::Expression::ImageLoad
227/// [`Emit`]: crate::Statement::Emit
228/// [`ImageStore`]: crate::Statement::ImageStore
229pub fn find_checked_indexes(
230    module: &crate::Module,
231    function: &crate::Function,
232    info: &valid::FunctionInfo,
233    policies: BoundsCheckPolicies,
234) -> HandleSet<crate::Expression> {
235    use crate::Expression as Ex;
236
237    let mut guarded_indices = HandleSet::for_arena(&function.expressions);
238
239    // Don't bother scanning if we never need `ReadZeroSkipWrite`.
240    if policies.contains(BoundsCheckPolicy::ReadZeroSkipWrite) {
241        for (_handle, expr) in function.expressions.iter() {
242            // There's no need to handle `AccessIndex` expressions, as their
243            // indices never need to be cached.
244            match *expr {
245                Ex::Access { base, index }
246                    if policies.choose_policy(base, &module.types, info)
247                        == BoundsCheckPolicy::ReadZeroSkipWrite
248                        && access_needs_check(
249                            base,
250                            GuardedIndex::Expression(index),
251                            module,
252                            &function.expressions,
253                            info,
254                        )
255                        .is_some() =>
256                {
257                    guarded_indices.insert(index);
258                }
259                Ex::ImageLoad {
260                    coordinate,
261                    array_index,
262                    sample,
263                    level,
264                    ..
265                } if policies.image_load == BoundsCheckPolicy::ReadZeroSkipWrite => {
266                    guarded_indices.insert(coordinate);
267                    if let Some(array_index) = array_index {
268                        guarded_indices.insert(array_index);
269                    }
270                    if let Some(sample) = sample {
271                        guarded_indices.insert(sample);
272                    }
273                    if let Some(level) = level {
274                        guarded_indices.insert(level);
275                    }
276                }
277                _ => {}
278            }
279        }
280    }
281
282    guarded_indices
283}
284
285/// Determine whether `index` is statically known to be in bounds for `base`.
286///
287/// If we can't be sure that the index is in bounds, return the limit within
288/// which valid indices must fall.
289///
290/// The return value is one of the following:
291///
292/// - `Some(Known(n))` indicates that `n` is the largest valid index.
293///
294/// - `Some(Computed(global))` indicates that the largest valid index is one
295///   less than the length of the array that is the last member of the
296///   struct held in `global`.
297///
298/// - `None` indicates that the index need not be checked, either because it
299///   is statically known to be in bounds, or because the applicable policy
300///   is `Unchecked`.
301///
302/// This function only handles subscriptable types: arrays, vectors, and
303/// matrices. It does not handle struct member indices; those never require
304/// run-time checks, so it's best to deal with them further up the call
305/// chain.
306///
307/// This function assumes that any relevant overrides have fully-evaluated
308/// constants as their values (as arranged by [`process_overrides`], for
309/// example).
310///
311/// [`process_overrides`]: crate::back::pipeline_constants::process_overrides
312///
313/// # Panics
314///
315/// - If `base` is not an indexable type, panic.
316///
317/// - If `base` is an override-sized array, but the override's value is not a
318///   fully-evaluated constant expression, panic.
319pub fn access_needs_check(
320    base: Handle<crate::Expression>,
321    mut index: GuardedIndex,
322    module: &crate::Module,
323    expressions: &crate::Arena<crate::Expression>,
324    info: &valid::FunctionInfo,
325) -> Option<IndexableLength> {
326    let base_inner = info[base].ty.inner_with(&module.types);
327    // Unwrap safety: `Err` here indicates unindexable base types and invalid
328    // length constants, but `access_needs_check` is only used by back ends, so
329    // validation should have caught those problems.
330    let length = base_inner.indexable_length_resolved(module).unwrap();
331    index.try_resolve_to_constant(expressions, module);
332    if let (&GuardedIndex::Known(index), &IndexableLength::Known(length)) = (&index, &length) {
333        if index < length {
334            // Index is statically known to be in bounds, no check needed.
335            return None;
336        }
337    };
338
339    Some(length)
340}
341
342/// Items returned by the [`bounds_check_iter`] iterator.
343#[cfg_attr(not(feature = "msl-out"), allow(dead_code))]
344pub(crate) struct BoundsCheck {
345    /// The base of the [`Access`] or [`AccessIndex`] expression.
346    ///
347    /// [`Access`]: crate::Expression::Access
348    /// [`AccessIndex`]: crate::Expression::AccessIndex
349    pub base: Handle<crate::Expression>,
350
351    /// The index being accessed.
352    pub index: GuardedIndex,
353
354    /// The length of `base`.
355    pub length: IndexableLength,
356}
357
358/// Returns an iterator of accesses within the chain of `Access` and
359/// `AccessIndex` expressions starting from `chain` that may need to be
360/// bounds-checked at runtime.
361///
362/// Items are yielded as [`BoundsCheck`] instances.
363///
364/// Accesses through a struct are omitted, since you never need a bounds check
365/// for accessing a struct field.
366///
367/// If `chain` isn't an `Access` or `AccessIndex` expression at all, the
368/// iterator is empty.
369pub(crate) fn bounds_check_iter<'a>(
370    mut chain: Handle<crate::Expression>,
371    module: &'a crate::Module,
372    function: &'a crate::Function,
373    info: &'a valid::FunctionInfo,
374) -> impl Iterator<Item = BoundsCheck> + 'a {
375    iter::from_fn(move || {
376        let (next_expr, result) = match function.expressions[chain] {
377            crate::Expression::Access { base, index } => {
378                (base, Some((base, GuardedIndex::Expression(index))))
379            }
380            crate::Expression::AccessIndex { base, index } => {
381                // Don't try to check indices into structs. Validation already took
382                // care of them, and access_needs_check doesn't handle that case.
383                let mut base_inner = info[base].ty.inner_with(&module.types);
384                if let crate::TypeInner::Pointer { base, .. } = *base_inner {
385                    base_inner = &module.types[base].inner;
386                }
387                match *base_inner {
388                    crate::TypeInner::Struct { .. } => (base, None),
389                    _ => (base, Some((base, GuardedIndex::Known(index)))),
390                }
391            }
392            _ => return None,
393        };
394        chain = next_expr;
395        Some(result)
396    })
397    .flatten()
398    .filter_map(|(base, index)| {
399        access_needs_check(base, index, module, &function.expressions, info).map(|length| {
400            BoundsCheck {
401                base,
402                index,
403                length,
404            }
405        })
406    })
407}
408
409/// Returns all the types which we need out-of-bounds locals for; that is,
410/// all of the types which the code might attempt to get an out-of-bounds
411/// pointer to, in which case we yield a pointer to the out-of-bounds local
412/// of the correct type.
413pub fn oob_local_types(
414    module: &crate::Module,
415    function: &crate::Function,
416    info: &valid::FunctionInfo,
417    policies: BoundsCheckPolicies,
418) -> FastHashSet<Handle<crate::Type>> {
419    let mut result = FastHashSet::default();
420
421    if policies.index != BoundsCheckPolicy::ReadZeroSkipWrite {
422        return result;
423    }
424
425    for statement in &function.body {
426        // The only situation in which we end up actually needing to create an
427        // out-of-bounds pointer is when passing one to a function.
428        //
429        // This is because pointers are never baked; they're just inlined everywhere
430        // they're used. That means that loads can just return 0, and stores can just do
431        // nothing; functions are the only case where you actually *have* to produce a
432        // pointer.
433        if let crate::Statement::Call {
434            function: callee,
435            ref arguments,
436            ..
437        } = *statement
438        {
439            // Now go through the arguments of the function looking for pointers which need bounds checks.
440            for (arg_info, &arg) in zip(&module.functions[callee].arguments, arguments) {
441                match module.types[arg_info.ty].inner {
442                    crate::TypeInner::ValuePointer { .. } => {
443                        // `ValuePointer`s should only ever be used when resolving the types of
444                        // expressions, since the arena can no longer be modified at that point; things
445                        // in the arena should always use proper `Pointer`s.
446                        unreachable!("`ValuePointer` found in arena")
447                    }
448                    crate::TypeInner::Pointer { base, .. } => {
449                        if bounds_check_iter(arg, module, function, info)
450                            .next()
451                            .is_some()
452                        {
453                            result.insert(base);
454                        }
455                    }
456                    _ => continue,
457                };
458            }
459        }
460    }
461    result
462}
463
464impl GuardedIndex {
465    /// Make a `GuardedIndex::Known` from a `GuardedIndex::Expression` if possible.
466    ///
467    /// Return values that are already `Known` unchanged.
468    pub(crate) fn try_resolve_to_constant(
469        &mut self,
470        expressions: &crate::Arena<crate::Expression>,
471        module: &crate::Module,
472    ) {
473        if let GuardedIndex::Expression(expr) = *self {
474            *self = GuardedIndex::from_expression(expr, expressions, module);
475        }
476    }
477
478    pub(crate) fn from_expression(
479        expr: Handle<crate::Expression>,
480        expressions: &crate::Arena<crate::Expression>,
481        module: &crate::Module,
482    ) -> Self {
483        match module.to_ctx().get_const_val_from(expr, expressions) {
484            Ok(value) => Self::Known(value),
485            Err(_) => Self::Expression(expr),
486        }
487    }
488}
489
490#[derive(Clone, Copy, Debug, thiserror::Error, PartialEq)]
491pub enum IndexableLengthError {
492    #[error("Type is not indexable, and has no length (validation error)")]
493    TypeNotIndexable,
494    #[error(transparent)]
495    ResolveArraySizeError(#[from] super::ResolveArraySizeError),
496    #[error("Array size is still pending")]
497    Pending(crate::ArraySize),
498}
499
500impl crate::TypeInner {
501    /// Return the length of a subscriptable type.
502    ///
503    /// The `self` parameter should be a handle to a vector, matrix, or array
504    /// type, a pointer to one of those, or a value pointer. Arrays may be
505    /// fixed-size, dynamically sized, or sized by a specializable constant.
506    /// This function does not handle struct member references, as with
507    /// `AccessIndex`.
508    ///
509    /// The value returned is appropriate for bounds checks on subscripting.
510    ///
511    /// Return an error if `self` does not describe a subscriptable type at all.
512    pub fn indexable_length(
513        &self,
514        module: &crate::Module,
515    ) -> Result<IndexableLength, IndexableLengthError> {
516        use crate::TypeInner as Ti;
517        let known_length = match *self {
518            Ti::Vector { size, .. } => size as _,
519            Ti::Matrix { columns, .. } => columns as _,
520            Ti::Array { size, .. } | Ti::BindingArray { size, .. } => {
521                return size.to_indexable_length(module);
522            }
523            Ti::ValuePointer {
524                size: Some(size), ..
525            } => size as _,
526            Ti::Pointer { base, .. } => {
527                // When assigning types to expressions, ResolveContext::Resolve
528                // does a separate sub-match here instead of a full recursion,
529                // so we'll do the same.
530                let base_inner = &module.types[base].inner;
531                match *base_inner {
532                    Ti::Vector { size, .. } => size as _,
533                    Ti::Matrix { columns, .. } => columns as _,
534                    Ti::Array { size, .. } | Ti::BindingArray { size, .. } => {
535                        return size.to_indexable_length(module)
536                    }
537                    _ => return Err(IndexableLengthError::TypeNotIndexable),
538                }
539            }
540            _ => return Err(IndexableLengthError::TypeNotIndexable),
541        };
542        Ok(IndexableLength::Known(known_length))
543    }
544
545    /// Return the length of `self`, assuming overrides are yet to be supplied.
546    ///
547    /// Return the number of elements in `self`:
548    ///
549    /// - If `self` is a runtime-sized array, then return
550    ///   [`IndexableLength::Dynamic`].
551    ///
552    /// - If `self` is an override-sized array, then assume that override values
553    ///   have not yet been supplied, and return [`IndexableLength::Dynamic`].
554    ///
555    /// - Otherwise, the type simply tells us the length of `self`, so return
556    ///   [`IndexableLength::Known`].
557    ///
558    /// If `self` is not an indexable type at all, return an error.
559    ///
560    /// The difference between this and `indexable_length_resolved` is that we
561    /// treat override-sized arrays and dynamically-sized arrays both as
562    /// [`Dynamic`], on the assumption that our callers want to treat both cases
563    /// as "not yet possible to check".
564    ///
565    /// [`Dynamic`]: IndexableLength::Dynamic
566    pub fn indexable_length_pending(
567        &self,
568        module: &crate::Module,
569    ) -> Result<IndexableLength, IndexableLengthError> {
570        let length = self.indexable_length(module);
571        if let Err(IndexableLengthError::Pending(_)) = length {
572            return Ok(IndexableLength::Dynamic);
573        }
574        length
575    }
576
577    /// Return the length of `self`, assuming overrides have been resolved.
578    ///
579    /// Return the number of elements in `self`:
580    ///
581    /// - If `self` is a runtime-sized array, then return
582    ///   [`IndexableLength::Dynamic`].
583    ///
584    /// - If `self` is an override-sized array, then assume that the override's
585    ///   value is a fully-evaluated constant expression, and return
586    ///   [`IndexableLength::Known`]. Otherwise, return an error.
587    ///
588    /// - Otherwise, the type simply tells us the length of `self`, so return
589    ///   [`IndexableLength::Known`].
590    ///
591    /// If `self` is not an indexable type at all, return an error.
592    ///
593    /// The difference between this and `indexable_length_pending` is
594    /// that if `self` is override-sized, we require the override's
595    /// value to be known.
596    pub fn indexable_length_resolved(
597        &self,
598        module: &crate::Module,
599    ) -> Result<IndexableLength, IndexableLengthError> {
600        let length = self.indexable_length(module);
601
602        // If the length is override-based, then try to compute its value now.
603        if let Err(IndexableLengthError::Pending(size)) = length {
604            if let IndexableLength::Known(computed) = size.resolve(module.to_ctx())? {
605                return Ok(IndexableLength::Known(computed));
606            }
607        }
608        length
609    }
610}
611
612/// The number of elements in an indexable type.
613///
614/// This summarizes the length of vectors, matrices, and arrays in a way that is
615/// convenient for indexing and bounds-checking code.
616#[derive(Debug)]
617pub enum IndexableLength {
618    /// Values of this type always have the given number of elements.
619    Known(u32),
620
621    /// The number of elements is determined at runtime.
622    Dynamic,
623}
624
625impl crate::ArraySize {
626    pub const fn to_indexable_length(
627        self,
628        _module: &crate::Module,
629    ) -> Result<IndexableLength, IndexableLengthError> {
630        match self {
631            Self::Constant(length) => Ok(IndexableLength::Known(length.get())),
632            Self::Pending(_) => Err(IndexableLengthError::Pending(self)),
633            Self::Dynamic => Ok(IndexableLength::Dynamic),
634        }
635    }
636}