use alloc::{boxed::Box, string::String, vec::Vec};
use miden_debug_types::{SourceSpan, Span, Spanned};
pub use midenc_hir_type as types;
use midenc_hir_type::{AddressSpace, Type, TypeRepr};
use super::{ConstantExpr, DocString, Ident};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TypeDecl {
Alias(TypeAlias),
Enum(EnumType),
}
impl TypeDecl {
pub fn name(&self) -> &Ident {
match self {
Self::Alias(ty) => &ty.name,
Self::Enum(ty) => &ty.name,
}
}
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(Ident),
}
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(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.addrspace.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 repr: Span<TypeRepr>,
pub fields: Vec<StructField>,
}
impl Eq for StructType {}
impl PartialEq for StructType {
fn eq(&self, other: &Self) -> bool {
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.repr.hash(state);
self.fields.hash(state);
}
}
impl Spanned for StructType {
fn span(&self) -> SourceSpan {
self.span
}
}
impl StructType {
pub fn new(fields: impl IntoIterator<Item = StructField>) -> Self {
Self {
span: SourceSpan::UNKNOWN,
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 name: Ident,
pub ty: TypeExpr,
}
impl TypeAlias {
pub fn new(name: Ident, ty: TypeExpr) -> Self {
Self { span: name.span(), docs: None, 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;
}
}
impl Eq for TypeAlias {}
impl PartialEq for TypeAlias {
fn eq(&self, other: &Self) -> bool {
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, name, ty } = self;
docs.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 doc = self
.docs
.as_ref()
.map(|docstring| docstring.render())
.unwrap_or(Document::Empty);
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>,
name: Ident,
ty: Type,
variants: Vec<Variant>,
}
impl EnumType {
pub fn new(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,
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 set_span(&mut self, span: SourceSpan) {
self.span = span;
}
pub fn name(&self) -> &Ident {
&self.name
}
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, name, ty, variants } = self;
let alias = TypeAlias {
span,
docs,
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.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, name, ty, variants } = self;
docs.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 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);
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 discriminant: ConstantExpr,
}
impl Variant {
pub fn new(name: Ident, discriminant: ConstantExpr) -> Self {
Self {
span: name.span(),
docs: None,
name,
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::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.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, discriminant } = self;
docs.hash(state);
name.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);
doc + display(&self.name) + const_text(" = ") + self.discriminant.render()
}
}