use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
use miden_debug_types::{SourceManager, SourceSpan, Span, Spanned};
pub use midenc_hir_type as types;
use midenc_hir_type::{AddressSpace, Type, TypeRepr};
use super::{
ConstantExpr, DocString, GlobalItemIndex, Ident, ItemIndex, Path, SymbolResolution,
SymbolResolutionError, Visibility,
};
const MAX_TYPE_EXPR_NESTING: usize = 256;
pub trait TypeResolver<E> {
fn source_manager(&self) -> Arc<dyn SourceManager>;
fn resolve_local_failed(&self, err: SymbolResolutionError) -> E;
fn get_type(&mut self, context: SourceSpan, gid: GlobalItemIndex) -> Result<Type, E>;
fn get_local_type(&mut self, context: SourceSpan, id: ItemIndex) -> Result<Option<Type>, E>;
fn resolve_type_ref(&mut self, ty: Span<&Path>) -> Result<SymbolResolution, E>;
fn resolve(&mut self, ty: &TypeExpr) -> Result<Option<Type>, E> {
ty.resolve_type(self)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TypeDecl {
Alias(TypeAlias),
Enum(EnumType),
}
impl TypeDecl {
pub fn with_docs(self, docs: Option<Span<String>>) -> Self {
match self {
Self::Alias(ty) => Self::Alias(ty.with_docs(docs)),
Self::Enum(ty) => Self::Enum(ty.with_docs(docs)),
}
}
pub fn name(&self) -> &Ident {
match self {
Self::Alias(ty) => &ty.name,
Self::Enum(ty) => &ty.name,
}
}
pub const fn visibility(&self) -> Visibility {
match self {
Self::Alias(ty) => ty.visibility,
Self::Enum(ty) => ty.visibility,
}
}
pub fn docs(&self) -> Option<Span<&str>> {
match self {
Self::Alias(ty) => ty.docs(),
Self::Enum(ty) => ty.docs(),
}
}
pub fn ty(&self) -> TypeExpr {
match self {
Self::Alias(ty) => ty.ty.clone(),
Self::Enum(ty) => TypeExpr::Primitive(Span::new(ty.span, ty.ty.clone())),
}
}
}
impl Spanned for TypeDecl {
fn span(&self) -> SourceSpan {
match self {
Self::Alias(spanned) => spanned.span,
Self::Enum(spanned) => spanned.span,
}
}
}
impl From<TypeAlias> for TypeDecl {
fn from(value: TypeAlias) -> Self {
Self::Alias(value)
}
}
impl From<EnumType> for TypeDecl {
fn from(value: EnumType) -> Self {
Self::Enum(value)
}
}
impl crate::prettier::PrettyPrint for TypeDecl {
fn render(&self) -> crate::prettier::Document {
match self {
Self::Alias(ty) => ty.render(),
Self::Enum(ty) => ty.render(),
}
}
}
#[derive(Debug, Clone)]
pub struct FunctionType {
pub span: SourceSpan,
pub cc: types::CallConv,
pub args: Vec<TypeExpr>,
pub results: Vec<TypeExpr>,
}
impl Eq for FunctionType {}
impl PartialEq for FunctionType {
fn eq(&self, other: &Self) -> bool {
self.cc == other.cc && self.args == other.args && self.results == other.results
}
}
impl core::hash::Hash for FunctionType {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.cc.hash(state);
self.args.hash(state);
self.results.hash(state);
}
}
impl Spanned for FunctionType {
fn span(&self) -> SourceSpan {
self.span
}
}
impl FunctionType {
pub fn new(cc: types::CallConv, args: Vec<TypeExpr>, results: Vec<TypeExpr>) -> Self {
Self {
span: SourceSpan::UNKNOWN,
cc,
args,
results,
}
}
#[inline]
pub fn with_span(mut self, span: SourceSpan) -> Self {
self.span = span;
self
}
}
impl crate::prettier::PrettyPrint for FunctionType {
fn render(&self) -> crate::prettier::Document {
use crate::prettier::*;
let singleline_args = self
.args
.iter()
.map(|arg| arg.render())
.reduce(|acc, arg| acc + const_text(", ") + arg)
.unwrap_or(Document::Empty);
let multiline_args = indent(
4,
nl() + self
.args
.iter()
.map(|arg| arg.render())
.reduce(|acc, arg| acc + const_text(",") + nl() + arg)
.unwrap_or(Document::Empty),
) + nl();
let args = singleline_args | multiline_args;
let args = const_text("(") + args + const_text(")");
match self.results.len() {
0 => args,
1 => args + const_text(" -> ") + self.results[0].render(),
_ => {
let results = self
.results
.iter()
.map(|r| r.render())
.reduce(|acc, r| acc + const_text(", ") + r)
.unwrap_or(Document::Empty);
args + const_text(" -> ") + const_text("(") + results + const_text(")")
},
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum TypeExpr {
Primitive(Span<Type>),
Ptr(PointerType),
Array(ArrayType),
Struct(StructType),
Ref(Span<Arc<Path>>),
}
impl TypeExpr {
pub fn set_name(&mut self, name: Ident) {
match self {
Self::Struct(struct_ty) => {
struct_ty.name = Some(name);
},
Self::Primitive(_) | Self::Ptr(_) | Self::Array(_) | Self::Ref(_) => (),
}
}
pub fn references(&self) -> Vec<Span<Arc<Path>>> {
use alloc::collections::BTreeSet;
let mut worklist = smallvec::SmallVec::<[_; 4]>::from_slice(&[self]);
let mut references = BTreeSet::new();
while let Some(ty) = worklist.pop() {
match ty {
Self::Primitive(_) => {},
Self::Ptr(ty) => {
worklist.push(&ty.pointee);
},
Self::Array(ty) => {
worklist.push(&ty.elem);
},
Self::Struct(ty) => {
for field in ty.fields.iter() {
worklist.push(&field.ty);
}
},
Self::Ref(ty) => {
references.insert(ty.clone());
},
}
}
references.into_iter().collect()
}
pub fn resolve_type<E, R>(&self, resolver: &mut R) -> Result<Option<Type>, E>
where
R: ?Sized + TypeResolver<E>,
{
self.resolve_type_with_depth(resolver, 0)
}
fn resolve_type_with_depth<E, R>(
&self,
resolver: &mut R,
depth: usize,
) -> Result<Option<Type>, E>
where
R: ?Sized + TypeResolver<E>,
{
if depth > MAX_TYPE_EXPR_NESTING {
let source_manager = resolver.source_manager();
return Err(resolver.resolve_local_failed(
SymbolResolutionError::type_expression_depth_exceeded(
self.span(),
MAX_TYPE_EXPR_NESTING,
source_manager.as_ref(),
),
));
}
match self {
TypeExpr::Ref(path) => {
let mut current_path = path.clone();
loop {
match resolver.resolve_type_ref(current_path.as_deref())? {
SymbolResolution::Local(item) => {
return resolver.get_local_type(current_path.span(), item.into_inner());
},
SymbolResolution::External(path) => {
if path == current_path {
break Ok(None);
}
current_path = path;
},
SymbolResolution::Exact { gid, .. } => {
return resolver.get_type(current_path.span(), gid).map(Some);
},
SymbolResolution::Module { path: module_path, .. } => {
break Err(resolver.resolve_local_failed(
SymbolResolutionError::invalid_symbol_type(
path.span(),
"type",
module_path.span(),
&resolver.source_manager(),
),
));
},
SymbolResolution::MastRoot(item) => {
break Err(resolver.resolve_local_failed(
SymbolResolutionError::invalid_symbol_type(
path.span(),
"type",
item.span(),
&resolver.source_manager(),
),
));
},
}
}
},
TypeExpr::Primitive(t) => Ok(Some(t.inner().clone())),
TypeExpr::Array(t) => Ok(t
.elem
.resolve_type_with_depth(resolver, depth + 1)?
.map(|elem| types::Type::Array(Arc::new(types::ArrayType::new(elem, t.arity))))),
TypeExpr::Ptr(ty) => Ok(ty
.pointee
.resolve_type_with_depth(resolver, depth + 1)?
.map(|pointee| types::Type::Ptr(Arc::new(types::PointerType::new(pointee))))),
TypeExpr::Struct(t) => {
let mut fields = Vec::with_capacity(t.fields.len());
for field in t.fields.iter() {
let field_ty = field.ty.resolve_type_with_depth(resolver, depth + 1)?;
if let Some(field_ty) = field_ty {
fields.push(field_ty);
} else {
return Ok(None);
}
}
Ok(Some(Type::Struct(Arc::new(types::StructType::from_parts(
t.name.clone().map(|id| id.into_inner()),
t.repr.into_inner(),
fields,
)))))
},
}
}
}
impl From<Type> for TypeExpr {
fn from(ty: Type) -> Self {
match ty {
Type::Array(t) => Self::Array(ArrayType::new(t.element_type().clone().into(), t.len())),
Type::Struct(t) => Self::Struct(StructType::new(
None,
t.fields().iter().enumerate().map(|(i, ft)| {
let name = Ident::new(format!("field{i}")).unwrap();
StructField {
span: SourceSpan::UNKNOWN,
name,
ty: ft.ty.clone().into(),
}
}),
)),
Type::Ptr(t) => Self::Ptr((*t).clone().into()),
Type::Function(_) => {
Self::Ptr(PointerType::new(TypeExpr::Primitive(Span::unknown(Type::Felt))))
},
Type::List(t) => Self::Ptr(
PointerType::new((*t).clone().into()).with_address_space(AddressSpace::Byte),
),
Type::I128 | Type::U128 => Self::Array(ArrayType::new(Type::U32.into(), 4)),
Type::I64 | Type::U64 => Self::Array(ArrayType::new(Type::U32.into(), 2)),
Type::Unknown | Type::Never | Type::F64 => panic!("unrepresentable type value: {ty}"),
ty => Self::Primitive(Span::unknown(ty)),
}
}
}
impl Spanned for TypeExpr {
fn span(&self) -> SourceSpan {
match self {
Self::Primitive(spanned) => spanned.span(),
Self::Ptr(spanned) => spanned.span(),
Self::Array(spanned) => spanned.span(),
Self::Struct(spanned) => spanned.span(),
Self::Ref(spanned) => spanned.span(),
}
}
}
impl crate::prettier::PrettyPrint for TypeExpr {
fn render(&self) -> crate::prettier::Document {
use crate::prettier::*;
match self {
Self::Primitive(ty) => display(ty),
Self::Ptr(ty) => ty.render(),
Self::Array(ty) => ty.render(),
Self::Struct(ty) => ty.render(),
Self::Ref(ty) => display(ty),
}
}
}
#[derive(Debug, Clone)]
pub struct PointerType {
pub span: SourceSpan,
pub pointee: Box<TypeExpr>,
addrspace: Option<AddressSpace>,
}
impl From<types::PointerType> for PointerType {
fn from(ty: types::PointerType) -> Self {
let types::PointerType { addrspace, pointee } = ty;
let pointee = Box::new(TypeExpr::from(pointee));
Self {
span: SourceSpan::UNKNOWN,
pointee,
addrspace: Some(addrspace),
}
}
}
impl Eq for PointerType {}
impl PartialEq for PointerType {
fn eq(&self, other: &Self) -> bool {
self.address_space() == other.address_space() && self.pointee == other.pointee
}
}
impl core::hash::Hash for PointerType {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.pointee.hash(state);
self.address_space().hash(state);
}
}
impl Spanned for PointerType {
fn span(&self) -> SourceSpan {
self.span
}
}
impl PointerType {
pub fn new(pointee: TypeExpr) -> Self {
Self {
span: SourceSpan::UNKNOWN,
pointee: Box::new(pointee),
addrspace: None,
}
}
#[inline]
pub fn with_span(mut self, span: SourceSpan) -> Self {
self.span = span;
self
}
#[inline]
pub fn with_address_space(mut self, addrspace: AddressSpace) -> Self {
self.addrspace = Some(addrspace);
self
}
#[inline]
pub fn address_space(&self) -> AddressSpace {
self.addrspace.unwrap_or(AddressSpace::Element)
}
}
impl crate::prettier::PrettyPrint for PointerType {
fn render(&self) -> crate::prettier::Document {
use crate::prettier::*;
let doc = const_text("ptr<") + self.pointee.render();
if let Some(addrspace) = self.addrspace.as_ref() {
doc + const_text(", ") + text(format!("addrspace({})", addrspace)) + const_text(">")
} else {
doc + const_text(">")
}
}
}
#[derive(Debug, Clone)]
pub struct ArrayType {
pub span: SourceSpan,
pub elem: Box<TypeExpr>,
pub arity: usize,
}
impl Eq for ArrayType {}
impl PartialEq for ArrayType {
fn eq(&self, other: &Self) -> bool {
self.arity == other.arity && self.elem == other.elem
}
}
impl core::hash::Hash for ArrayType {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.elem.hash(state);
self.arity.hash(state);
}
}
impl Spanned for ArrayType {
fn span(&self) -> SourceSpan {
self.span
}
}
impl ArrayType {
pub fn new(elem: TypeExpr, arity: usize) -> Self {
Self {
span: SourceSpan::UNKNOWN,
elem: Box::new(elem),
arity,
}
}
#[inline]
pub fn with_span(mut self, span: SourceSpan) -> Self {
self.span = span;
self
}
}
impl crate::prettier::PrettyPrint for ArrayType {
fn render(&self) -> crate::prettier::Document {
use crate::prettier::*;
const_text("[")
+ self.elem.render()
+ const_text("; ")
+ display(self.arity)
+ const_text("]")
}
}
#[derive(Debug, Clone)]
pub struct StructType {
pub span: SourceSpan,
pub name: Option<Ident>,
pub repr: Span<TypeRepr>,
pub fields: Vec<StructField>,
}
impl Eq for StructType {}
impl PartialEq for StructType {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && self.repr == other.repr && self.fields == other.fields
}
}
impl core::hash::Hash for StructType {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
self.repr.hash(state);
self.fields.hash(state);
}
}
impl Spanned for StructType {
fn span(&self) -> SourceSpan {
self.span
}
}
impl StructType {
pub fn new(name: Option<Ident>, fields: impl IntoIterator<Item = StructField>) -> Self {
Self {
span: SourceSpan::UNKNOWN,
name,
repr: Span::unknown(TypeRepr::Default),
fields: fields.into_iter().collect(),
}
}
#[inline]
pub fn with_repr(mut self, repr: Span<TypeRepr>) -> Self {
self.repr = repr;
self
}
#[inline]
pub fn with_span(mut self, span: SourceSpan) -> Self {
self.span = span;
self
}
}
impl crate::prettier::PrettyPrint for StructType {
fn render(&self) -> crate::prettier::Document {
use crate::prettier::*;
let repr = match &*self.repr {
TypeRepr::Default => Document::Empty,
TypeRepr::BigEndian => const_text("@bigendian "),
repr @ (TypeRepr::Align(_) | TypeRepr::Packed(_) | TypeRepr::Transparent) => {
text(format!("@{repr} "))
},
};
let singleline_body = self
.fields
.iter()
.map(|field| field.render())
.reduce(|acc, field| acc + const_text(", ") + field)
.unwrap_or(Document::Empty);
let multiline_body = indent(
4,
nl() + self
.fields
.iter()
.map(|field| field.render())
.reduce(|acc, field| acc + const_text(",") + nl() + field)
.unwrap_or(Document::Empty),
) + nl();
let body = singleline_body | multiline_body;
repr + const_text("struct") + const_text(" { ") + body + const_text(" }")
}
}
#[derive(Debug, Clone)]
pub struct StructField {
pub span: SourceSpan,
pub name: Ident,
pub ty: TypeExpr,
}
impl Eq for StructField {}
impl PartialEq for StructField {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && self.ty == other.ty
}
}
impl core::hash::Hash for StructField {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
self.ty.hash(state);
}
}
impl Spanned for StructField {
fn span(&self) -> SourceSpan {
self.span
}
}
impl crate::prettier::PrettyPrint for StructField {
fn render(&self) -> crate::prettier::Document {
use crate::prettier::*;
display(&self.name) + const_text(": ") + self.ty.render()
}
}
#[derive(Debug, Clone)]
pub struct TypeAlias {
span: SourceSpan,
docs: Option<DocString>,
pub visibility: Visibility,
pub name: Ident,
pub ty: TypeExpr,
}
impl TypeAlias {
pub fn new(visibility: Visibility, name: Ident, ty: TypeExpr) -> Self {
Self {
span: name.span(),
docs: None,
visibility,
name,
ty,
}
}
pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
self.docs = docs.map(DocString::new);
self
}
#[inline]
pub fn with_span(mut self, span: SourceSpan) -> Self {
self.span = span;
self
}
#[inline]
pub fn set_span(&mut self, span: SourceSpan) {
self.span = span;
}
pub fn docs(&self) -> Option<Span<&str>> {
self.docs.as_ref().map(|docstring| docstring.as_spanned_str())
}
pub fn name(&self) -> &Ident {
&self.name
}
#[inline]
pub const fn visibility(&self) -> Visibility {
self.visibility
}
}
impl Eq for TypeAlias {}
impl PartialEq for TypeAlias {
fn eq(&self, other: &Self) -> bool {
self.visibility == other.visibility
&& self.name == other.name
&& self.docs == other.docs
&& self.ty == other.ty
}
}
impl core::hash::Hash for TypeAlias {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
let Self { span: _, docs, visibility, name, ty } = self;
docs.hash(state);
visibility.hash(state);
name.hash(state);
ty.hash(state);
}
}
impl Spanned for TypeAlias {
fn span(&self) -> SourceSpan {
self.span
}
}
impl crate::prettier::PrettyPrint for TypeAlias {
fn render(&self) -> crate::prettier::Document {
use crate::prettier::*;
let mut doc = self
.docs
.as_ref()
.map(|docstring| docstring.render())
.unwrap_or(Document::Empty);
if self.visibility.is_public() {
doc += display(self.visibility) + const_text(" ");
}
doc + const_text("type")
+ const_text(" ")
+ display(&self.name)
+ const_text(" = ")
+ self.ty.render()
}
}
#[derive(Debug, Clone)]
pub struct EnumType {
span: SourceSpan,
docs: Option<DocString>,
visibility: Visibility,
name: Ident,
ty: Type,
variants: Vec<Variant>,
}
impl EnumType {
pub fn new(
visibility: Visibility,
name: Ident,
ty: Type,
variants: impl IntoIterator<Item = Variant>,
) -> Self {
assert!(ty.is_integer(), "only integer types are allowed in enum type definitions");
Self {
span: name.span(),
docs: None,
visibility,
name,
ty,
variants: Vec::from_iter(variants),
}
}
pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
self.docs = docs.map(DocString::new);
self
}
pub fn with_span(mut self, span: SourceSpan) -> Self {
self.span = span;
self
}
pub fn is_c_like(&self) -> bool {
!self.variants.is_empty() && self.variants.iter().all(|v| v.value_ty.is_none())
}
pub fn set_span(&mut self, span: SourceSpan) {
self.span = span;
}
pub fn name(&self) -> &Ident {
&self.name
}
pub const fn visibility(&self) -> Visibility {
self.visibility
}
pub fn docs(&self) -> Option<Span<&str>> {
self.docs.as_ref().map(|docstring| docstring.as_spanned_str())
}
pub fn ty(&self) -> &Type {
&self.ty
}
pub fn variants(&self) -> &[Variant] {
&self.variants
}
pub fn variants_mut(&mut self) -> &mut Vec<Variant> {
&mut self.variants
}
pub fn into_parts(self) -> (TypeAlias, Vec<Variant>) {
let Self {
span,
docs,
visibility,
name,
ty,
variants,
} = self;
let alias = TypeAlias {
span,
docs,
visibility,
name,
ty: TypeExpr::Primitive(Span::new(span, ty)),
};
(alias, variants)
}
}
impl Spanned for EnumType {
fn span(&self) -> SourceSpan {
self.span
}
}
impl Eq for EnumType {}
impl PartialEq for EnumType {
fn eq(&self, other: &Self) -> bool {
self.visibility == other.visibility
&& self.name == other.name
&& self.docs == other.docs
&& self.ty == other.ty
&& self.variants == other.variants
}
}
impl core::hash::Hash for EnumType {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
let Self {
span: _,
docs,
visibility,
name,
ty,
variants,
} = self;
docs.hash(state);
visibility.hash(state);
name.hash(state);
ty.hash(state);
variants.hash(state);
}
}
impl crate::prettier::PrettyPrint for EnumType {
fn render(&self) -> crate::prettier::Document {
use crate::prettier::*;
let mut doc = self
.docs
.as_ref()
.map(|docstring| docstring.render())
.unwrap_or(Document::Empty);
let variants = self
.variants
.iter()
.map(|v| v.render())
.reduce(|acc, v| acc + const_text(",") + nl() + v)
.unwrap_or(Document::Empty);
if self.visibility.is_public() {
doc += display(self.visibility) + const_text(" ");
}
doc + const_text("enum")
+ const_text(" ")
+ display(&self.name)
+ const_text(" : ")
+ self.ty.render()
+ const_text(" {")
+ nl()
+ variants
+ const_text("}")
}
}
#[derive(Debug, Clone)]
pub struct Variant {
pub span: SourceSpan,
pub docs: Option<DocString>,
pub name: Ident,
pub value_ty: Option<TypeExpr>,
pub discriminant: ConstantExpr,
}
impl Variant {
pub fn new(name: Ident, discriminant: ConstantExpr, payload: Option<TypeExpr>) -> Self {
Self {
span: name.span(),
docs: None,
name,
value_ty: payload,
discriminant,
}
}
pub fn with_span(mut self, span: SourceSpan) -> Self {
self.span = span;
self
}
pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
self.docs = docs.map(DocString::new);
self
}
pub fn assert_instance_of(&self, ty: &Type) -> Result<(), crate::SemanticAnalysisError> {
use crate::{FIELD_MODULUS, SemanticAnalysisError};
let value = match &self.discriminant {
ConstantExpr::Int(value) => value.as_int(),
_ => {
return Err(SemanticAnalysisError::InvalidEnumDiscriminant {
span: self.discriminant.span(),
repr: ty.clone(),
});
},
};
match ty {
Type::Felt if value >= FIELD_MODULUS => {
Err(SemanticAnalysisError::InvalidEnumDiscriminant {
span: self.discriminant.span(),
repr: ty.clone(),
})
},
Type::Felt => Ok(()),
Type::I1 if value > 1 => Err(SemanticAnalysisError::InvalidEnumDiscriminant {
span: self.discriminant.span(),
repr: ty.clone(),
}),
Type::I1 => Ok(()),
Type::I8 | Type::U8 if value > u8::MAX as u64 => {
Err(SemanticAnalysisError::InvalidEnumDiscriminant {
span: self.discriminant.span(),
repr: ty.clone(),
})
},
Type::I8 | Type::U8 => Ok(()),
Type::I16 | Type::U16 if value > u16::MAX as u64 => {
Err(SemanticAnalysisError::InvalidEnumDiscriminant {
span: self.discriminant.span(),
repr: ty.clone(),
})
},
Type::I16 | Type::U16 => Ok(()),
Type::I32 | Type::U32 if value > u32::MAX as u64 => {
Err(SemanticAnalysisError::InvalidEnumDiscriminant {
span: self.discriminant.span(),
repr: ty.clone(),
})
},
Type::I32 | Type::U32 => Ok(()),
Type::I64 | Type::U64 if value >= FIELD_MODULUS => {
Err(SemanticAnalysisError::InvalidEnumDiscriminant {
span: self.discriminant.span(),
repr: ty.clone(),
})
},
_ => Err(SemanticAnalysisError::InvalidEnumRepr { span: self.span }),
}
}
}
impl Spanned for Variant {
fn span(&self) -> SourceSpan {
self.span
}
}
impl Eq for Variant {}
impl PartialEq for Variant {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
&& self.value_ty == other.value_ty
&& self.discriminant == other.discriminant
&& self.docs == other.docs
}
}
impl core::hash::Hash for Variant {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
let Self {
span: _,
docs,
name,
value_ty,
discriminant,
} = self;
docs.hash(state);
name.hash(state);
value_ty.hash(state);
discriminant.hash(state);
}
}
impl crate::prettier::PrettyPrint for Variant {
fn render(&self) -> crate::prettier::Document {
use crate::prettier::*;
let doc = self
.docs
.as_ref()
.map(|docstring| docstring.render())
.unwrap_or(Document::Empty);
let name = display(&self.name);
let name_and_payload = if let Some(value_ty) = self.value_ty.as_ref() {
name + const_text("(") + value_ty.render() + const_text(")")
} else {
name
};
doc + name_and_payload + const_text(" = ") + self.discriminant.render()
}
}
#[cfg(test)]
mod tests {
use alloc::sync::Arc;
use core::str::FromStr;
use miden_debug_types::DefaultSourceManager;
use super::*;
struct DummyResolver {
source_manager: Arc<dyn SourceManager>,
}
impl DummyResolver {
fn new() -> Self {
Self {
source_manager: Arc::new(DefaultSourceManager::default()),
}
}
}
impl TypeResolver<SymbolResolutionError> for DummyResolver {
fn source_manager(&self) -> Arc<dyn SourceManager> {
self.source_manager.clone()
}
fn resolve_local_failed(&self, err: SymbolResolutionError) -> SymbolResolutionError {
err
}
fn get_type(
&mut self,
context: SourceSpan,
_gid: GlobalItemIndex,
) -> Result<Type, SymbolResolutionError> {
Err(SymbolResolutionError::undefined(context, self.source_manager.as_ref()))
}
fn get_local_type(
&mut self,
_context: SourceSpan,
_id: ItemIndex,
) -> Result<Option<Type>, SymbolResolutionError> {
Ok(None)
}
fn resolve_type_ref(
&mut self,
ty: Span<&Path>,
) -> Result<SymbolResolution, SymbolResolutionError> {
Err(SymbolResolutionError::undefined(ty.span(), self.source_manager.as_ref()))
}
}
fn nested_type_expr(depth: usize) -> TypeExpr {
let mut expr = TypeExpr::Primitive(Span::unknown(Type::Felt));
for i in 0..depth {
expr = match i % 3 {
0 => TypeExpr::Ptr(PointerType::new(expr)),
1 => TypeExpr::Array(ArrayType::new(expr, 1)),
_ => {
let field = StructField {
span: SourceSpan::UNKNOWN,
name: Ident::from_str("field").expect("valid ident"),
ty: expr,
};
TypeExpr::Struct(StructType::new(None, [field]))
},
};
}
expr
}
#[test]
fn type_expr_depth_boundary() {
let mut resolver = DummyResolver::new();
let ok_expr = nested_type_expr(MAX_TYPE_EXPR_NESTING);
assert!(ok_expr.resolve_type(&mut resolver).is_ok());
let err_expr = nested_type_expr(MAX_TYPE_EXPR_NESTING + 1);
let err = err_expr.resolve_type(&mut resolver).expect_err("expected depth-exceeded error");
assert!(
matches!(err, SymbolResolutionError::TypeExpressionDepthExceeded { max_depth, .. }
if max_depth == MAX_TYPE_EXPR_NESTING)
);
}
}