diff --git a/Makefile b/Makefile index 15cba1db9..8a4a60496 100755 --- a/Makefile +++ b/Makefile @@ -74,7 +74,7 @@ testsuite: build -d tests \ --output ./tmp/tests \ --compiler $(COMPILER) \ - --flags "--stdlib=$(STDLIB) --quiet" + --flags "--stdlib=$(STDLIB) --quiet --jobs 1" testsuite-fail: build $(PY_CMD) ./tests/test_suite.py \ @@ -82,4 +82,4 @@ testsuite-fail: build --output ./tmp/tests \ --compiler $(COMPILER) \ --fail \ - --flags "--stdlib=$(STDLIB) --quiet" \ No newline at end of file + --flags "--stdlib=$(STDLIB) --quiet --jobs 1" \ No newline at end of file diff --git a/crates/cyrusc_analyzer/src/analyze/block_stmt.rs b/crates/cyrusc_analyzer/src/analyze/block_stmt.rs index 5b0115b73..7a1166f00 100644 --- a/crates/cyrusc_analyzer/src/analyze/block_stmt.rs +++ b/crates/cyrusc_analyzer/src/analyze/block_stmt.rs @@ -12,7 +12,7 @@ impl<'a> AnalysisContext<'a> { let mut terminated = false; let stmts = std::mem::take(&mut block_stmt.stmts); - let mut final_stmts = Vec::with_capacity(block_stmt.stmts.len()); + let mut final_stmts = Vec::with_capacity(stmts.len()); for mut stmt in stmts { let stmt_state = self.analyze_stmt(&mut stmt); diff --git a/crates/cyrusc_analyzer/src/analyze/control_flow.rs b/crates/cyrusc_analyzer/src/analyze/control_flow.rs index ce1828832..c518cfeb1 100644 --- a/crates/cyrusc_analyzer/src/analyze/control_flow.rs +++ b/crates/cyrusc_analyzer/src/analyze/control_flow.rs @@ -373,7 +373,19 @@ impl<'a> AnalysisContext<'a> { match enum_variant { TypedEnumVariant::Tuple { fields, .. } => { - if fields.len() != items.len() {} + if fields.len() != items.len() { + this.reporter.report(Diag { + level: DiagLevel::Error, + kind: Box::new(AnalyzerDiagKind::EnumVariantArgCountMismatch { + variant_name: ident.as_string(), + expected: fields.len() as u32, + provided: items.len() as u32, + }), + loc: Some(pattern.loc), + hint: None, + }); + return; + } *exporting_pattern_count += 1; @@ -414,11 +426,7 @@ impl<'a> AnalysisContext<'a> { } // .StructVariant { x, y, .. } - TypedSwitchCasePatternKind::EnumStructVariant { - ident, - items, - has_rest: _, - } => { + TypedSwitchCasePatternKind::EnumStructVariant { ident, items, has_rest } => { let Some(enum_variant) = find_variant(inst_enum_decl, ident) else { this.reporter.report(Diag { level: DiagLevel::Error, @@ -449,6 +457,20 @@ impl<'a> AnalysisContext<'a> { TypedEnumVariant::Struct { fields, .. } => { let mut has_error = false; + if !has_rest && fields.len() != items.len() { + this.reporter.report(Diag { + level: DiagLevel::Error, + kind: Box::new(AnalyzerDiagKind::EnumVariantArgCountMismatch { + variant_name: ident.as_string(), + expected: fields.len() as u32, + provided: items.len() as u32, + }), + loc: Some(pattern.loc), + hint: None, + }); + return; + } + for item in items { let exists = fields.iter().any(|field| field.name == item.name); diff --git a/crates/cyrusc_analyzer/src/analyze/enum_stmt.rs b/crates/cyrusc_analyzer/src/analyze/enum_stmt.rs index 3a335fe5a..02d5ad1c3 100644 --- a/crates/cyrusc_analyzer/src/analyze/enum_stmt.rs +++ b/crates/cyrusc_analyzer/src/analyze/enum_stmt.rs @@ -17,7 +17,7 @@ impl<'a> AnalysisContext<'a> { pub(crate) fn analyze_enum_stmt(&mut self, enum_stmt: &mut TypedEnumStmt) { let mut enum_decl = self.decl_tables.enum_decl(enum_stmt.enum_decl_id); - if enum_stmt.is_generic() { + if !enum_stmt.is_generic() { let object_type = SemaType::Named(NamedType { type_decl_id: TypeDeclID::Enum(enum_stmt.enum_decl_id), type_args: TypedTypeArgs::new(), @@ -58,6 +58,12 @@ impl<'a> AnalysisContext<'a> { self.analyze_generic_bounds(&enum_decl.generic_params); + let object_type_decl_id = TypeDeclID::Enum(enum_decl_id); + + if !enum_decl.is_generic() { + self.analyze_object_methods(object_type_decl_id, &enum_decl.methods); + } + self.analyze_object_implements_interfaces( &object_name, enum_decl.is_generic(), @@ -65,12 +71,6 @@ impl<'a> AnalysisContext<'a> { &enum_decl.methods, ); - let object_type_decl_id = TypeDeclID::Enum(enum_decl_id); - - if !enum_decl.is_generic() { - self.analyze_object_methods(object_type_decl_id, &enum_decl.methods); - } - self.decl_tables.with_enum_decl_mut(enum_decl_id, |_enum_decl| { _enum_decl.variants = enum_decl.variants.clone(); }); diff --git a/crates/cyrusc_analyzer/src/analyze/interfaces.rs b/crates/cyrusc_analyzer/src/analyze/interfaces.rs index d1d38fc11..2d41d4d3b 100644 --- a/crates/cyrusc_analyzer/src/analyze/interfaces.rs +++ b/crates/cyrusc_analyzer/src/analyze/interfaces.rs @@ -23,7 +23,14 @@ impl<'a> AnalysisContext<'a> { let mut methods: Vec = Vec::new(); for (_, method_decl_id) in interface.methods.iter() { - let method_decl = self.decl_tables.method_decl(*method_decl_id); + let mut method_decl = self.decl_tables.method_decl(*method_decl_id); + + self.analyze_method_decl(TypeDeclID::Interface(interface.interface_decl_id), &mut method_decl); + + self.decl_tables.with_method_decl_mut(*method_decl_id, |_method_decl| { + *_method_decl = method_decl.clone(); + }); + let func_decl = method_decl.func_decl; let params_start = if let Some(self_modifier) = func_decl.params.get_self_modifier() { @@ -137,6 +144,7 @@ impl<'a> AnalysisContext<'a> { for (_, method_decl_id) in interface_decl.methods.iter() { let interface_method_decl = self.decl_tables.method_decl(*method_decl_id); + let func_decl = instantiate_method_decl( &interface_method_decl, &interface_decl.generic_params, @@ -232,7 +240,6 @@ impl<'a> AnalysisContext<'a> { for (_, method_decl_id) in interface_decl.methods.iter() { // interface method - let interface_method_decl = &mut this.decl_tables.method_decl(*method_decl_id).func_decl; let self_modifier = interface_method_decl.params.get_self_modifier_mut().unwrap(); @@ -242,7 +249,6 @@ impl<'a> AnalysisContext<'a> { interface_method_decl.ret_type = this.substitute_type(&interface_method_decl.ret_type); // object method - let Some(method_decl_id) = method_decls.get(&interface_method_decl.name) else { continue; }; diff --git a/crates/cyrusc_analyzer/src/analyze/methods.rs b/crates/cyrusc_analyzer/src/analyze/methods.rs index c96d0eac3..4a20acf2a 100644 --- a/crates/cyrusc_analyzer/src/analyze/methods.rs +++ b/crates/cyrusc_analyzer/src/analyze/methods.rs @@ -3,6 +3,7 @@ use crate::{context::AnalysisContext, infer::InferCtx}; use cyrusc_typed_ast::{ + debug_assert_func_decl_resolved, decls::{MethodDecl, MethodDeclID, MethodDecls}, stmts::{TypedFuncParamKind, TypedTypeArgs}, types::{NamedType, SemaType, TypeDeclID}, @@ -16,6 +17,9 @@ impl<'a> AnalysisContext<'a> { self.analyze_method_decl(object_type_decl_id, &mut method_decl); + #[cfg(debug_assertions)] + debug_assert_func_decl_resolved!(&method_decl.func_decl); + self.decl_tables.with_method_decl_mut(*method_decl_id, |_method_decl| { *_method_decl = method_decl; }); @@ -42,7 +46,7 @@ impl<'a> AnalysisContext<'a> { // If the method is non-generic, eagerly lower all SelfType occurrences // to the concrete object type. Generic methods defer SelfType substitution // to the call-site via the instantiated generic environment. - if !method_decl.func_decl.is_generic() { + if !method_decl.func_decl.is_generic() && !object_type_decl_id.is_interface() { let concrete_self_type = SemaType::Named(NamedType { type_decl_id: object_type_decl_id, type_args: TypedTypeArgs::new(), @@ -90,10 +94,6 @@ impl<'a> AnalysisContext<'a> { match param_kind { TypedFuncParamKind::FuncParam(func_param) => { func_param.ty = self.substitute_self_type(func_param.ty.clone(), concrete_self_type); - - if let Some(infer) = &self.func_env.infer { - func_param.ty = infer.resolve(&func_param.ty); - } } TypedFuncParamKind::SelfModifier(self_modifier) => { self_modifier.ty = self.substitute_self_type(self_modifier.ty.clone(), concrete_self_type); @@ -106,6 +106,12 @@ impl<'a> AnalysisContext<'a> { } } + if let Some(ret_type) = + self.normalize_sema_type(method_decl.func_decl.ret_type.clone(), method_decl.func_decl.loc, 0) + { + method_decl.func_decl.ret_type = ret_type; + } + let mut substituted_ret_type = self.substitute_self_type(method_decl.func_decl.ret_type.clone(), concrete_self_type); diff --git a/crates/cyrusc_analyzer/src/analyze/struct_stmt.rs b/crates/cyrusc_analyzer/src/analyze/struct_stmt.rs index 793080350..60fdee446 100644 --- a/crates/cyrusc_analyzer/src/analyze/struct_stmt.rs +++ b/crates/cyrusc_analyzer/src/analyze/struct_stmt.rs @@ -58,18 +58,18 @@ impl<'a> AnalysisContext<'a> { self.analyze_generic_bounds(&struct_decl.generic_params); + let object_type_decl_id = TypeDeclID::Struct(struct_decl_id); + + if !struct_decl.is_generic() { + self.analyze_object_methods(object_type_decl_id, &struct_decl.methods); + } + self.analyze_object_implements_interfaces( &object_name, struct_decl.is_generic(), &struct_decl.impls, &struct_decl.methods, ); - - let object_type_decl_id = TypeDeclID::Struct(struct_decl_id); - - if !struct_decl.is_generic() { - self.analyze_object_methods(object_type_decl_id, &struct_decl.methods); - } } fn analyze_struct_fields(&mut self, struct_decl_id: StructDeclID, struct_fields: &mut [TypedStructField]) { diff --git a/crates/cyrusc_analyzer/src/analyze/union_stmt.rs b/crates/cyrusc_analyzer/src/analyze/union_stmt.rs index 50dcddd30..0308cf9d1 100644 --- a/crates/cyrusc_analyzer/src/analyze/union_stmt.rs +++ b/crates/cyrusc_analyzer/src/analyze/union_stmt.rs @@ -54,18 +54,18 @@ impl<'a> AnalysisContext<'a> { self.analyze_generic_bounds(&union_decl.generic_params); + let object_type_decl_id = TypeDeclID::Union(union_decl_id); + + if !union_decl.is_generic() { + self.analyze_object_methods(object_type_decl_id, &union_decl.methods); + } + self.analyze_object_implements_interfaces( &object_name, union_decl.is_generic(), &union_decl.impls, &union_decl.methods, ); - - let object_type_decl_id = TypeDeclID::Union(union_decl_id); - - if !union_decl.is_generic() { - self.analyze_object_methods(object_type_decl_id, &union_decl.methods); - } } fn analyze_union_fields(&mut self, union_decl_id: UnionDeclID, union_fields: &mut [TypedUnionField]) { diff --git a/crates/cyrusc_analyzer/src/analyze/vars.rs b/crates/cyrusc_analyzer/src/analyze/vars.rs index 08bfd25e8..a64f3a010 100644 --- a/crates/cyrusc_analyzer/src/analyze/vars.rs +++ b/crates/cyrusc_analyzer/src/analyze/vars.rs @@ -61,7 +61,7 @@ impl<'a> AnalysisContext<'a> { }; if let Some(ty) = &global_var.ty { - if !self.validate_variable_type(ty, global_var.expr.is_some(), global_var.loc) { + if !self.validate_variable_type(ty, global_var.expr.is_some() || global_var.is_undef, global_var.loc) { return; } @@ -154,7 +154,7 @@ impl<'a> AnalysisContext<'a> { self.report_const_qualified_type_assigned_to_non_const_variable(var.loc); } - if !self.validate_variable_type(ty, var.rhs.is_some(), var.loc) { + if !self.validate_variable_type(ty, var.rhs.is_some() || var.is_undef, var.loc) { return; } diff --git a/crates/cyrusc_analyzer/src/diagnostics.rs b/crates/cyrusc_analyzer/src/diagnostics.rs index 3f5ccb732..7623accd4 100644 --- a/crates/cyrusc_analyzer/src/diagnostics.rs +++ b/crates/cyrusc_analyzer/src/diagnostics.rs @@ -49,9 +49,14 @@ pub enum AnalyzerDiagKind { #[error("Cannot infer dynamic interface type.")] CannotInferDynamicInterfaceType, - #[error("Cannot apply 'dynamic' to an already dynamic value.")] + #[error("Cannot apply dynamic to an already dynamic value.")] InvalidMultipleDynamicType, + #[error("Cannot apply dynamic to type '{type_name}'.")] + InvalidDynamicType { + type_name: String + }, + #[error( "Method '{method_name}' requires mutable access to 'self', but instance of type '{type_name}' is declared 'const'." )] @@ -470,6 +475,9 @@ pub enum AnalyzerDiagKind { #[error("Symbol '{symbol_name}' is not an enum.")] NonEnumSymbol { symbol_name: String }, + #[error("Symbol '{symbol_name}' is not a type.")] + NonTypeSymbol { symbol_name: String }, + #[error("Cannot use {elements} elements in an array of size {expected}.")] ArrayElementsCountMismatch { elements: u32, expected: u32 }, diff --git a/crates/cyrusc_analyzer/src/env/generic_env.rs b/crates/cyrusc_analyzer/src/env/generic_env.rs index 8945f73d3..b59ab7cc8 100644 --- a/crates/cyrusc_analyzer/src/env/generic_env.rs +++ b/crates/cyrusc_analyzer/src/env/generic_env.rs @@ -78,7 +78,14 @@ impl GenericEnv { #[inline] pub fn substitute_sema_type(&self, ty: &SemaType) -> SemaType { match ty { - SemaType::Unresolved(_) | SemaType::InferVar(_) | SemaType::Plain(_) | SemaType::Placeholder => ty.clone(), + SemaType::SelfType(self_type) => SemaType::SelfType(self_type.clone()), + + SemaType::Err(_) + | SemaType::Unresolved(_) + | SemaType::InferVar(_) + | SemaType::Plain(_) + | SemaType::Placeholder => ty.clone(), + SemaType::Named(named_type) => { let type_args = named_type .type_args @@ -154,9 +161,6 @@ impl GenericEnv { Some(ty) => ty.clone(), None => ty.clone(), }, - SemaType::SelfType(self_type) => SemaType::SelfType(self_type.clone()), - - SemaType::Err(_) => ty.clone(), } } } diff --git a/crates/cyrusc_analyzer/src/lint/var_usage.rs b/crates/cyrusc_analyzer/src/lint/var_usage.rs deleted file mode 100644 index f64e73a53..000000000 --- a/crates/cyrusc_analyzer/src/lint/var_usage.rs +++ /dev/null @@ -1,9 +0,0 @@ -// TODO - -// struct VarUsage { -// reads: u32, -// writes: u32, -// } - - -// if symbol.mutability == Var && symbol.write_count == 1 \ No newline at end of file diff --git a/crates/cyrusc_analyzer/src/monomorph.rs b/crates/cyrusc_analyzer/src/monomorph.rs index 012fa84d3..236b36a8e 100644 --- a/crates/cyrusc_analyzer/src/monomorph.rs +++ b/crates/cyrusc_analyzer/src/monomorph.rs @@ -517,7 +517,7 @@ impl<'a> AnalysisContext<'a> { } } TypedExprKind::EnumInit(enum_init) => match &mut enum_init.arg { - TypedEnumInitArgs::Unit => todo!(), + TypedEnumInitArgs::Unit => {} TypedEnumInitArgs::Tuple(elements) => { for element in elements { self.specialize_expr(element, decl_map); diff --git a/crates/cyrusc_analyzer/src/normalizer.rs b/crates/cyrusc_analyzer/src/normalizer.rs index 064b5bf26..ac70c6d4e 100644 --- a/crates/cyrusc_analyzer/src/normalizer.rs +++ b/crates/cyrusc_analyzer/src/normalizer.rs @@ -183,9 +183,21 @@ impl<'a> AnalysisContext<'a> { indirection: u8, ) -> Option { let ty = match unresolved_type { - UnresolvedType::Decl(symbol_id) => self - .lookup_symbol_as_decl_id(symbol_id) - .and_then(|decl_id| self.resolve_symbol_type(decl_id, loc, indirection))?, + UnresolvedType::Decl(symbol_id) => self.lookup_symbol_as_decl_id(symbol_id).and_then(|decl_id| { + if decl_id.as_type_decl_id().is_none() { + let symbol_name = self.formatter.format_decl(decl_id); + + self.reporter.report(Diag { + level: DiagLevel::Error, + kind: Box::new(AnalyzerDiagKind::NonTypeSymbol { symbol_name }), + loc: Some(loc), + hint: None, + }); + return None; + } + + self.resolve_symbol_type(decl_id, loc, indirection) + })?, UnresolvedType::GenericInst { base_symbol_id, @@ -282,7 +294,7 @@ impl<'a> AnalysisContext<'a> { let elements: Vec<_> = tuple_type .elements .into_iter() - .filter_map(|(ty, loc)| { + .filter_map(|(ty, loc)| { self.normalize_sema_type(ty, tuple_type.loc, indirection) .map(|ty| (ty, loc)) }) @@ -299,7 +311,6 @@ impl<'a> AnalysisContext<'a> { })) } - // TODO: Implement slices. fn normalize_array_capacity( &mut self, mut array: TypedArrayType, @@ -378,7 +389,7 @@ impl<'a> AnalysisContext<'a> { for type_arg in type_args.iter_mut() { match type_arg { TypedTypeArg::Type(ty, loc) => { - *ty = match self.normalize_and_check_type_formation(ty.clone(), *loc, indirection) { + *ty = match self.normalize_sema_type(ty.clone(), *loc, indirection) { Some(sema_type) => sema_type, None => continue, }; diff --git a/crates/cyrusc_analyzer/src/substitute.rs b/crates/cyrusc_analyzer/src/substitute.rs index 7f9446454..4ad879775 100644 --- a/crates/cyrusc_analyzer/src/substitute.rs +++ b/crates/cyrusc_analyzer/src/substitute.rs @@ -3,8 +3,8 @@ use crate::context::AnalysisContext; use cyrusc_typed_ast::{ - stmts::{TypedFuncParamKind, TypedFuncParams, TypedFuncTypeParams, TypedFuncTypeVariadicParam, TypedTypeArg}, - types::{NamedType, SemaType, TypedArrayType, TypedFuncType, TypedTupleType}, + stmts::{TypedFuncParamKind, TypedFuncParams, TypedFuncTypeVariadicParam, TypedTypeArg}, + types::SemaType, }; impl<'a> AnalysisContext<'a> { @@ -12,11 +12,8 @@ impl<'a> AnalysisContext<'a> { let mut result = ty.clone(); for generic_env in self.generic_env_stack.iter().rev() { - if result.is_self_type() { - if let Some(object_type) = &self.func_env.current_object { - result = generic_env.substitute_sema_type(&object_type); - continue; - } + if let Some(object_type) = &self.func_env.current_object { + result = self.substitute_self_type(result, object_type); } result = generic_env.substitute_sema_type(&result); @@ -50,9 +47,7 @@ impl<'a> AnalysisContext<'a> { params } - pub fn substitute_self_type(&mut self, mut ty: SemaType, self_type: &SemaType) -> SemaType { - ty = self.substitute_type(&ty); - + pub fn substitute_self_type(&self, ty: SemaType, self_type: &SemaType) -> SemaType { match ty { SemaType::InterfaceObject(_) | SemaType::Unresolved(_) @@ -64,70 +59,66 @@ impl<'a> AnalysisContext<'a> { SemaType::SelfType(_) => self_type.clone(), - SemaType::Named(named) => { - let type_args = named + SemaType::Named(mut named) => { + named.type_args = named .type_args .iter() .map(|arg| match arg { TypedTypeArg::Type(ty, loc) => { TypedTypeArg::Type(self.substitute_self_type(ty.clone(), self_type), *loc) } - TypedTypeArg::Infer => TypedTypeArg::Infer, }) .collect(); - SemaType::Named(NamedType { - type_decl_id: named.type_decl_id, - type_args, - }) + SemaType::Named(named) + } + + SemaType::Array(mut array) => { + array.element_type = Box::new(self.substitute_self_type(*array.element_type, self_type)); + SemaType::Array(array) + } + + SemaType::Const(mut inner) => { + *inner = self.substitute_self_type(*inner, self_type); + SemaType::Const(inner) } - SemaType::Array(array) => SemaType::Array(TypedArrayType { - element_type: Box::new(self.substitute_self_type(*array.element_type.clone(), self_type)), - capacity: array.capacity.clone(), - loc: array.loc, - }), - - SemaType::Const(inner) => SemaType::Const(Box::new(self.substitute_self_type(*inner.clone(), self_type))), - SemaType::Pointer(inner) => { - SemaType::Pointer(Box::new(self.substitute_self_type(*inner.clone(), self_type))) + + SemaType::Pointer(mut inner) => { + *inner = self.substitute_self_type(*inner, self_type); + SemaType::Pointer(inner) } - SemaType::Tuple(tuple) => { - let elements = tuple + SemaType::Tuple(mut tuple) => { + tuple.elements = tuple .elements - .iter() - .map(|(ty, loc)| (self.substitute_self_type(ty.clone(), self_type), *loc)) + .into_iter() + .map(|(ty, loc)| (self.substitute_self_type(ty, self_type), loc)) .collect(); - SemaType::Tuple(TypedTupleType { - elements, - loc: tuple.loc, - }) + SemaType::Tuple(tuple) } - SemaType::FuncType(func) => { - let params = func + SemaType::FuncType(mut func) => { + func.params.list = func .params .list - .iter() - .map(|ty| self.substitute_self_type(ty.clone(), self_type)) + .into_iter() + .map(|ty| self.substitute_self_type(ty, self_type)) .collect(); - let variadic = func.params.variadic.as_ref().map(|vbox| match vbox.as_ref() { - TypedFuncTypeVariadicParam::UntypedCStyle => vbox.clone(), - - TypedFuncTypeVariadicParam::Typed(ty) => Box::new(TypedFuncTypeVariadicParam::Typed( - self.substitute_self_type(ty.clone(), self_type), - )), + func.params.variadic = func.params.variadic.map(|vbox| { + Box::new(match vbox.as_ref() { + TypedFuncTypeVariadicParam::UntypedCStyle => vbox.as_ref().clone(), + TypedFuncTypeVariadicParam::Typed(ty) => { + TypedFuncTypeVariadicParam::Typed(self.substitute_self_type(ty.clone(), self_type)) + } + }) }); - SemaType::FuncType(TypedFuncType { - params: TypedFuncTypeParams { list: params, variadic }, - ret_type: Box::new(self.substitute_self_type(*func.ret_type.clone(), self_type)), - is_public: func.is_public, - loc: func.loc, - }) + func.ret_type = Box::new(self.substitute_self_type(*func.ret_type, self_type)); + + SemaType::FuncType(func) } } } diff --git a/crates/cyrusc_analyzer/src/typecheck/call.rs b/crates/cyrusc_analyzer/src/typecheck/call.rs index 3ff9ed246..49352b2f8 100644 --- a/crates/cyrusc_analyzer/src/typecheck/call.rs +++ b/crates/cyrusc_analyzer/src/typecheck/call.rs @@ -6,6 +6,7 @@ use cyrusc_const_eval::resolver::ConstResolver; use cyrusc_diagcentral::{Diag, DiagLevel}; use cyrusc_source_loc::Loc; use cyrusc_typed_ast::{ + debug_assert_func_decl_resolved, decls::{DeclID, FuncDecl, MethodDecl, MethodDecls}, exprs::{ TypedExpr, TypedFuncCall, TypedFuncCallDispatch, TypedInterfaceCallDispatch, TypedMethodCall, @@ -114,8 +115,6 @@ impl<'a> AnalysisContext<'a> { return None; } - func_decl.ret_type = func_decl.ret_type.clone(); - func_call.dispatch = TypedFuncCallDispatch::Normal { func_decl_id: func_decl_id, }; @@ -522,7 +521,7 @@ impl<'a> AnalysisContext<'a> { method_decl.func_decl.ret_type = this.substitute_type(&method_decl.func_decl.ret_type); #[cfg(debug_assertions)] - debug_assert_func_decl_resolved(&method_decl.func_decl); + debug_assert_func_decl_resolved!(&method_decl.func_decl); let ret_type = method_decl.func_decl.ret_type.clone(); @@ -639,9 +638,13 @@ impl<'a> AnalysisContext<'a> { .is_referenced() ); - interface_method_decl.func_decl.ret_type = this.substitute_type(&interface_method_decl.func_decl.ret_type); + let mut ret_type = interface_method_decl.func_decl.ret_type.clone(); + ret_type = this.normalize_sema_type(ret_type.clone(), method_call.loc, 0)?; + ret_type = this.substitute_type(&ret_type); + interface_method_decl.func_decl.ret_type = ret_type.clone(); - let ret_type = interface_method_decl.func_decl.ret_type.clone(); + #[cfg(debug_assertions)] + debug_assert_func_decl_resolved!(&interface_method_decl.func_decl); let method_index = interface_decl.method_index(&method_call.name).unwrap(); @@ -1129,25 +1132,3 @@ impl<'a> AnalysisContext<'a> { Some(*func_type.ret_type.clone()) } } - -#[cfg(debug_assertions)] -fn debug_assert_func_decl_resolved(func_decl: &FuncDecl) { - for func_param in &func_decl.params.list { - match func_param { - TypedFuncParamKind::FuncParam(func_param) => { - debug_assert!(!func_param.ty.is_unresolved(), "func decl param type is unresolved"); - } - TypedFuncParamKind::SelfModifier(self_modifier) => { - debug_assert!( - !self_modifier.ty.is_unresolved(), - "func decl self_modifier type in unresolved" - ); - } - } - } - - debug_assert!( - !func_decl.ret_type.is_unresolved(), - "func decl return type is unresolved" - ); -} diff --git a/crates/cyrusc_analyzer/src/typecheck/dynamic.rs b/crates/cyrusc_analyzer/src/typecheck/dynamic.rs index cb1d034df..468c6176a 100644 --- a/crates/cyrusc_analyzer/src/typecheck/dynamic.rs +++ b/crates/cyrusc_analyzer/src/typecheck/dynamic.rs @@ -33,7 +33,17 @@ impl<'a> AnalysisContext<'a> { return None; } - let named_type = operand_type.as_named_type().unwrap(); + let Some(named_type) = operand_type.as_named_type() else { + let type_name = format_sema_type(operand_type, self.formatter); + + self.reporter.report(Diag { + level: DiagLevel::Error, + kind: Box::new(AnalyzerDiagKind::InvalidDynamicType { type_name }), + loc: Some(dynamic.loc), + hint: None, + }); + return None; + }; let object_generic_params = self.decl_tables.type_decl_generic_params(named_type.type_decl_id); let object_impls = self.implement_interfaces_of_named_type(named_type).unwrap(); let object_name = self.formatter.format_type_decl(named_type.type_decl_id); diff --git a/crates/cyrusc_analyzer/src/typecheck/enum_init.rs b/crates/cyrusc_analyzer/src/typecheck/enum_init.rs index 29140a9d4..5d73999e7 100644 --- a/crates/cyrusc_analyzer/src/typecheck/enum_init.rs +++ b/crates/cyrusc_analyzer/src/typecheck/enum_init.rs @@ -62,7 +62,7 @@ impl<'a> AnalysisContext<'a> { &enum_name, enum_decl.generic_params.clone(), &named_type.type_args, - enum_decl.loc, + enum_init.loc, )?; self.with_generic_env(generic_env, |this| { diff --git a/crates/cyrusc_analyzer/src/typecheck/literal.rs b/crates/cyrusc_analyzer/src/typecheck/literal.rs index 5238b899f..d2f35d9a3 100644 --- a/crates/cyrusc_analyzer/src/typecheck/literal.rs +++ b/crates/cyrusc_analyzer/src/typecheck/literal.rs @@ -61,8 +61,7 @@ impl<'a> AnalysisContext<'a> { let ty = if let Some(prefix) = prefix_opt { match prefix { - StringPrefix::C => SemaType::Pointer(Box::new(SemaType::Plain(PlainType::UInt8))), - StringPrefix::B => SemaType::Array(TypedArrayType { + StringPrefix::Byte => SemaType::Array(TypedArrayType { element_type: Box::new(SemaType::Const(Box::new(SemaType::Plain(PlainType::UInt8)))), capacity: TypedArrayCapacity::Fixed(Box::new(capacity)), loc: literal.loc, diff --git a/crates/cyrusc_analyzer/src/types.rs b/crates/cyrusc_analyzer/src/types.rs index b28ca82fe..03dc69479 100644 --- a/crates/cyrusc_analyzer/src/types.rs +++ b/crates/cyrusc_analyzer/src/types.rs @@ -167,7 +167,9 @@ impl<'a> AnalysisContext<'a> { self.is_enum_decl_assignable_to(&decl1, &decl2, env1, env2, loc) } - (TypeDeclID::Interface(id1), TypeDeclID::Interface(id2)) => id1 == id2, + (TypeDeclID::Interface(id1), TypeDeclID::Interface(id2)) => { + id1 == id2 && named_type1.type_args == named_type2.type_args + } _ => false, } @@ -383,10 +385,10 @@ impl<'a> AnalysisContext<'a> { (TypedArrayCapacity::Fixed(value_capacity_expr), TypedArrayCapacity::Fixed(target_capacity_expr)) => { let mut folder = ConstFolder::new(self, &self.decl_tables, self.target, self.tctx.clone(), self); - let value_capacity = folder.expr_as_const_int(&value_capacity_expr, self).unwrap(); - let target_capacity = folder.expr_as_const_int(&target_capacity_expr, self).unwrap(); + let value_capacity = folder.expr_as_const_int(&value_capacity_expr, self); + let target_capacity = folder.expr_as_const_int(&target_capacity_expr, self); - value_capacity == target_capacity + matches!((value_capacity, target_capacity), (v, t) if v == t) } _ => false, // not valid } diff --git a/crates/cyrusc_ast/src/format.rs b/crates/cyrusc_ast/src/format.rs index 2ba3d1abe..33b367f95 100755 --- a/crates/cyrusc_ast/src/format.rs +++ b/crates/cyrusc_ast/src/format.rs @@ -285,7 +285,7 @@ impl fmt::Display for UnnamedStructType { impl fmt::Display for PrefixOperator { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - PrefixOperator::Bang => write!(f, "&"), + PrefixOperator::Bang => write!(f, "!"), PrefixOperator::Minus => write!(f, "-"), PrefixOperator::BitwiseNot => write!(f, "~"), } @@ -411,7 +411,7 @@ impl fmt::Display for ASTExpr { write!(f, ".{}", unnamed_enum_value.ident.as_string())?; match &unnamed_enum_value.kind { - UnnamedEnumValueKind::Plain => todo!(), + UnnamedEnumValueKind::Plain => {} UnnamedEnumValueKind::Tuple(exprs) => { write!(f, "({})", format_expr_series(exprs))?; } diff --git a/crates/cyrusc_ast/src/lib.rs b/crates/cyrusc_ast/src/lib.rs index d4d5a5a4c..42b192198 100755 --- a/crates/cyrusc_ast/src/lib.rs +++ b/crates/cyrusc_ast/src/lib.rs @@ -1515,6 +1515,7 @@ impl PartialEq for UnnamedEnumValueKind { match (self, other) { (UnnamedEnumValueKind::Plain, UnnamedEnumValueKind::Plain) => true, (UnnamedEnumValueKind::Tuple(exprs1), UnnamedEnumValueKind::Tuple(exprs2)) => exprs1 == exprs2, + (UnnamedEnumValueKind::Struct(fields1), UnnamedEnumValueKind::Struct(fields2)) => fields1 == fields2, _ => false, } } diff --git a/crates/cyrusc_cir_lower/src/lib.rs b/crates/cyrusc_cir_lower/src/lib.rs index 61f1de1e1..5258ef43b 100644 --- a/crates/cyrusc_cir_lower/src/lib.rs +++ b/crates/cyrusc_cir_lower/src/lib.rs @@ -8,7 +8,6 @@ use cyrusc_ast::operators::InfixOperator; use cyrusc_internal::abi::mangler::*; use cyrusc_internal::abi::target::ABITarget; use cyrusc_internal::cir::cir::*; -use cyrusc_internal::cir::lower::cir_fat_ptr_type; use cyrusc_internal::cir::lower::lower_enum_type; use cyrusc_internal::cir::lower::lower_func_type; use cyrusc_internal::cir::lower::lower_sema_type; @@ -1879,7 +1878,7 @@ impl<'a> CIRLower<'a> { let irv_id = self.new_ir_value_id(); - let vtable_type = cir_fat_ptr_type(&self.tctx, None, loc); + let vtable_type = self.tctx.fat_ptr_type(); let cir_global = CIRGlobalVarStmt { irv_id, @@ -2053,11 +2052,11 @@ impl<'a> CIRLower<'a> { LiteralKind::Float(value, ..) => CIRLiteralKind::Float(*value), LiteralKind::Bool(value) => CIRLiteralKind::Bool(*value), LiteralKind::Char(value) => CIRLiteralKind::Char(*value), - LiteralKind::Null => CIRLiteralKind::Null, - LiteralKind::String(value, prefix_opt) => match prefix_opt.clone().unwrap_or(StringPrefix::C) { - StringPrefix::C => CIRLiteralKind::CString(value.clone()), - StringPrefix::B => CIRLiteralKind::ByteString(value.clone()), + LiteralKind::String(value, prefix_opt) => match prefix_opt.clone() { + Some(StringPrefix::Byte) => CIRLiteralKind::ByteString(value.clone()), + _ => CIRLiteralKind::CString(value.clone()), }, + LiteralKind::Null => CIRLiteralKind::Null, }; let ty = self.lower_sema_type(&literal.ty.clone().unwrap()); diff --git a/crates/cyrusc_codegen_llvm/src/builder/exprs.rs b/crates/cyrusc_codegen_llvm/src/builder/exprs.rs index df5100bdf..6c64ca99b 100644 --- a/crates/cyrusc_codegen_llvm/src/builder/exprs.rs +++ b/crates/cyrusc_codegen_llvm/src/builder/exprs.rs @@ -19,7 +19,6 @@ use cyrusc_internal::{ }, cir::{ cir::*, - lower::cir_fat_ptr_type, types::{CIRArrayType, CIREnumType, CIRFuncType, CIRType, CIRUnionType}, }, }; @@ -154,7 +153,7 @@ impl<'ll> CodeGenIRBuilder<'ll> { .unwrap() .into_struct_value(); - let fat_ptr_type = cir_fat_ptr_type(&self.tctx, Some(dynamic.data_expr.ty.clone()), dynamic.loc); + let fat_ptr_type = self.tctx.fat_ptr_type(); InternalValue::new( fat_ptr_type, diff --git a/crates/cyrusc_codegen_llvm/src/builder/vtables.rs b/crates/cyrusc_codegen_llvm/src/builder/vtables.rs index a8ff4809d..39d439834 100644 --- a/crates/cyrusc_codegen_llvm/src/builder/vtables.rs +++ b/crates/cyrusc_codegen_llvm/src/builder/vtables.rs @@ -2,7 +2,7 @@ // Copyright (c) 2026 The Cyrus Language use crate::builder::{builder::CodeGenIRBuilder, irreg::LocalIRValue}; -use cyrusc_internal::{cir::lower::cir_fat_ptr_type, vtable::VTableInfo}; +use cyrusc_internal::vtable::VTableInfo; use inkwell::{ AddressSpace, module::Linkage, @@ -30,10 +30,7 @@ impl<'ll> CodeGenIRBuilder<'ll> { global_value.set_linkage(Linkage::Internal); - self.insert_local_ir_value( - irv_id, - LocalIRValue::Global(global_value, cir_fat_ptr_type(&self.tctx, None, vtable_info.loc)), - ); + self.insert_local_ir_value(irv_id, LocalIRValue::Global(global_value, self.tctx.fat_ptr_type())); } } diff --git a/crates/cyrusc_compiler/src/context.rs b/crates/cyrusc_compiler/src/context.rs index 897ff99ef..796222d03 100644 --- a/crates/cyrusc_compiler/src/context.rs +++ b/crates/cyrusc_compiler/src/context.rs @@ -155,9 +155,7 @@ impl CodeGenContext { match self.linker_output_kind { CompilerOption_LinkerOutputKind::Executable => { - let output_path_cow = output_path.to_string_lossy(); - - self.linker.link_executable(&object_files, &output_path_cow.to_string()) + self.linker.link_executable(&object_files, output_path.to_path_buf()) } CompilerOption_LinkerOutputKind::SharedLib => { let lib_name = canonical_project_name(&self.opts); diff --git a/crates/cyrusc_compiler/src/driver.rs b/crates/cyrusc_compiler/src/driver.rs index dcd2c1a01..686d8caaf 100644 --- a/crates/cyrusc_compiler/src/driver.rs +++ b/crates/cyrusc_compiler/src/driver.rs @@ -491,8 +491,18 @@ pub fn get_executable_output_path( pub fn get_final_build_directory_path(build_dir: &CompilerOption_BuildDir) -> PathBuf { fn temp_build_dir() -> PathBuf { let temp_dir = env::temp_dir(); - ensure_output_dir(&temp_dir); - temp_dir + + // create unique subdirectory using PID and timestamp + let unique_dir = temp_dir.join(format!( + "cyrusc_build_{}_{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_millis() + )); + ensure_output_dir(&unique_dir); + unique_dir } match build_dir { diff --git a/crates/cyrusc_compiler/src/linker.rs b/crates/cyrusc_compiler/src/linker.rs index dd5913a12..111e240b2 100644 --- a/crates/cyrusc_compiler/src/linker.rs +++ b/crates/cyrusc_compiler/src/linker.rs @@ -53,7 +53,7 @@ impl Linker { } /// Link object files into a binary executable - pub fn link_executable(&self, object_files: &[String], output_path: &str) -> Result<(), String> { + pub fn link_executable(&self, object_files: &[String], output_path: PathBuf) -> Result<(), String> { let mut cmd = Command::new(&self.linker_path); if self.opts.linker_options.link_static { @@ -65,7 +65,7 @@ impl Linker { if self.opts.linker_options.no_pie { cmd.arg("-no-pie"); } - if !self.opts.linker_options.link_static && (self.opts.linker_options.pie || self.opts.linker_options.no_pie) { + if !self.opts.linker_options.link_static && self.opts.linker_options.pie { cmd.args(["-ldl", "-rdynamic"]); } if self.opts.linker_options.link_static { diff --git a/crates/cyrusc_compiler/src/options.rs b/crates/cyrusc_compiler/src/options.rs index 1398fe4ea..a04d99286 100644 --- a/crates/cyrusc_compiler/src/options.rs +++ b/crates/cyrusc_compiler/src/options.rs @@ -318,7 +318,7 @@ pub fn compiler_options_from_scaffold(scaffold: &ScaffoldConfig) -> CompilerOpti if let Some(opt) = &compiler.optimize { // parse the optimize flag in a case-insensitive way opts.opt_level = match opt.to_lowercase().as_str() { - "none" => Some(CompilerOption_Optimize::O1), + "none" => Some(CompilerOption_Optimize::O0), "o1" => Some(CompilerOption_Optimize::O1), "o2" => Some(CompilerOption_Optimize::O2), "o3" => Some(CompilerOption_Optimize::O3), diff --git a/crates/cyrusc_diagcentral/src/reporter.rs b/crates/cyrusc_diagcentral/src/reporter.rs index f4322efbb..3249fa90b 100644 --- a/crates/cyrusc_diagcentral/src/reporter.rs +++ b/crates/cyrusc_diagcentral/src/reporter.rs @@ -75,7 +75,6 @@ impl DiagReporter { } diags.clear(); - drop(diags); } #[inline] @@ -139,7 +138,7 @@ impl DiagReporter { let lines: Vec<&str> = source_file.content.lines().collect(); - let line = lines[loc.line - 1]; + let line = lines[loc.line.saturating_sub(1)]; out.push_str(line.trim_end()); @@ -187,4 +186,4 @@ macro_rules! exit_with_single_diag { cyrusc_diagcentral::reporter::DiagReporter::display_single($diag); std::process::exit(1); }; -} \ No newline at end of file +} diff --git a/crates/cyrusc_diagcentral/src/tests.rs b/crates/cyrusc_diagcentral/src/tests.rs index 5e15d665b..9cd603719 100644 --- a/crates/cyrusc_diagcentral/src/tests.rs +++ b/crates/cyrusc_diagcentral/src/tests.rs @@ -30,7 +30,7 @@ mod tests { let diag = Diag { level: DiagLevel::Error, kind: Box::new(DummyDiagKind::Mismatch), - loc: Some(Loc::new(FileID(0), 0, 0, 3, 10)), + loc: Some(Loc::new(FileID(0), 1, 1, 3, 10)), hint: Some("A simple example of hint message.".to_string()), }; diff --git a/crates/cyrusc_internal/src/abi/layout.rs b/crates/cyrusc_internal/src/abi/layout.rs index 1ed01aaf9..95368ac1a 100644 --- a/crates/cyrusc_internal/src/abi/layout.rs +++ b/crates/cyrusc_internal/src/abi/layout.rs @@ -17,6 +17,7 @@ pub enum ABIFieldOffsetInfo { index: u32, offset: u32, original_index: usize, + size: usize, }, Padding { index: u32, @@ -64,11 +65,12 @@ impl ABITypeLayout { for entry in &self.field_offsets { if let ABIFieldOffsetInfo::Normal { offset: field_offset, + size: field_size, index, .. } = entry { - if offset >= *field_offset && offset < (*field_offset + self.size) { + if offset >= *field_offset && (offset as usize) < ((*field_offset as usize) + *field_size) { match best { Some((_, best_size)) if best_size >= self.size => {} _ => best = Some((*index as usize, self.size)), @@ -97,11 +99,12 @@ impl ABITypeLayout { } impl ABIFieldOffsetInfo { - pub fn normal(index: u32, offset: u32, original_index: usize) -> Self { + pub fn normal(index: u32, offset: u32, original_index: usize, size: usize) -> Self { ABIFieldOffsetInfo::Normal { index, offset, original_index, + size, } } diff --git a/crates/cyrusc_internal/src/abi/mangler.rs b/crates/cyrusc_internal/src/abi/mangler.rs index f48b46005..bdb39b333 100644 --- a/crates/cyrusc_internal/src/abi/mangler.rs +++ b/crates/cyrusc_internal/src/abi/mangler.rs @@ -7,7 +7,7 @@ use cyrusc_ast::{ }; use cyrusc_typed_ast::{ stmts::{TypedFuncTypeVariadicParam, TypedTypeArg, TypedTypeArgs}, - types::{SemaType, TypeDeclID, TypedArrayCapacity}, + types::{NamedType, SemaType, TypeDeclID, TypedArrayCapacity}, }; use once_cell::sync::Lazy; @@ -163,27 +163,34 @@ fn mangle_type_args(type_args: &TypedTypeArgs) -> String { .join(",") } -fn mangle_sema_type(sema_type: &SemaType) -> String { - match sema_type { - SemaType::Named(named_type) => { - let decl_id = match named_type.type_decl_id { - TypeDeclID::Struct(struct_decl_id) => format!("struct_{}", struct_decl_id.0), - TypeDeclID::Enum(enum_decl_id) => format!("enum_{}", enum_decl_id.0), - TypeDeclID::Union(union_decl_id) => format!("union_{}", union_decl_id.0), - TypeDeclID::Interface(interface_decl_id) => format!("interface_{}", interface_decl_id.0), - TypeDeclID::Typedef(_) => unreachable!(), - }; +fn mangle_named_type(named_type: &NamedType) -> String { + let decl_id = match named_type.type_decl_id { + TypeDeclID::Struct(struct_decl_id) => format!("struct_{}", struct_decl_id.0), + TypeDeclID::Enum(enum_decl_id) => format!("enum_{}", enum_decl_id.0), + TypeDeclID::Union(union_decl_id) => format!("union_{}", union_decl_id.0), + TypeDeclID::Interface(interface_decl_id) => format!("interface_{}", interface_decl_id.0), + TypeDeclID::Typedef(_) => unreachable!(), + }; + + if named_type.type_args.is_empty() { + format!("{decl_id}") + } else { + let type_args = mangle_type_args(&named_type.type_args); + + format!("{decl_id}<{type_args}>") + } +} - if named_type.type_args.is_empty() { - format!("{decl_id}") - } else { - let type_args = mangle_type_args(&named_type.type_args); +fn mangle_sema_type(ty: &SemaType) -> String { + match ty { + SemaType::Named(named_type) => mangle_named_type(named_type), - format!("{decl_id}<{type_args}>") - } - } SemaType::InterfaceObject(interface_object) => { - mangle_sema_type(&SemaType::InterfaceObject(interface_object.clone())) + format!( + "{}(vtable_id: {})", + mangle_named_type(&interface_object.interface_type), + interface_object.vtable_id + ) } SemaType::Plain(plain_type) => { // Use the provided to_string() method for PlainType diff --git a/crates/cyrusc_internal/src/abi/targets/x86_64/classify.rs b/crates/cyrusc_internal/src/abi/targets/x86_64/classify.rs index bbcfdeb50..0893e35fe 100644 --- a/crates/cyrusc_internal/src/abi/targets/x86_64/classify.rs +++ b/crates/cyrusc_internal/src/abi/targets/x86_64/classify.rs @@ -189,7 +189,6 @@ impl X86_64 { } let layout = self.tctx.layout_of(source_type); - assert!(layout.size != source_offset); let remaining_bytes = layout.size - source_offset; diff --git a/crates/cyrusc_internal/src/cir/lower.rs b/crates/cyrusc_internal/src/cir/lower.rs index 425dfac80..2497d5291 100644 --- a/crates/cyrusc_internal/src/cir/lower.rs +++ b/crates/cyrusc_internal/src/cir/lower.rs @@ -10,7 +10,6 @@ use crate::{ }, }; use cyrusc_ast::abi::CallConv; -use cyrusc_source_loc::Loc; use cyrusc_typed_ast::{ decls::{EnumDecl, EnumDeclID, StructDecl, StructDeclID, UnionDecl, UnionDeclID, table::DeclTablesRegistry}, stmts::{TypedEnumVariant, TypedFuncTypeParams, TypedTypeArgs}, @@ -18,7 +17,7 @@ use cyrusc_typed_ast::{ instantiate_enum_decl_with_type_args, instantiate_struct_decl_with_type_args, instantiate_union_decl_with_type_args, }, - types::{NamedType, PlainType, SemaType, TypeDeclID, TypedArrayCapacity, TypedFuncType}, + types::{NamedType, SemaType, TypeDeclID, TypedArrayCapacity, TypedFuncType}, }; use fx_hash::FxHashSet; use std::sync::Arc; @@ -85,14 +84,17 @@ pub fn lower_sema_type( CIRType::FuncType(lower_func_type(decl_tables, target, tctx.clone(), func_type)) } - SemaType::InterfaceObject(interface_object) => cir_fat_ptr_type(&tctx, None, interface_object.loc), + SemaType::InterfaceObject(_) => tctx.fat_ptr_type(), SemaType::Unresolved(_) | SemaType::GenericParam(_) | SemaType::SelfType(_) | SemaType::InferVar(_) | SemaType::Placeholder - | SemaType::Err(_) => unreachable!(), + | SemaType::Err(_) => { + dbg!(ty.clone()); + unreachable!() + } } .const_inner() .clone() @@ -162,17 +164,14 @@ pub fn lower_named_type( named_type.type_args.clone(), ) } - TypeDeclID::Interface(interface_decl_id) => { - let interface_decl = decl_tables.interface_decl(interface_decl_id); - - cir_fat_ptr_type(&tctx, None, interface_decl.loc) - } TypeDeclID::Typedef(typedef_decl_id) => { let typedef = decl_tables.typedef_decl(typedef_decl_id); lower_sema_type(decl_tables, target, tctx, &typedef.ty) } + + TypeDeclID::Interface(_) => tctx.fat_ptr_type(), } } @@ -544,21 +543,3 @@ pub fn lower_func_type( cir_type.abi_func_info = Some(target.target_abi.classify_func(&cir_type).unwrap()); cir_type } - -pub fn cir_fat_ptr_type(tctx: &CIRTypeContext, data_type: Option, loc: Loc) -> CIRType { - let struct_type = CIRStructType { - decl_key: None, - name: None, - fields: vec![ - CIRType::Pointer(Box::new(data_type.unwrap_or(CIRType::Plain(PlainType::Void)))), // T* or void* - CIRType::Pointer(Box::new(CIRType::Plain(PlainType::Void))), - ], - fields_info: vec![("data_ptr".to_string(), loc), ("vtable_ptr".to_string(), loc)], - repr_attr: None, - align: None, - loc, - }; - - let type_id = tctx.insert_struct(struct_type); - CIRType::Struct(type_id) -} diff --git a/crates/cyrusc_internal/src/cir/typectx.rs b/crates/cyrusc_internal/src/cir/typectx.rs index 28351268d..898ffff7f 100644 --- a/crates/cyrusc_internal/src/cir/typectx.rs +++ b/crates/cyrusc_internal/src/cir/typectx.rs @@ -426,6 +426,7 @@ impl CIRTypeContext { index: i as u32, offset: element_layout.align * i as u32, original_index: i, + size: element_layout.size as usize, }); } @@ -471,6 +472,7 @@ impl CIRTypeContext { field_offset_index, offset, field_original_index, + field_layout.size as usize, )); field_offset_index += 1; @@ -510,7 +512,12 @@ impl CIRTypeContext { max_size = max_size.max(field_layout.size); max_align = max_align.max(field_layout.align); - field_offsets.push(ABIFieldOffsetInfo::normal(original_index as u32, 0, original_index)); + field_offsets.push(ABIFieldOffsetInfo::normal( + original_index as u32, + 0, + original_index, + field_layout.size as usize, + )); } let total_size = align_offset(max_size, max_align); diff --git a/crates/cyrusc_internal/src/cir/types.rs b/crates/cyrusc_internal/src/cir/types.rs index a68942b16..7dc0648de 100644 --- a/crates/cyrusc_internal/src/cir/types.rs +++ b/crates/cyrusc_internal/src/cir/types.rs @@ -214,28 +214,6 @@ impl CIRType { } } - pub fn is_scalar(&self) -> bool { - match self.const_inner() { - // plain types like int, float, bool, char, etc. - CIRType::Plain(plain_type) => plain_type.is_scalar(), - - // const does not affect scalar-ness - CIRType::Const(inner) => inner.is_scalar(), - - // pointers are scalar regardless of pointee - CIRType::Pointer(_) => true, - - // enums are scalar if they lower to an integer - CIRType::Enum(_) => true, - - CIRType::Struct(_) => false, - CIRType::Union(_) => false, - CIRType::Array(_) => false, - CIRType::Dynamic(_) => false, - CIRType::FuncType(_) => false, - } - } - pub fn is_array(&self) -> bool { match self.const_inner() { CIRType::Array(_) => true, diff --git a/crates/cyrusc_lexer/src/lib.rs b/crates/cyrusc_lexer/src/lib.rs index ce4b7ab74..bbfbb3047 100755 --- a/crates/cyrusc_lexer/src/lib.rs +++ b/crates/cyrusc_lexer/src/lib.rs @@ -182,14 +182,9 @@ impl<'source_map, 'source_file> Lexer<'source_map, 'source_file> { } fn read_ident_or_prefix(&mut self) -> TokenKind { - if self.ch == 'c' && self.peek_char() == '"' { - self.read_char(); - return self.read_string_literal(Some(StringPrefix::C)); - } - if self.ch == 'b' && self.peek_char() == '"' { self.read_char(); - return self.read_string_literal(Some(StringPrefix::B)); + return self.read_string_literal(Some(StringPrefix::Byte)); } self.read_ident() @@ -473,12 +468,10 @@ impl<'source_map, 'source_file> Lexer<'source_map, 'source_file> { self.reporter.report(Diag { level: DiagLevel::Error, - kind: Box::new(LexicalDiagKind::InvalidChar(self.ch)), + kind: Box::new(LexicalDiagKind::InvalidEscapeSequence), loc: Some(Loc::new(self.file_id(), line, column, start, end)), hint: Some(err.to_string()), }); - - self.read_char(); return TokenKind::Invalid; } }; @@ -797,13 +790,11 @@ fn lookup_identifier(ident: String) -> TokenKind { "intptr" => TokenKind::IntPtr, "isize" => TokenKind::ISize, "usize" => TokenKind::USize, - "int" => TokenKind::Int, "int8" => TokenKind::Int8, "int16" => TokenKind::Int16, "int32" => TokenKind::Int32, "int64" => TokenKind::Int64, "int128" => TokenKind::Int128, - "uint" => TokenKind::UInt, "uint8" => TokenKind::UInt8, "uint16" => TokenKind::UInt16, "uint32" => TokenKind::UInt32, @@ -813,7 +804,6 @@ fn lookup_identifier(ident: String) -> TokenKind { "float32" => TokenKind::Float32, "float64" => TokenKind::Float64, "float128" => TokenKind::Float128, - "char" => TokenKind::Char, "bool" => TokenKind::Bool, _ => TokenKind::Ident(ident), } diff --git a/crates/cyrusc_parser/src/common.rs b/crates/cyrusc_parser/src/common.rs index 115ac1101..1bba8745c 100755 --- a/crates/cyrusc_parser/src/common.rs +++ b/crates/cyrusc_parser/src/common.rs @@ -285,8 +285,8 @@ impl<'source_file> Parser<'source_file> { depth += 1; // check that it's disqualified until the first greater-then token or not. - let mut i = 2; - while let Some(peek_token) = self.peek_n_token(i) + let mut j = 2; + while let Some(peek_token) = self.peek_n_token(j) && peek_token.kind != TokenKind::GreaterThan { if self.token_disqualifies_type_arg(&peek_token.kind) { @@ -296,7 +296,7 @@ impl<'source_file> Parser<'source_file> { }; } - i += 1; + j += 1; } } TokenKind::GreaterThan => { diff --git a/crates/cyrusc_parser/src/exprs.rs b/crates/cyrusc_parser/src/exprs.rs index 27d70f3b9..d98844d94 100755 --- a/crates/cyrusc_parser/src/exprs.rs +++ b/crates/cyrusc_parser/src/exprs.rs @@ -273,7 +273,6 @@ impl<'source_file> Parser<'source_file> { if self.current_token_is(TokenKind::Comma) { // considered as tuple construction, not grouped expr - self.next_token(); self.parse_tuple_value(Some(expr))? } else { expr @@ -683,18 +682,29 @@ impl<'source_file> Parser<'source_file> { let mut elements: Vec = vec![first]; + self.expect_current(TokenKind::Comma)?; + loop { + // optional comma + if self.current_token_is(TokenKind::RightParen) { + let end = self.current_token().loc.end; + + return Ok(ASTExpr::Tuple(ASTTupleValueExpr { + elements, + loc: Loc::new(self.file_id(), line, column, start, end), + })); + } + let expr = self.parse_expr(Precedence::Lowest)?; self.next_token(); elements.push(expr); - match self.current_token().kind { - TokenKind::Comma => { - self.next_token(); - continue; - } - _ => break, + if self.current_token_is(TokenKind::Comma) { + self.next_token(); + continue; + } else { + break; } } @@ -1511,13 +1521,11 @@ fn can_start_expr(kind: &TokenKind) -> bool { | TokenKind::IntPtr | TokenKind::ISize | TokenKind::USize - | TokenKind::Int | TokenKind::Int8 | TokenKind::Int16 | TokenKind::Int32 | TokenKind::Int64 | TokenKind::Int128 - | TokenKind::UInt | TokenKind::UInt8 | TokenKind::UInt16 | TokenKind::UInt32 @@ -1527,7 +1535,6 @@ fn can_start_expr(kind: &TokenKind) -> bool { | TokenKind::Float32 | TokenKind::Float64 | TokenKind::Float128 - | TokenKind::Char | TokenKind::Void | TokenKind::Bool | TokenKind::Const diff --git a/crates/cyrusc_parser/src/stmts.rs b/crates/cyrusc_parser/src/stmts.rs index 43665ec4c..9f072b44d 100755 --- a/crates/cyrusc_parser/src/stmts.rs +++ b/crates/cyrusc_parser/src/stmts.rs @@ -31,7 +31,7 @@ impl<'source_file> Parser<'source_file> { let stmts = self.parse_stmt(grouped_modifiers, toplevel)?; - if !stmts.len() == 1 { + if stmts.len() != 1 { return Err(self.error_invalid_token()); } @@ -561,14 +561,6 @@ impl<'source_file> Parser<'source_file> { let mut fields = Vec::new(); loop { - if self.current_token_is(TokenKind::RightBrace) { - return Err(self.error_with_hint( - &self.current_token(), - ParserDiagKind::InvalidToken(self.current_token().kind), - "Consider to add a field to enum struct variant or remove the braces.", - )); - } - let loc = self.current_token().loc; let (line, column, start) = (loc.line, loc.column, loc.start); @@ -591,6 +583,10 @@ impl<'source_file> Parser<'source_file> { if self.current_token_is(TokenKind::RightBrace) { self.next_token(); break; + } else if self.current_token_is(TokenKind::Comma) && self.peek_token_is(TokenKind::RightBrace) { + self.next_token(); + self.next_token(); + break; } else { self.expect_current(TokenKind::Comma)?; continue; @@ -800,16 +796,18 @@ impl<'source_file> Parser<'source_file> { })); } - variants.push(self.parse_enum_variant()?); - - while self.current_token_is(TokenKind::Comma) { - self.expect_current(TokenKind::Comma)?; - + loop { if self.current_token_is(TokenKind::RightBrace) { break; } + if self.current_token_is(TokenKind::Comma) { + self.next_token(); + continue; + } + variants.push(self.parse_enum_variant()?); + if self.peek_token_is(TokenKind::RightBrace) || self.peek_token_is(TokenKind::Function) || self.peek_token_is(TokenKind::Extern) @@ -820,7 +818,7 @@ impl<'source_file> Parser<'source_file> { } } - // consume optional comma at the end of the variant + // consume optional comma of ending variant if self.current_token_is(TokenKind::Comma) { self.next_token(); } @@ -1280,15 +1278,10 @@ impl<'source_file> Parser<'source_file> { // Check for non-conditional for loop if self.current_token_is(TokenKind::LeftBrace) { - let body: Box; - if self.current_token_is(TokenKind::LeftBrace) { - body = Box::new(self.parse_block()?); + let body = Box::new(self.parse_block()?); - if self.peek_token_is(TokenKind::Semicolon) { - self.next_token(); - } - } else { - return Err(self.error_at_current(ParserDiagKind::MissingOpeningBrace)); + if self.peek_token_is(TokenKind::Semicolon) { + self.next_token(); } let end = self.current_token().loc.end; @@ -1681,7 +1674,7 @@ impl<'source_file> Parser<'source_file> { self.must_be_semicolon()?; - let end = self.peek_token().loc.end; + let end = self.current_token().loc.end; Ok(ASTStmt::Return(ASTReturnStmt { argument: Some(argument), diff --git a/crates/cyrusc_resolver/src/fs_module_loader.rs b/crates/cyrusc_resolver/src/fs_module_loader.rs index af2785b86..d4e887c55 100644 --- a/crates/cyrusc_resolver/src/fs_module_loader.rs +++ b/crates/cyrusc_resolver/src/fs_module_loader.rs @@ -214,7 +214,10 @@ impl ModuleLoader for FsModuleLoader { let module_file_path = &resolved_module_file.file_path; // verify file exists - if std::fs::read_to_string(module_file_path).is_err() { + if !std::fs::metadata(module_file_path) + .map(|m| m.is_file()) + .unwrap_or(false) + { loaded_modules_list.push(Err(Some(Box::new(ModuleFSLoaderDiagKind::ModuleNotFound { module_name: format_module_segments(&sub_import.segments), })))); diff --git a/crates/cyrusc_resolver/src/modules.rs b/crates/cyrusc_resolver/src/modules.rs index 18d16166c..ec58bfd9a 100644 --- a/crates/cyrusc_resolver/src/modules.rs +++ b/crates/cyrusc_resolver/src/modules.rs @@ -192,7 +192,7 @@ impl<'a> Resolver<'a> { } *is_module_safe_to_be_resolved = false; - continue; + return; } }; @@ -208,7 +208,7 @@ impl<'a> Resolver<'a> { }); *is_module_safe_to_be_resolved = false; - continue; + return; } } @@ -226,6 +226,7 @@ impl<'a> Resolver<'a> { visiting.done.insert(loaded_module.file_id); *is_module_safe_to_be_resolved = false; + return; } // insert file module diff --git a/crates/cyrusc_resolver/src/traverse.rs b/crates/cyrusc_resolver/src/traverse.rs index c573e088d..45eb69383 100644 --- a/crates/cyrusc_resolver/src/traverse.rs +++ b/crates/cyrusc_resolver/src/traverse.rs @@ -264,7 +264,7 @@ impl<'a> Resolver<'a> { return Some(symbol_id); } - if let Some(symbol_id) = self.lookup_symbol_id(self.current_scope.unwrap(), &ident.value) { + if let Some(symbol_id) = self.lookup_symbol_id(self.current_scope?, &ident.value) { return Some(symbol_id); } @@ -300,7 +300,7 @@ impl<'a> Resolver<'a> { return None; }; - let current_scope_id = self.current_scope.unwrap(); + let current_scope_id = self.current_scope?; let mut current_symbol = match self.lookup_symbol_id(current_scope_id, &first_ident.value) { Some(symbol_id) => symbol_id, @@ -496,7 +496,7 @@ impl<'a> Resolver<'a> { return Some(SemaType::GenericParam(generic_param_id)); } - if let Some(symbol_id) = self.lookup_symbol_id(self.current_scope.unwrap(), &ident.value) { + if let Some(symbol_id) = self.lookup_symbol_id(self.current_scope?, &ident.value) { return Some(SemaType::Unresolved(UnresolvedType::Decl(symbol_id))); } @@ -746,8 +746,8 @@ impl<'a> Resolver<'a> { .iter() .map(|type_arg| match type_arg { TypeArg::Type(type_spec) => { - let sema_type = self.resolve_type(type_spec.clone(), type_spec.loc())?; - Some(TypedTypeArg::Type(sema_type, type_spec.loc())) + let ty = self.resolve_type(type_spec.clone(), type_spec.loc())?; + Some(TypedTypeArg::Type(ty, type_spec.loc())) } TypeArg::Infer => Some(TypedTypeArg::Infer), }) @@ -820,7 +820,7 @@ impl<'a> Resolver<'a> { } fn resolve_typedef(&mut self, typedef: &ASTTypedefStmt) -> Option { - let symbol_id = self.lookup_symbol_id(self.current_scope.unwrap(), &typedef.ident.value)?; + let symbol_id = self.lookup_symbol_id(self.current_scope?, &typedef.ident.value)?; let generic_params = self.resolve_generic_params(&typedef.generic_params)?; self.with_generic_scope(&generic_params.clone(), |this| { let typedef_decl_id = this.decl_tables.insert_typedef(TypedefDecl { @@ -925,7 +925,7 @@ impl<'a> Resolver<'a> { fn resolve_interface_stmt(&mut self, interface: &ASTInterfaceStmt) -> Option { let name = interface.ident.as_string(); - let symbol_id = self.lookup_symbol_id(self.current_scope.unwrap(), &name).unwrap(); + let symbol_id = self.lookup_symbol_id(self.current_scope?, &name)?; let loc = interface.loc; let generic_params = self.resolve_generic_params(&interface.generic_params)?; @@ -1010,7 +1010,7 @@ impl<'a> Resolver<'a> { fn resolve_union_stmt(&mut self, union_decl: &ASTUnionStmt) -> Option { let name = union_decl.ident.as_string(); - let symbol_id = self.lookup_symbol_id(self.current_scope.unwrap(), &name).unwrap(); + let symbol_id = self.lookup_symbol_id(self.current_scope?, &name)?; let loc = union_decl.loc; let generic_params = self.resolve_generic_params(&union_decl.generic_params)?; @@ -1171,7 +1171,7 @@ impl<'a> Resolver<'a> { fn resolve_enum_stmt(&mut self, enum_decl: &ASTEnumStmt) -> Option { let name = enum_decl.ident.as_string(); - let symbol_id = self.lookup_symbol_id(self.current_scope.unwrap(), &name).unwrap(); + let symbol_id = self.lookup_symbol_id(self.current_scope?, &name)?; let loc = enum_decl.loc; let generic_params = self.resolve_generic_params(&enum_decl.generic_params)?; @@ -1230,7 +1230,7 @@ impl<'a> Resolver<'a> { fn resolve_global_var_stmt(&mut self, global_var: &ASTGlobalVarStmt) -> Option { let name = global_var.ident.as_string(); - let symbol_id = self.lookup_symbol_id(self.current_scope.unwrap(), &name).unwrap(); + let symbol_id = self.lookup_symbol_id(self.current_scope?, &name)?; let loc = global_var.loc; let sema_type = match &global_var.type_spec { @@ -1396,7 +1396,7 @@ impl<'a> Resolver<'a> { fn resolve_struct_stmt(&mut self, struct_decl: &ASTStructStmt) -> Option { let name = struct_decl.ident.as_string(); - let symbol_id = self.lookup_symbol_id(self.current_scope.unwrap(), &name).unwrap(); + let symbol_id = self.lookup_symbol_id(self.current_scope?, &name)?; let loc = struct_decl.loc; let generic_params = self.resolve_generic_params(&struct_decl.generic_params)?; @@ -1585,7 +1585,7 @@ impl<'a> Resolver<'a> { fn resolve_func_decl(&mut self, ast_func_decl: &ASTFuncDeclStmt) -> Option { let name = ast_func_decl.usable_name(); - let symbol_id = self.lookup_symbol_id(self.current_scope.unwrap(), &name).unwrap(); + let symbol_id = self.lookup_symbol_id(self.current_scope?, &name)?; let generic_params = self.resolve_generic_params(&ast_func_decl.generic_params)?; @@ -1639,7 +1639,7 @@ impl<'a> Resolver<'a> { } fn resolve_func_def(&mut self, func_def: &ASTFuncDefStmt) -> Option { - let symbol_id = self.lookup_symbol_id(self.current_scope.unwrap(), &func_def.ident.value)?; + let symbol_id = self.lookup_symbol_id(self.current_scope?, &func_def.ident.value)?; let scope = LocalScope::new(); @@ -2139,8 +2139,14 @@ impl<'a> Resolver<'a> { #[inline] fn resolve_module_import_expr(&mut self, module_import: &ASTModuleImport) -> Option { if let Some(ident) = module_import.as_ident() { - // FIXME: Order of resolution is not correct. - // Generic param as type resolution must be after `try-in-local-first`. + // try to resolve ident in local first + if self.resolve_local_scope_symbol(&ident.value).is_some() + && let Some(expr) = self.resolve_ident_expr(&ident) + { + return Some(expr); + } + + // try to resolve as generic param if let Some(generic_param_id) = self.resolve_generic_param_as_type(&ident) { return Some(TypedExpr { kind: TypedExprKind::SemaType { @@ -2653,7 +2659,7 @@ impl<'a> Resolver<'a> { loc: Loc, ) -> Option { match string_prefix { - Some(StringPrefix::B) => { + Some(StringPrefix::Byte) => { let len = string_value.len() + 1; let len_expr = literal_expr_from_const_int(len, loc); @@ -2664,8 +2670,6 @@ impl<'a> Resolver<'a> { })) } - Some(StringPrefix::C) => Some(SemaType::Pointer(Box::new(SemaType::Plain(PlainType::UInt8)))), - None => Some(SemaType::Pointer(Box::new(SemaType::Plain(PlainType::UInt8)))), } } diff --git a/crates/cyrusc_tokens/src/lib.rs b/crates/cyrusc_tokens/src/lib.rs index ef95edd2d..9f23bb306 100644 --- a/crates/cyrusc_tokens/src/lib.rs +++ b/crates/cyrusc_tokens/src/lib.rs @@ -105,13 +105,11 @@ pub enum TokenKind { IntPtr, ISize, USize, - Int, Int8, Int16, Int32, Int64, Int128, - UInt, UInt8, UInt16, UInt32, @@ -121,7 +119,6 @@ pub enum TokenKind { Float32, Float64, Float128, - Char, Void, Bool, Const, @@ -151,7 +148,6 @@ pub enum TokenKind { pub const PRIMITIVE_TYPES: &[TokenKind] = &[ // signed integers - TokenKind::Int, TokenKind::Int8, TokenKind::Int16, TokenKind::Int32, @@ -160,7 +156,6 @@ pub const PRIMITIVE_TYPES: &[TokenKind] = &[ TokenKind::IntPtr, TokenKind::ISize, // unsigned integers - TokenKind::UInt, TokenKind::UInt8, TokenKind::UInt16, TokenKind::UInt32, @@ -173,7 +168,6 @@ pub const PRIMITIVE_TYPES: &[TokenKind] = &[ TokenKind::Float32, TokenKind::Float64, TokenKind::Float128, - TokenKind::Char, TokenKind::Bool, TokenKind::Void, ]; @@ -190,8 +184,7 @@ impl TokenKind { #[inline] pub fn is_unsigned(&self) -> bool { match self { - TokenKind::UInt - | TokenKind::UInt8 + TokenKind::UInt8 | TokenKind::UInt16 | TokenKind::UInt32 | TokenKind::UInt64 @@ -229,8 +222,8 @@ impl fmt::Display for TokenKind { TokenKind::RightParen => write!(f, ")"), TokenKind::LeftBrace => write!(f, "{{"), TokenKind::RightBrace => write!(f, "}}"), - TokenKind::LeftBracket => write!(f, "[["), - TokenKind::RightBracket => write!(f, "]]"), + TokenKind::LeftBracket => write!(f, "["), + TokenKind::RightBracket => write!(f, "]"), TokenKind::At => write!(f, "@"), TokenKind::Comma => write!(f, ","), TokenKind::DoubleDot => write!(f, ".."), @@ -277,13 +270,11 @@ impl fmt::Display for TokenKind { TokenKind::IntPtr => write!(f, "intptr"), TokenKind::ISize => write!(f, "isize"), TokenKind::USize => write!(f, "usize"), - TokenKind::Int => write!(f, "int"), TokenKind::Int8 => write!(f, "int8"), TokenKind::Int16 => write!(f, "int16"), TokenKind::Int32 => write!(f, "int32"), TokenKind::Int64 => write!(f, "int64"), TokenKind::Int128 => write!(f, "int128"), - TokenKind::UInt => write!(f, "uint"), TokenKind::UInt8 => write!(f, "uint8"), TokenKind::UInt16 => write!(f, "uint16"), TokenKind::UInt32 => write!(f, "uint32"), @@ -293,7 +284,6 @@ impl fmt::Display for TokenKind { TokenKind::Float32 => write!(f, "float32"), TokenKind::Float64 => write!(f, "float64"), TokenKind::Float128 => write!(f, "float128"), - TokenKind::Char => write!(f, "char"), TokenKind::Bool => write!(f, "bool"), TokenKind::Void => write!(f, "void"), TokenKind::Enum => write!(f, "enum"), @@ -327,7 +317,7 @@ impl fmt::Display for TokenKind { TokenKind::Tilde => write!(f, "~"), TokenKind::NotEqual => write!(f, "!="), TokenKind::Bang => write!(f, "!"), - TokenKind::DoubleColon => write!(f, "\""), + TokenKind::DoubleColon => write!(f, "::"), TokenKind::Goto => write!(f, "goto"), TokenKind::Defer => write!(f, "defer"), TokenKind::Union => write!(f, "union"), diff --git a/crates/cyrusc_tokens/src/literals.rs b/crates/cyrusc_tokens/src/literals.rs index d63acdd61..a0b4f2e52 100644 --- a/crates/cyrusc_tokens/src/literals.rs +++ b/crates/cyrusc_tokens/src/literals.rs @@ -87,8 +87,7 @@ pub enum IntLiteralKind { #[derive(Debug, Clone, PartialEq)] pub enum StringPrefix { - C, // C-style string which is const char* - B, // Bytes string + Byte, // Bytes string } impl IntLiteralKind { @@ -114,8 +113,7 @@ impl fmt::Display for LiteralKind { LiteralKind::String(string_type, prefix) => { if let Some(prefix) = prefix { match prefix { - StringPrefix::C => write!(f, "c")?, - StringPrefix::B => write!(f, "b")?, + StringPrefix::Byte => write!(f, "b")?, }; } write!(f, "\"{}\"", string_type) diff --git a/crates/cyrusc_typed_ast/src/decls/mod.rs b/crates/cyrusc_typed_ast/src/decls/mod.rs index 0dcfffdda..60cff8527 100644 --- a/crates/cyrusc_typed_ast/src/decls/mod.rs +++ b/crates/cyrusc_typed_ast/src/decls/mod.rs @@ -20,6 +20,33 @@ use std::hash::Hash; pub mod table; +#[macro_export] +macro_rules! debug_assert_func_decl_resolved { + ($func_decl:expr) => { + for func_param in &$func_decl.params.list { + match func_param { + TypedFuncParamKind::FuncParam(func_param) => { + debug_assert!( + !func_param.ty.includes_unresolved_type(), + "func decl param type is unresolved" + ); + } + TypedFuncParamKind::SelfModifier(self_modifier) => { + debug_assert!( + !self_modifier.ty.includes_unresolved_type(), + "func decl self_modifier type in unresolved" + ); + } + } + } + + debug_assert!( + !$func_decl.ret_type.includes_unresolved_type(), + "func decl return type is unresolved" + ); + }; +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum DeclID { Struct(StructDeclID), @@ -77,7 +104,7 @@ pub struct StructDecl { pub align: Option, pub loc: Loc, - pub is_normalized: bool + pub is_normalized: bool, } #[derive(Debug, Clone)] @@ -91,7 +118,7 @@ pub struct UnionDecl { pub align: Option, pub loc: Loc, - pub is_normalized: bool + pub is_normalized: bool, } #[derive(Debug, Clone)] @@ -106,7 +133,7 @@ pub struct EnumDecl { pub align: Option, pub loc: Loc, - pub is_normalized: bool + pub is_normalized: bool, } #[derive(Debug, Clone)] diff --git a/crates/cyrusc_typed_ast/src/stmts.rs b/crates/cyrusc_typed_ast/src/stmts.rs index 456f3dd34..9284e5bfd 100644 --- a/crates/cyrusc_typed_ast/src/stmts.rs +++ b/crates/cyrusc_typed_ast/src/stmts.rs @@ -829,8 +829,8 @@ impl TypedSwitchStmt { self.cases.iter().any(|case| { case.patterns.iter().any(|pattern| match &pattern.kind { TypedSwitchCasePatternKind::Expr(expr, ..) => { - let sema_type = expr.ty.as_ref().unwrap(); - sema_type.is_uint8() || sema_type.is_integer() + let ty = expr.ty.as_ref().unwrap(); + ty.is_uint8() || ty.is_integer() } _ => false, }) diff --git a/crates/cyrusc_typed_ast/src/substitute.rs b/crates/cyrusc_typed_ast/src/substitute.rs index 21405e44b..58027faf2 100644 --- a/crates/cyrusc_typed_ast/src/substitute.rs +++ b/crates/cyrusc_typed_ast/src/substitute.rs @@ -149,7 +149,7 @@ pub fn instantiate_struct_decl_with_type_args(struct_decl: &StructDecl, type_arg fields, impls: struct_decl.impls.clone(), methods: struct_decl.methods.clone(), - generic_params: struct_decl.generic_params.clone(), + generic_params: TypedGenericParams::new(), // clear generic params modifiers: struct_decl.modifiers.clone(), align: struct_decl.align, loc: struct_decl.loc, @@ -175,7 +175,7 @@ pub fn instantiate_union_decl_with_type_args(union_decl: &UnionDecl, type_args: fields, impls: union_decl.impls.clone(), methods: union_decl.methods.clone(), - generic_params: union_decl.generic_params.clone(), + generic_params: TypedGenericParams::new(), // clear generic params modifiers: union_decl.modifiers.clone(), align: union_decl.align, loc: union_decl.loc, @@ -234,7 +234,7 @@ pub fn instantiate_enum_decl_with_type_args(enum_decl: &EnumDecl, type_args: &Ty methods: enum_decl.methods.clone(), variants, impls: enum_decl.impls.clone(), - generic_params: enum_decl.generic_params.clone(), + generic_params: TypedGenericParams::new(), // clear generic params modifiers: enum_decl.modifiers.clone(), tag_type: enum_decl.tag_type.clone(), align: enum_decl.align, diff --git a/crates/cyrusc_typed_ast/src/types.rs b/crates/cyrusc_typed_ast/src/types.rs index e7f27569c..46d9638b2 100644 --- a/crates/cyrusc_typed_ast/src/types.rs +++ b/crates/cyrusc_typed_ast/src/types.rs @@ -230,6 +230,58 @@ impl UnresolvedType { } impl SemaType { + /// Returns true if this type contains `Unresolved` inside it. + pub fn includes_unresolved_type(&self) -> bool { + match self { + SemaType::Unresolved(_) => true, + + SemaType::Err(_) + | SemaType::Plain(_) + | SemaType::SelfType(_) + | SemaType::GenericParam(_) + | SemaType::InferVar(_) + | SemaType::Placeholder => false, + + SemaType::Named(named_type) => named_type.type_args.iter().any(|type_arg| match type_arg { + TypedTypeArg::Type(ty, _) => ty.includes_unresolved_type(), + TypedTypeArg::Infer => false, + }), + + SemaType::Const(inner) | SemaType::Pointer(inner) => inner.includes_unresolved_type(), + + SemaType::Array(array) => array.element_type.includes_unresolved_type(), + + SemaType::FuncType(func) => { + func.params.list.iter().any(|ty| ty.includes_unresolved_type()) + || func + .params + .variadic + .as_ref() + .map(|variadic| match &**variadic { + TypedFuncTypeVariadicParam::UntypedCStyle => false, + TypedFuncTypeVariadicParam::Typed(ty) => ty.includes_unresolved_type(), + }) + .unwrap_or(false) + || func.ret_type.includes_unresolved_type() + } + + SemaType::Tuple(tuple) => tuple.elements.iter().any(|(ty, _)| ty.includes_unresolved_type()), + + SemaType::InterfaceObject(interface_obj) => { + // Check both the interface type and concrete type + interface_obj + .interface_type + .type_args + .iter() + .any(|type_arg| match type_arg { + TypedTypeArg::Type(ty, _) => ty.includes_unresolved_type(), + TypedTypeArg::Infer => false, + }) + || interface_obj.concrete_type.includes_unresolved_type() + } + } + } + #[inline] pub fn as_unresolved_decl_id(&self) -> Option { match &self.const_inner() { diff --git a/selfhost/.gitignore b/selfhost/.gitignore new file mode 100644 index 000000000..e390b124a --- /dev/null +++ b/selfhost/.gitignore @@ -0,0 +1 @@ +build/** \ No newline at end of file diff --git a/selfhost/Makefile b/selfhost/Makefile new file mode 100644 index 000000000..dc689704f --- /dev/null +++ b/selfhost/Makefile @@ -0,0 +1,19 @@ +COMPILER ?= ../target/debug/cyrus +STDLIB ?= ../stdlib + +release: + $(COMPILER) build --profile=release \ + --stdlib=$(STDLIB) \ + --linker=clang \ + --optimize=o2 \ + -z="-flto" \ + --jobs 1 + +debug: + $(COMPILER) build --profile=debug \ + --sanitize=address -g \ + --stdlib=$(STDLIB) \ + --linker=clang \ + --optimize=o0 \ + --jobs 1 + \ No newline at end of file diff --git a/selfhost/Project.toml b/selfhost/Project.toml new file mode 100644 index 000000000..865b1cf43 --- /dev/null +++ b/selfhost/Project.toml @@ -0,0 +1,15 @@ +[project] +name = "cyrus" +version = "0.0.0" +type = "executable" + +[dependencies] +libraries = [] +library_path = [] + +[compiler] +cpu = "generic" +optimize = "o2" +sources = ["./src"] +version = "0.0.4" +build_dir = "./build" diff --git a/selfhost/src/diags/index.cyrus b/selfhost/src/diags/index.cyrus new file mode 100644 index 000000000..22c99914e --- /dev/null +++ b/selfhost/src/diags/index.cyrus @@ -0,0 +1,7 @@ +import std::libc{stderr, fprintf}; +import std::os{exit}; + +pub fn compile_error(msg: const uint8*) { + fprintf(stderr, "error: %s\n", msg); + exit(.Failure); +} \ No newline at end of file diff --git a/selfhost/src/main.cyrus b/selfhost/src/main.cyrus new file mode 100644 index 000000000..cd3f9c447 --- /dev/null +++ b/selfhost/src/main.cyrus @@ -0,0 +1,52 @@ +import std::mem::alloc::libc{LibcAllocator}; +import utils::file{read_file_content}; +import std::io{Writer, StringWriter}; +import diags{compile_error}; +import std::mem{Allocator}; +import std::string{String}; +import parse::lex{Lexer}; +import std::libc{printf}; + +import parse::lex::tokens{Token, Literal}; + +pub fn main(argc: int32, argv: uint8**) { + var allocator: Allocator = dynamic LibcAllocator.new(); + + var format = String.new(allocator); + defer format.free(); + + // var name = String.from_str(allocator, "Taha"); + // defer name.free(); + // const token = Token.Literal(Literal.String { + // value: name, + // suffix: .None + // }); + // const token = Token.Literal(Literal.Float { + // value: 3.14, + // suffix: .None + // }); + // const token = Token.Literal(Literal.Integer { + // value: .Signed(255), + // suffix: .Some(.ISize) + // }); + const token = Token.Literal(Literal.Null); + + var writer: Writer = dynamic StringWriter.new(&format); + + token.format(writer); + + printf("token: %s\n", format.as_str_const()); + + // const file_name = argv[1]; + + // if (!file_name) { + // compile_error("no input files"); + // } + + // var source = read_file_content(file_name); + // defer source.free(); + + // const lexer = Lexer.new(&source); + + // printf("%s\n", source.as_str_const()); +} \ No newline at end of file diff --git a/selfhost/src/parse/index.cyrus b/selfhost/src/parse/index.cyrus new file mode 100644 index 000000000..e69de29bb diff --git a/selfhost/src/parse/lex/index.cyrus b/selfhost/src/parse/lex/index.cyrus new file mode 100644 index 000000000..528468d6c --- /dev/null +++ b/selfhost/src/parse/lex/index.cyrus @@ -0,0 +1,40 @@ +import std::string{String}; +import std::collections{Vector}; + +import parse::lex::tokens{ + Token, + Literal, + IntegerSuffix, + FloatSuffix, + StringSuffix +}; + +pub struct Lexer { + source: String*, + pos: usize, + cur: uint8, + line: usize, + col: usize, + + pub fn new(source: String*) Self { + @assert(source, "source cannot be null"); + + const cur = source->as_str_const()[0]; + + return Self { + source, + cur, + pos: 0, + line: 1, + col: 1, + }; + } + + pub fn tokenize(&self) Vector { + @todo(); + } + + pub fn next(&self) { + @todo(); + } +} diff --git a/selfhost/src/parse/lex/tokens.cyrus b/selfhost/src/parse/lex/tokens.cyrus new file mode 100644 index 000000000..374dfe4a5 --- /dev/null +++ b/selfhost/src/parse/lex/tokens.cyrus @@ -0,0 +1,511 @@ +import std::string{String}; +import std::option{Option}; +import std::io{Writer}; +import std::format{ + Format, + format_int128, + format_uint128, + format_float64, + format_bool +}; + +pub enum Token : Format { + Ident(String), + Literal(Literal), + + // Operators + Plus, + Minus, + Slash, + Asterisk, + Percent, + Increment, + Decrement, + + // Symbols + ShiftRight, + ShiftLeft, + Caret, + AmpTilde, + Tilde, + Assign, + Equal, + NotEqual, + Bang, + LeftParen, + RightParen, + LeftBrace, + RightBrace, + LeftBracket, + RightBracket, + Comma, + ThinArrow, + FatArrow, + Dot, + DoubleDot, + TripleDot, + DoubleQuote, + SingleQuote, + Pipe, + Ampersand, + Semicolon, + Colon, + DoubleColon, + LessThan, + GreaterThan, + LessEqual, + GreaterEqual, + And, + Or, + At, + Underscore, + + // Keywords + Module, + Var, + Dynamic, + Goto, + Defer, + Union, + Interface, + Function, + Typedef, + Switch, + Case, + Default, + If, + Else, + Return, + For, + While, + Foreach, + Break, + Continue, + Struct, + Import, + In, + Enum, + True, + False, + Null, + As, + + // Types + UIntPtr, + IntPtr, + ISize, + USize, + Int, + Int8, + Int16, + Int32, + Int64, + Int128, + UInt, + UInt8, + UInt16, + UInt32, + UInt64, + UInt128, + Float16, + Float32, + Float64, + Float128, + Char, + Void, + Bool, + Const, + + // Modifiers + Weak, + LinkOnce, + Callconv, + Naked, + NoReturn, + Hot, + Cold, + Extern, + Public, + Inline, + NoInline, + AlwaysInline, + DllImport, + DllExport, + OptSize, + OptNone, + NoSanitize, + NoUnwind, + Section, + Repr, + + EOF, + + pub inline fn is_eof(&const self) bool { + switch (*self) { + case .EOF => return true; + default => return false; + } + } + + pub fn lookup_keyword(str: const uint8*) Option { + switch (str) { + case "dynamic" => return .Some(.Dynamic); + case "var" => return .Some(.Var); + case "mod" => return .Some(.Module); + case "interface" => return .Some(.Interface); + case "type" => return .Some(.Typedef); + case "fn" => return .Some(.Function); + case "switch" => return .Some(.Switch); + case "case" => return .Some(.Case); + case "default" => return .Some(.Default); + case "struct" => return .Some(.Struct); + case "import" => return .Some(.Import); + case "if" => return .Some(.If); + case "else" => return .Some(.Else); + case "return" => return .Some(.Return); + case "for" => return .Some(.For); + case "foreach" => return .Some(.Foreach); + case "break" => return .Some(.Break); + case "continue" => return .Some(.Continue); + case "true" => return .Some(.True); + case "false" => return .Some(.False); + case "null" => return .Some(.Null); + case "uintptr" => return .Some(.UIntPtr); + case "intptr" => return .Some(.IntPtr); + case "isize" => return .Some(.ISize); + case "usize" => return .Some(.USize); + case "int" => return .Some(.Int); + case "int8" => return .Some(.Int8); + case "int16" => return .Some(.Int16); + case "int32" => return .Some(.Int32); + case "int64" => return .Some(.Int64); + case "int128" => return .Some(.Int128); + case "uint" => return .Some(.UInt); + case "uint8" => return .Some(.UInt8); + case "uint16" => return .Some(.UInt16); + case "uint32" => return .Some(.UInt32); + case "uint64" => return .Some(.UInt64); + case "uint128" => return .Some(.UInt128); + case "float16" => return .Some(.Float16); + case "float32" => return .Some(.Float32); + case "float64" => return .Some(.Float64); + case "float128" => return .Some(.Float128); + case "char" => return .Some(.Char); + case "bool" => return .Some(.Bool); + case "void" => return .Some(.Void); + case "enum" => return .Some(.Enum); + case "in" => return .Some(.In); + case "as" => return .Some(.As); + case "extern" => return .Some(.Extern); + case "inline" => return .Some(.Inline); + case "pub" => return .Some(.Public); + case "const" => return .Some(.Const); + case "weak" => return .Some(.Weak); + case "linkonce" => return .Some(.LinkOnce); + case "callconv" => return .Some(.Callconv); + case "naked" => return .Some(.Naked); + case "noreturn" => return .Some(.NoReturn); + case "hot" => return .Some(.Hot); + case "cold" => return .Some(.Cold); + case "dllimport" => return .Some(.DllImport); + case "dllexport" => return .Some(.DllExport); + case "optsize" => return .Some(.OptSize); + case "optnone" => return .Some(.OptNone); + case "no_sanitize" => return .Some(.NoSanitize); + case "nounwind" => return .Some(.NoUnwind); + case "section" => return .Some(.Section); + case "repr" => return .Some(.Repr); + case "goto" => return .Some(.Goto); + case "defer" => return .Some(.Defer); + case "union" => return .Some(.Union); + case "while" => return .Some(.While); + case "noinline" => return .Some(.NoInline); + case "alwaysinline" => return .Some(.AlwaysInline); + } + + return .None; + } + + pub fn format(&const self, writer: Writer) bool { + switch (*self) { + case .Ident(ident) => writer.write(ident.as_str_const()); + case .Literal(lit) => lit.format(writer); + + case .Underscore => writer.write("_"); + case .Plus => writer.write("+"); + case .Minus => writer.write("-"); + case .Asterisk => writer.write("*"); + case .Slash => writer.write("/"); + case .Percent => writer.write("%"); + case .Assign => writer.write("="); + case .Equal => writer.write("=="); + case .LeftParen => writer.write("("); + case .RightParen => writer.write(")"); + case .LeftBrace => writer.write("{"); + case .RightBrace => writer.write("}"); + case .LeftBracket => writer.write("["); + case .RightBracket => writer.write("]"); + case .At => writer.write("@"); + case .Comma => writer.write(","); + case .DoubleDot => writer.write(".."); + case .TripleDot => writer.write("..."); + case .Dot => writer.write("."); + case .DoubleQuote => writer.write("\""); + case .SingleQuote => writer.write("'"); + case .Pipe => writer.write("|"); + case .Ampersand => writer.write("&"); + case .LessThan => writer.write("<"); + case .GreaterThan => writer.write(">"); + case .LessEqual => writer.write("<="); + case .GreaterEqual => writer.write(">="); + case .And => writer.write("&&"); + case .Or => writer.write("||"); + case .Semicolon => writer.write(";"); + case .Colon => writer.write(":"); + case .ThinArrow => writer.write("->"); + case .FatArrow => writer.write("=>"); + case .Dynamic => writer.write("dynamic"); + case .Var => writer.write("var"); + case .Module => writer.write("mod"); + case .Interface => writer.write("interface"); + case .Typedef => writer.write("type"); + case .Function => writer.write("fn"); + case .Switch => writer.write("switch"); + case .Case => writer.write("case"); + case .Default => writer.write("default"); + case .Struct => writer.write("struct"); + case .Import => writer.write("import"); + case .If => writer.write("if"); + case .Else => writer.write("else"); + case .Return => writer.write("return"); + case .For => writer.write("for"); + case .Foreach => writer.write("foreach"); + case .Break => writer.write("break"); + case .Continue => writer.write("continue"); + case .True => writer.write("true"); + case .False => writer.write("false"); + case .Null => writer.write("null"); + case .UIntPtr => writer.write("uintptr"); + case .IntPtr => writer.write("intptr"); + case .ISize => writer.write("isize"); + case .USize => writer.write("usize"); + case .Int => writer.write("int"); + case .Int8 => writer.write("int8"); + case .Int16 => writer.write("int16"); + case .Int32 => writer.write("int32"); + case .Int64 => writer.write("int64"); + case .Int128 => writer.write("int128"); + case .UInt => writer.write("uint"); + case .UInt8 => writer.write("uint8"); + case .UInt16 => writer.write("uint16"); + case .UInt32 => writer.write("uint32"); + case .UInt64 => writer.write("uint64"); + case .UInt128 => writer.write("uint128"); + case .Float16 => writer.write("float16"); + case .Float32 => writer.write("float32"); + case .Float64 => writer.write("float64"); + case .Float128 => writer.write("float128"); + case .Char => writer.write("char"); + case .Bool => writer.write("bool"); + case .Void => writer.write("void"); + case .Enum => writer.write("enum"); + case .In => writer.write("in"); + case .As => writer.write("as"); + case .Extern => writer.write("extern"); + case .Inline => writer.write("inline"); + case .Public => writer.write("pub"); + case .Const => writer.write("const"); + case .Weak => writer.write("weak"); + case .LinkOnce => writer.write("linkonce"); + case .Callconv => writer.write("callconv"); + case .Naked => writer.write("naked"); + case .NoReturn => writer.write("noreturn"); + case .Hot => writer.write("hot"); + case .Cold => writer.write("cold"); + case .DllImport => writer.write("dllimport"); + case .DllExport => writer.write("dllexport"); + case .OptSize => writer.write("optsize"); + case .OptNone => writer.write("optnone"); + case .NoSanitize => writer.write("no_sanitize"); + case .NoUnwind => writer.write("nounwind"); + case .Section => writer.write("section"); + case .Repr => writer.write("repr"); + case .Increment => writer.write("++"); + case .Decrement => writer.write("--"); + case .ShiftRight => writer.write(">>"); + case .ShiftLeft => writer.write("<<"); + case .Caret => writer.write("^"); + case .AmpTilde => writer.write("&~"); + case .Tilde => writer.write("~"); + case .NotEqual => writer.write("!="); + case .Bang => writer.write("!"); + case .DoubleColon => writer.write("::"); + case .Goto => writer.write("goto"); + case .Defer => writer.write("defer"); + case .Union => writer.write("union"); + case .While => writer.write("while"); + case .NoInline => writer.write("noinline"); + case .AlwaysInline => writer.write("alwaysinline"); + + case .EOF => writer.write("EOF"); + } + return true; + } +} + +pub enum Literal : Format { + Integer { + value: enum { + Signed(int128), + Unsigned(uint128) + }, + suffix: Option + }, + Float { + value: float64, + suffix: Option + }, + String { + value: String, + suffix: Option + }, + Bool(bool), + Null, + + pub fn format(&const self, writer: Writer) bool { + switch (*self) { + case .Integer { value, suffix } => { + switch (value) { + case .Signed(x) => format_int128(writer, x); + case .Unsigned(x) => format_uint128(writer, x); + } + switch (suffix) { + case .Some(s) => writer.write(s.to_str()); + } + } + case .Float { value, suffix } => { + format_float64(writer, value); + switch (suffix) { + case .Some(s) => writer.write(s.to_str()); + } + } + case .String { value, suffix } => { + switch (suffix) { + case .Some(s) => writer.write(s.to_str()); + } + writer.write("\""); + writer.write(value.as_str_const()); + writer.write("\""); + } + case .Bool(value) => { + format_bool(writer, value); + } + case .Null => { + writer.write("null"); + } + } + return true; + } +} + +pub enum IntegerSuffix { + ISize, + USize, + Int, + Int8, + Int16, + Int32, + Int64, + Int128, + UInt, + UInt8, + UInt16, + UInt32, + UInt64, + UInt128, + Char, + + pub fn from_str(str: const uint8*) Option { + switch (str) { + case "isize" => return .Some(.ISize); + case "usize" => return .Some(.USize); + case "int" => return .Some(.Int); + case "int8" => return .Some(.Int8); + case "int16" => return .Some(.Int16); + case "int32" => return .Some(.Int32); + case "int64" => return .Some(.Int64); + case "int128" => return .Some(.Int128); + case "uint" => return .Some(.UInt); + case "uint8" => return .Some(.UInt8); + case "uint16" => return .Some(.UInt16); + case "uint32" => return .Some(.UInt32); + case "uint64" => return .Some(.UInt64); + case "uint128" => return .Some(.UInt128); + case "char" => return .Some(.Char); + default => return .None; + } + } + + pub fn to_str(const self) const uint8* { + switch (self) { + case .ISize => return "isize"; + case .USize => return "usize"; + case .Int => return "int"; + case .Int8 => return "int8"; + case .Int16 => return "int16"; + case .Int32 => return "int32"; + case .Int64 => return "int64"; + case .Int128 => return "int128"; + case .UInt => return "uint"; + case .UInt8 => return "uint8"; + case .UInt16 => return "uint16"; + case .UInt32 => return "uint32"; + case .UInt64 => return "uint64"; + case .UInt128 => return "uint128"; + case .Char => return "char"; + } + } +} + +pub enum FloatSuffix { + Float16, + Float32, + Float64, + Float128, + + pub fn from_str(str: const uint8*) Option { + switch (str) { + case "float16" => return .Some(.Float16); + case "float32" => return .Some(.Float32); + case "float64" => return .Some(.Float64); + case "float128" => return .Some(.Float128); + default => return .None; + } + } + + pub fn to_str(const self) const uint8* { + switch (self) { + case .Float16 => return "float16"; + case .Float32 => return "float32"; + case .Float64 => return "float64"; + case .Float128 => return "float128"; + } + } +} + +pub enum StringSuffix { + Byte, + + pub fn from_str(str: const uint8*) Option { + switch (str) { + case "b" => return .Some(.Byte); + default => return .None; + } + } + + pub fn to_str(&const self) const uint8* { + switch (*self) { + case .Byte => return "b"; + } + } +} \ No newline at end of file diff --git a/selfhost/src/utils/file.cyrus b/selfhost/src/utils/file.cyrus new file mode 100644 index 000000000..2d4749a45 --- /dev/null +++ b/selfhost/src/utils/file.cyrus @@ -0,0 +1,34 @@ +import std::mem::alloc::libc{LibcAllocator}; +import std::mem{Allocator}; +import std::string{String}; +import std::os{File}; +import std::path{Path}; + +import diags{compile_error}; + +pub fn read_file_content(const file_name: const uint8*) String { + const allocator: Allocator = dynamic LibcAllocator.new(); + + var path = Path.new(allocator, file_name); + defer path.free(); + + var file: File = undefined; + switch (File.open(&path, .Read)) { + case .Value(_file) => file = _file; + case .Error(_) => { + compile_error("failed to open source file"); + @unreachable(); + } + } + defer file.close(); + + const result = file.read(allocator); + + switch (result) { + case .Value(content) => return content; + case .Error(_) => { + compile_error("failed to read source file"); + @unreachable(); + } + } +} \ No newline at end of file diff --git a/stdlib/collections/index.cyrus b/stdlib/collections/index.cyrus index fce979f44..630bb38bf 100644 --- a/stdlib/collections/index.cyrus +++ b/stdlib/collections/index.cyrus @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2026 The Cyrus Language -import std::libc{memmove, malloc, printf}; +import std::libc{memmove, malloc}; import std::mem{Allocator}; import std::option{Option}; import std::iterator{Iterator}; @@ -15,13 +15,9 @@ pub struct Vector { len: usize, /// Create a new array using a specific allocator. - pub inline fn new(const allocator: Allocator) Self { + pub inline fn new(allocator: Allocator) Self { const cap = initial_array_capacity; - - // FIXME: Using allocator.alloc instead of malloc causes a crash in the test suite. Need to investigate why. - // const data: T* = allocator.alloc(cap * @sizeof(T)); - const data: T* = malloc(cap * @sizeof(T)); - + const data: T* = allocator.alloc(cap * @sizeof(T)); if (!data) { @panic("allocation failed"); } @@ -35,13 +31,10 @@ pub struct Vector { } /// Pre-allocate memory to avoid initial reallocations. - pub inline fn with_capacity(const allocator: Allocator, const cap: usize) Self { + pub inline fn with_capacity(allocator: Allocator, const cap: usize) Self { @assert(cap != 0, "initial capacity cannot be zero"); - // FIXME: Using allocator.alloc instead of malloc causes a crash in the test suite. Need to investigate why. - // const data: T* = allocator.alloc(cap * @sizeof(T)); - const data: T* = malloc(cap * @sizeof(T)); - + const data: T* = allocator.alloc(cap * @sizeof(T)); if (data == null) { @panic("initial allocation failed"); } @@ -133,10 +126,10 @@ pub struct Vector { if (self->len == 0) { @panic("cannot pop from empty array"); } - + + const ch = self->data[self->len]; self->len--; - - return self->data[self->len]; + return ch; } pub inline fn insert(&self, index: usize, value: T) { diff --git a/stdlib/format.cyrus b/stdlib/format.cyrus new file mode 100644 index 000000000..309ec6e8a --- /dev/null +++ b/stdlib/format.cyrus @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2026 The Cyrus Language + +import std::io{Writer, IOError}; +import std::libc{snprintf}; + +const INT32_BUF_SIZE: usize = 12; +const UINT32_BUF_SIZE: usize = 11; +const INT64_BUF_SIZE: usize = 21; +const UINT64_BUF_SIZE: usize = 21; +const INT128_BUF_SIZE: usize = 41; +const UINT128_BUF_SIZE: usize = 40; +const FLOAT16_BUF_SIZE: usize = 48; +const FLOAT32_BUF_SIZE: usize = 48; +const FLOAT64_BUF_SIZE: usize = 64; +const FLOAT128_BUF_SIZE: usize = 5000; + +pub interface Format { + fn format(&const self, writer: Writer) bool; +} + +pub fn format_bool(writer: Writer, x: bool) { + switch (x) { + case true => writer.write("true"); + case false => writer.write("false"); + } +} + +// NOTE: These integer formatters here are temporary, +// we expect to implement more sophisticated formatter after +// implementing comptime and reflection. + +pub fn format_int32(writer: Writer, x: int32) { + var buffer: uint8[INT32_BUF_SIZE]; + snprintf(&buffer[0], INT32_BUF_SIZE, "%d", x); + writer.write(&buffer[0]); +} + +pub fn format_uint32(writer: Writer, x: uint32) { + var buffer: uint8[UINT32_BUF_SIZE]; + snprintf(&buffer[0], UINT32_BUF_SIZE, "%u", x); + writer.write(&buffer[0]); +} + +pub fn format_int64(writer: Writer, x: int64) { + var buffer: uint8[INT64_BUF_SIZE]; + snprintf(&buffer[0], INT64_BUF_SIZE, "%ld", x); + writer.write(&buffer[0]); +} + +pub fn format_uint64(writer: Writer, x: uint64) { + var buffer: uint8[UINT64_BUF_SIZE]; + snprintf(&buffer[0], UINT64_BUF_SIZE, "%lu", x); + writer.write(&buffer[0]); +} + +pub fn format_int128(writer: Writer, x: int128) { + var buffer: uint8[INT128_BUF_SIZE]; + snprintf(&buffer[0], INT128_BUF_SIZE, "%lld", x); + writer.write(&buffer[0]); +} + +pub fn format_uint128(writer: Writer, x: uint128) { + var buffer: uint8[UINT128_BUF_SIZE]; + snprintf(&buffer[0], UINT128_BUF_SIZE, "%llu", x); + writer.write(&buffer[0]); +} + +pub fn format_float16(writer: Writer, x: float16) { + var buffer: uint8[FLOAT16_BUF_SIZE]; + snprintf(&buffer[0], FLOAT16_BUF_SIZE, "%g", x); + writer.write(&buffer[0]); +} + +pub fn format_float32(writer: Writer, x: float32) { + var buffer: uint8[FLOAT32_BUF_SIZE]; + snprintf(&buffer[0], FLOAT32_BUF_SIZE, "%g", x); + writer.write(&buffer[0]); +} + +pub fn format_float64(writer: Writer, x: float64) { + var buffer: uint8[FLOAT64_BUF_SIZE]; + snprintf(&buffer[0], FLOAT64_BUF_SIZE, "%g", x); + writer.write(&buffer[0]); +} + +// FIXME: UNSUPPORTED YET (#407) +// +// pub fn format_float128(writer: Writer, x: float128) { +// var buffer: uint8[FLOAT128_BUF_SIZE]; +// snprintf(&buffer[0], @sizeof(x), "%Lf", x); +// writer.write(&buffer[0]); +// } + +// TODO: After implementing comptime and reflection features +// we should add auto-format helpers here for sructs, unions, enums, +// tuples, and values. \ No newline at end of file diff --git a/stdlib/io.cyrus b/stdlib/io.cyrus index a2c0bb8e3..ce65d8aa3 100755 --- a/stdlib/io.cyrus +++ b/stdlib/io.cyrus @@ -1,2 +1,105 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2026 The Cyrus Language + +import std::collections{Vector}; +import std::errors{ErrorOr}; +import std::string{String}; + +pub type IOError = ErrorOr<(), ()>; + +pub interface Writer { + fn write_all(&self, data: const uint8*) IOError; + fn write(&self, data: const uint8*) IOError; + fn writeln(&self, data: const uint8*) IOError; + fn flush(&self) IOError; + fn len(&const self) usize; +} + +pub struct StringWriter : Writer { + buffer: String*, + + pub fn new(const buffer: String*) Self { + @assert(buffer, "buffer cannot be null"); + return Self { buffer }; + } + + pub fn to_str(&const self) String* { + return self->buffer; + } + + pub fn write_all(&self, data: const uint8*) IOError { + @assert(data, "data cannot be null"); + self->buffer->clear(); + self->buffer->push_str(data); + return .Value(()); + } + + pub fn write(&self, data: const uint8*) IOError { + @assert(data, "data cannot be null"); + self->buffer->push_str(data); + return .Value(()); + } + + pub fn writeln(&self, data: const uint8*) IOError { + @assert(data, "data cannot be null"); + self->buffer->push_str(data); + self->buffer->push('\n'); + return .Value(()); + } + + pub fn flush(&self) IOError { + return .Value(()); + } + + pub fn len(&self) usize { + return self->buffer->len(); + } +} + +pub struct MultiWriter { + writers: Vector, + + pub fn new(writers: Vector) Self { + return Self { writers }; + } + + pub fn write_all(&self, data: const uint8*) IOError { + @assert(data, "data cannot be null"); + for (var i: usize = 0; i < self->writers.len(); i++) { + var writer = self->writers.get(i).unwrap(); + writer.write_all(data); + } + return .Value(()); + } + + pub fn write(&self, data: const uint8*) IOError { + @assert(data, "data cannot be null"); + for (var i: usize = 0; i < self->writers.len(); i++) { + var writer = self->writers.get(i).unwrap(); + writer.write(data); + } + return .Value(()); + } + + pub fn writeln(&self, data: const uint8*) IOError { + @assert(data, "data cannot be null"); + for (var i: usize = 0; i < self->writers.len(); i++) { + var writer = self->writers.get(i).unwrap(); + writer.writeln(data); + } + return .Value(()); + } + + pub fn flush(&self) IOError { + return .Value(()); + } + + pub fn len(&self) usize { + var sum: usize = 0; + for (var i: usize = 0; i < self->writers.len(); i++) { + const writer = self->writers.get(i).unwrap(); + sum += writer.len(); + } + return sum; + } +} \ No newline at end of file diff --git a/stdlib/math/checked.cyrus b/stdlib/math/checked.cyrus index fd474f7d7..c6bc3cdd1 100644 --- a/stdlib/math/checked.cyrus +++ b/stdlib/math/checked.cyrus @@ -5,9 +5,11 @@ import std::option{Option}; pub fn checked_add(const a: T, const b: T) Option { const is_signed = @cast(T, -1) < @cast(T, 0); + if (is_signed) { const min = @cast(T, 1) << @cast(T, @sizeof(T) * 8 - 1); const max = ~min; + if (b > @cast(T, 0) && a > max - b) { return .None; } @@ -19,14 +21,17 @@ pub fn checked_add(const a: T, const b: T) Option { return .None; } } + return .Some(a + b); } pub fn checked_sub(const a: T, const b: T) Option { const is_signed = @cast(T, -1) < @cast(T, 0); + if (is_signed) { const min = @cast(T, 1) << @cast(T, @sizeof(T) * 8 - 1); const max = ~min; + if (b > @cast(T, 0) && a < min + b) { return .None; } @@ -38,14 +43,17 @@ pub fn checked_sub(const a: T, const b: T) Option { return .None; } } + return .Some(a - b); } pub fn checked_mul(const a: T, const b: T) Option { const is_signed = @cast(T, -1) < @cast(T, 0); + if (is_signed) { const min = @cast(T, 1) << @cast(T, @sizeof(T) * 8 - 1); const max = ~min; + if (a > @cast(T, 0)) { if (b > @cast(T, 0)) { if (a > max / b) { @@ -72,5 +80,6 @@ pub fn checked_mul(const a: T, const b: T) Option { return .None; } } + return .Some(a * b); } diff --git a/stdlib/os/index.cyrus b/stdlib/os/index.cyrus index 14e948ed1..126d35781 100644 --- a/stdlib/os/index.cyrus +++ b/stdlib/os/index.cyrus @@ -45,7 +45,7 @@ pub struct File { return result == 0; } - pub fn read(&const self, const allocator: Allocator) ErrorOr { + pub fn read(&const self, allocator: Allocator) ErrorOr { libc::fseek(self->file, 0, FileSeek.End.as_num()); const size = @cast(usize, libc::ftell(self->file)); libc::rewind(self->file); @@ -115,7 +115,7 @@ pub struct File { } } - pub fn rename(const old_path: const Path*, const new_path: const Path*) ErrorOr<(), ()s> { + pub fn rename(const old_path: const Path*, const new_path: const Path*) ErrorOr<(), ()> { const old_path_str = old_path->as_string_const().as_str_const(); const new_path_str = new_path->as_string_const().as_str_const(); diff --git a/stdlib/random.cyrus b/stdlib/random.cyrus index d5039b625..4933a3f74 100644 --- a/stdlib/random.cyrus +++ b/stdlib/random.cyrus @@ -10,7 +10,6 @@ pub struct Random { // Static method that creates an instance of 'Random' struct // and uses a UNIX time seed for first number pub fn new() Self { - var init_seed_isize = time(null); var init_seed = @cast(uint64, init_seed_isize); @@ -53,27 +52,21 @@ pub struct Random { // Generates a random float64 number in a given range pub fn rand_float(&self, max: float64, min: float64) float64 { - var floated_rand = self->rand_to_float(); - return min + floated_rand * (max - min); } // Generates a random upperclass character pub fn rand_upperc(&self) uint8 { var rnd = self->rand_int(0, @cast(int64, 'Z') - @cast(int64, 'A')); - var char_index = @cast(int64, 'A') + rnd; - return @cast(uint8, char_index); } // Generates a random lowerclass character pub fn rand_lowerc(&self) uint8 { var rnd = self->rand_int(0, @cast(int64, 'z') - @cast(int64, 'a')); - var char_index = @cast(int64, 'a') + rnd; - return @cast(uint8, char_index); } diff --git a/stdlib/string.cyrus b/stdlib/string.cyrus index 7ae78a743..729cf0f55 100644 --- a/stdlib/string.cyrus +++ b/stdlib/string.cyrus @@ -12,17 +12,13 @@ pub struct String { pub fn new(const allocator: Allocator) Self { var vec: Vector = Vector.new(allocator); - vec.push('\0'); // null-terminated - return Self { vec }; } pub fn with_capacity(const allocator: Allocator, const cap: usize) Self { var vec: Vector = Vector.with_capacity(allocator, cap); - - vec.push('\0'); - + vec.push('\0'); // null-terminated return Self { vec }; } @@ -32,13 +28,11 @@ pub struct String { const len = strlen(str); var vec: Vector = Vector.new(allocator); - for (var i: usize = 0; i < len; i++) { vec.push(str[i]); } - vec.push('\0'); - + return Self { vec }; } @@ -57,7 +51,7 @@ pub struct String { } pub fn reserve(&self, const extra: usize) { - const required = self->len() + extra + 1; + const required = self->len() + extra; if (required > self->cap()) { self->vec.reserve(required); @@ -67,25 +61,20 @@ pub struct String { pub fn push(&self, const ch: uint8) { var data = self->vec.data(); var current = &data[self->len()]; - *current = ch; - self->vec.push('\0'); } - pub fn push_str(&self, const str: uint8*) { + pub fn push_str(&self, const str: const uint8*) { @assert(str, "input string cannot be null"); const len = strlen(str); - self->vec.pop(); - self->reserve(len); - + self->vec.pop(); for (var i: usize = 0; i < len; i++) { self->vec.push(str[i]); - } - + } self->vec.push('\0'); } diff --git a/tests/issues/#402.cyrus b/tests/issues/#402.cyrus new file mode 100644 index 000000000..e9d196e51 --- /dev/null +++ b/tests/issues/#402.cyrus @@ -0,0 +1,39 @@ +// @stdout: bar, baz + +import std::libc{printf}; + +enum Option { + Some(T), + None +} + +enum Foo { + Bar, + Baz, + + pub fn bar() Self { + return .Bar; + } + + pub fn get_opt() Option { + return .Some(.Baz); + } +} + +pub fn main() { + const bar = Foo.bar(); + switch (bar) { + case .Bar => printf("bar, "); + } + + const foo = Foo.get_opt(); + switch (foo) { + case .Some(x) => { + switch (x) { + case .Baz => { + printf("baz\n"); + } + } + } + } +} \ No newline at end of file diff --git a/tests/std/format/format_bool.cyrus b/tests/std/format/format_bool.cyrus new file mode 100644 index 000000000..4b94cebdc --- /dev/null +++ b/tests/std/format/format_bool.cyrus @@ -0,0 +1,22 @@ +// @stdout: true, false + +import std::mem::alloc::libc{LibcAllocator}; +import std::format{Format, format_bool}; +import std::io{Writer, StringWriter}; +import std::string{String}; +import std::mem{Allocator}; +import std::libc{printf}; + +pub fn main() { + var allocator: Allocator = dynamic LibcAllocator.new(); + + var buffer = String.new(allocator); + defer buffer.free(); + + var writer: Writer = dynamic StringWriter.new(&buffer); + + format_bool(writer, true); + writer.write(", "); + format_bool(writer, false); + printf("%s\n", buffer.as_str()); +} \ No newline at end of file diff --git a/tests/std/format/format_enum.cyrus b/tests/std/format/format_enum.cyrus new file mode 100644 index 000000000..85cf530ef --- /dev/null +++ b/tests/std/format/format_enum.cyrus @@ -0,0 +1,53 @@ +// @stdout: (10, 20, 30) + +import std::mem::alloc::libc{LibcAllocator}; +import std::io{Writer, StringWriter}; +import std::libc{printf, snprintf}; +import std::format{Format}; +import std::mem{Allocator}; +import std::string{String}; + +enum Color : Format { + Red, + Blue, + Custom(int32, int32, int32) + + pub fn format(&const self, writer: Writer) bool { + switch (*self) { + case .Red => writer.write_all("red"); + case .Blue => writer.write_all("blue"); + case .Custom(a, b, c) => { + var buffer: uint8[@sizeof(a)]; + + writer.write("("); + snprintf(&buffer[0], @sizeof(a), "%d", a); + writer.write(&buffer[0]); + writer.write(","); + snprintf(&buffer[0], @sizeof(a), " %d", b); + writer.write(&buffer[0]); + writer.write(","); + snprintf(&buffer[0], @sizeof(a), " %d", c); + writer.write(&buffer[0]); + writer.write(")"); + } + } + return true; + } +} + +pub fn main() { + var allocator: Allocator = dynamic LibcAllocator.new(); + + var buffer = String.new(allocator); + defer buffer.free(); + + var writer: Writer = dynamic StringWriter.new(&buffer); + + const color = Color.Custom(10, 20, 30); + + if (!color.format(writer)) { + @panic("format failed"); + } + + printf("%s\n", buffer.as_str()); +} \ No newline at end of file diff --git a/tests/std/format/format_float.cyrus b/tests/std/format/format_float.cyrus new file mode 100644 index 000000000..dcd5022db --- /dev/null +++ b/tests/std/format/format_float.cyrus @@ -0,0 +1,33 @@ +// @stdout: 1.2002, 3.155, 5.5555 + +import std::mem::alloc::libc{LibcAllocator}; +import std::io{Writer, StringWriter}; +import std::string{String}; +import std::mem{Allocator}; +import std::libc{printf}; +import std::format{ + Format, + format_float16, + format_float32, + format_float64, + // format_float128, +}; + +pub fn main() { + var allocator: Allocator = dynamic LibcAllocator.new(); + + var buffer = String.new(allocator); + defer buffer.free(); + + var writer: Writer = dynamic StringWriter.new(&buffer); + + format_float16(writer, 1.2); + writer.write(", "); + format_float32(writer, 3.155); + writer.write(", "); + format_float64(writer, 5.5555); + // writer.write(", "); + // FIXME + // format_float128(writer, 1234567.12345678); + printf("%s\n", buffer.as_str()); +} \ No newline at end of file diff --git a/tests/std/format/format_number.cyrus b/tests/std/format/format_number.cyrus new file mode 100644 index 000000000..1b924dcb7 --- /dev/null +++ b/tests/std/format/format_number.cyrus @@ -0,0 +1,32 @@ +// @stdout: 10, 20, 30, 40 + +import std::mem::alloc::libc{LibcAllocator}; +import std::io{Writer, StringWriter}; +import std::string{String}; +import std::mem{Allocator}; +import std::libc{printf}; +import std::format{ + Format, + format_int32, + format_uint32, + format_int64, + format_uint64, +}; + +pub fn main() { + var allocator: Allocator = dynamic LibcAllocator.new(); + + var buffer = String.new(allocator); + defer buffer.free(); + + var writer: Writer = dynamic StringWriter.new(&buffer); + + format_int32(writer, 10); + writer.write(", "); + format_uint32(writer, 20); + writer.write(", "); + format_int64(writer, 30); + writer.write(", "); + format_uint64(writer, 40); + printf("%s\n", buffer.as_str()); +} \ No newline at end of file diff --git a/tests/std/io/multi_writer.cyrus b/tests/std/io/multi_writer.cyrus new file mode 100644 index 000000000..709688640 --- /dev/null +++ b/tests/std/io/multi_writer.cyrus @@ -0,0 +1,35 @@ +// @stdout: Cyrus The Great + +import std::mem::alloc::libc{LibcAllocator}; +import std::io{Writer, StringWriter, MultiWriter}; +import std::collections{Vector}; +import std::mem{Allocator}; +import std::string{String}; +import std::libc{printf}; + +pub fn main() { + var allocator: Allocator = dynamic LibcAllocator.new(); + + var buffer1 = String.new(allocator); + defer buffer1.free(); + + var buffer2 = String.new(allocator); + defer buffer2.free(); + + var writer1 = StringWriter.new(&buffer1); + var writer2 = StringWriter.new(&buffer2); + + var writers: Vector = Vector.new(allocator); + defer writers.free(); + writers.push(dynamic writer1); + writers.push(dynamic writer2); + + var multi_writer = MultiWriter.new(writers); + multi_writer.write("Cyrus "); + multi_writer.write("The "); + multi_writer.write("Great"); + + @assert(buffer1.as_str() == buffer2.as_str(), "buffers must be equal"); + + printf("%s\n", buffer1.as_str()); +} \ No newline at end of file diff --git a/tests/std/io/string_writer.cyrus b/tests/std/io/string_writer.cyrus new file mode 100644 index 000000000..c408c5f63 --- /dev/null +++ b/tests/std/io/string_writer.cyrus @@ -0,0 +1,23 @@ +// @stdout: Hello World, 11, Cyrus, 5 + +import std::mem::alloc::libc{LibcAllocator}; +import std::io{StringWriter}; +import std::mem{Allocator}; +import std::string{String}; +import std::libc{printf}; + +pub fn main() { + var allocator: Allocator = dynamic LibcAllocator.new(); + + var buffer = String.new(allocator); + defer buffer.free(); + + var writer = StringWriter.new(&buffer); + writer.write("Hello World"); + printf("%s, ", buffer.as_str()); + printf("%lu, ", writer.len()); + + writer.write_all("Cyrus"); + printf("%s, ", buffer.as_str()); + printf("%lu\n", writer.len()); +} \ No newline at end of file diff --git a/tests/std/string/new.cyrus b/tests/std/string/new.cyrus index a6f438692..05ce2b421 100644 --- a/tests/std/string/new.cyrus +++ b/tests/std/string/new.cyrus @@ -1,4 +1,4 @@ -// @stdout: meow +// @stdout: empty import std::mem::alloc::libc{LibcAllocator}; import std::mem{Allocator}; @@ -12,6 +12,6 @@ pub fn main() { defer name.free(); if (name.len() == 0) { - printf("meow\n"); + printf("empty\n"); } } \ No newline at end of file diff --git a/tests/std/string/push.cyrus b/tests/std/string/push.cyrus new file mode 100644 index 000000000..a26cd94d8 --- /dev/null +++ b/tests/std/string/push.cyrus @@ -0,0 +1,18 @@ +// @stdout: Cyrus + +import std::mem::alloc::libc{LibcAllocator}; +import std::mem{Allocator}; +import std::string{String}; +import std::libc{printf}; + +pub fn main() { + const allocator: Allocator = dynamic LibcAllocator.new(); + + var name = String.new(allocator); + defer name.free(); + + name.push_str("Cyru"); + name.push('s'); + + printf("%s\n", name.as_str()); +} \ No newline at end of file diff --git a/tests/tuples/tuple_trailing_comma.cyrus b/tests/tuples/tuple_trailing_comma.cyrus new file mode 100644 index 000000000..f934746f0 --- /dev/null +++ b/tests/tuples/tuple_trailing_comma.cyrus @@ -0,0 +1,11 @@ +// @stdout: 1 1 2 1 2 3 + +import std::libc{printf}; + +pub fn main() { + const a = (1, ); + const b = (1, 2, ); + const c = (1, 2, 3, ); + + printf("%d %d %d %d %d %d\n", a.0, b.0, b.1, c.0, c.1, c.2); +} \ No newline at end of file