1use alloc::{format, string::String};
2
3use thiserror::Error;
4
5use crate::{
6 arena::{Arena, Handle, UniqueArena},
7 common::ForDebugWithTypes,
8 ir,
9};
10
11#[derive(Debug, PartialEq)]
91#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
92#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
93pub enum TypeResolution {
94 Handle(Handle<crate::Type>),
96
97 Value(crate::TypeInner),
110}
111
112impl TypeResolution {
113 pub const fn handle(&self) -> Option<Handle<crate::Type>> {
114 match *self {
115 Self::Handle(handle) => Some(handle),
116 Self::Value(_) => None,
117 }
118 }
119
120 pub fn inner_with<'a>(&'a self, arena: &'a UniqueArena<crate::Type>) -> &'a crate::TypeInner {
121 match *self {
122 Self::Handle(handle) => &arena[handle].inner,
123 Self::Value(ref inner) => inner,
124 }
125 }
126}
127
128impl Clone for TypeResolution {
130 fn clone(&self) -> Self {
131 use crate::TypeInner as Ti;
132 match *self {
133 Self::Handle(handle) => Self::Handle(handle),
134 Self::Value(ref v) => Self::Value(match *v {
135 Ti::Scalar(scalar) => Ti::Scalar(scalar),
136 Ti::Vector { size, scalar } => Ti::Vector { size, scalar },
137 Ti::Matrix {
138 rows,
139 columns,
140 scalar,
141 } => Ti::Matrix {
142 rows,
143 columns,
144 scalar,
145 },
146 Ti::CooperativeMatrix {
147 columns,
148 rows,
149 scalar,
150 role,
151 } => Ti::CooperativeMatrix {
152 columns,
153 rows,
154 scalar,
155 role,
156 },
157 Ti::Pointer { base, space } => Ti::Pointer { base, space },
158 Ti::ValuePointer {
159 size,
160 scalar,
161 space,
162 } => Ti::ValuePointer {
163 size,
164 scalar,
165 space,
166 },
167 Ti::Array { base, size, stride } => Ti::Array { base, size, stride },
168 _ => unreachable!("Unexpected clone type: {:?}", v),
169 }),
170 }
171 }
172}
173
174#[derive(Clone, Debug, Error, PartialEq)]
175pub enum ResolveError {
176 #[error("Index {index} is out of bounds for expression {expr:?}")]
177 OutOfBoundsIndex {
178 expr: Handle<crate::Expression>,
179 index: u32,
180 },
181 #[error("Invalid access into expression {expr:?}, indexed: {indexed}")]
182 InvalidAccess {
183 expr: Handle<crate::Expression>,
184 indexed: bool,
185 },
186 #[error("Invalid sub-access into type {ty:?}, indexed: {indexed}")]
187 InvalidSubAccess {
188 ty: Handle<crate::Type>,
189 indexed: bool,
190 },
191 #[error("Invalid scalar {0:?}")]
192 InvalidScalar(Handle<crate::Expression>),
193 #[error("Invalid vector {0:?}")]
194 InvalidVector(Handle<crate::Expression>),
195 #[error("Invalid pointer {0:?}")]
196 InvalidPointer(Handle<crate::Expression>),
197 #[error("Invalid image {0:?}")]
198 InvalidImage(Handle<crate::Expression>),
199 #[error("Function {name} not defined")]
200 FunctionNotDefined { name: String },
201 #[error("Function without return type")]
202 FunctionReturnsVoid,
203 #[error("Incompatible operands: {0}")]
204 IncompatibleOperands(String),
205 #[error("Function argument {0} doesn't exist")]
206 FunctionArgumentNotFound(u32),
207 #[error("Special type is not registered within the module")]
208 MissingSpecialType,
209 #[error("Call to builtin {0} has incorrect or ambiguous arguments")]
210 BuiltinArgumentsInvalid(String),
211}
212
213impl From<crate::proc::MissingSpecialType> for ResolveError {
214 fn from(_unit_struct: crate::proc::MissingSpecialType) -> Self {
215 ResolveError::MissingSpecialType
216 }
217}
218
219pub struct ResolveContext<'a> {
220 pub constants: &'a Arena<crate::Constant>,
221 pub overrides: &'a Arena<crate::Override>,
222 pub types: &'a UniqueArena<crate::Type>,
223 pub special_types: &'a crate::SpecialTypes,
224 pub global_vars: &'a Arena<crate::GlobalVariable>,
225 pub local_vars: &'a Arena<crate::LocalVariable>,
226 pub functions: &'a Arena<crate::Function>,
227 pub arguments: &'a [crate::FunctionArgument],
228}
229
230impl<'a> ResolveContext<'a> {
231 pub const fn with_locals(
233 module: &'a crate::Module,
234 local_vars: &'a Arena<crate::LocalVariable>,
235 arguments: &'a [crate::FunctionArgument],
236 ) -> Self {
237 Self {
238 constants: &module.constants,
239 overrides: &module.overrides,
240 types: &module.types,
241 special_types: &module.special_types,
242 global_vars: &module.global_variables,
243 local_vars,
244 functions: &module.functions,
245 arguments,
246 }
247 }
248
249 pub fn resolve(
265 &self,
266 expr: &crate::Expression,
267 past: impl Fn(Handle<crate::Expression>) -> Result<&'a TypeResolution, ResolveError>,
268 ) -> Result<TypeResolution, ResolveError> {
269 use crate::TypeInner as Ti;
270 let types = self.types;
271 Ok(match *expr {
272 crate::Expression::Access { base, .. } => match *past(base)?.inner_with(types) {
273 Ti::Array { base, .. } => TypeResolution::Handle(base),
277 Ti::Matrix { rows, scalar, .. } => {
278 TypeResolution::Value(Ti::Vector { size: rows, scalar })
279 }
280 Ti::Vector { size: _, scalar } => TypeResolution::Value(Ti::Scalar(scalar)),
281 Ti::ValuePointer {
282 size: Some(_),
283 scalar,
284 space,
285 } => TypeResolution::Value(Ti::ValuePointer {
286 size: None,
287 scalar,
288 space,
289 }),
290 Ti::Pointer { base, space } => {
291 TypeResolution::Value(match types[base].inner {
292 Ti::Array { base, .. } => Ti::Pointer { base, space },
293 Ti::Vector { size: _, scalar } => Ti::ValuePointer {
294 size: None,
295 scalar,
296 space,
297 },
298 Ti::Matrix {
300 columns: _,
301 rows,
302 scalar,
303 } => Ti::ValuePointer {
304 size: Some(rows),
305 scalar,
306 space,
307 },
308 Ti::BindingArray { base, .. } => Ti::Pointer { base, space },
309 ref other => {
310 log::error!("Access sub-type {other:?}");
311 return Err(ResolveError::InvalidSubAccess {
312 ty: base,
313 indexed: false,
314 });
315 }
316 })
317 }
318 Ti::BindingArray { base, .. } => TypeResolution::Handle(base),
319 ref other => {
320 log::error!("Access type {other:?}");
321 return Err(ResolveError::InvalidAccess {
322 expr: base,
323 indexed: false,
324 });
325 }
326 },
327 crate::Expression::AccessIndex { base, index } => {
328 match *past(base)?.inner_with(types) {
329 Ti::Vector { size, scalar } => {
330 if index >= size as u32 {
331 return Err(ResolveError::OutOfBoundsIndex { expr: base, index });
332 }
333 TypeResolution::Value(Ti::Scalar(scalar))
334 }
335 Ti::Matrix {
336 columns,
337 rows,
338 scalar,
339 } => {
340 if index >= columns as u32 {
341 return Err(ResolveError::OutOfBoundsIndex { expr: base, index });
342 }
343 TypeResolution::Value(crate::TypeInner::Vector { size: rows, scalar })
344 }
345 Ti::Array { base, .. } => TypeResolution::Handle(base),
346 Ti::Struct { ref members, .. } => {
347 let member = members
348 .get(index as usize)
349 .ok_or(ResolveError::OutOfBoundsIndex { expr: base, index })?;
350 TypeResolution::Handle(member.ty)
351 }
352 Ti::ValuePointer {
353 size: Some(size),
354 scalar,
355 space,
356 } => {
357 if index >= size as u32 {
358 return Err(ResolveError::OutOfBoundsIndex { expr: base, index });
359 }
360 TypeResolution::Value(Ti::ValuePointer {
361 size: None,
362 scalar,
363 space,
364 })
365 }
366 Ti::Pointer {
367 base: ty_base,
368 space,
369 } => TypeResolution::Value(match types[ty_base].inner {
370 Ti::Array { base, .. } => Ti::Pointer { base, space },
371 Ti::Vector { size, scalar } => {
372 if index >= size as u32 {
373 return Err(ResolveError::OutOfBoundsIndex { expr: base, index });
374 }
375 Ti::ValuePointer {
376 size: None,
377 scalar,
378 space,
379 }
380 }
381 Ti::Matrix {
382 rows,
383 columns,
384 scalar,
385 } => {
386 if index >= columns as u32 {
387 return Err(ResolveError::OutOfBoundsIndex { expr: base, index });
388 }
389 Ti::ValuePointer {
390 size: Some(rows),
391 scalar,
392 space,
393 }
394 }
395 Ti::Struct { ref members, .. } => {
396 let member = members
397 .get(index as usize)
398 .ok_or(ResolveError::OutOfBoundsIndex { expr: base, index })?;
399 Ti::Pointer {
400 base: member.ty,
401 space,
402 }
403 }
404 Ti::BindingArray { base, .. } => Ti::Pointer { base, space },
405 ref other => {
406 log::error!("Access index sub-type {other:?}");
407 return Err(ResolveError::InvalidSubAccess {
408 ty: ty_base,
409 indexed: true,
410 });
411 }
412 }),
413 Ti::BindingArray { base, .. } => TypeResolution::Handle(base),
414 ref other => {
415 log::error!("Access index type {other:?}");
416 return Err(ResolveError::InvalidAccess {
417 expr: base,
418 indexed: true,
419 });
420 }
421 }
422 }
423 crate::Expression::Splat { size, value } => match *past(value)?.inner_with(types) {
424 Ti::Scalar(scalar) => TypeResolution::Value(Ti::Vector { size, scalar }),
425 ref other => {
426 log::error!("Scalar type {other:?}");
427 return Err(ResolveError::InvalidScalar(value));
428 }
429 },
430 crate::Expression::Swizzle {
431 size,
432 vector,
433 pattern: _,
434 } => match *past(vector)?.inner_with(types) {
435 Ti::Vector { size: _, scalar } => {
436 TypeResolution::Value(Ti::Vector { size, scalar })
437 }
438 ref other => {
439 log::error!("Vector type {other:?}");
440 return Err(ResolveError::InvalidVector(vector));
441 }
442 },
443 crate::Expression::Literal(lit) => TypeResolution::Value(lit.ty_inner()),
444 crate::Expression::Constant(h) => TypeResolution::Handle(self.constants[h].ty),
445 crate::Expression::Override(h) => TypeResolution::Handle(self.overrides[h].ty),
446 crate::Expression::ZeroValue(ty) => TypeResolution::Handle(ty),
447 crate::Expression::Compose { ty, .. } => TypeResolution::Handle(ty),
448 crate::Expression::FunctionArgument(index) => {
449 let arg = self
450 .arguments
451 .get(index as usize)
452 .ok_or(ResolveError::FunctionArgumentNotFound(index))?;
453 TypeResolution::Handle(arg.ty)
454 }
455 crate::Expression::GlobalVariable(h) => {
456 let var = &self.global_vars[h];
457 if var.space == crate::AddressSpace::Handle {
458 TypeResolution::Handle(var.ty)
459 } else {
460 TypeResolution::Value(Ti::Pointer {
461 base: var.ty,
462 space: var.space,
463 })
464 }
465 }
466 crate::Expression::LocalVariable(h) => {
467 let var = &self.local_vars[h];
468 TypeResolution::Value(Ti::Pointer {
469 base: var.ty,
470 space: crate::AddressSpace::Function,
471 })
472 }
473 crate::Expression::Load { pointer } => match *past(pointer)?.inner_with(types) {
474 Ti::Pointer { base, space: _ } => {
475 if let Ti::Atomic(scalar) = types[base].inner {
476 TypeResolution::Value(Ti::Scalar(scalar))
477 } else {
478 TypeResolution::Handle(base)
479 }
480 }
481 Ti::ValuePointer {
482 size,
483 scalar,
484 space: _,
485 } => TypeResolution::Value(match size {
486 Some(size) => Ti::Vector { size, scalar },
487 None => Ti::Scalar(scalar),
488 }),
489 ref other => {
490 log::error!("Pointer {pointer:?} type {other:?}");
491 return Err(ResolveError::InvalidPointer(pointer));
492 }
493 },
494 crate::Expression::ImageSample {
495 image,
496 gather: Some(_),
497 ..
498 } => match *past(image)?.inner_with(types) {
499 Ti::Image { class, .. } => TypeResolution::Value(Ti::Vector {
500 scalar: crate::Scalar {
501 kind: match class {
502 crate::ImageClass::Sampled { kind, multi: _ } => kind,
503 _ => crate::ScalarKind::Float,
504 },
505 width: 4,
506 },
507 size: crate::VectorSize::Quad,
508 }),
509 ref other => {
510 log::error!("Image type {other:?}");
511 return Err(ResolveError::InvalidImage(image));
512 }
513 },
514 crate::Expression::ImageSample { image, .. }
515 | crate::Expression::ImageLoad { image, .. } => match *past(image)?.inner_with(types) {
516 Ti::Image { class, .. } => TypeResolution::Value(match class {
517 crate::ImageClass::Depth { multi: _ } => Ti::Scalar(crate::Scalar::F32),
518 crate::ImageClass::Sampled { kind, multi: _ } => Ti::Vector {
519 scalar: crate::Scalar { kind, width: 4 },
520 size: crate::VectorSize::Quad,
521 },
522 crate::ImageClass::Storage { format, .. } => Ti::Vector {
523 scalar: format.into(),
524 size: crate::VectorSize::Quad,
525 },
526 crate::ImageClass::External => Ti::Vector {
527 scalar: crate::Scalar::F32,
528 size: crate::VectorSize::Quad,
529 },
530 }),
531 ref other => {
532 log::error!("Image type {other:?}");
533 return Err(ResolveError::InvalidImage(image));
534 }
535 },
536 crate::Expression::ImageQuery { image, query } => TypeResolution::Value(match query {
537 crate::ImageQuery::Size { level: _ } => match *past(image)?.inner_with(types) {
538 Ti::Image { dim, .. } => match dim {
539 crate::ImageDimension::D1 => Ti::Scalar(crate::Scalar::U32),
540 crate::ImageDimension::D2 | crate::ImageDimension::Cube => Ti::Vector {
541 size: crate::VectorSize::Bi,
542 scalar: crate::Scalar::U32,
543 },
544 crate::ImageDimension::D3 => Ti::Vector {
545 size: crate::VectorSize::Tri,
546 scalar: crate::Scalar::U32,
547 },
548 },
549 ref other => {
550 log::error!("Image type {other:?}");
551 return Err(ResolveError::InvalidImage(image));
552 }
553 },
554 crate::ImageQuery::NumLevels
555 | crate::ImageQuery::NumLayers
556 | crate::ImageQuery::NumSamples => Ti::Scalar(crate::Scalar::U32),
557 }),
558 crate::Expression::Unary { expr, .. } => past(expr)?.clone(),
559 crate::Expression::Binary { op, left, right } => match op {
560 crate::BinaryOperator::Add
561 | crate::BinaryOperator::Subtract
562 | crate::BinaryOperator::Divide
563 | crate::BinaryOperator::Modulo => past(left)?.clone(),
564 crate::BinaryOperator::Multiply => {
565 let (res_left, res_right) = (past(left)?, past(right)?);
566 match (res_left.inner_with(types), res_right.inner_with(types)) {
567 (
568 &Ti::Matrix {
569 columns: _,
570 rows,
571 scalar,
572 },
573 &Ti::Matrix { columns, .. },
574 ) => TypeResolution::Value(Ti::Matrix {
575 columns,
576 rows,
577 scalar,
578 }),
579 (
580 &Ti::Matrix {
581 columns: _,
582 rows,
583 scalar,
584 },
585 &Ti::Vector { .. },
586 ) => TypeResolution::Value(Ti::Vector { size: rows, scalar }),
587 (
588 &Ti::Vector { .. },
589 &Ti::Matrix {
590 columns,
591 rows: _,
592 scalar,
593 },
594 ) => TypeResolution::Value(Ti::Vector {
595 size: columns,
596 scalar,
597 }),
598 (&Ti::Scalar { .. }, _) => res_right.clone(),
599 (_, &Ti::Scalar { .. }) => res_left.clone(),
600 (&Ti::Vector { .. }, &Ti::Vector { .. }) => res_left.clone(),
601 (
602 &Ti::CooperativeMatrix {
603 columns: _,
604 rows,
605 scalar,
606 role,
607 },
608 &Ti::CooperativeMatrix { columns, .. },
609 ) => TypeResolution::Value(Ti::CooperativeMatrix {
610 columns,
611 rows,
612 scalar,
613 role,
614 }),
615 (tl, tr) => {
616 return Err(ResolveError::IncompatibleOperands(format!(
617 "{tl:?} * {tr:?}"
618 )))
619 }
620 }
621 }
622 crate::BinaryOperator::Equal
623 | crate::BinaryOperator::NotEqual
624 | crate::BinaryOperator::Less
625 | crate::BinaryOperator::LessEqual
626 | crate::BinaryOperator::Greater
627 | crate::BinaryOperator::GreaterEqual => {
628 let scalar = crate::Scalar::BOOL;
630 let inner = match *past(left)?.inner_with(types) {
631 Ti::Scalar { .. } => Ti::Scalar(scalar),
632 Ti::Vector { size, .. } => Ti::Vector { size, scalar },
633 ref other => {
634 return Err(ResolveError::IncompatibleOperands(format!(
635 "{op:?}({other:?}, _)"
636 )))
637 }
638 };
639 TypeResolution::Value(inner)
640 }
641 crate::BinaryOperator::LogicalAnd | crate::BinaryOperator::LogicalOr => {
642 let bool = Ti::Scalar(crate::Scalar::BOOL);
644 let ty = past(left)?.inner_with(types);
645 if *ty == bool {
646 TypeResolution::Value(bool)
647 } else {
648 return Err(ResolveError::IncompatibleOperands(format!(
649 "{op:?}({:?}, _)",
650 ty.for_debug(types),
651 )));
652 }
653 }
654 crate::BinaryOperator::And
655 | crate::BinaryOperator::ExclusiveOr
656 | crate::BinaryOperator::InclusiveOr
657 | crate::BinaryOperator::ShiftLeft
658 | crate::BinaryOperator::ShiftRight => past(left)?.clone(),
659 },
660 crate::Expression::AtomicResult { ty, .. } => TypeResolution::Handle(ty),
661 crate::Expression::SubgroupOperationResult { ty } => TypeResolution::Handle(ty),
662 crate::Expression::WorkGroupUniformLoadResult { ty } => TypeResolution::Handle(ty),
663 crate::Expression::Select { accept, .. } => past(accept)?.clone(),
664 crate::Expression::Derivative { expr, .. } => past(expr)?.clone(),
665 crate::Expression::Relational { fun, argument } => match fun {
666 crate::RelationalFunction::All | crate::RelationalFunction::Any => {
667 TypeResolution::Value(Ti::Scalar(crate::Scalar::BOOL))
668 }
669 crate::RelationalFunction::IsNan | crate::RelationalFunction::IsInf => {
670 match *past(argument)?.inner_with(types) {
671 Ti::Scalar { .. } => TypeResolution::Value(Ti::Scalar(crate::Scalar::BOOL)),
672 Ti::Vector { size, .. } => TypeResolution::Value(Ti::Vector {
673 scalar: crate::Scalar::BOOL,
674 size,
675 }),
676 ref other => {
677 return Err(ResolveError::IncompatibleOperands(format!(
678 "{fun:?}({other:?})"
679 )))
680 }
681 }
682 }
683 },
684 crate::Expression::Math {
685 fun,
686 arg,
687 arg1,
688 arg2: _,
689 arg3: _,
690 } => {
691 use crate::proc::OverloadSet as _;
692
693 let mut overloads = fun.overloads();
694 log::debug!(
695 "initial overloads for {fun:?}, {:#?}",
696 overloads.for_debug(types)
697 );
698
699 let res_arg = past(arg)?;
707 overloads = overloads.arg(0, res_arg.inner_with(types), types);
708 log::debug!(
709 "overloads after arg 0 of type {:?}: {:#?}",
710 res_arg.for_debug(types),
711 overloads.for_debug(types)
712 );
713
714 if let Some(arg1) = arg1 {
715 let res_arg1 = past(arg1)?;
716 overloads = overloads.arg(1, res_arg1.inner_with(types), types);
717 log::debug!(
718 "overloads after arg 1 of type {:?}: {:#?}",
719 res_arg1.for_debug(types),
720 overloads.for_debug(types)
721 );
722 }
723
724 if overloads.is_empty() {
725 return Err(ResolveError::BuiltinArgumentsInvalid(format!("{fun:?}")));
726 }
727
728 let rule = overloads.most_preferred();
729
730 rule.conclusion.into_resolution(self.special_types)?
731 }
732 crate::Expression::As {
733 expr,
734 kind,
735 convert,
736 } => match *past(expr)?.inner_with(types) {
737 Ti::Scalar(crate::Scalar { width, .. }) => {
738 TypeResolution::Value(Ti::Scalar(crate::Scalar {
739 kind,
740 width: convert.unwrap_or(width),
741 }))
742 }
743 Ti::Vector {
744 size,
745 scalar: crate::Scalar { kind: _, width },
746 } => TypeResolution::Value(Ti::Vector {
747 size,
748 scalar: crate::Scalar {
749 kind,
750 width: convert.unwrap_or(width),
751 },
752 }),
753 Ti::Matrix {
754 columns,
755 rows,
756 mut scalar,
757 } => {
758 if let Some(width) = convert {
759 scalar.width = width;
760 }
761 TypeResolution::Value(Ti::Matrix {
762 columns,
763 rows,
764 scalar,
765 })
766 }
767 ref other => {
768 return Err(ResolveError::IncompatibleOperands(format!(
769 "{other:?} as {kind:?}"
770 )))
771 }
772 },
773 crate::Expression::CallResult(function) => {
774 let result = self.functions[function]
775 .result
776 .as_ref()
777 .ok_or(ResolveError::FunctionReturnsVoid)?;
778 TypeResolution::Handle(result.ty)
779 }
780 crate::Expression::ArrayLength(_) => {
781 TypeResolution::Value(Ti::Scalar(crate::Scalar::U32))
782 }
783 crate::Expression::RayQueryProceedResult => {
784 TypeResolution::Value(Ti::Scalar(crate::Scalar::BOOL))
785 }
786 crate::Expression::RayQueryGetIntersection { .. } => {
787 let result = self
788 .special_types
789 .ray_intersection
790 .ok_or(ResolveError::MissingSpecialType)?;
791 TypeResolution::Handle(result)
792 }
793 crate::Expression::RayQueryVertexPositions { .. } => {
794 let result = self
795 .special_types
796 .ray_vertex_return
797 .ok_or(ResolveError::MissingSpecialType)?;
798 TypeResolution::Handle(result)
799 }
800 crate::Expression::SubgroupBallotResult => TypeResolution::Value(Ti::Vector {
801 scalar: crate::Scalar::U32,
802 size: crate::VectorSize::Quad,
803 }),
804 crate::Expression::CooperativeLoad {
805 columns,
806 rows,
807 role,
808 ref data,
809 } => {
810 let scalar = past(data.pointer)?
811 .inner_with(types)
812 .pointer_base_type()
813 .and_then(|tr| tr.inner_with(types).scalar())
814 .ok_or(ResolveError::InvalidPointer(data.pointer))?;
815 TypeResolution::Value(Ti::CooperativeMatrix {
816 columns,
817 rows,
818 scalar,
819 role,
820 })
821 }
822 crate::Expression::CooperativeMultiplyAdd { a: _, b: _, c } => past(c)?.clone(),
823 })
824 }
825}
826
827pub fn compare_types(
841 lhs: &TypeResolution,
842 rhs: &TypeResolution,
843 types: &UniqueArena<crate::Type>,
844) -> bool {
845 match lhs {
846 &TypeResolution::Handle(lhs_handle)
847 if matches!(
848 types[lhs_handle],
849 ir::Type {
850 inner: ir::TypeInner::Struct { .. },
851 ..
852 }
853 ) =>
854 {
855 rhs.handle()
857 .is_some_and(|rhs_handle| lhs_handle == rhs_handle)
858 }
859 _ => lhs
860 .inner_with(types)
861 .non_struct_equivalent(rhs.inner_with(types), types),
862 }
863}
864
865#[test]
866fn test_error_size() {
867 assert_eq!(size_of::<ResolveError>(), 32);
868}