diff --git a/src/ast/enumeration.rs b/src/ast/enumeration.rs index f384b0e..36221f4 100644 --- a/src/ast/enumeration.rs +++ b/src/ast/enumeration.rs @@ -5,6 +5,7 @@ use num::BigInt; #[derive(Clone, Debug)] pub struct Enum { + pub name: String, pub backing_type: Option, pub source: Source, pub members: IndexMap, diff --git a/src/ast/file.rs b/src/ast/file.rs index dafd8e5..71b2fae 100644 --- a/src/ast/file.rs +++ b/src/ast/file.rs @@ -9,9 +9,9 @@ use indexmap::IndexMap; pub struct AstFile { pub functions: Vec, pub structures: Vec, - pub type_aliases: IndexMap, + pub type_aliases: Vec, pub global_variables: Vec, - pub enums: IndexMap, + pub enums: Vec, pub helper_exprs: IndexMap, pub settings: Option, } @@ -21,9 +21,9 @@ impl AstFile { AstFile { functions: vec![], structures: vec![], - type_aliases: IndexMap::default(), + type_aliases: vec![], global_variables: vec![], - enums: IndexMap::default(), + enums: vec![], helper_exprs: IndexMap::default(), settings: None, } diff --git a/src/ast/type_alias.rs b/src/ast/type_alias.rs index 4f28e74..481858b 100644 --- a/src/ast/type_alias.rs +++ b/src/ast/type_alias.rs @@ -3,6 +3,7 @@ use crate::source_files::Source; #[derive(Clone, Debug)] pub struct TypeAlias { + pub name: String, pub value: Type, pub source: Source, pub privacy: Privacy, diff --git a/src/ast/workspace/mod.rs b/src/ast/workspace/mod.rs index fd5224b..23d62a6 100644 --- a/src/ast/workspace/mod.rs +++ b/src/ast/workspace/mod.rs @@ -21,24 +21,6 @@ pub struct AstWorkspace<'a> { impl<'a> AstWorkspace<'a> { pub const DEFAULT_SETTINGS_ID: SettingsId = SettingsId(0); - pub fn get_owning_module(&self, fs_node_id: FsNodeId) -> Option { - let mut fs_node_id = fs_node_id; - - loop { - if self.module_folders.contains_key(&fs_node_id) { - return Some(fs_node_id); - } - - if let Some(parent) = self.fs.get(fs_node_id).parent { - fs_node_id = parent; - } else { - break; - } - } - - None - } - pub fn new( fs: Fs, files: IndexMap, @@ -70,6 +52,24 @@ impl<'a> AstWorkspace<'a> { workspace } + pub fn get_owning_module(&self, fs_node_id: FsNodeId) -> Option { + let mut fs_node_id = fs_node_id; + + loop { + if self.module_folders.contains_key(&fs_node_id) { + return Some(fs_node_id); + } + + if let Some(parent) = self.fs.get(fs_node_id).parent { + fs_node_id = parent; + } else { + break; + } + } + + None + } + pub fn get_mut(&mut self, id: FsNodeId) -> Option<&mut AstFile> { self.files.get_mut(&id) } diff --git a/src/c/translation/mod.rs b/src/c/translation/mod.rs index 4b3a7b7..73d47d8 100644 --- a/src/c/translation/mod.rs +++ b/src/c/translation/mod.rs @@ -10,7 +10,6 @@ use crate::{ ast::{self, AstFile, Privacy}, c::parser::{CTypedef, DeclarationSpecifiers, Declarator, ParseError}, diagnostics::Diagnostics, - name::Name, }; use std::collections::HashMap; @@ -32,14 +31,12 @@ pub fn declare_named_declaration( )?; if is_typedef { - ast_file.type_aliases.insert( - Name::plain(name.clone()), - ast::TypeAlias { - value: ast_type.clone(), - source: declarator.source, - privacy: Privacy::Public, - }, - ); + ast_file.type_aliases.push(ast::TypeAlias { + name: name.clone(), + value: ast_type.clone(), + source: declarator.source, + privacy: Privacy::Public, + }); typedefs.insert(name, CTypedef { ast_type }); return Ok(()); diff --git a/src/interpreter_env/mod.rs b/src/interpreter_env/mod.rs index dfc25c1..4f5843d 100644 --- a/src/interpreter_env/mod.rs +++ b/src/interpreter_env/mod.rs @@ -106,30 +106,28 @@ pub fn setup_build_system_interpreter_symbols(file: &mut AstFile) { privacy: Privacy::Public, }); - file.enums.insert( - Name::plain("ProjectKind"), - Enum { - backing_type: Some(TypeKind::u64().at(source)), - source, - members: IndexMap::from_iter([ - ( - "ConsoleApp".into(), - EnumMember { - value: (ProjectKind::ConsoleApp as u64).into(), - explicit_value: true, - }, - ), - ( - "WindowedApp".into(), - EnumMember { - value: (ProjectKind::WindowedApp as u64).into(), - explicit_value: true, - }, - ), - ]), - privacy: Privacy::Private, - }, - ); + file.enums.push(Enum { + name: "ProjectKind".into(), + backing_type: Some(TypeKind::u64().at(source)), + source, + members: IndexMap::from_iter([ + ( + "ConsoleApp".into(), + EnumMember { + value: (ProjectKind::ConsoleApp as u64).into(), + explicit_value: true, + }, + ), + ( + "WindowedApp".into(), + EnumMember { + value: (ProjectKind::WindowedApp as u64).into(), + explicit_value: true, + }, + ), + ]), + privacy: Privacy::Private, + }); file.structures.push(Structure { name: Name::plain("Project"), diff --git a/src/lower/mod.rs b/src/lower/mod.rs index 752089e..b362b14 100644 --- a/src/lower/mod.rs +++ b/src/lower/mod.rs @@ -84,7 +84,7 @@ fn lower_global( let mangled_name = if global.is_foreign { global.name.plain().to_string() } else { - global.name.display(resolved_ast.fs).to_string() + global.name.display(&resolved_ast.workspace.fs).to_string() }; ir_module.globals.insert( @@ -167,7 +167,10 @@ fn lower_function( } else { return Err(LowerErrorKind::MustReturnValueOfTypeBeforeExitingFunction { return_type: function.return_type.to_string(), - function: function.name.display(resolved_ast.fs).to_string(), + function: function + .name + .display(&resolved_ast.workspace.fs) + .to_string(), } .at(function.source)); } @@ -200,7 +203,10 @@ fn lower_function( } else if function.is_foreign { function.name.plain().to_string() } else { - function.name.display(resolved_ast.fs).to_string() + function + .name + .display(&resolved_ast.workspace.fs) + .to_string() }; let is_main = mangled_name == "main"; @@ -341,6 +347,7 @@ fn lower_type( use resolved::{IntegerBits as Bits, IntegerSign as Sign}; match &resolved_type.kind { + resolved::TypeKind::Unresolved => panic!("got unresolved type during lower_type!"), resolved::TypeKind::Boolean => Ok(ir::Type::Boolean), resolved::TypeKind::Integer(bits, sign) => Ok(match (bits, sign) { (Bits::Bits8, Sign::Signed) => ir::Type::S8, @@ -395,14 +402,22 @@ fn lower_type( }))) } resolved::TypeKind::FunctionPointer(_function_pointer) => Ok(ir::Type::FunctionPointer), - resolved::TypeKind::Enum(enum_name) => { + resolved::TypeKind::Enum(_human_name, enum_ref) => { let enum_definition = resolved_ast .enums - .get(enum_name) + .get(*enum_ref) .expect("referenced enum to exist"); lower_type(target, &enum_definition.resolved_type, resolved_ast) } + resolved::TypeKind::TypeAlias(_, type_alias_ref) => { + let resolved_type = resolved_ast + .type_aliases + .get(*type_alias_ref) + .expect("referenced type alias to exist"); + + lower_type(target, resolved_type, resolved_ast) + } } } @@ -888,7 +903,7 @@ fn lower_expr( ExprKind::EnumMemberLiteral(enum_member_literal) => { let enum_definition = resolved_ast .enums - .get(&enum_member_literal.enum_name) + .get(enum_member_literal.enum_ref) .expect("referenced enum to exist for enum member literal"); let member = enum_definition @@ -896,10 +911,7 @@ fn lower_expr( .get(&enum_member_literal.variant_name) .ok_or_else(|| { LowerErrorKind::NoSuchEnumMember { - enum_name: enum_member_literal - .enum_name - .display(resolved_ast.fs) - .to_string(), + enum_name: enum_member_literal.human_name.to_string(), variant_name: enum_member_literal.variant_name.clone(), } .at(enum_member_literal.source) @@ -916,10 +928,7 @@ fn lower_expr( let make_error = |_| { LowerErrorKind::CannotFit { value: value.to_string(), - expected_type: enum_member_literal - .enum_name - .display(resolved_ast.fs) - .to_string(), + expected_type: enum_member_literal.human_name.to_string(), } .at(enum_definition.source) }; @@ -951,10 +960,7 @@ fn lower_expr( } _ => { return Err(LowerErrorKind::EnumBackingTypeMustBeInteger { - enum_name: enum_member_literal - .enum_name - .display(resolved_ast.fs) - .to_string(), + enum_name: enum_member_literal.human_name.to_string(), } .at(enum_definition.source)) } diff --git a/src/name.rs b/src/name.rs index 419047e..025e167 100644 --- a/src/name.rs +++ b/src/name.rs @@ -88,14 +88,14 @@ impl Display for DisplayResolvedName<'_> { #[allow(dead_code)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let filename = &self.fs.get(self.name.fs_node_id).filename; - let prefix = if cfg!(target_os = "windows") { "/" } else { "" }; + let prefix = if cfg!(target_os = "windows") { "" } else { "/" }; write!( f, - "{}{} ::: {}", + "{}{} - {}", prefix, filename.to_string_lossy(), - self.name.plain() + self.name.plain(), )?; Ok(()) diff --git a/src/parser/parse_enum.rs b/src/parser/parse_enum.rs index a558e45..8bb2a45 100644 --- a/src/parser/parse_enum.rs +++ b/src/parser/parse_enum.rs @@ -1,8 +1,7 @@ use super::{annotation::Annotation, error::ParseError, Parser}; use crate::{ - ast::{Enum, EnumMember, Named, Privacy}, + ast::{Enum, EnumMember, Privacy}, inflow::Inflow, - name::Name, parser::annotation::AnnotationKind, token::{Token, TokenKind}, }; @@ -10,18 +9,16 @@ use indexmap::IndexMap; use num::{BigInt, Zero}; impl<'a, I: Inflow> Parser<'a, I> { - pub fn parse_enum(&mut self, annotations: Vec) -> Result, ParseError> { + pub fn parse_enum(&mut self, annotations: Vec) -> Result { let source = self.source_here(); assert!(self.input.advance().is_enum_keyword()); let mut privacy = Privacy::Private; - let mut namespace = None; let name = self.parse_identifier(Some("for enum name after 'enum' keyword"))?; self.ignore_newlines(); for annotation in annotations { match annotation.kind { - AnnotationKind::Namespace(new_namespace) => namespace = Some(new_namespace), AnnotationKind::Public => privacy = Privacy::Public, _ => return Err(self.unexpected_annotation(&annotation, Some("for enum"))), } @@ -58,14 +55,12 @@ impl<'a, I: Inflow> Parser<'a, I> { self.parse_token(TokenKind::CloseParen, Some("to close enum body"))?; - Ok(Named:: { - name: Name::new(namespace, name), - value: Enum { - backing_type: None, - members, - source, - privacy, - }, + Ok(Enum { + name, + backing_type: None, + members, + source, + privacy, }) } } diff --git a/src/parser/parse_top_level.rs b/src/parser/parse_top_level.rs index 327bbe8..91a6ad9 100644 --- a/src/parser/parse_top_level.rs +++ b/src/parser/parse_top_level.rs @@ -4,7 +4,7 @@ use super::{ Parser, }; use crate::{ - ast::{AstFile, Enum, HelperExpr, Named, TypeAlias}, + ast::{AstFile, HelperExpr, Named}, index_map_ext::IndexMapExt, inflow::Inflow, token::{Token, TokenKind}, @@ -60,31 +60,13 @@ impl<'a, I: Inflow> Parser<'a, I> { ast_file.structures.push(self.parse_structure(annotations)?) } TokenKind::TypeAliasKeyword => { - let Named:: { name, value: alias } = - self.parse_type_alias(annotations)?; - let source = alias.source; - - ast_file.type_aliases.try_insert(name, alias, |name| { - ParseErrorKind::TypeAliasHasMultipleDefinitions { - name: name.to_string(), - } - .at(source) - })?; + let type_alias = self.parse_type_alias(annotations)?; + ast_file.type_aliases.push(type_alias); } TokenKind::EnumKeyword => { - let Named:: { - name, - value: enum_definition, - } = self.parse_enum(annotations)?; + let enum_definition = self.parse_enum(annotations)?; - let source = enum_definition.source; - - ast_file.enums.try_insert(name, enum_definition, |name| { - ParseErrorKind::EnumHasMultipleDefinitions { - name: name.to_string(), - } - .at(source) - })?; + ast_file.enums.push(enum_definition); } TokenKind::DefineKeyword => { let Named:: { diff --git a/src/parser/parse_type_alias.rs b/src/parser/parse_type_alias.rs index 2acc779..a25784f 100644 --- a/src/parser/parse_type_alias.rs +++ b/src/parser/parse_type_alias.rs @@ -1,8 +1,7 @@ use super::{annotation::Annotation, error::ParseError, Parser}; use crate::{ - ast::{Named, Privacy, TypeAlias}, + ast::{Privacy, TypeAlias}, inflow::Inflow, - name::Name, parser::annotation::AnnotationKind, token::{Token, TokenKind}, }; @@ -11,19 +10,16 @@ impl<'a, I: Inflow> Parser<'a, I> { pub fn parse_type_alias( &mut self, annotations: Vec, - ) -> Result, ParseError> { + ) -> Result { let source = self.source_here(); assert!(self.input.advance().is_type_alias_keyword()); - let mut namespace = None; let mut privacy = Privacy::Private; let name = self.parse_identifier(Some("for alias name after 'typealias' keyword"))?; self.ignore_newlines(); - #[allow(clippy::never_loop, clippy::match_single_binding)] for annotation in annotations { match annotation.kind { - AnnotationKind::Namespace(new_namespace) => namespace = Some(new_namespace), AnnotationKind::Public => privacy = Privacy::Public, _ => return Err(self.unexpected_annotation(&annotation, Some("for type alias"))), } @@ -33,13 +29,11 @@ impl<'a, I: Inflow> Parser<'a, I> { let becomes_type = self.parse_type(None::<&str>, Some("for type alias"))?; - Ok(Named:: { - name: Name::new(namespace, name), - value: TypeAlias { - value: becomes_type, - source, - privacy, - }, + Ok(TypeAlias { + name, + value: becomes_type, + source, + privacy, }) } } diff --git a/src/resolve/conform/from_c_integer.rs b/src/resolve/conform/from_c_integer.rs index cdcf9ce..c8adc06 100644 --- a/src/resolve/conform/from_c_integer.rs +++ b/src/resolve/conform/from_c_integer.rs @@ -3,14 +3,15 @@ use crate::{ ast::{CInteger, IntegerBits, OptionIntegerSignExt}, logic::implies, resolved::{ - Cast, CastFrom, ExprKind, IntegerSign, Type, TypeKind, TypedExpr, UnaryMathOperation, + Cast, CastFrom, Expr, ExprKind, IntegerSign, Type, TypeKind, TypedExpr, UnaryMathOperation, UnaryMathOperator, }, source_files::Source, }; pub fn from_c_integer( - expr: &TypedExpr, + expr: &Expr, + from_type: &Type, mode: ConformMode, from_c_integer: CInteger, from_sign: Option, @@ -18,7 +19,7 @@ pub fn from_c_integer( source: Source, ) -> ObjectiveResult { match &to_type.kind { - TypeKind::Boolean => from_c_integer_to_bool::(expr, mode, source), + TypeKind::Boolean => from_c_integer_to_bool::(expr, from_type, mode, source), TypeKind::Integer(to_bits, to_sign) => from_c_integer_to_integer::( expr, mode, @@ -42,7 +43,8 @@ pub fn from_c_integer( } fn from_c_integer_to_bool( - expr: &TypedExpr, + expr: &Expr, + from_type: &Type, mode: ConformMode, source: Source, ) -> ObjectiveResult { @@ -55,7 +57,7 @@ fn from_c_integer_to_bool( TypeKind::Boolean.at(source), ExprKind::UnaryMathOperation(Box::new(UnaryMathOperation { operator: UnaryMathOperator::IsNonZero, - inner: expr.clone(), + inner: TypedExpr::new(from_type.clone(), expr.clone()), })) .at(source), ) @@ -63,7 +65,7 @@ fn from_c_integer_to_bool( } pub fn from_c_integer_to_c_integer( - expr: &TypedExpr, + expr: &Expr, mode: ConformMode, from_c_integer: CInteger, from_sign: Option, @@ -89,7 +91,7 @@ pub fn from_c_integer_to_c_integer( TypedExpr::new( target_type.clone(), ExprKind::IntegerCast(Box::new(CastFrom { - cast: Cast::new(target_type, expr.expr.clone()), + cast: Cast::new(target_type, expr.clone()), from_type: TypeKind::CInteger(from_c_integer, from_sign).at(source), })) .at(source), @@ -101,7 +103,7 @@ pub fn from_c_integer_to_c_integer( } fn from_c_integer_to_integer( - expr: &TypedExpr, + expr: &Expr, mode: ConformMode, from_c_integer: CInteger, from_sign: Option, @@ -119,7 +121,7 @@ fn from_c_integer_to_integer( TypedExpr::new( target_type.clone(), ExprKind::IntegerCast(Box::new(CastFrom { - cast: Cast::new(target_type, expr.expr.clone()), + cast: Cast::new(target_type, expr.clone()), from_type: TypeKind::CInteger(from_c_integer, from_sign).at(source), })) .at(source), diff --git a/src/resolve/conform/from_float.rs b/src/resolve/conform/from_float.rs index d44b4a0..71b9a1e 100644 --- a/src/resolve/conform/from_float.rs +++ b/src/resolve/conform/from_float.rs @@ -6,7 +6,7 @@ use crate::{ }; pub fn from_float( - expr: &TypedExpr, + expr: &Expr, mode: ConformMode, from_size: FloatSize, to_type: &Type, @@ -17,7 +17,7 @@ pub fn from_float( match &to_type.kind { TypeKind::Floating(to_size) => { - from_float_to_float::(&expr.expr, from_size, *to_size, to_type.source) + from_float_to_float::(&expr, from_size, *to_size, to_type.source) } _ => O::fail(), } diff --git a/src/resolve/conform/from_integer.rs b/src/resolve/conform/from_integer.rs index 7a97b33..1e973d0 100644 --- a/src/resolve/conform/from_integer.rs +++ b/src/resolve/conform/from_integer.rs @@ -7,7 +7,8 @@ use crate::{ }; pub fn from_integer( - expr: &TypedExpr, + expr: &Expr, + from_type: &Type, mode: ConformMode, behavior: ConformBehavior, from_bits: IntegerBits, @@ -17,7 +18,7 @@ pub fn from_integer( match &to_type.kind { TypeKind::Integer(to_bits, to_sign) => match behavior { ConformBehavior::Adept(_) => from_integer_adept_mode::( - &expr.expr, + &expr, mode, from_bits, from_sign, @@ -26,7 +27,7 @@ pub fn from_integer( to_type.source, ), ConformBehavior::C => from_integer_c_mode::( - &expr.expr, + &expr, mode, from_bits, from_sign, @@ -37,6 +38,7 @@ pub fn from_integer( }, TypeKind::CInteger(to_c_integer, to_sign) => conform_from_integer_to_c_integer::( expr, + from_type, mode, behavior, from_bits, @@ -92,7 +94,8 @@ fn from_integer_c_mode( } fn conform_from_integer_to_c_integer( - expr: &TypedExpr, + expr: &Expr, + from_type: &Type, mode: ConformMode, behavior: ConformBehavior, from_bits: IntegerBits, @@ -116,8 +119,8 @@ fn conform_from_integer_to_c_integer( if mode.allow_lossy_integer() || is_lossless { let cast_from = CastFrom { - cast: Cast::new(target_type.clone(), expr.expr.clone()), - from_type: expr.resolved_type.clone(), + cast: Cast::new(target_type.clone(), expr.clone()), + from_type: from_type.clone(), }; return O::success(|| { diff --git a/src/resolve/conform/from_pointer.rs b/src/resolve/conform/from_pointer.rs index 0eb4a8a..6c73519 100644 --- a/src/resolve/conform/from_pointer.rs +++ b/src/resolve/conform/from_pointer.rs @@ -1,31 +1,36 @@ -use super::{ConformMode, Objective, ObjectiveResult}; -use crate::resolved::{Type, TypeKind, TypedExpr}; +use super::{warn_type_alias_depth_exceeded, ConformMode, Objective, ObjectiveResult}; +use crate::{ + resolve::expr::ResolveExprCtx, + resolved::{Expr, Type, TypeKind, TypedExpr}, +}; pub fn from_pointer( - expr: &TypedExpr, + ctx: &ResolveExprCtx, + expr: &Expr, mode: ConformMode, - from_inner: &Type, + from_inner_type: &Type, to_type: &Type, ) -> ObjectiveResult { - if from_inner.kind.is_void() { - return match &to_type.kind { - TypeKind::Pointer(to_inner) => O::success(|| { - TypedExpr::new( - TypeKind::Pointer(to_inner.clone()).at(to_type.source), - expr.expr.clone(), - ) - }), - _ => O::fail(), - }; + let Ok(from_inner_type) = ctx.resolved_ast.unalias(from_inner_type) else { + warn_type_alias_depth_exceeded(from_inner_type); + return O::fail(); + }; + + let TypeKind::Pointer(to_inner_type) = &to_type.kind else { + return O::fail(); + }; + + let Ok(to_inner_type) = ctx.resolved_ast.unalias(to_inner_type) else { + warn_type_alias_depth_exceeded(to_inner_type); + return O::fail(); + }; + + if from_inner_type.kind.is_void() { + return O::success(|| TypedExpr::new(to_type.clone(), expr.clone())); } - if to_type.kind.is_void_pointer() && mode.allow_pointer_into_void_pointer() { - return O::success(|| { - TypedExpr::new( - TypeKind::Pointer(Box::new(TypeKind::Void.at(to_type.source))).at(to_type.source), - expr.expr.clone(), - ) - }); + if to_inner_type.kind.is_void() && mode.allow_pointer_into_void_pointer() { + return O::success(|| TypedExpr::new(to_type.clone(), expr.clone())); } O::fail() diff --git a/src/resolve/conform/mod.rs b/src/resolve/conform/mod.rs index c6c230b..7e6f2f2 100644 --- a/src/resolve/conform/mod.rs +++ b/src/resolve/conform/mod.rs @@ -12,7 +12,10 @@ use self::{ from_integer::from_integer, from_integer_literal::from_integer_literal, from_pointer::from_pointer, }; -use super::error::{ResolveError, ResolveErrorKind}; +use super::{ + error::{ResolveError, ResolveErrorKind}, + expr::ResolveExprCtx, +}; use crate::{ ast::ConformBehavior, resolved::{Type, TypeKind, TypedExpr}, @@ -54,45 +57,79 @@ impl Objective for Validate { } } +pub fn warn_type_alias_depth_exceeded(resolved_type: &Type) { + // TODO: WARNING: When this happens, it might not be obvious why there + // wasn't a match. This should probably cause an error message + // TODO: Make this more transparent by adding a good error message + eprintln!( + "warning: ignoring type '{}' since it exceeds maximum type alias recursion depth", + resolved_type + ); +} + pub fn conform_expr( + ctx: &ResolveExprCtx, expr: &TypedExpr, to_type: &Type, mode: ConformMode, behavior: ConformBehavior, conform_source: Source, ) -> ObjectiveResult { - if expr.resolved_type == *to_type { - return O::success(|| expr.clone()); + let Ok(from_type) = ctx.resolved_ast.unalias(&expr.resolved_type) else { + warn_type_alias_depth_exceeded(&expr.resolved_type); + return O::fail(); + }; + + let Ok(to_type) = ctx.resolved_ast.unalias(to_type) else { + warn_type_alias_depth_exceeded(to_type); + return O::fail(); + }; + + if *from_type == *to_type { + return O::success(|| TypedExpr { + resolved_type: to_type.clone(), + expr: expr.expr.clone(), + is_initialized: expr.is_initialized, + }); } - match &expr.resolved_type.kind { + match &from_type.kind { TypeKind::IntegerLiteral(from) => from_integer_literal::( from, behavior.c_integer_assumptions(), expr.expr.source, to_type, ), - TypeKind::Integer(from_bits, from_sign) => { - from_integer::(expr, mode, behavior, *from_bits, *from_sign, to_type) - } + TypeKind::Integer(from_bits, from_sign) => from_integer::( + &expr.expr, from_type, mode, behavior, *from_bits, *from_sign, to_type, + ), TypeKind::FloatLiteral(from) => from_float_literal::(*from, to_type, conform_source), - TypeKind::Floating(from_size) => from_float::(expr, mode, *from_size, to_type), - TypeKind::Pointer(from_inner) => from_pointer::(expr, mode, from_inner, to_type), - TypeKind::CInteger(from_size, from_sign) => { - from_c_integer::(expr, mode, *from_size, *from_sign, to_type, conform_source) + TypeKind::Floating(from_size) => from_float::(&expr.expr, mode, *from_size, to_type), + TypeKind::Pointer(from_inner) => { + from_pointer::(ctx, &expr.expr, mode, from_inner, to_type) } + TypeKind::CInteger(from_size, from_sign) => from_c_integer::( + &expr.expr, + from_type, + mode, + *from_size, + *from_sign, + to_type, + conform_source, + ), _ => O::fail(), } } pub fn conform_expr_or_error( + ctx: &ResolveExprCtx, expr: &TypedExpr, target_type: &Type, mode: ConformMode, behavior: ConformBehavior, conform_source: Source, ) -> Result { - conform_expr::(expr, target_type, mode, behavior, conform_source).or_else(|_| { + conform_expr::(ctx, expr, target_type, mode, behavior, conform_source).or_else(|_| { Err(ResolveErrorKind::TypeMismatch { left: expr.resolved_type.to_string(), right: target_type.to_string(), diff --git a/src/resolve/core_structure_info.rs b/src/resolve/core_structure_info.rs index c486d7a..1e6a05b 100644 --- a/src/resolve/core_structure_info.rs +++ b/src/resolve/core_structure_info.rs @@ -1,15 +1,19 @@ use super::error::{ResolveError, ResolveErrorKind}; use crate::{ - name::ResolvedName, - resolved::{self, StructureRef}, + resolved::{self, HumanName, StructureRef}, source_files::Source, }; -pub fn get_core_structure_info( - resolved_type: &resolved::Type, +pub fn get_core_structure_info<'a, 'b>( + resolved_ast: &'b resolved::Ast<'a>, + resolved_type: &'a resolved::Type, source: Source, -) -> Result<(&ResolvedName, StructureRef), ResolveError> { - match &resolved_type.kind { +) -> Result<(&'b HumanName, StructureRef), ResolveError> { + match &resolved_ast + .unalias(resolved_type) + .map_err(|e| ResolveErrorKind::from(e).at(source))? + .kind + { resolved::TypeKind::Structure(name, structure_ref) => Ok((name, *structure_ref)), _ => Err(ResolveErrorKind::CannotCreateStructLiteralForNonStructure { bad_type: resolved_type.to_string(), diff --git a/src/resolve/error.rs b/src/resolve/error.rs index ef9a659..874ff93 100644 --- a/src/resolve/error.rs +++ b/src/resolve/error.rs @@ -1,5 +1,6 @@ use super::function_search_ctx::FindFunctionError; use crate::{ + resolved::UnaliasError, show::Show, source_files::{Source, SourceFiles}, }; @@ -18,6 +19,12 @@ impl ResolveError { } } +impl From for ResolveErrorKind { + fn from(value: UnaliasError) -> Self { + Self::UnaliasError(value) + } +} + #[derive(Clone, Debug)] pub enum ResolveErrorKind { CannotReturnValueOfType { @@ -31,8 +38,9 @@ pub enum ResolveErrorKind { value: String, }, FailedToFindFunction { - name: String, + signature: String, reason: FindFunctionError, + almost_matches: Vec, }, UndeclaredVariable { name: String, @@ -164,6 +172,7 @@ pub enum ResolveErrorKind { name: String, }, UndeterminedCharacterLiteral, + UnaliasError(UnaliasError), Other { message: String, }, @@ -211,12 +220,33 @@ impl Display for ResolveErrorKind { value )?; } - ResolveErrorKind::FailedToFindFunction { name, reason } => match reason { - FindFunctionError::NotDefined => write!(f, "Failed to find function '{}'", name)?, - FindFunctionError::Ambiguous => { - write!(f, "Multiple possibilities for function '{}'", name)? + ResolveErrorKind::FailedToFindFunction { + signature, + reason, + almost_matches, + } => { + match reason { + FindFunctionError::NotDefined => { + write!(f, "Failed to find function '{}'", signature)? + } + FindFunctionError::Ambiguous => { + write!(f, "Multiple possibilities for function '{}'", signature)? + } } - }, + + if !almost_matches.is_empty() { + write!(f, "\n Did you mean?")?; + + for almost_match in almost_matches { + write!(f, "\n {}", almost_match)?; + } + } else { + write!( + f, + "\n No available functions have that name, is it public?" + )?; + } + } ResolveErrorKind::UndeclaredVariable { name } => { write!(f, "Undeclared variable '{}'", name)?; } @@ -425,6 +455,12 @@ impl Display for ResolveErrorKind { "Undetermined character literal, consider using c'A' if you want a 'char'" )?; } + ResolveErrorKind::UnaliasError(details) => match details { + UnaliasError::MaxDepthExceeded => write!( + f, + "Maximum type alias depth exceeded, please ensure your type alias is not recursive" + )?, + }, ResolveErrorKind::Other { message } => { write!(f, "{}", message)?; } diff --git a/src/resolve/expr/basic_binary_operation.rs b/src/resolve/expr/basic_binary_operation.rs index 26b2dbb..d66e26f 100644 --- a/src/resolve/expr/basic_binary_operation.rs +++ b/src/resolve/expr/basic_binary_operation.rs @@ -45,6 +45,7 @@ pub fn resolve_basic_binary_operation_expr( } let unified_type = unify_types( + ctx, preferred_type.map(|preferred_type| preferred_type.view(ctx.resolved_ast)), &mut [&mut left, &mut right], ctx.adept_conform_behavior(), diff --git a/src/resolve/expr/call.rs b/src/resolve/expr/call.rs index 71c2503..a7b313b 100644 --- a/src/resolve/expr/call.rs +++ b/src/resolve/expr/call.rs @@ -4,11 +4,12 @@ use crate::{ resolve::{ conform::{conform_expr, to_default::conform_expr_to_default, ConformMode, Perform}, error::{ResolveError, ResolveErrorKind}, - resolve_type, Initialized, + Initialized, }, resolved::{self, TypedExpr}, source_files::Source, }; +use itertools::Itertools; pub fn resolve_call_expr( ctx: &mut ResolveExprCtx<'_, '_>, @@ -35,9 +36,21 @@ pub fn resolve_call_expr( ) { Ok(function_ref) => function_ref, Err(reason) => { + let args = arguments + .iter() + .map(|arg| arg.resolved_type.to_string()) + .collect_vec(); + + let signature = format!("{}({})", call.function_name, args.join(", ")); + + let almost_matches = ctx + .function_search_ctx + .find_function_almost_matches(ctx, &call.function_name); + return Err(ResolveErrorKind::FailedToFindFunction { - name: call.function_name.to_string(), + signature, reason, + almost_matches, } .at(source)); } @@ -58,6 +71,7 @@ pub fn resolve_call_expr( preferred_type.map(|preferred_type| preferred_type.view(ctx.resolved_ast)) { if let Ok(conformed_argument) = conform_expr::( + ctx, &argument, preferred_type, ConformMode::ParameterPassing, @@ -69,7 +83,10 @@ pub fn resolve_call_expr( return Err(ResolveErrorKind::BadTypeForArgumentToFunction { expected: preferred_type.to_string(), got: argument.resolved_type.to_string(), - name: function.name.display(ctx.resolved_ast.fs).to_string(), + name: function + .name + .display(&ctx.resolved_ast.workspace.fs) + .to_string(), i, } .at(source)); @@ -88,13 +105,15 @@ pub fn resolve_call_expr( } if let Some(required_ty) = &call.expected_to_return { - let resolved_required_ty = - resolve_type(ctx.type_search_ctx, required_ty, &mut Default::default())?; + let resolved_required_ty = ctx.type_ctx().resolve(required_ty)?; if resolved_required_ty != return_type { return Err(ResolveErrorKind::FunctionMustReturnType { of: required_ty.to_string(), - function_name: function.name.display(ctx.resolved_ast.fs).to_string(), + function_name: function + .name + .display(&ctx.resolved_ast.workspace.fs) + .to_string(), } .at(function.return_type.source)); } diff --git a/src/resolve/expr/conditional.rs b/src/resolve/expr/conditional.rs index 8e8daca..87256cb 100644 --- a/src/resolve/expr/conditional.rs +++ b/src/resolve/expr/conditional.rs @@ -33,6 +33,7 @@ pub fn resolve_conditional_expr( let stmts = resolve_stmts(ctx, &block.stmts)?; let condition = conform_expr_or_error( + ctx, &condition, &resolved::TypeKind::Boolean.at(source), ConformMode::Normal, @@ -103,6 +104,7 @@ pub fn resolve_conditional_expr( .collect_vec(); unify_types( + ctx, preferred_type.map(|preferred_type| preferred_type.view(ctx.resolved_ast)), &mut last_exprs[..], ctx.adept_conform_behavior(), diff --git a/src/resolve/expr/member_expr.rs b/src/resolve/expr/member_expr.rs index 2664fd3..b4b765d 100644 --- a/src/resolve/expr/member_expr.rs +++ b/src/resolve/expr/member_expr.rs @@ -20,7 +20,8 @@ pub fn resolve_member_expr( ) -> Result { let resolved_subject = resolve_expr(ctx, subject, None, Initialized::Require)?; - let (_, structure_ref) = get_core_structure_info(&resolved_subject.resolved_type, source)?; + let (_, structure_ref) = + get_core_structure_info(ctx.resolved_ast, &resolved_subject.resolved_type, source)?; let structure = ctx .resolved_ast diff --git a/src/resolve/expr/mod.rs b/src/resolve/expr/mod.rs index 1deff11..8746a69 100644 --- a/src/resolve/expr/mod.rs +++ b/src/resolve/expr/mod.rs @@ -20,9 +20,8 @@ use super::{ error::ResolveError, function_search_ctx::FunctionSearchCtx, global_search_ctx::GlobalSearchCtx, - type_search_ctx::TypeSearchCtx, variable_search_ctx::VariableSearchCtx, - Initialized, + Initialized, ResolveTypeCtx, }; use crate::{ ast::{ @@ -38,7 +37,7 @@ use crate::{ struct_literal::resolve_struct_literal_expr, unary_operation::resolve_unary_math_operation_expr, variable::resolve_variable_expr, }, - resolve_stmts, resolve_type, + resolve_stmts, }, resolved::{self, Expr, ExprKind, FunctionRef, StructureRef, TypeKind, TypedExpr}, workspace::fs::FsNodeId, @@ -52,17 +51,22 @@ use std::collections::HashMap; pub struct ResolveExprCtx<'a, 'b> { pub resolved_ast: &'b mut resolved::Ast<'a>, pub function_search_ctx: &'b FunctionSearchCtx, - pub type_search_ctx: &'b TypeSearchCtx<'a>, pub global_search_ctx: &'b GlobalSearchCtx, pub variable_search_ctx: VariableSearchCtx, pub resolved_function_ref: resolved::FunctionRef, pub helper_exprs: &'b IndexMap, pub settings: &'b Settings, pub public_functions: &'b HashMap>>, + pub types_in_modules: &'b HashMap>, pub module_fs_node_id: FsNodeId, + pub physical_fs_node_id: FsNodeId, } impl<'a, 'b> ResolveExprCtx<'a, 'b> { + pub fn type_ctx<'c>(&'c self) -> ResolveTypeCtx<'c> { + ResolveTypeCtx::from(self) + } + pub fn adept_conform_behavior(&self) -> ConformBehavior { ConformBehavior::Adept(self.c_integer_assumptions()) } @@ -322,13 +326,16 @@ pub fn resolve_expr( ast::ExprKind::While(while_loop) => { ctx.variable_search_ctx.begin_scope(); + let expr = resolve_expr( + ctx, + &while_loop.condition, + Some(PreferredType::of(&resolved::TypeKind::Boolean.at(source))), + Initialized::Require, + )?; + let condition = conform_expr_or_error( - &resolve_expr( - ctx, - &while_loop.condition, - Some(PreferredType::of(&resolved::TypeKind::Boolean.at(source))), - Initialized::Require, - )?, + ctx, + &expr, &resolved::TypeKind::Boolean.at(source), ConformMode::Normal, ctx.adept_conform_behavior(), @@ -352,14 +359,12 @@ pub fn resolve_expr( resolved::Expr::new(resolved::ExprKind::BooleanLiteral(*value), source), )), ast::ExprKind::EnumMemberLiteral(enum_member_literal) => { - let resolved_type = resolve_type( - ctx.type_search_ctx, + let resolved_type = ctx.type_ctx().resolve( &ast::TypeKind::Named(enum_member_literal.enum_name.clone()) .at(enum_member_literal.source), - &mut Default::default(), )?; - let TypeKind::Enum(resolved_name) = &resolved_type.kind else { + let TypeKind::Enum(human_name, enum_ref) = &resolved_type.kind else { return Err(ResolveErrorKind::StaticMemberOfTypeDoesNotExist { ty: enum_member_literal.enum_name.to_string(), member: enum_member_literal.variant_name.clone(), @@ -367,13 +372,12 @@ pub fn resolve_expr( .at(source)); }; - let resolved_name = resolved_name.clone(); - Ok(TypedExpr::new( - resolved_type, + resolved_type.clone(), resolved::Expr::new( resolved::ExprKind::EnumMemberLiteral(Box::new(resolved::EnumMemberLiteral { - enum_name: resolved_name, + human_name: human_name.clone(), + enum_ref: *enum_ref, variant_name: enum_member_literal.variant_name.clone(), source, })), @@ -388,17 +392,11 @@ pub fn resolve_expr( result_type, } = &**info; - let resolved_type = - resolve_type(ctx.type_search_ctx, result_type, &mut Default::default())?; - + let resolved_type = ctx.type_ctx().resolve(result_type)?; let mut resolved_args = Vec::with_capacity(args.len()); for (expected_arg_type, arg) in args { - let preferred_type = resolve_type( - ctx.type_search_ctx, - expected_arg_type, - &mut Default::default(), - )?; + let preferred_type = ctx.type_ctx().resolve(expected_arg_type)?; resolved_args.push( resolve_expr( diff --git a/src/resolve/expr/short_circuiting_binary_operation.rs b/src/resolve/expr/short_circuiting_binary_operation.rs index 1ebca63..95bfb7c 100644 --- a/src/resolve/expr/short_circuiting_binary_operation.rs +++ b/src/resolve/expr/short_circuiting_binary_operation.rs @@ -26,6 +26,7 @@ pub fn resolve_short_circuiting_binary_operation_expr( )?; let left = conform_expr::( + ctx, &left, &local_bool_type, ConformMode::Normal, @@ -52,6 +53,7 @@ pub fn resolve_short_circuiting_binary_operation_expr( )?; let right = conform_expr::( + ctx, &right, &local_bool_type, ConformMode::Normal, diff --git a/src/resolve/expr/struct_literal.rs b/src/resolve/expr/struct_literal.rs index 1b444a9..430dc15 100644 --- a/src/resolve/expr/struct_literal.rs +++ b/src/resolve/expr/struct_literal.rs @@ -5,7 +5,7 @@ use crate::{ conform::{conform_expr, ConformMode, Perform}, core_structure_info::get_core_structure_info, error::{ResolveError, ResolveErrorKind}, - resolve_type, Initialized, + Initialized, }, resolved::{self, StructLiteral, StructureRef, TypedExpr}, source_files::Source, @@ -14,7 +14,7 @@ use indexmap::IndexMap; use itertools::Itertools; fn get_field_info<'a>( - ctx: &'a ResolveExprCtx<'_, '_>, + ctx: &'a ResolveExprCtx, structure_ref: StructureRef, field_name: &str, ) -> (usize, &'a resolved::Field) { @@ -30,15 +30,16 @@ fn get_field_info<'a>( } pub fn resolve_struct_literal_expr( - ctx: &mut ResolveExprCtx<'_, '_>, + ctx: &mut ResolveExprCtx, ast_type: &ast::Type, fields: &[FieldInitializer], fill_behavior: FillBehavior, conform_behavior: ConformBehavior, source: Source, ) -> Result { - let resolved_type = resolve_type(ctx.type_search_ctx, ast_type, &mut Default::default())?; - let (struct_name, structure_ref) = get_core_structure_info(&resolved_type, source)?; + let resolved_struct_type = ctx.type_ctx().resolve(ast_type)?; + let (struct_name, structure_ref) = + get_core_structure_info(ctx.resolved_ast, &resolved_struct_type, source)?; let structure_type = resolved::TypeKind::Structure(struct_name.clone(), structure_ref).at(source); @@ -97,6 +98,7 @@ pub fn resolve_struct_literal_expr( }; let resolved_expr = conform_expr::( + ctx, &resolved_expr, &field.resolved_type, mode, @@ -116,8 +118,11 @@ pub fn resolve_struct_literal_expr( .insert(field_name.to_string(), (resolved_expr.expr, index)) .is_some() { + let (struct_name, _) = + get_core_structure_info(ctx.resolved_ast, &resolved_struct_type, source)?; + return Err(ResolveErrorKind::FieldSpecifiedMoreThanOnce { - struct_name: struct_name.display(ctx.resolved_ast.fs).to_string(), + struct_name: struct_name.to_string(), field_name: field_name.to_string(), } .at(ast_type.source)); @@ -161,7 +166,7 @@ pub fn resolve_struct_literal_expr( } Ok(TypedExpr::new( - resolved_type.clone(), + resolved_struct_type.clone(), resolved::Expr::new( resolved::ExprKind::StructLiteral(Box::new(StructLiteral { structure_type, diff --git a/src/resolve/function_search_ctx.rs b/src/resolve/function_search_ctx.rs index 987ce28..53ba365 100644 --- a/src/resolve/function_search_ctx.rs +++ b/src/resolve/function_search_ctx.rs @@ -9,6 +9,7 @@ use crate::{ source_files::Source, workspace::fs::FsNodeId, }; +use itertools::Itertools; use std::collections::HashMap; #[derive(Clone, Debug)] @@ -134,6 +135,7 @@ impl FunctionSearchCtx { let argument_conform = if let Some(preferred_type) = preferred_type.map(|p| p.view(ctx.resolved_ast)) { conform_expr::( + ctx, argument, preferred_type, ConformMode::ParameterPassing, @@ -151,4 +153,42 @@ impl FunctionSearchCtx { true } + + pub fn find_function_almost_matches(&self, ctx: &ResolveExprCtx, name: &Name) -> Vec { + let resolved_name = ResolvedName::new(self.fs_node_id, name); + + let local_matches = self.available.get(&resolved_name).into_iter().flatten(); + + let remote_matches = (!name.namespace.is_empty()) + .then(|| { + ctx.settings + .namespace_to_dependency + .get(name.namespace.as_ref()) + }) + .flatten() + .into_iter() + .flatten() + .flat_map(|dependency| { + ctx.settings + .dependency_to_module + .get(dependency) + .and_then(|module_fs_node_id| ctx.public_functions.get(module_fs_node_id)) + .and_then(|public| public.get(name.basename.as_ref())) + .into_iter() + }) + .flatten(); + + local_matches + .chain(remote_matches) + .map(|function_ref| { + let function = ctx.resolved_ast.functions.get(*function_ref).unwrap(); + + format!( + "{}({})", + function.name.display(&ctx.resolved_ast.workspace.fs), + function.parameters + ) + }) + .collect_vec() + } } diff --git a/src/resolve/global_search_ctx.rs b/src/resolve/global_search_ctx.rs index 02918ac..0a5f5d8 100644 --- a/src/resolve/global_search_ctx.rs +++ b/src/resolve/global_search_ctx.rs @@ -7,7 +7,7 @@ use crate::{ }; use std::collections::HashMap; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct GlobalSearchCtx { globals: HashMap, } diff --git a/src/resolve/mod.rs b/src/resolve/mod.rs index 035fe3f..bc607dc 100644 --- a/src/resolve/mod.rs +++ b/src/resolve/mod.rs @@ -6,7 +6,6 @@ mod expr; mod function_search_ctx; mod global_search_ctx; mod stmt; -mod type_search_ctx; mod unify_types; mod variable_search_ctx; @@ -15,15 +14,17 @@ use self::{ expr::ResolveExprCtx, global_search_ctx::GlobalSearchCtx, stmt::resolve_stmts, - type_search_ctx::TypeSearchCtx, variable_search_ctx::VariableSearchCtx, }; use crate::{ ast::{self, AstWorkspace, Type}, cli::BuildOptions, index_map_ext::IndexMapExt, - name::ResolvedName, - resolved::{self, Enum, TypedExpr, VariableStorage}, + name::{Name, ResolvedName}, + resolved::{ + self, EnumRef, HumanName, StructureRef, TypeAliasRef, TypeDecl, TypeKind, TypedExpr, + VariableStorage, + }, source_files::Source, tag::Tag, workspace::fs::FsNodeId, @@ -32,10 +33,9 @@ use ast::{IntegerBits, IntegerSign}; use function_search_ctx::FunctionSearchCtx; use indexmap::IndexMap; use std::{ - borrow::Borrow, + borrow::{Borrow, Cow}, collections::{HashMap, HashSet, VecDeque}, }; -use type_search_ctx::TypeMapping; enum Job { Regular(FsNodeId, usize, resolved::FunctionRef), @@ -43,24 +43,22 @@ enum Job { struct ResolveCtx<'a> { pub jobs: VecDeque, - pub type_search_ctxs: IndexMap>, pub function_search_ctxs: IndexMap, pub global_search_ctxs: IndexMap, pub helper_exprs: IndexMap, pub public_functions: HashMap>>, - pub public_types: HashMap>>>, + pub types_in_modules: HashMap>, } impl<'a> ResolveCtx<'a> { fn new(helper_exprs: IndexMap) -> Self { Self { jobs: Default::default(), - type_search_ctxs: Default::default(), function_search_ctxs: Default::default(), global_search_ctxs: Default::default(), helper_exprs, public_functions: HashMap::new(), - public_types: HashMap::new(), + types_in_modules: HashMap::new(), } } } @@ -101,140 +99,219 @@ pub fn resolve<'a>( let mut ctx = ResolveCtx::new(helper_exprs); let source_files = ast_workspace.source_files; - let mut resolved_ast = resolved::Ast::new(source_files, &ast_workspace.fs); - - // Unify type aliases into single map - for (real_file_id, file) in ast_workspace.files.iter() { - let file_id = ast_workspace - .get_owning_module(*real_file_id) - .unwrap_or(*real_file_id); - - let imported_namespaces = - &ast_workspace.settings[file.settings.unwrap_or_default().0].imported_namespaces; - - let type_search_ctx = ctx.type_search_ctxs.get_or_insert_with(file_id, || { - TypeSearchCtx::new(imported_namespaces.clone(), source_files, file_id) - }); - - for (alias_name, alias) in file.type_aliases.iter() { - type_search_ctx.put_type_alias(&alias_name, alias, alias.source)?; - } + let mut resolved_ast = resolved::Ast::new(source_files, &ast_workspace); + + #[derive(Clone, Debug)] + struct TypeJob { + physical_file_id: FsNodeId, + type_aliases: Vec, + structures: Vec, + enums: Vec, } - // Temporarily used stack to keep track of used type aliases - let mut used_aliases = HashSet::::new(); + let mut type_jobs = Vec::with_capacity(ast_workspace.files.len()); - // Pre-compute resolved enum types for (physical_file_id, file) in ast_workspace.files.iter() { let file_id = ast_workspace .get_owning_module(*physical_file_id) .unwrap_or(*physical_file_id); - let type_search_ctx = ctx.type_search_ctxs.get_mut(&file_id).unwrap(); + let mut job = TypeJob { + physical_file_id: *physical_file_id, + type_aliases: Vec::with_capacity(file.type_aliases.len()), + structures: Vec::with_capacity(file.structures.len()), + enums: Vec::with_capacity(file.enums.len()), + }; - for (enum_name, enum_definition) in file.enums.iter() { - let resolved_type = resolve_enum_backing_type( - type_search_ctx, - enum_definition.backing_type.as_ref(), - &mut used_aliases, - enum_definition.source, - )?; + for structure in file.structures.iter() { + let privacy = structure.privacy; + let source = structure.source; + let resolved_name = ResolvedName::new(file_id, &structure.name); - let members = enum_definition.members.clone(); + let structure_ref = resolved_ast.structures.insert(resolved::Structure { + name: resolved_name.clone(), + fields: IndexMap::new(), + is_packed: structure.is_packed, + source: structure.source, + }); - resolved_ast.enums.insert( - ResolvedName::new(file_id, enum_name), - Enum { - resolved_type, - source: enum_definition.source, - members, + let struct_type_kind = + TypeKind::Structure(HumanName(structure.name.to_string()), structure_ref); + + let Some(name) = structure.name.as_plain_str() else { + eprintln!( + "warning: internal namespaced structures ignored by new type resolution system" + ); + continue; + }; + + let types_in_module = ctx + .types_in_modules + .entry(file_id) + .or_insert_with(HashMap::new); + + types_in_module.insert( + name.to_string(), + TypeDecl { + kind: struct_type_kind, + source, + privacy, }, ); - type_search_ctx.put_type( - enum_name, - resolved::TypeKind::Enum(ResolvedName::new(file_id, enum_name)), - enum_definition.source, - )?; + job.structures.push(structure_ref); } - } - // Precompute resolved struct types - for (physical_file_id, file) in ast_workspace.files.iter() { - let file_id = ast_workspace - .get_owning_module(*physical_file_id) - .unwrap_or(*physical_file_id); + for definition in file.enums.iter() { + let enum_ref = resolved_ast.enums.insert(resolved::Enum { + resolved_type: TypeKind::Unresolved.at(definition.source), + source: definition.source, + members: definition.members.clone(), + }); - let type_search_ctx = ctx.type_search_ctxs.get_mut(&file_id).unwrap(); + let kind = TypeKind::Enum(HumanName(definition.name.to_string()), enum_ref); + let source = definition.source; + let privacy = definition.privacy; + + let types_in_module = ctx + .types_in_modules + .entry(file_id) + .or_insert_with(HashMap::new); + + types_in_module.insert( + definition.name.to_string(), + TypeDecl { + kind, + source, + privacy, + }, + ); - for structure in file.structures.iter() { - let mut fields = IndexMap::new(); + job.enums.push(enum_ref); + } + for definition in file.type_aliases.iter() { + let type_alias_ref = resolved_ast + .type_aliases + .insert(resolved::TypeKind::Unresolved.at(definition.value.source)); + + let source = definition.source; + let privacy = definition.privacy; + let kind = TypeKind::TypeAlias(HumanName(definition.name.to_string()), type_alias_ref); + + let types_in_module = ctx + .types_in_modules + .entry(file_id) + .or_insert_with(HashMap::new); + + types_in_module.insert( + definition.name.to_string(), + TypeDecl { + kind, + source, + privacy, + }, + ); + + job.type_aliases.push(type_alias_ref); + } + + type_jobs.push(job); + } + + // Create edges between types + for job in type_jobs.iter() { + let file = ast_workspace + .files + .get(&job.physical_file_id) + .expect("valid ast file"); + + let module_file_id = ast_workspace + .get_owning_module(job.physical_file_id) + .unwrap_or(job.physical_file_id); + + for (structure_ref, structure) in job.structures.iter().zip(file.structures.iter()) { for (field_name, field) in structure.fields.iter() { - fields.insert( - field_name.into(), + let type_ctx = ResolveTypeCtx::new( + &resolved_ast, + module_file_id, + job.physical_file_id, + &ctx.types_in_modules, + ); + + let resolved_type = type_ctx.resolve_or_undeclared(&field.ast_type)?; + + let resolved_struct = resolved_ast + .structures + .get_mut(*structure_ref) + .expect("valid struct"); + + resolved_struct.fields.insert( + field_name.clone(), resolved::Field { - resolved_type: resolve_type( - type_search_ctx, - &field.ast_type, - &mut used_aliases, - )?, + resolved_type, privacy: field.privacy, source: field.source, }, ); } + } - let resolved_name = ResolvedName::new(file_id, &structure.name); + for (enum_ref, definition) in job.enums.iter().zip(file.enums.iter()) { + let type_ctx = ResolveTypeCtx::new( + &resolved_ast, + module_file_id, + job.physical_file_id, + &ctx.types_in_modules, + ); - let structure_key = resolved_ast.structures.insert(resolved::Structure { - name: resolved_name.clone(), - fields, - is_packed: structure.is_packed, - source: structure.source, - }); + let ast_type = definition + .backing_type + .as_ref() + .map(Cow::Borrowed) + .unwrap_or_else(|| Cow::Owned(ast::TypeKind::u32().at(definition.source))); - type_search_ctx.put_type( - &structure.name, - resolved::TypeKind::Structure(resolved_name, structure_key), - structure.source, - )?; - } - } + let resolved_type = type_ctx.resolve_or_undeclared(&ast_type)?; - // Resolve type aliases - for (real_file_id, file) in ast_workspace.files.iter() { - let file_id = ast_workspace - .get_owning_module(*real_file_id) - .unwrap_or(*real_file_id); + let definition = resolved_ast.enums.get_mut(*enum_ref).unwrap(); + definition.resolved_type = resolved_type; + } - let type_search_ctx = ctx.type_search_ctxs.get_mut(&file_id).unwrap(); + for (type_alias_ref, definition) in job.type_aliases.iter().zip(file.type_aliases.iter()) { + let type_ctx = ResolveTypeCtx::new( + &resolved_ast, + module_file_id, + job.physical_file_id, + &ctx.types_in_modules, + ); - for (alias_name, alias) in file.type_aliases.iter() { - let resolved_type = - resolve_type_or_undeclared(type_search_ctx, &alias.value, &mut used_aliases)?; + let resolved_type = type_ctx.resolve_or_undeclared(&definition.value)?; - type_search_ctx.override_type(&alias_name, resolved_type.kind); + let binding = resolved_ast.type_aliases.get_mut(*type_alias_ref).unwrap(); + *binding = resolved_type; } } // Resolve global variables for (physical_file_id, file) in ast_workspace.files.iter() { - let file_id = ast_workspace + let module_file_id = ast_workspace .get_owning_module(*physical_file_id) .unwrap_or(*physical_file_id); - let type_search_ctx = ctx.type_search_ctxs.get_mut(&file_id).unwrap(); - - let global_search_context = ctx - .global_search_ctxs - .get_or_insert_with(file_id, || GlobalSearchCtx::new()); - for global in file.global_variables.iter() { - let resolved_type = - resolve_type(type_search_ctx, &global.ast_type, &mut Default::default())?; + let type_ctx = ResolveTypeCtx::new( + &resolved_ast, + module_file_id, + *physical_file_id, + &ctx.types_in_modules, + ); + let resolved_type = type_ctx.resolve(&global.ast_type)?; - let resolved_name = ResolvedName::new(file_id, &global.name); + let global_search_context = ctx + .global_search_ctxs + .get_or_insert_with(module_file_id, || GlobalSearchCtx::new()); + + let resolved_name = ResolvedName::new(module_file_id, &global.name); let global_ref = resolved_ast.globals.insert(resolved::GlobalVar { name: resolved_name.clone(), @@ -250,23 +327,25 @@ pub fn resolve<'a>( // Create initial function jobs for (physical_file_id, file) in ast_workspace.files.iter() { - let file_id = ast_workspace + let module_file_id = ast_workspace .get_owning_module(*physical_file_id) .unwrap_or(*physical_file_id); - let type_search_ctx = ctx.type_search_ctxs.get_mut(&file_id).unwrap(); - for (function_i, function) in file.functions.iter().enumerate() { - let name = ResolvedName::new(file_id, &function.name); + let name = ResolvedName::new(module_file_id, &function.name); + let type_ctx = ResolveTypeCtx::new( + &resolved_ast, + module_file_id, + *physical_file_id, + &ctx.types_in_modules, + ); + let parameters = resolve_parameters(&type_ctx, &function.parameters)?; + let return_type = type_ctx.resolve(&function.return_type)?; let function_ref = resolved_ast.functions.insert(resolved::Function { name: name.clone(), - parameters: resolve_parameters(type_search_ctx, &function.parameters)?, - return_type: resolve_type( - type_search_ctx, - &function.return_type, - &mut Default::default(), - )?, + parameters, + return_type, stmts: vec![], is_foreign: function.is_foreign, variables: VariableStorage::new(), @@ -287,7 +366,7 @@ pub fn resolve<'a>( if function.privacy.is_public() { let public_of_module = ctx .public_functions - .entry(file_id) + .entry(module_file_id) .or_insert_with(HashMap::new); // TODO: Add proper error message @@ -310,14 +389,15 @@ pub fn resolve<'a>( let imported_namespaces = settings.map(|settings| &settings.imported_namespaces); let function_search_context = - ctx.function_search_ctxs.get_or_insert_with(file_id, || { - FunctionSearchCtx::new( - imported_namespaces - .map(|namespaces| namespaces.clone()) - .unwrap_or_else(|| vec![]), - file_id, - ) - }); + ctx.function_search_ctxs + .get_or_insert_with(module_file_id, || { + FunctionSearchCtx::new( + imported_namespaces + .map(|namespaces| namespaces.clone()) + .unwrap_or_else(|| vec![]), + module_file_id, + ) + }); function_search_context .available @@ -331,24 +411,17 @@ pub fn resolve<'a>( while let Some(job) = ctx.jobs.pop_front() { match job { Job::Regular(real_file_id, function_index, resolved_function_ref) => { - let file_id = ast_workspace + let module_file_id = ast_workspace .get_owning_module(real_file_id) .unwrap_or(real_file_id); + // NOTE: This module should already have a function search context let function_search_ctx = ctx .function_search_ctxs - .get(&file_id) + .get(&module_file_id) .expect("function search context to exist for file"); - let type_search_ctx = ctx - .type_search_ctxs - .get(&file_id) - .expect("type search context to exist for file"); - - let global_search_ctx = ctx - .global_search_ctxs - .get(&file_id) - .expect("global search context to exist for file"); + let global_search_ctx = &*ctx.global_search_ctxs.entry(module_file_id).or_default(); let ast_file = ast_workspace .files @@ -363,17 +436,20 @@ pub fn resolve<'a>( let mut variable_search_ctx = VariableSearchCtx::new(); { - let function = resolved_ast - .functions - .get_mut(resolved_function_ref) - .unwrap(); - for parameter in ast_function.parameters.required.iter() { - let resolved_type = resolve_type( - type_search_ctx, - ¶meter.ast_type, - &mut Default::default(), - )?; + let type_ctx = ResolveTypeCtx::new( + &resolved_ast, + module_file_id, + real_file_id, + &ctx.types_in_modules, + ); + + let resolved_type = type_ctx.resolve(¶meter.ast_type)?; + + let function = resolved_ast + .functions + .get_mut(resolved_function_ref) + .unwrap(); let variable_key = function.variables.add_parameter(resolved_type.clone()); @@ -396,14 +472,15 @@ pub fn resolve<'a>( let mut ctx = ResolveExprCtx { resolved_ast: &mut resolved_ast, function_search_ctx, - type_search_ctx, global_search_ctx, variable_search_ctx, resolved_function_ref, helper_exprs: &ctx.helper_exprs, settings, public_functions: &ctx.public_functions, - module_fs_node_id: file_id, + types_in_modules: &ctx.types_in_modules, + module_fs_node_id: module_file_id, + physical_fs_node_id: real_file_id, }; resolve_stmts(&mut ctx, &ast_function.stmts)? @@ -428,107 +505,211 @@ enum Initialized { AllowUninitialized, } -fn resolve_type_or_undeclared<'a>( - type_search_ctx: &'a TypeSearchCtx<'_>, - ast_type: &'a ast::Type, - used_aliases_stack: &mut HashSet, -) -> Result { - match resolve_type(type_search_ctx, ast_type, used_aliases_stack) { - Ok(inner) => Ok(inner), - Err(_) if ast_type.kind.allow_indirect_undefined() => { - Ok(resolved::TypeKind::Void.at(ast_type.source)) - } - Err(err) => Err(err), +#[derive(Debug)] +pub struct ResolveTypeCtx<'a> { + resolved_ast: &'a resolved::Ast<'a>, + module_fs_node_id: FsNodeId, + file_fs_node_id: FsNodeId, + types_in_modules: &'a HashMap>, + used_aliases_stack: HashSet, +} + +impl<'a, 'b, 'c> From<&'c ResolveExprCtx<'a, 'b>> for ResolveTypeCtx<'c> { + fn from(ctx: &'c ResolveExprCtx<'a, 'b>) -> Self { + Self::new( + ctx.resolved_ast, + ctx.module_fs_node_id, + ctx.physical_fs_node_id, + ctx.types_in_modules, + ) } } -fn resolve_type<'a>( - type_search_ctx: &'a TypeSearchCtx<'_>, - ast_type: &'a ast::Type, - used_aliases_stack: &mut HashSet, -) -> Result { - match &ast_type.kind { - ast::TypeKind::Boolean => Ok(resolved::TypeKind::Boolean), - ast::TypeKind::Integer(bits, sign) => Ok(resolved::TypeKind::Integer(*bits, *sign)), - ast::TypeKind::CInteger(integer, sign) => Ok(resolved::TypeKind::CInteger(*integer, *sign)), - ast::TypeKind::Pointer(inner) => { - let inner = resolve_type_or_undeclared(type_search_ctx, inner, used_aliases_stack)?; - - Ok(resolved::TypeKind::Pointer(Box::new(inner))) +impl<'a> ResolveTypeCtx<'a> { + pub fn new( + resolved_ast: &'a resolved::Ast, + module_fs_node_id: FsNodeId, + file_fs_node_id: FsNodeId, + types_in_modules: &'a HashMap>, + ) -> Self { + Self { + resolved_ast, + module_fs_node_id, + file_fs_node_id, + types_in_modules, + used_aliases_stack: Default::default(), } - ast::TypeKind::Void => Ok(resolved::TypeKind::Void), - ast::TypeKind::Named(name) => match type_search_ctx.find_type(name, used_aliases_stack) { - Ok(found) => Ok(found.into_owned()), - Err(err) => Err(err.into_resolve_error(name, ast_type.source)), - }, - ast::TypeKind::Floating(size) => Ok(resolved::TypeKind::Floating(*size)), - ast::TypeKind::AnonymousStruct(..) => todo!("resolve anonymous struct type"), - ast::TypeKind::AnonymousUnion(..) => todo!("resolve anonymous union type"), - ast::TypeKind::AnonymousEnum(anonymous_enum) => { - let resolved_type = Box::new(resolve_enum_backing_type( - type_search_ctx, - anonymous_enum.backing_type.as_deref(), - &mut Default::default(), - ast_type.source, - )?); - - let members = anonymous_enum.members.clone(); - - Ok(resolved::TypeKind::AnonymousEnum(resolved::AnonymousEnum { - resolved_type, - source: ast_type.source, - members, - })) + } + + pub fn resolve_or_undeclared( + &self, + ast_type: &'a ast::Type, + ) -> Result { + match self.resolve(ast_type) { + Ok(inner) => Ok(inner), + Err(_) if ast_type.kind.allow_indirect_undefined() => { + Ok(resolved::TypeKind::Void.at(ast_type.source)) + } + Err(err) => Err(err), } - ast::TypeKind::FixedArray(fixed_array) => { - if let ast::ExprKind::Integer(integer) = &fixed_array.count.kind { - if let Ok(size) = integer.value().try_into() { - let inner = - resolve_type(type_search_ctx, &fixed_array.ast_type, used_aliases_stack)?; - - Ok(resolved::TypeKind::FixedArray(Box::new( - resolved::FixedArray { size, inner }, - ))) + } + + pub fn resolve(&self, ast_type: &'a ast::Type) -> Result { + match &ast_type.kind { + ast::TypeKind::Boolean => Ok(resolved::TypeKind::Boolean), + ast::TypeKind::Integer(bits, sign) => Ok(resolved::TypeKind::Integer(*bits, *sign)), + ast::TypeKind::CInteger(integer, sign) => { + Ok(resolved::TypeKind::CInteger(*integer, *sign)) + } + ast::TypeKind::Pointer(inner) => { + let inner = self.resolve_or_undeclared(inner)?; + Ok(resolved::TypeKind::Pointer(Box::new(inner))) + } + ast::TypeKind::Void => Ok(resolved::TypeKind::Void), + ast::TypeKind::Named(name) => match self.find_type(name) { + Ok(found) => Ok(found.into_owned()), + Err(err) => Err(err.into_resolve_error(name, ast_type.source)), + }, + ast::TypeKind::Floating(size) => Ok(resolved::TypeKind::Floating(*size)), + ast::TypeKind::AnonymousStruct(..) => todo!("resolve anonymous struct type"), + ast::TypeKind::AnonymousUnion(..) => todo!("resolve anonymous union type"), + ast::TypeKind::AnonymousEnum(anonymous_enum) => { + let resolved_type = Box::new(resolve_enum_backing_type( + self, + anonymous_enum.backing_type.as_deref(), + ast_type.source, + )?); + + let members = anonymous_enum.members.clone(); + + Ok(resolved::TypeKind::AnonymousEnum(resolved::AnonymousEnum { + resolved_type, + source: ast_type.source, + members, + })) + } + ast::TypeKind::FixedArray(fixed_array) => { + if let ast::ExprKind::Integer(integer) = &fixed_array.count.kind { + if let Ok(size) = integer.value().try_into() { + let inner = self.resolve(&fixed_array.ast_type)?; + + Ok(resolved::TypeKind::FixedArray(Box::new( + resolved::FixedArray { size, inner }, + ))) + } else { + Err(ResolveErrorKind::ArraySizeTooLarge.at(fixed_array.count.source)) + } } else { - Err(ResolveErrorKind::ArraySizeTooLarge.at(fixed_array.count.source)) + todo!("resolve fixed array type with variable size") } - } else { - todo!("resolve fixed array type with variable size") + } + ast::TypeKind::FunctionPointer(function_pointer) => { + let mut parameters = Vec::with_capacity(function_pointer.parameters.len()); + + for parameter in function_pointer.parameters.iter() { + let resolved_type = self.resolve(¶meter.ast_type)?; + + parameters.push(resolved::Parameter { + name: parameter.name.clone(), + resolved_type, + }); + } + + let return_type = Box::new(self.resolve(&function_pointer.return_type)?); + + Ok(resolved::TypeKind::FunctionPointer( + resolved::FunctionPointer { + parameters, + return_type, + is_cstyle_variadic: function_pointer.is_cstyle_variadic, + }, + )) } } - ast::TypeKind::FunctionPointer(function_pointer) => { - let mut parameters = Vec::with_capacity(function_pointer.parameters.len()); + .map(|kind| kind.at(ast_type.source)) + } - for parameter in function_pointer.parameters.iter() { - let resolved_type = - resolve_type(type_search_ctx, ¶meter.ast_type, used_aliases_stack)?; + pub fn find_type(&self, name: &Name) -> Result, FindTypeError> { + let settings = &self.resolved_ast.workspace.settings[self + .resolved_ast + .workspace + .files + .get(&self.file_fs_node_id) + .unwrap() + .settings + .expect("valid settings id") + .0]; + + if let Some(name) = name.as_plain_str() { + if let Some(types_in_module) = self.types_in_modules.get(&self.module_fs_node_id) { + if let Some(decl) = types_in_module.get(name) { + return Ok(Cow::Borrowed(&decl.kind)); + } + } + } - parameters.push(resolved::Parameter { - name: parameter.name.clone(), - resolved_type, - }); + if !name.namespace.is_empty() { + let Name { + namespace, + basename, + .. + } = name; + + let mut matches = settings + .namespace_to_dependency + .get(namespace.as_ref()) + .into_iter() + .flatten() + .flat_map(|dep| settings.dependency_to_module.get(dep)) + .flat_map(|fs_node_id| self.types_in_modules.get(fs_node_id)) + .flat_map(|decls| decls.get(basename.as_ref())) + .filter(|decl| decl.privacy.is_public()); + + if let Some(found) = matches.next() { + if matches.next().is_some() { + return Err(FindTypeError::Ambiguous); + } else { + return Ok(Cow::Borrowed(&found.kind)); + } } + } - let return_type = Box::new(resolve_type( - type_search_ctx, - &function_pointer.return_type, - used_aliases_stack, - )?); - - Ok(resolved::TypeKind::FunctionPointer( - resolved::FunctionPointer { - parameters, - return_type, - is_cstyle_variadic: function_pointer.is_cstyle_variadic, - }, - )) + Err(FindTypeError::NotDefined) + } +} + +#[derive(Clone, Debug)] +pub enum FindTypeError { + NotDefined, + Ambiguous, + RecursiveAlias(ResolvedName), + ResolveError(ResolveError), +} + +impl FindTypeError { + pub fn into_resolve_error(self: FindTypeError, name: &Name, source: Source) -> ResolveError { + let name = name.to_string(); + + match self { + FindTypeError::NotDefined => ResolveErrorKind::UndeclaredType { + name: name.to_string(), + } + .at(source), + FindTypeError::Ambiguous => ResolveErrorKind::AmbiguousType { + name: name.to_string(), + } + .at(source), + FindTypeError::RecursiveAlias(_) => ResolveErrorKind::RecursiveTypeAlias { + name: name.to_string(), + } + .at(source), + FindTypeError::ResolveError(err) => err, } } - .map(|kind| kind.at(ast_type.source)) } fn resolve_parameters( - type_search_ctx: &TypeSearchCtx<'_>, + type_ctx: &ResolveTypeCtx, parameters: &ast::Parameters, ) -> Result { let mut required = Vec::with_capacity(parameters.required.len()); @@ -536,11 +717,7 @@ fn resolve_parameters( for parameter in parameters.required.iter() { required.push(resolved::Parameter { name: parameter.name.clone(), - resolved_type: resolve_type( - type_search_ctx, - ¶meter.ast_type, - &mut Default::default(), - )?, + resolved_type: type_ctx.resolve(¶meter.ast_type)?, }); } @@ -570,13 +747,12 @@ fn ensure_initialized( } fn resolve_enum_backing_type( - type_search_ctx: &TypeSearchCtx, + ctx: &ResolveTypeCtx, backing_type: Option>, - used_aliases: &mut HashSet, source: Source, ) -> Result { if let Some(backing_type) = backing_type.as_ref().map(Borrow::borrow) { - resolve_type(type_search_ctx, backing_type, used_aliases) + ctx.resolve(backing_type) } else { Ok(resolved::TypeKind::Integer(IntegerBits::Bits64, IntegerSign::Unsigned).at(source)) } diff --git a/src/resolve/stmt.rs b/src/resolve/stmt.rs index b0f186d..4954934 100644 --- a/src/resolve/stmt.rs +++ b/src/resolve/stmt.rs @@ -3,7 +3,7 @@ use super::{ destination::resolve_expr_to_destination, error::{ResolveError, ResolveErrorKind}, expr::{resolve_basic_binary_operator, resolve_expr, PreferredType, ResolveExprCtx}, - resolve_type, Initialized, + Initialized, }; use crate::{ast, resolved}; @@ -44,6 +44,7 @@ pub fn resolve_stmt( .return_type; if let Ok(result) = conform_expr::( + ctx, &result, return_type, ConformMode::Normal, @@ -85,11 +86,7 @@ pub fn resolve_stmt( source, )), ast::StmtKind::Declaration(declaration) => { - let resolved_type = resolve_type( - ctx.type_search_ctx, - &declaration.ast_type, - &mut Default::default(), - )?; + let resolved_type = ctx.type_ctx().resolve(&declaration.ast_type)?; let value = declaration .initial_value @@ -106,6 +103,7 @@ pub fn resolve_stmt( .as_ref() .map(|value| { conform_expr::( + ctx, value, &resolved_type, ConformMode::Normal, @@ -168,6 +166,7 @@ pub fn resolve_stmt( )?; let value = conform_expr::( + ctx, &value, &destination_expr.resolved_type, ConformMode::Normal, diff --git a/src/resolve/type_search_ctx.rs b/src/resolve/type_search_ctx.rs deleted file mode 100644 index b7323c7..0000000 --- a/src/resolve/type_search_ctx.rs +++ /dev/null @@ -1,174 +0,0 @@ -use super::{ - error::{ResolveError, ResolveErrorKind}, - resolve_type, -}; -use crate::{ - ast, - name::{Name, ResolvedName}, - resolved, - source_files::{Source, SourceFiles}, - workspace::fs::FsNodeId, -}; -use indexmap::IndexMap; -use std::{borrow::Cow, collections::HashSet}; - -#[derive(Clone, Debug)] -pub struct TypeSearchCtx<'a> { - types: IndexMap>, - imported_namespaces: Vec>, - source_files: &'a SourceFiles, - fs_node_id: FsNodeId, -} - -#[derive(Clone, Debug)] -pub enum TypeMapping<'a> { - Normal(resolved::TypeKind), - Alias(&'a ast::TypeAlias), -} - -#[derive(Clone, Debug)] -pub enum FindTypeError { - NotDefined, - Ambiguous, - RecursiveAlias(ResolvedName), - ResolveError(ResolveError), -} - -impl FindTypeError { - pub fn into_resolve_error(self: FindTypeError, name: &Name, source: Source) -> ResolveError { - let name = name.to_string(); - - match self { - FindTypeError::NotDefined => ResolveErrorKind::UndeclaredType { - name: name.to_string(), - } - .at(source), - FindTypeError::Ambiguous => ResolveErrorKind::AmbiguousType { - name: name.to_string(), - } - .at(source), - FindTypeError::RecursiveAlias(_) => ResolveErrorKind::RecursiveTypeAlias { - name: name.to_string(), - } - .at(source), - FindTypeError::ResolveError(err) => err, - } - } -} - -impl<'a> TypeSearchCtx<'a> { - pub fn new( - imported_namespaces: Vec>, - source_files: &'a SourceFiles, - fs_node_id: FsNodeId, - ) -> Self { - Self { - types: Default::default(), - imported_namespaces, - source_files, - fs_node_id, - } - } - - pub fn find_type( - &'a self, - name: &Name, - used_aliases_stack: &mut HashSet, - ) -> Result, FindTypeError> { - let resolved_name = ResolvedName::new(self.fs_node_id, name); - - if let Some(mapping) = self.types.get(&resolved_name) { - return self.resolve_mapping(&resolved_name, mapping, used_aliases_stack); - } - - if name.namespace.is_empty() { - let mut matches = self.imported_namespaces.iter().filter_map(|namespace| { - let resolved_name = ResolvedName::new( - self.fs_node_id, - &Name::new(Some(namespace.clone()), name.basename.clone()), - ); - self.types.get(&resolved_name) - }); - - if let Some(found) = matches.next() { - if matches.next().is_some() { - return Err(FindTypeError::Ambiguous); - } else { - return self.resolve_mapping(&resolved_name, found, used_aliases_stack); - } - } - } - - Err(FindTypeError::NotDefined) - } - - pub fn resolve_mapping( - &self, - resolved_name: &ResolvedName, - mapping: &'a TypeMapping, - used_aliases_stack: &mut HashSet, - ) -> Result, FindTypeError> { - match mapping { - TypeMapping::Normal(kind) => Ok(Cow::Borrowed(kind)), - TypeMapping::Alias(alias) => { - if used_aliases_stack.insert(resolved_name.clone()) { - let inner = resolve_type(self, &alias.value, used_aliases_stack) - .map_err(FindTypeError::ResolveError)?; - used_aliases_stack.remove(&resolved_name); - Ok(Cow::Owned(inner.kind.clone())) - } else { - Err(FindTypeError::RecursiveAlias(resolved_name.clone())) - } - } - } - } - - pub fn put_type( - &mut self, - name: &Name, - value: resolved::TypeKind, - source: Source, - ) -> Result<(), ResolveError> { - let resolved_name = ResolvedName::new(self.fs_node_id, name); - - if self - .types - .insert(resolved_name, TypeMapping::Normal(value)) - .is_some() - { - return Err(ResolveErrorKind::MultipleDefinitionsOfTypeNamed { - name: name.to_string(), - } - .at(source)); - } - - Ok(()) - } - - pub fn override_type(&mut self, name: &Name, value: resolved::TypeKind) { - let resolved_name = ResolvedName::new(self.fs_node_id, name); - self.types.insert(resolved_name, TypeMapping::Normal(value)); - } - - pub fn put_type_alias( - &mut self, - name: &Name, - value: &'a ast::TypeAlias, - source: Source, - ) -> Result<(), ResolveError> { - let resolved_name = ResolvedName::new(self.fs_node_id, &name); - - if self - .types - .insert(resolved_name, TypeMapping::Alias(value)) - .is_some() - { - return Err(ResolveErrorKind::MultipleDefinitionsOfTypeNamed { - name: name.to_string(), - } - .at(source)); - } - - Ok(()) - } -} diff --git a/src/resolve/unify_types/mod.rs b/src/resolve/unify_types/mod.rs index 101c72e..a7a14b4 100644 --- a/src/resolve/unify_types/mod.rs +++ b/src/resolve/unify_types/mod.rs @@ -1,7 +1,10 @@ mod compute; mod integer_literals; -use super::conform::{conform_expr, ConformMode, Perform}; +use super::{ + conform::{conform_expr, ConformMode, Perform}, + expr::ResolveExprCtx, +}; use crate::{ ast::ConformBehavior, resolved::{self, TypedExpr}, @@ -10,6 +13,7 @@ use crate::{ use compute::compute_unifying_type; pub fn unify_types( + ctx: &ResolveExprCtx, preferred_type: Option<&resolved::Type>, exprs: &mut [&mut TypedExpr], behavior: ConformBehavior, @@ -24,6 +28,7 @@ pub fn unify_types( // Conform the supplied expressions if a unifying type was found for expr in exprs { **expr = match conform_expr::( + ctx, expr, &unified_type, ConformMode::Normal, diff --git a/src/resolved/mod.rs b/src/resolved/mod.rs index d67aff3..72ab058 100644 --- a/src/resolved/mod.rs +++ b/src/resolved/mod.rs @@ -6,13 +6,13 @@ pub use crate::ast::{ ShortCircuitingBinaryOperator, UnaryMathOperator, }; use crate::{ - ast::fmt_c_integer, + ast::{fmt_c_integer, AstWorkspace}, ir::InterpreterSyscallKind, name::ResolvedName, source_files::{Source, SourceFiles}, tag::Tag, target::Target, - workspace::fs::Fs, + workspace::fs::FsNodeId, }; use derive_more::{IsVariant, Unwrap}; use indexmap::IndexMap; @@ -20,6 +20,7 @@ use num_bigint::BigInt; use num_traits::Zero; use slotmap::{new_key_type, SlotMap}; use std::{ + collections::HashMap, ffi::CString, fmt::{Debug, Display}, }; @@ -29,6 +30,8 @@ new_key_type! { pub struct FunctionRef; pub struct GlobalVarRef; pub struct StructureRef; + pub struct EnumRef; + pub struct TypeAliasRef; } #[derive(Clone, Debug)] @@ -38,24 +41,54 @@ pub struct Ast<'a> { pub functions: SlotMap, pub structures: SlotMap, pub globals: SlotMap, - pub enums: IndexMap, - pub fs: &'a Fs, + pub enums: SlotMap, + pub type_aliases: SlotMap, + pub workspace: &'a AstWorkspace<'a>, + pub types_per_module: HashMap>, } impl<'a> Ast<'a> { - pub fn new(source_files: &'a SourceFiles, fs: &'a Fs) -> Self { + const MAX_UNALIAS_DEPTH: usize = 1024; + + pub fn new(source_files: &'a SourceFiles, workspace: &'a AstWorkspace) -> Self { Self { source_files, entry_point: None, functions: SlotMap::with_key(), structures: SlotMap::with_key(), globals: SlotMap::with_key(), - enums: IndexMap::new(), - fs, + enums: SlotMap::with_key(), + type_aliases: SlotMap::with_key(), + workspace, + types_per_module: HashMap::new(), + } + } + + pub fn unalias(&'a self, mut resolved_type: &'a Type) -> Result<&'a Type, UnaliasError> { + let mut depth = 0; + + while let TypeKind::TypeAlias(_, type_alias_ref) = resolved_type.kind { + resolved_type = self + .type_aliases + .get(type_alias_ref) + .expect("valid type alias ref"); + + depth += 1; + + if depth > Self::MAX_UNALIAS_DEPTH { + return Err(UnaliasError::MaxDepthExceeded); + } } + + Ok(resolved_type) } } +#[derive(Clone, Debug)] +pub enum UnaliasError { + MaxDepthExceeded, +} + #[derive(Clone, Debug)] pub struct Enum { pub resolved_type: Type, @@ -91,12 +124,39 @@ pub struct Parameters { pub is_cstyle_vararg: bool, } +impl Display for Parameters { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for (i, param) in self.required.iter().enumerate() { + if i != 0 { + write!(f, ", ")?; + } + write!(f, "{}", param)?; + } + + if self.is_cstyle_vararg { + if !self.required.is_empty() { + write!(f, ", ")?; + } + + write!(f, "...")?; + } + + Ok(()) + } +} + #[derive(Clone, Debug)] pub struct Parameter { pub name: String, pub resolved_type: Type, } +impl Display for Parameter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.name, self.resolved_type) + } +} + impl PartialEq for Parameter { fn eq(&self, other: &Self) -> bool { self.resolved_type.eq(&other.resolved_type) @@ -128,7 +188,10 @@ pub struct Type { impl Type { pub fn pointer(self, source: Source) -> Self { - TypeKind::Pointer(Box::new(self)).at(source) + Self { + kind: TypeKind::Pointer(Box::new(self)), + source, + } } pub fn is_ambiguous(&self) -> bool { @@ -148,8 +211,25 @@ impl PartialEq for Type { } } +#[derive(Clone, Debug)] +pub struct TypeDecl { + pub kind: TypeKind, + pub source: Source, + pub privacy: Privacy, +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct HumanName(pub String); + +impl Display for HumanName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + #[derive(Clone, Debug, PartialEq, IsVariant, Unwrap)] pub enum TypeKind { + Unresolved, Boolean, Integer(IntegerBits, IntegerSign), CInteger(CInteger, Option), @@ -157,14 +237,15 @@ pub enum TypeKind { FloatLiteral(f64), Floating(FloatSize), Pointer(Box), - Structure(ResolvedName, StructureRef), Void, AnonymousStruct(), AnonymousUnion(), AnonymousEnum(AnonymousEnum), FixedArray(Box), FunctionPointer(FunctionPointer), - Enum(ResolvedName), + Enum(HumanName, EnumRef), + Structure(HumanName, StructureRef), + TypeAlias(HumanName, TypeAliasRef), } impl TypeKind { @@ -236,7 +317,7 @@ pub struct AnonymousEnum { impl PartialEq for AnonymousEnum { fn eq(&self, other: &Self) -> bool { - self.resolved_type.kind.eq(&other.resolved_type.kind) && self.members.eq(&other.members) + self.resolved_type.eq(&other.resolved_type) && self.members.eq(&other.members) } } @@ -274,6 +355,8 @@ impl TypeKind { target.map(|target| target.default_c_integer_sign(*integer)) } } + TypeKind::TypeAlias(_, _type_ref) => todo!(), + TypeKind::Unresolved => panic!(), TypeKind::Floating(_) | TypeKind::FloatLiteral(_) | TypeKind::Pointer(_) @@ -283,22 +366,18 @@ impl TypeKind { | TypeKind::AnonymousUnion(..) | TypeKind::FixedArray(..) | TypeKind::FunctionPointer(..) - | TypeKind::Enum(_) + | TypeKind::Enum(_, _) | TypeKind::AnonymousEnum(_) => None, } } - - pub fn is_void_pointer(&self) -> bool { - matches!(self, TypeKind::Pointer(inner) if inner.kind.is_void()) - } } impl Display for TypeKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - TypeKind::Boolean => { - write!(f, "bool")?; - } + TypeKind::Unresolved => panic!("cannot display unresolved type"), + TypeKind::TypeAlias(name, _) => write!(f, "{}", name)?, + TypeKind::Boolean => write!(f, "bool")?, TypeKind::Integer(bits, sign) => { f.write_str(match (bits, sign) { (IntegerBits::Bits8, IntegerSign::Signed) => "i8", @@ -323,18 +402,18 @@ impl Display for TypeKind { }, TypeKind::FloatLiteral(value) => write!(f, "float {}", value)?, TypeKind::Pointer(inner) => { - write!(f, "ptr<{}>", inner.kind)?; + write!(f, "ptr<{}>", **inner)?; } TypeKind::Void => f.write_str("void")?, - TypeKind::Structure(name, _) => write!(f, "{}", name.plain())?, - TypeKind::AnonymousStruct() => f.write_str("(anonymous struct)")?, - TypeKind::AnonymousUnion() => f.write_str("(anonymous union)")?, - TypeKind::AnonymousEnum(..) => f.write_str("(anonymous enum)")?, + TypeKind::Structure(name, _) => write!(f, "{}", name)?, + TypeKind::AnonymousStruct() => f.write_str("anonymous-struct")?, + TypeKind::AnonymousUnion() => f.write_str("anonymous-union")?, + TypeKind::AnonymousEnum(..) => f.write_str("anonymous-enum")?, TypeKind::FixedArray(fixed_array) => { write!(f, "array<{}, {}>", fixed_array.size, fixed_array.inner.kind)?; } - TypeKind::FunctionPointer(..) => f.write_str("(function pointer type)")?, - TypeKind::Enum(enum_name) => write!(f, "(enum) {}", enum_name.plain())?, + TypeKind::FunctionPointer(..) => f.write_str("function-pointer-type")?, + TypeKind::Enum(name, _) => write!(f, "{}", name)?, } Ok(()) @@ -493,7 +572,8 @@ pub struct ArrayAccess { #[derive(Clone, Debug)] pub struct EnumMemberLiteral { - pub enum_name: ResolvedName, + pub human_name: HumanName, + pub enum_ref: EnumRef, pub variant_name: String, pub source: Source, }