diff --git a/src/parser/ast/typ.rs b/src/parser/ast/typ.rs index d95012d..2633b48 100644 --- a/src/parser/ast/typ.rs +++ b/src/parser/ast/typ.rs @@ -1,8 +1,8 @@ -use std::fmt::Display; +use std::{clone, collections::HashSet, fmt::Display}; use anyhow::bail; -use crate::{common::loc::LocBox, info, validator::predefined::{BuiltinType, get_builtin_from_name}}; +use crate::{common::loc::LocBox, error, info, validator::predefined::{BuiltinType, get_builtin_from_name}}; use super::{expr::Expr, literal::Literal, Ident, Program}; @@ -75,6 +75,38 @@ impl Type { } } + pub fn get_real_value_of_alias(&self, name: &Ident, program: &Program) -> anyhow::Result { + let mut used_types = HashSet::new(); + used_types.insert(name.clone()); + fn f(slf: &mut Type, program: &Program, used_types: &mut HashSet) -> anyhow::Result { + match slf { + Type::Ref {inner, ..} => { + f(inner, program, used_types) + } + Type::Owned(ident) => { + if let Some(name) = used_types.get(ident) { + error!("Recursive type alias: {name}"); + bail!("") + } + if let Some(t) = program.types.get(ident) { + used_types.insert(ident.clone()); + let mut t = t.inner().clone(); + f(&mut t, program, used_types) + } else { + Ok(slf.clone()) + } + } + Type::SizedArray { inner, .. } | + Type::UnsizedArray { inner } => { + f(inner, program, used_types) + } + Type::Builtin { .. } => Ok(slf.clone()), + } + } + let mut t = self.clone(); + f(&mut t, program, &mut used_types) + } + pub fn get_absolute_value(&self, program: &Program) -> anyhow::Result { match self { Self::Ref { inner, .. } => inner.get_absolute_value(program), diff --git a/src/parser/typ.rs b/src/parser/typ.rs index 5b7345a..bcf50e0 100644 --- a/src/parser/typ.rs +++ b/src/parser/typ.rs @@ -1,6 +1,6 @@ use anyhow::{Result, bail}; -use crate::{cli::CliArgs, common::loc::LocBox, lerror, parser::{Delimiter, ast::{Program, expr::Expr, literal::Literal}}, tokeniser::Token, validator::predefined::get_builtin_from_name}; +use crate::{cli::CliArgs, common::loc::LocBox, lerror, parser::{Delimiter, ast::{Ident, Program, expr::Expr, literal::Literal}}, tokeniser::Token, validator::predefined::get_builtin_from_name}; use super::{ast::{typ::Type, TokenType}, expr::parse_expr, utils, Keyword, Punctuation}; diff --git a/src/validator/mod.rs b/src/validator/mod.rs index 7796e2e..cf7f6f5 100644 --- a/src/validator/mod.rs +++ b/src/validator/mod.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, panic}; use anyhow::bail; -use crate::{common::{Loc, loc::LocBox}, parser::ast::{Ast, Program, Punctuation, Scope, TokenType, expr::*, literal::Literal, statement::*, typ::Type}, validator::predefined::get_builtin_from_name}; +use crate::{common::{Loc, loc::LocBox}, parser::ast::{Ast, Ident, Program, Punctuation, Scope, TokenType, expr::*, literal::Literal, statement::*, typ::Type}, validator::predefined::get_builtin_from_name}; pub mod predefined; @@ -669,7 +669,7 @@ fn check_that_types_exist_for_items(prog: &mut Program, items: &Vec) -> any Ok(()) } -fn validate_type(prog: &mut Program, typ: &LocBox) -> anyhow::Result { +pub fn validate_type(prog: &mut Program, typ: &LocBox) -> anyhow::Result { fn f(prog: &mut Program, typ: &Type, loc: &Loc) -> anyhow::Result { match typ { Type::SizedArray { .. } | @@ -691,7 +691,7 @@ fn validate_type(prog: &mut Program, typ: &LocBox) -> anyhow::Result Type::Builtin { .. } => Ok(typ.clone()) } } - f(prog, typ.inner(), typ.loc()) + f(prog, &typ.inner().get_real_value_of_alias(&Ident::new(""), prog)?, typ.loc()) } diff --git a/tests/parser/typ.rs b/tests/parser/typ.rs index 56d123e..9b3f26e 100644 --- a/tests/parser/typ.rs +++ b/tests/parser/typ.rs @@ -1,6 +1,7 @@ use std::vec; -use mclangc::{common::{Loc, loc::LocBox}, parser::{self, ast::{Ident, Keyword, Program, Punctuation, TokenType, statement::{Enum, Struct}, typ::Type}}, tokeniser::Token, validator::predefined::{BuiltinType, get_builtin_from_name, load_builtin}}; +use anyhow::bail; +use mclangc::{common::{Loc, loc::LocBox}, parser::{self, ast::{Ident, Keyword, Program, Punctuation, TokenType, statement::{Enum, Struct}, typ::Type}}, tokeniser::Token, validator::{self, predefined::{BuiltinType, get_builtin_from_name, load_builtin}}}; @@ -20,9 +21,63 @@ fn get_prog() -> Program { name: Ident::new("MyEnum"), fields: vec![], })); + prog.types.insert(Ident::new("MyType"), LocBox::new(&loc, Type::Owned(Ident::new("MyStruct")))); + prog.types.insert(Ident::new("MyType2"), LocBox::new(&loc, Type::Owned(Ident::new("MyType")))); + prog.types.insert(Ident::new("MyType3"), LocBox::new(&loc, Type::Owned(Ident::new("MyType2")).as_ref())); + prog.types.insert(Ident::new("MyTypeInf"), LocBox::new(&loc, Type::Owned(Ident::new("MyTypeInf")))); + prog.types.insert(Ident::new("MyTypeInfIndirect"), LocBox::new(&loc, Type::Owned(Ident::new("MyType4")))); + prog.types.insert(Ident::new("MyType4"), LocBox::new(&loc, Type::Owned(Ident::new("MyType5")))); + prog.types.insert(Ident::new("MyType5"), LocBox::new(&loc, Type::Owned(Ident::new("MyType4")))); + prog } +#[test] +fn parse_type_alias_simple() { + let mut tokens = vec![ + Token::new_test(TokenType::Ident(Ident::new("MyType"))) + ]; + let ret = parser::typ::parse_type(&mut tokens); + let mut prog = get_prog(); + let ret = validator::validate_type(&mut prog, &ret.unwrap()); + assert!(ret.is_ok()); + assert_eq!(ret.unwrap(), Type::Owned(Ident::new("MyStruct"))) +} + +#[test] +fn parse_type_alias_deep() { + let mut tokens = vec![ + Token::new_test(TokenType::Ident(Ident::new("MyType3"))) + ]; + let ret = parser::typ::parse_type(&mut tokens); + let mut prog = get_prog(); + let ret = validator::validate_type(&mut prog, &ret.unwrap()); + assert!(ret.is_ok()); + assert_eq!(ret.unwrap(), Type::Owned(Ident::new("MyStruct"))) +} + +#[test] +fn parse_type_alias_recursive_simple() { + let mut tokens = vec![ + Token::new_test(TokenType::Ident(Ident::new("MyTypeInf"))) + ]; + let ret = parser::typ::parse_type(&mut tokens); + let mut prog = get_prog(); + let ret = validator::validate_type(&mut prog, &ret.unwrap()); + assert!(ret.is_err()); +} + +#[test] +fn parse_type_alias_recursive_indirect() { + let mut tokens = vec![ + Token::new_test(TokenType::Ident(Ident::new("MyType4"))) + ]; + let ret = parser::typ::parse_type(&mut tokens); + let mut prog = get_prog(); + let ret = validator::validate_type(&mut prog, &ret.unwrap()); + assert!(ret.is_err()); +} + #[test] fn parse_type_named_struct() { let mut tokens = vec![