From 72316799ba564b1ac4dfa53d51b41ce22e3bb96b Mon Sep 17 00:00:00 2001 From: kamome <2038975825@qq.com> Date: Tue, 18 Apr 2023 13:42:35 +0800 Subject: [PATCH 1/3] refactor: better private field --- Cargo.lock | 7 ++ Cargo.toml | 1 + src/ast/builder/llvmbuilder.rs | 16 ++--- src/ast/builder/mod.rs | 4 +- src/ast/builder/no_op_builder.rs | 2 +- src/ast/ctx.rs | 2 +- src/ast/node/interface.rs | 48 ++------------ src/ast/node/types.rs | 47 +++++--------- src/ast/pltype.rs | 108 ++++++++++++++++++++----------- 9 files changed, 116 insertions(+), 119 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3890a1d04..d5f01b99c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -989,6 +989,12 @@ dependencies = [ "cc", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.3.4" @@ -1261,6 +1267,7 @@ dependencies = [ "internal_macro", "kagari", "lazy_static", + "linked-hash-map", "llvm-sys", "log", "lsp-server", diff --git a/Cargo.toml b/Cargo.toml index fde0bbe8c..e67aa8c48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ pl_linker = { path = "./pl_linker", optional = true } immix = { path = "./immix", optional = true, features = ["llvm_gc_plugin", "llvm_stackmap"] } vm = { path = "./vm", optional = true, features = ["jit"] } indexmap = "1.9" +linked-hash-map = "0.5.6" lazy_static = "1.4" paste = "1.0" internal_macro = { path = "./internal_macro", default-features = false } diff --git a/src/ast/builder/llvmbuilder.rs b/src/ast/builder/llvmbuilder.rs index 95a81efb5..eb24138fd 100644 --- a/src/ast/builder/llvmbuilder.rs +++ b/src/ast/builder/llvmbuilder.rs @@ -819,8 +819,8 @@ impl<'a, 'ctx> LLVMBuilder<'a, 'ctx> { let mut m = vec![]; ctx.run_in_type_mod(x, |ctx, x| { m = x - .ordered_fields - .iter() + .get_all_field() + .values() .map(|v| { let offset = td.offset_of_element(&sttp, v.index).unwrap() * 8; let (tp, _) = self.get_field_di_type(v, ctx, offset); @@ -957,9 +957,8 @@ impl<'a, 'ctx> LLVMBuilder<'a, 'ctx> { ctx.run_in_type_mod(pltp, |ctx, pltp| { st.set_body( &pltp - .ordered_fields - .clone() - .into_iter() + .get_all_field() + .values() .map(|order_field| { self.get_basic_type_op( &order_field @@ -1195,11 +1194,12 @@ impl<'a, 'ctx> IRBuilder<'a, 'ctx> for LLVMBuilder<'a, 'ctx> { self.context.opaque_struct_type(name); } - fn add_body_to_struct_type(&self, name: &str, order_fields: &[Field], ctx: &mut Ctx<'a>) { + fn add_body_to_struct_type(&self, name: &str, sttype: &STType, ctx: &mut Ctx<'a>) { let st = self.module.get_struct_type(name).unwrap(); st.set_body( - &order_fields - .iter() + &sttype + .get_all_field() + .values() .map(|order_field| { self.get_basic_type_op( &order_field diff --git a/src/ast/builder/mod.rs b/src/ast/builder/mod.rs index 75338ce2b..61f434cc6 100644 --- a/src/ast/builder/mod.rs +++ b/src/ast/builder/mod.rs @@ -14,7 +14,7 @@ use super::{ ctx::Ctx, diag::PLDiag, node::{types::TypedIdentifierNode, TypeNodeEnum}, - pltype::{FNValue, Field, PLType, PriType, STType}, + pltype::{FNValue, PLType, PriType, STType}, range::{Pos, Range}, }; @@ -119,7 +119,7 @@ pub trait IRBuilder<'a, 'ctx> { ) -> ValueHandle; fn build_unconditional_branch(&self, bb: BlockHandle); fn position_at_end_block(&self, block: BlockHandle); - fn add_body_to_struct_type(&self, name: &str, order_fields: &[Field], ctx: &mut Ctx<'a>); + fn add_body_to_struct_type(&self, name: &str, order_fields: &STType, ctx: &mut Ctx<'a>); fn get_or_insert_fn_handle(&self, pltp: &FNValue, ctx: &mut Ctx<'a>) -> ValueHandle; fn get_or_add_global( &self, diff --git a/src/ast/builder/no_op_builder.rs b/src/ast/builder/no_op_builder.rs index f91385799..841891dfb 100644 --- a/src/ast/builder/no_op_builder.rs +++ b/src/ast/builder/no_op_builder.rs @@ -201,7 +201,7 @@ impl<'a, 'ctx> IRBuilder<'a, 'ctx> for NoOpBuilder<'a, 'ctx> { fn add_body_to_struct_type( &self, _name: &str, - _order_fields: &[crate::ast::pltype::Field], + _order_fields: &STType, _ctx: &mut crate::ast::ctx::Ctx<'a>, ) { } diff --git a/src/ast/ctx.rs b/src/ast/ctx.rs index d1cd3584a..946e5f7dd 100644 --- a/src/ast/ctx.rs +++ b/src/ast/ctx.rs @@ -274,7 +274,7 @@ impl<'a, 'ctx> Ctx<'a> { )); } let trait_handle = builder.alloc("tmp_traitv", &trait_pltype.borrow(), self, None); - for (name, f) in &t.fields { + for (name, f) in t.fields.iter() { let mthd = st.find_method(self, name).unwrap(); let fnhandle = builder.get_or_insert_fn_handle(&mthd, self); let targetftp = f.typenode.get_type(self, builder).unwrap(); diff --git a/src/ast/node/interface.rs b/src/ast/node/interface.rs index c775ce2a7..01dc442bc 100644 --- a/src/ast/node/interface.rs +++ b/src/ast/node/interface.rs @@ -10,7 +10,7 @@ use crate::{ }; use indexmap::IndexMap; use internal_macro::node; -use rustc_hash::FxHashMap; +use linked_hash_map::LinkedHashMap; #[node] pub struct MultiTraitNode { pub traits: Box, @@ -113,13 +113,13 @@ impl TraitDefNode { generic_map: IndexMap::default(), name: self.id.name.clone(), path: ctx.plmod.path.clone(), - fields: FxHashMap::default(), - ordered_fields: vec![], + fields: LinkedHashMap::default(), range: self.id.range(), doc: vec![], derives: vec![], modifier: self.modifier, body_range: self.range(), + is_trait: true, }))); builder.opaque_struct_type(&ctx.plmod.get_full_name(&self.id.name)); _ = ctx.add_type(self.id.name.clone(), stu, self.id.range); @@ -129,44 +129,20 @@ impl TraitDefNode { ctx: &'b mut Ctx<'a>, builder: &'b BuilderEnum<'a, 'ctx>, ) -> Result<(), PLDiag> { - let mut fields = FxHashMap::::default(); - let mut order_fields = Vec::::new(); - let mut i = 0; + let mut fields = LinkedHashMap::new(); // add generic type before field add type let mut derives = vec![]; for de in &self.derives { derives.push(de.get_type(ctx, builder)?); } - // type hash - order_fields.push(Field { - index: i, - typenode: Box::new(TypeNameNode::new_from_str("u64").into()), - name: "__type_hash".to_string(), - range: Default::default(), - modifier: None, - }); - i += 1; - // pointer to real value - order_fields.push(Field { - index: i, - typenode: Box::new(TypeNodeEnum::Pointer(PointerTypeNode { - elm: Box::new(TypeNameNode::new_from_str("i64").into()), - range: Default::default(), - })), - name: "__ptr".to_string(), - range: Default::default(), - modifier: None, - }); - i += 1; let pltype = ctx.get_type(self.id.name.as_str(), self.id.range)?; - let clone_map = ctx.plmod.types.clone(); - for field in self.methods.iter() { + for (i, field) in self.methods.iter().enumerate() { let mut tp = field.clone(); tp.paralist .insert(0, Box::new(new_i64ptr_tf_with_name("self"))); let id = field.id.clone(); let f = Field { - index: i, + index: i as u32 + 2, typenode: Box::new(tp.into()), name: field.id.name.clone(), range: field.range, @@ -190,21 +166,11 @@ impl TraitDefNode { // ctx.set_if_refs(f.refs.clone(), field.id.range); fields.insert(id.name.to_string(), f.clone()); - order_fields.push(f); - i += 1; } - let newf = order_fields.clone(); - builder.add_body_to_struct_type( - &ctx.plmod.get_full_name(&self.id.name), - &order_fields, - ctx, - ); - ctx.plmod.types = clone_map; if let PLType::Trait(st) = &mut *pltype.borrow_mut() { st.fields = fields; - st.ordered_fields = newf; st.derives = derives; - // st.doc = self.doc.clone(); + builder.add_body_to_struct_type(&ctx.plmod.get_full_name(&self.id.name), st, ctx); } ctx.add_doc_symbols(pltype); // ctx.save_if_comment_doc_hover(self.range, Some(self.doc.clone())); diff --git a/src/ast/node/types.rs b/src/ast/node/types.rs index 8fa68793f..c629de90c 100644 --- a/src/ast/node/types.rs +++ b/src/ast/node/types.rs @@ -20,8 +20,8 @@ use crate::ast::tokens::TokenType; use indexmap::IndexMap; use internal_macro::node; +use linked_hash_map::LinkedHashMap; use lsp_types::SemanticTokenType; -use rustc_hash::FxHashMap; #[node] pub struct TypeNameNode { pub id: Option, @@ -494,14 +494,14 @@ impl StructDefNode { let stu = Arc::new(RefCell::new(PLType::Struct(STType { name: self.id.name.clone(), path: ctx.plmod.path.clone(), - fields: FxHashMap::default(), - ordered_fields: vec![], + fields: LinkedHashMap::new(), range: self.id.range(), doc: vec![], generic_map, derives: vec![], modifier: self.modifier, body_range: self.range(), + is_trait: false, }))); builder.opaque_struct_type(&ctx.plmod.get_full_name(&self.id.name)); _ = ctx.add_type(self.id.name.clone(), stu, self.id.range); @@ -519,28 +519,16 @@ impl StructDefNode { IndexMap::default() }; ctx.protect_generic_context(&generic_map, |ctx| { - let mut fields = FxHashMap::::default(); - let mut order_fields = Vec::::new(); - // gcrtti fields - let vtable_field = Field { - index: 0, - typenode: Box::new(TypeNameNode::new_from_str("u64").into()), - name: "_vtable".to_string(), - range: Default::default(), - modifier: None, - }; - fields.insert("_vtable".to_string(), vtable_field.clone()); - order_fields.push(vtable_field); - let mut i = 1; + let mut fields = LinkedHashMap::new(); let mut field_pltps = vec![]; let clone_map = ctx.plmod.types.clone(); - for field in self.fields.iter() { + for (i, field) in self.fields.iter().enumerate() { if !field.has_semi { ctx.add_diag(field.id.range.new_err(ErrorCode::COMPLETION)); } let id = field.id.id.clone(); let f = Field { - index: i, + index: i as u32 + 1, typenode: field.id.typenode.clone(), name: id.name.clone(), range: field.id.id.range, @@ -558,25 +546,24 @@ impl StructDefNode { ctx.set_field_refs(pltype.clone(), &f, f.range); ctx.send_if_go_to_def(f.range, f.range, ctx.plmod.path.clone()); fields.insert(id.name.to_string(), f.clone()); - order_fields.push(f); ctx.set_if_refs_tp(tp.clone(), field.id.typenode.range()); - i += 1; - } - let newf = order_fields.clone(); - if self.generics.is_none() { - builder.add_body_to_struct_type( - &ctx.plmod.get_full_name(&self.id.name), - &order_fields, - ctx, - ); } ctx.plmod.types = clone_map; if let PLType::Struct(st) = &mut *pltype.borrow_mut() { - builder.gen_st_visit_function(ctx, st, &field_pltps); st.fields = fields; - st.ordered_fields = newf; st.doc = self.doc.clone(); } + if let PLType::Struct(st) = &*pltype.borrow() { + if self.generics.is_none() { + builder.add_body_to_struct_type( + &ctx.plmod.get_full_name(&self.id.name), + st, + ctx, + ); + } + // gen st vist function must be called after add_body_to_struct_type + builder.gen_st_visit_function(ctx, st, &field_pltps); + } ctx.set_if_refs_tp(pltype.clone(), self.id.range); ctx.add_doc_symbols(pltype.clone()); ctx.save_if_comment_doc_hover(self.range, Some(self.doc.clone())); diff --git a/src/ast/pltype.rs b/src/ast/pltype.rs index 7a14a676c..808a00315 100644 --- a/src/ast/pltype.rs +++ b/src/ast/pltype.rs @@ -32,6 +32,7 @@ use super::range::Range; use immix::ObjectType; use indexmap::IndexMap; +use linked_hash_map::LinkedHashMap; use lsp_types::Command; use lsp_types::CompletionItem; use lsp_types::CompletionItemKind; @@ -40,11 +41,9 @@ use lsp_types::InsertTextFormat; use lsp_types::Location; use lsp_types::SymbolKind; -use rustc_hash::FxHashMap; use std::cell::RefCell; use std::sync::Arc; - /// # PLType /// Type for pivot-lang /// including primitive type, struct type, function type, void type @@ -112,18 +111,11 @@ impl UnionType { let mut res = self.clone(); res.name = name; ctx.add_type_without_check(Arc::new(RefCell::new(PLType::Union(res.clone())))); - - let (tps, errors): (Vec<_>, Vec<_>) = res + res.sum_types = self .sum_types .iter() - .map(|t| { - Ok::, PLDiag>(t.get_type(ctx, builder)?.borrow().get_typenode()) - }) - .partition(Result::is_ok); - if !errors.is_empty() { - return Err(errors[0].clone().unwrap_err()); - } - res.sum_types = tps.into_iter().map(Result::unwrap).collect::>(); + .map(|t| t.get_type(ctx, builder).unwrap().borrow().get_typenode()) + .collect(); res.generic_map.clear(); let pltype = ctx.get_type(&res.name, Default::default()).unwrap(); pltype.replace(PLType::Union(res.clone())); @@ -761,17 +753,63 @@ impl ARRType { pub struct STType { pub name: String, pub path: String, - pub fields: FxHashMap, - pub ordered_fields: Vec, + pub fields: LinkedHashMap, pub range: Range, pub doc: Vec>, pub generic_map: IndexMap>>, pub derives: Vec>>, pub modifier: Option<(TokenType, Range)>, pub body_range: Range, + pub is_trait: bool, } impl STType { + // get all field include type_hash,ptr,vtable,this func only be used in llvmbuilder + pub fn get_all_field(&self) -> LinkedHashMap { + let mut fields = LinkedHashMap::new(); + if self.is_trait { + fields.insert( + "__type_hash".to_string(), + Field { + index: 0, + typenode: Box::new(TypeNameNode::new_from_str("u64").into()), + name: "__type_hash".to_string(), + range: Default::default(), + modifier: None, + }, + ); + // pointer to real value + fields.insert( + "__ptr".to_string(), + Field { + index: 1, + typenode: Box::new(TypeNodeEnum::Pointer(PointerTypeNode { + elm: Box::new(TypeNameNode::new_from_str("i64").into()), + range: Default::default(), + })), + name: "__ptr".to_string(), + range: Default::default(), + modifier: None, + }, + ); + } else { + // gcrtti fields + fields.insert( + "_vtable".to_string(), + Field { + index: 0, + typenode: Box::new(TypeNameNode::new_from_str("u64").into()), + name: "_vtable".to_string(), + range: Default::default(), + modifier: None, + }, + ); + } + self.fields.iter().for_each(|(k, v)| { + fields.insert(k.clone(), v.clone()); + }); + fields + } fn implements(&self, tp: &PLType, plmod: &Mod) -> bool { plmod .impls @@ -857,24 +895,25 @@ impl STType { let mut res = self.clone(); res.name = name; ctx.add_type_without_check(Arc::new(RefCell::new(PLType::Struct(res.clone())))); - let (tps, errors): (Vec<_>, Vec<_>) = self - .ordered_fields - .iter() + res.fields = self + .fields + .values() .map(|f| { let mut nf = f.clone(); - nf.typenode = f.typenode.get_type(ctx, builder)?.borrow().get_typenode(); - Ok::(nf) + nf.typenode = f + .typenode + .get_type(ctx, builder) + .unwrap() + .borrow() + .get_typenode(); + (nf.name.clone(), nf) }) - .partition(Result::is_ok); - if !errors.is_empty() { - return Err(errors.into_iter().map(Result::unwrap_err).next().unwrap()); - } - res.ordered_fields = tps.into_iter().map(Result::unwrap).collect::>(); - let mut field_pltps = vec![]; - res.ordered_fields.iter().for_each(|f| { - field_pltps.push(f.typenode.get_type(ctx, builder).unwrap()); - res.fields.insert(f.name.clone(), f.clone()); - }); + .collect(); + let field_pltps = res + .fields + .values() + .map(|f| f.typenode.get_type(ctx, builder).unwrap()) + .collect::>(); builder.gen_st_visit_function(ctx, &res, &field_pltps); res.generic_map.clear(); let pltype = ctx.get_type(&res.name, Default::default()).unwrap(); @@ -883,10 +922,7 @@ impl STType { } pub fn get_field_completions(&self, must_pub: bool) -> Vec { let mut completions = Vec::new(); - for (name, f) in &self.fields { - if f.index == 0 { - continue; - } + for (name, f) in self.fields.iter() { if must_pub { skip_if_not_modified_by!(f.modifier, TokenType::PUB); } @@ -918,7 +954,7 @@ impl STType { } pub fn get_trait_field_completions(&self) -> Vec { let mut completions = Vec::new(); - for (name, f) in &self.fields { + for (name, f) in self.fields.iter() { if let TypeNodeEnum::Func(func) = &*f.typenode { completions.push(CompletionItem { kind: Some(CompletionItemKind::METHOD), @@ -950,8 +986,8 @@ impl STType { } pub fn get_doc_symbol(&self) -> DocumentSymbol { let children: Vec = self - .ordered_fields - .iter() + .fields + .values() .map(|order_field| order_field.get_doc_symbol()) .collect(); #[allow(deprecated)] From 87d01f1c28c044e0bac9735d13750046ffb30b8d Mon Sep 17 00:00:00 2001 From: kamome <2038975825@qq.com> Date: Sat, 22 Apr 2023 00:14:39 +0800 Subject: [PATCH 2/3] feat: impl multi trait --- src/ast/builder/llvmbuilder.rs | 6 +- src/ast/ctx.rs | 4 +- src/ast/diag.rs | 3 +- src/ast/fmt.rs | 12 ++ src/ast/node/implement.rs | 209 +++++++++++++++++---------------- src/ast/node/operator.rs | 7 +- src/ast/pltype.rs | 140 ++++++++++++++++------ test/main.pi | 3 +- test/test/multi_trait.pi | 51 ++++++++ 9 files changed, 288 insertions(+), 147 deletions(-) create mode 100644 test/test/multi_trait.pi diff --git a/src/ast/builder/llvmbuilder.rs b/src/ast/builder/llvmbuilder.rs index eb24138fd..33d3187a4 100644 --- a/src/ast/builder/llvmbuilder.rs +++ b/src/ast/builder/llvmbuilder.rs @@ -820,7 +820,7 @@ impl<'a, 'ctx> LLVMBuilder<'a, 'ctx> { ctx.run_in_type_mod(x, |ctx, x| { m = x .get_all_field() - .values() + .iter() .map(|v| { let offset = td.offset_of_element(&sttp, v.index).unwrap() * 8; let (tp, _) = self.get_field_di_type(v, ctx, offset); @@ -958,7 +958,7 @@ impl<'a, 'ctx> LLVMBuilder<'a, 'ctx> { st.set_body( &pltp .get_all_field() - .values() + .iter() .map(|order_field| { self.get_basic_type_op( &order_field @@ -1199,7 +1199,7 @@ impl<'a, 'ctx> IRBuilder<'a, 'ctx> for LLVMBuilder<'a, 'ctx> { st.set_body( &sttype .get_all_field() - .values() + .iter() .map(|order_field| { self.get_basic_type_op( &order_field diff --git a/src/ast/ctx.rs b/src/ast/ctx.rs index 946e5f7dd..99e2c9902 100644 --- a/src/ast/ctx.rs +++ b/src/ast/ctx.rs @@ -274,8 +274,8 @@ impl<'a, 'ctx> Ctx<'a> { )); } let trait_handle = builder.alloc("tmp_traitv", &trait_pltype.borrow(), self, None); - for (name, f) in t.fields.iter() { - let mthd = st.find_method(self, name).unwrap(); + for f in t.list_trait_fields().iter() { + let mthd = st.find_method(self, &f.name).unwrap(); let fnhandle = builder.get_or_insert_fn_handle(&mthd, self); let targetftp = f.typenode.get_type(self, builder).unwrap(); let casted = builder.bitcast(self, fnhandle, &targetftp.borrow(), "fncast_tmp"); diff --git a/src/ast/diag.rs b/src/ast/diag.rs index fd99aff2a..490e65c3e 100644 --- a/src/ast/diag.rs +++ b/src/ast/diag.rs @@ -118,7 +118,8 @@ define_error!( UNION_DOES_NOT_CONTAIN_TYPE = "union does not contain type", INVALID_IS_EXPR = "invalid `is` expression", INVALID_CAST = "invalid cast", - METHOD_NOT_FOUND = "method not found" + METHOD_NOT_FOUND = "method not found", + DERIVE_TRAIT_NOT_IMPL = "derive trait not impl" ); macro_rules! define_warn { ($( diff --git a/src/ast/fmt.rs b/src/ast/fmt.rs index bcda7dc1e..6367d74b0 100644 --- a/src/ast/fmt.rs +++ b/src/ast/fmt.rs @@ -629,6 +629,18 @@ impl FmtBuilder { self.token("trait"); self.space(); self.token(node.id.name.as_str()); + if !node.derives.is_empty() { + self.colon(); + node.derives[0..node.derives.len() - 1] + .iter() + .for_each(|d| { + d.format(self); + self.space(); + self.token("+"); + self.space(); + }); + node.derives.last().unwrap().format(self); + } self.space(); self.l_brace(); self.enter(); diff --git a/src/ast/node/implement.rs b/src/ast/node/implement.rs index d6ee8f2cc..1bbaf11b6 100644 --- a/src/ast/node/implement.rs +++ b/src/ast/node/implement.rs @@ -26,7 +26,69 @@ impl PrintTrait for ImplNode { } } } - +fn check_fn<'a, 'b, 'ctx>( + ctx: &'b mut Ctx<'a>, + builder: &'b BuilderEnum<'a, 'ctx>, + method: &FuncDefNode, + trait_tp: Arc>, + traitfns: &mut FxHashSet, + fntype: Arc>, +) -> Result<(), PLDiag> { + if let Some((m, r)) = method.modifier { + r.new_err(ErrorCode::TRAIT_METHOD_SHALL_NOT_HAVE_MODIFIER) + .add_label( + r, + ctx.get_file(), + format_label!("modifier {} shall be removed", m.get_str()), + ) + .add_help( + "trait methods share the same modifier with \ + trait, so you shall not add modifier here", + ) + .add_to_ctx(ctx); + } + if let PLType::Trait(st) = &*trait_tp.borrow() { + if !st.fields.iter().any(|(_, f)| { + let tp = f.typenode.get_type(ctx, builder).unwrap(); + let re = match (&*tp.borrow(), &*fntype.borrow()) { + (PLType::Fn(f1), PLType::Fn(f2)) => { + if f1.eq_except_receiver(f2, ctx, builder) { + traitfns.remove(&f1.name); + true + } else { + false + } + } + _ => unreachable!(), + }; + re + }) { + method + .range() + .new_err(ErrorCode::METHOD_NOT_IN_TRAIT) + .add_label( + method.range(), + ctx.get_file(), + format_label!( + "method {} not in trait {}", + method.id.name.split("::").last().unwrap(), + &st.name + ), + ) + .add_label( + st.range, + ctx.get_file(), + format_label!("trait {} def here", &st.name), + ) + .add_help( + "move this method to another impl block or remove it from current impl block", + ) + .add_to_ctx(ctx); + } + return Ok(()); + } + unreachable!() +} impl Node for ImplNode { fn emit<'a, 'ctx, 'b>( &mut self, @@ -38,19 +100,27 @@ impl Node for ImplNode { } let mut traittpandrange = None; let mut traitfns = FxHashSet::default(); - if let Some((t, (_, r))) = &self.impl_trait { - let tp = t.get_type(ctx, builder); - if let Ok(tp) = tp { - if let PLType::Trait(st) = &*tp.borrow() { - traittpandrange = Some((tp.clone(), t.range())); - for name in st.fields.keys() { - traitfns.insert(name.clone()); - } + if let Some((typename, _)) = &self.impl_trait { + typename.emit_highlight(ctx); + let trait_tp = typename.get_type(ctx, builder)?; + if let PLType::Trait(st) = &*trait_tp.borrow() { + ctx.send_if_go_to_def(typename.range(), st.range, st.path.clone()); + traittpandrange = Some((trait_tp.clone(), typename.range())); + for name in st.fields.keys() { + traitfns.insert(name.clone()); } - } - t.emit_highlight(ctx); - ctx.push_semantic_token(*r, SemanticTokenType::KEYWORD, 0) - } + } else { + return Err(typename + .range() + .new_err(ErrorCode::EXPECT_TRAIT_TYPE) + .add_label( + typename.range(), + ctx.get_file(), + format_label!("type {}", trait_tp.borrow().get_kind_name()), + ) + .add_to_ctx(ctx)); + }; + }; self.target.emit_highlight(ctx); let mut method_docsymbols = vec![]; if let TypeNodeEnum::Basic(bt) = &*self.target { @@ -61,99 +131,38 @@ impl Node for ImplNode { let v = bt.id.as_ref().unwrap().get_type(ctx)?.get_value(); let st_pltype = v.unwrap().get_ty(); if let PLType::Struct(sttp) = &*st_pltype.borrow() { + if let Some((trait_tp, r)) = &traittpandrange { + if let PLType::Trait(trait_tp) = &*trait_tp.borrow() { + trait_tp.check_impl_derives(ctx, sttp, *r); + } else { + unreachable!() + } + } ctx.send_if_go_to_def(self.target.range(), sttp.range, sttp.path.clone()); }; } - if let Some((typename, _)) = &self.impl_trait { - let trait_tp = typename.get_type(ctx, builder)?; - if let PLType::Trait(st) = &*trait_tp.borrow_mut() { - ctx.send_if_go_to_def(typename.range(), st.range, st.path.clone()); - } else { - typename - .range() - .new_err(ErrorCode::EXPECT_TRAIT_TYPE) - .add_label( - typename.range(), - ctx.get_file(), - format_label!("type {}", trait_tp.borrow().get_kind_name()), - ) - .add_to_ctx(ctx); - }; - } for method in &mut self.methods { - let res = method.emit(ctx, builder); - if res.is_err() { - continue; - } - let pltype = res.unwrap().get_value(); - if pltype.is_none() { - continue; - } - let tmp = pltype.unwrap().get_ty(); - if self.impl_trait.is_some() { - // 检查是否有modifier - if let Some((m, r)) = method.modifier { - r.new_err(ErrorCode::TRAIT_METHOD_SHALL_NOT_HAVE_MODIFIER) - .add_label( - r, - ctx.get_file(), - format_label!("modifier {} shall be removed", m.get_str()), - ) - .add_help( - "trait methods share the same modifier with \ - trait, so you shall not add modifier here", - ) - .add_to_ctx(ctx); - } - - // 检查方法是否在trait中 - let trait_tp = traittpandrange.clone().unwrap().0; - if let PLType::Trait(st) = &*trait_tp.borrow() { - if !st.fields.iter().any(|(_, f)| { - let tp = f.typenode.get_type(ctx, builder); - if tp.is_err() { - return false; - } - let tp = tp.unwrap(); - let re = match (&*tp.borrow(), &*tmp.borrow()) { - (PLType::Fn(f1), PLType::Fn(f2)) => { - if f1.eq_except_receiver(f2, ctx, builder) { - traitfns.remove(&f1.name); - true - } else { - false - } - } - _ => unreachable!(), - }; - re - }) { - method - .range() - .new_err(ErrorCode::METHOD_NOT_IN_TRAIT) - .add_label( - method.range(), - ctx.get_file(), - format_label!("method {} not in trait {}", - method.id.name.split("::").last().unwrap(), - &st.name - ), - ) - .add_label( - st.range, - ctx.get_file(), - format_label!("trait {} def here", &st.name), - ).add_help("move this method to another impl block or remove it from current impl block") - .add_to_ctx(ctx); + if let Ok(res) = method.emit(ctx, builder) { + if let Some(node_val) = res.get_value() { + let fntype = node_val.get_ty(); + let f = if let PLType::Fn(f) = &*fntype.borrow() { + f.get_doc_symbol() + } else { + unreachable!() + }; + method_docsymbols.push(f); + if let Some((trait_tp, _)) = &traittpandrange { + check_fn( + ctx, + builder, + method, + trait_tp.clone(), + &mut traitfns, + fntype, + )?; } - }; + } } - let f = if let PLType::Fn(f) = &*tmp.borrow() { - f.get_doc_symbol() - } else { - continue; - }; - method_docsymbols.push(f); } for f in traitfns { let (tp, r) = traittpandrange.clone().unwrap(); diff --git a/src/ast/node/operator.rs b/src/ast/node/operator.rs index ca835f55f..1f7496569 100644 --- a/src/ast/node/operator.rs +++ b/src/ast/node/operator.rs @@ -290,12 +290,13 @@ impl Node for TakeOpNode { let (head_pltype, headptr) = ctx.auto_deref(head_pltype, nv.get_value(), builder); match &*head_pltype.clone().borrow() { PLType::Trait(s) => { - let field = s.fields.get(&id.name); + let field = s.get_trait_field(&id.name); if let Some(field) = field { - _ = s.expect_field_pub(ctx, field, id_range); + _ = s.expect_field_pub(ctx, &field, id_range); ctx.push_semantic_token(id_range, SemanticTokenType::METHOD, 0); - ctx.set_field_refs(head_pltype, field, id_range); + ctx.set_field_refs(head_pltype, &field, id_range); ctx.send_if_go_to_def(id_range, field.range, s.path.clone()); + let re = field.typenode.get_type(ctx, builder)?; let fnv = builder .build_struct_gep(headptr, field.index, "mthd_ptr") diff --git a/src/ast/pltype.rs b/src/ast/pltype.rs index 808a00315..be34340f3 100644 --- a/src/ast/pltype.rs +++ b/src/ast/pltype.rs @@ -762,53 +762,119 @@ pub struct STType { pub body_range: Range, pub is_trait: bool, } - impl STType { + pub fn check_impl_derives(&self, ctx: &Ctx, st: &STType, range: Range) { + let errnames = self + .derives + .iter() + .map(|derive| { + if let PLType::Trait(derive) = &*derive.borrow() { + derive.clone() + } else { + unreachable!() + } + }) + .filter(|derive| !st.implements_trait(derive, &ctx.plmod)) + .map(|derive| derive.name) + .collect::>(); + if !errnames.is_empty() { + range + .new_err(ErrorCode::DERIVE_TRAIT_NOT_IMPL) + .add_label( + range, + ctx.get_file(), + format_label!("the derive trait {} not impl", errnames.join(",")), + ) + .add_to_ctx(ctx); + } + } + pub fn get_trait_field(&self, k: &str) -> Option { + fn walk(st: &STType, k: &str) -> (Option, u32) { + if let Some(f) = st.fields.get(k) { + return (Some(f.clone()), f.index - 1); + } + let mut offset = st.fields.len() as u32; + for derive in st.derives.iter() { + if let PLType::Trait(t) = &*derive.borrow() { + let (f, walk_offset) = walk(t, k); + offset += walk_offset; + if let Some(f) = f { + return (Some(f), offset); + } + } + } + (None, offset) + } + let (f, offset) = walk(self, k); + if let Some(mut f) = f { + f.index = offset + 1; + return Some(f); + } + None + } + fn merge_trait_field(&self) -> Vec { + self.fields + .values() + .map(Clone::clone) + .chain(self.derives.iter().flat_map(|pltype| { + if let PLType::Trait(t) = &*pltype.borrow() { + return t.merge_trait_field(); + } + unreachable!() + })) + .collect::>() + } + pub fn list_trait_fields(&self) -> Vec { + self.merge_trait_field() + .iter() + .enumerate() + .map(|(i, f)| { + let mut f = f.clone(); + f.index = i as u32 + 2; + f + }) + .collect() + } // get all field include type_hash,ptr,vtable,this func only be used in llvmbuilder - pub fn get_all_field(&self) -> LinkedHashMap { - let mut fields = LinkedHashMap::new(); + pub fn get_all_field(&self) -> Vec { + let mut fields = vec![]; if self.is_trait { - fields.insert( - "__type_hash".to_string(), - Field { - index: 0, - typenode: Box::new(TypeNameNode::new_from_str("u64").into()), - name: "__type_hash".to_string(), - range: Default::default(), - modifier: None, - }, - ); + fields.push(Field { + index: 0, + typenode: Box::new(TypeNameNode::new_from_str("u64").into()), + name: "__type_hash".to_string(), + range: Default::default(), + modifier: None, + }); // pointer to real value - fields.insert( - "__ptr".to_string(), - Field { - index: 1, - typenode: Box::new(TypeNodeEnum::Pointer(PointerTypeNode { - elm: Box::new(TypeNameNode::new_from_str("i64").into()), - range: Default::default(), - })), - name: "__ptr".to_string(), + fields.push(Field { + index: 1, + typenode: Box::new(TypeNodeEnum::Pointer(PointerTypeNode { + elm: Box::new(TypeNameNode::new_from_str("i64").into()), range: Default::default(), - modifier: None, - }, - ); + })), + name: "__ptr".to_string(), + range: Default::default(), + modifier: None, + }); } else { // gcrtti fields - fields.insert( - "_vtable".to_string(), - Field { - index: 0, - typenode: Box::new(TypeNameNode::new_from_str("u64").into()), - name: "_vtable".to_string(), - range: Default::default(), - modifier: None, - }, - ); + fields.push(Field { + index: 0, + typenode: Box::new(TypeNameNode::new_from_str("u64").into()), + name: "_vtable".to_string(), + range: Default::default(), + modifier: None, + }); } - self.fields.iter().for_each(|(k, v)| { - fields.insert(k.clone(), v.clone()); + self.merge_trait_field().iter().for_each(|f| { + fields.push(f.clone()); }); fields + .iter_mut() + .enumerate() + .for_each(|(i, f)| f.index = i as u32); + fields } fn implements(&self, tp: &PLType, plmod: &Mod) -> bool { plmod diff --git a/test/main.pi b/test/main.pi index a969f3cd9..36caf18d7 100644 --- a/test/main.pi +++ b/test/main.pi @@ -11,8 +11,8 @@ use project1::test::module; use project1::test::string; use project1::test::macros; use project1::test::union; +use project1::test::multi_trait; use pl_test::main; -use std::io; pub fn main() i64 { macros::test_macros(); @@ -29,6 +29,7 @@ pub fn main() i64 { module::test_module(); string::test_string(); union::test_union(); + multi_trait::test_multi_trait(); return 0; } diff --git a/test/test/multi_trait.pi b/test/test/multi_trait.pi new file mode 100644 index 000000000..6c4c2e3fb --- /dev/null +++ b/test/test/multi_trait.pi @@ -0,0 +1,51 @@ +use core::panic; +trait A { + fn a() bool; + +} + +trait B { + fn b() i64; + +} + +trait C:A + B { + fn c() void; + +} + +pub fn test_multi_trait() void { + let t = test_struct{}; + let c: C; + c = t; + let x = c.a(); + let y = c.b(); + panic::assert(x); + panic::assert(y == 1000); + return; +} + +struct test_struct { +} + +impl C for test_struct { + fn c() void { + return; + } + +} + +impl A for test_struct { + fn a() bool { + return true; + } + +} + +impl B for test_struct { + fn b() i64 { + return 1000; + } + +} + From ad9375b2998a4dfbb1ef71afa7d8b949842d909d Mon Sep 17 00:00:00 2001 From: kamome <2038975825@qq.com> Date: Wed, 26 Apr 2023 13:41:10 +0800 Subject: [PATCH 3/3] feat: generic multi trait --- src/ast/builder/mod.rs | 2 +- src/ast/builder/no_op_builder.rs | 2 +- src/ast/ctx.rs | 20 +++++---- src/ast/fmt.rs | 25 +++++------ src/ast/node/interface.rs | 71 +++++++++++++++++++++++++------- src/ast/node/types.rs | 1 + src/ast/pltype.rs | 18 ++++---- src/ast/test.rs | 28 ++++++++----- src/nomparser/function.rs | 37 +---------------- src/nomparser/types.rs | 41 +++++++++++++++++- test/fmt/test_fmt.pi | 12 ++++++ test/lsp_diag/test_diag.pi | 18 +++++--- test/test/multi_trait.pi | 9 +++- 13 files changed, 185 insertions(+), 99 deletions(-) diff --git a/src/ast/builder/mod.rs b/src/ast/builder/mod.rs index 61f434cc6..b0eb4bd5c 100644 --- a/src/ast/builder/mod.rs +++ b/src/ast/builder/mod.rs @@ -119,7 +119,7 @@ pub trait IRBuilder<'a, 'ctx> { ) -> ValueHandle; fn build_unconditional_branch(&self, bb: BlockHandle); fn position_at_end_block(&self, block: BlockHandle); - fn add_body_to_struct_type(&self, name: &str, order_fields: &STType, ctx: &mut Ctx<'a>); + fn add_body_to_struct_type(&self, name: &str, sttype: &STType, ctx: &mut Ctx<'a>); fn get_or_insert_fn_handle(&self, pltp: &FNValue, ctx: &mut Ctx<'a>) -> ValueHandle; fn get_or_add_global( &self, diff --git a/src/ast/builder/no_op_builder.rs b/src/ast/builder/no_op_builder.rs index 841891dfb..b5021f176 100644 --- a/src/ast/builder/no_op_builder.rs +++ b/src/ast/builder/no_op_builder.rs @@ -201,7 +201,7 @@ impl<'a, 'ctx> IRBuilder<'a, 'ctx> for NoOpBuilder<'a, 'ctx> { fn add_body_to_struct_type( &self, _name: &str, - _order_fields: &STType, + _sttype: &STType, _ctx: &mut crate::ast::ctx::Ctx<'a>, ) { } diff --git a/src/ast/ctx.rs b/src/ast/ctx.rs index 99e2c9902..9c359f140 100644 --- a/src/ast/ctx.rs +++ b/src/ast/ctx.rs @@ -1012,9 +1012,12 @@ impl<'a, 'ctx> Ctx<'a> { need_up_cast: false, }; } - } else if !self - .eq(lg.trait_impl.as_ref().unwrap().clone(), r.clone()) - .eq + } else if lg + .trait_impl + .as_ref() + .unwrap() + .iter() + .any(|lt| !self.eq(lt.clone(), r.clone()).eq) { return EqRes { eq: false, @@ -1031,6 +1034,12 @@ impl<'a, 'ctx> Ctx<'a> { unreachable!() } if l != r { + if matches!(&*l.borrow(), PLType::Union(_)) { + return EqRes { + eq: true, + need_up_cast: true, + }; + } let trait_pltype = l; let st_pltype = self.auto_deref_tp(r); if let (PLType::Trait(t), PLType::Struct(st)) = @@ -1040,11 +1049,6 @@ impl<'a, 'ctx> Ctx<'a> { eq: st.implements_trait(t, &self.plmod), need_up_cast: true, }; - } else if let PLType::Union(_) = &*trait_pltype.borrow() { - return EqRes { - eq: true, - need_up_cast: true, - }; } return EqRes { eq: false, diff --git a/src/ast/fmt.rs b/src/ast/fmt.rs index 6367d74b0..f132547e9 100644 --- a/src/ast/fmt.rs +++ b/src/ast/fmt.rs @@ -629,18 +629,7 @@ impl FmtBuilder { self.token("trait"); self.space(); self.token(node.id.name.as_str()); - if !node.derives.is_empty() { - self.colon(); - node.derives[0..node.derives.len() - 1] - .iter() - .for_each(|d| { - d.format(self); - self.space(); - self.token("+"); - self.space(); - }); - node.derives.last().unwrap().format(self); - } + node.derives.format(self); self.space(); self.l_brace(); self.enter(); @@ -699,13 +688,19 @@ impl FmtBuilder { pub fn parse_trait_bound_node(&mut self, node: &TraitBoundNode) { node.generic.format(self); if let Some(impl_trait) = &node.impl_trait { - self.token(":"); - self.space(); impl_trait.format(self); } } pub fn parse_multi_trait_node(&mut self, node: &MultiTraitNode) { - node.traits.format(self) + if !node.traits.is_empty() { + self.colon(); + self.space(); + node.traits[0..node.traits.len() - 1].iter().for_each(|d| { + d.format(self); + self.token("+"); + }); + node.traits.last().unwrap().format(self); + } } pub fn parse_union_def_node(&mut self, node: &UnionDefNode) { self.prefix(); diff --git a/src/ast/node/interface.rs b/src/ast/node/interface.rs index 01dc442bc..3a29980ea 100644 --- a/src/ast/node/interface.rs +++ b/src/ast/node/interface.rs @@ -13,19 +13,63 @@ use internal_macro::node; use linked_hash_map::LinkedHashMap; #[node] pub struct MultiTraitNode { - pub traits: Box, + pub traits: Vec>, } impl MultiTraitNode { - pub fn merge_traits<'a, 'ctx, 'b>( + fn merge_traits<'a, 'ctx, 'b>( &self, ctx: &'b mut Ctx<'a>, builder: &'b BuilderEnum<'a, 'ctx>, ) -> Result>, PLDiag> { - let trait_pltype = self.traits.get_type(ctx, builder)?; - if matches!(*trait_pltype.borrow(), PLType::Trait(_)) { - return Ok(trait_pltype); + let derives = self.get_types(ctx, builder)?; + if derives.len() == 1 { + return Ok(derives[0].clone()); + } + let name = derives + .iter() + .map(|t| t.borrow().get_name()) + .collect::>() + .join("+"); + let st = STType { + generic_map: IndexMap::default(), + name: name.clone(), + path: ctx.plmod.path.clone(), + fields: LinkedHashMap::default(), + range: Default::default(), + doc: vec![], + derives, + modifier: None, + body_range: Default::default(), + is_trait: true, + }; + builder.opaque_struct_type(&ctx.plmod.get_full_name(&name)); + builder.add_body_to_struct_type(&ctx.plmod.get_full_name(&name), &st, ctx); + let trait_tp = Arc::new(RefCell::new(PLType::Trait(st))); + _ = ctx.add_type(name, trait_tp.clone(), Default::default()); + Ok(trait_tp) + } + pub fn emit_highlight(&self, ctx: &mut Ctx) { + for t in &self.traits { + t.emit_highlight(ctx); } - Err(ctx.add_diag(self.range().new_err(ErrorCode::EXPECT_TRAIT_TYPE))) + } + pub fn get_types<'a, 'ctx, 'b>( + &self, + ctx: &'b mut Ctx<'a>, + builder: &'b BuilderEnum<'a, 'ctx>, + ) -> Result>>, PLDiag> { + let mut traits = vec![]; + for t in &self.traits { + let trait_tp = t.get_type(ctx, builder)?; + if !matches!(&*trait_tp.borrow(), PLType::Trait(_)) { + return Err(t + .range() + .new_err(ErrorCode::EXPECT_TRAIT_TYPE) + .add_to_ctx(ctx)); + } + traits.push(trait_tp); + } + Ok(traits) } } #[node] @@ -44,7 +88,8 @@ impl TraitBoundNode { return Err(ctx.add_diag(self.generic.range().new_err(ErrorCode::GENERIC_NOT_FOUND))); } if let Some(impl_trait) = &self.impl_trait { - let trait_pltype = impl_trait.merge_traits(ctx, builder)?; + let trait_pltype = impl_trait.get_types(ctx, builder)?; + let trait_place_holder = impl_trait.merge_traits(ctx, builder)?; let generic_type = generic_map.get(&self.generic.name).unwrap(); if let PLType::Generic(generic_type) = &mut *generic_type.borrow_mut() { if generic_type.trait_impl.is_some() { @@ -53,6 +98,7 @@ impl TraitBoundNode { ); } generic_type.trait_impl = Some(trait_pltype); + generic_type.trait_place_holder = Some(trait_place_holder); return Ok(()); } unreachable!() @@ -70,7 +116,7 @@ impl TraitBoundNode { pub struct TraitDefNode { pub id: Box, pub methods: Vec, - pub derives: Vec>, + pub derives: MultiTraitNode, pub modifier: Option<(TokenType, Range)>, } @@ -93,9 +139,7 @@ impl Node for TraitDefNode { _builder: &'b BuilderEnum<'a, 'ctx>, ) -> NodeResult { ctx.push_semantic_token(self.id.range, SemanticTokenType::INTERFACE, 0); - for de in &self.derives { - de.emit_highlight(ctx); - } + self.derives.emit_highlight(ctx); for method in &self.methods { method.emit_highlight(ctx); } @@ -131,10 +175,7 @@ impl TraitDefNode { ) -> Result<(), PLDiag> { let mut fields = LinkedHashMap::new(); // add generic type before field add type - let mut derives = vec![]; - for de in &self.derives { - derives.push(de.get_type(ctx, builder)?); - } + let derives = self.derives.get_types(ctx, builder)?; let pltype = ctx.get_type(self.id.name.as_str(), self.id.range)?; for (i, field) in self.methods.iter().enumerate() { let mut tp = field.clone(); diff --git a/src/ast/node/types.rs b/src/ast/node/types.rs index c629de90c..a3ee3eb7a 100644 --- a/src/ast/node/types.rs +++ b/src/ast/node/types.rs @@ -835,6 +835,7 @@ impl GenericDefNode { range, curpltype: None, trait_impl: None, + trait_place_holder: None, refs: Arc::new(MutVec::new(vec![])), }; let pltp = Arc::new(RefCell::new(PLType::Generic(gentype))); diff --git a/src/ast/pltype.rs b/src/ast/pltype.rs index be34340f3..f7541c956 100644 --- a/src/ast/pltype.rs +++ b/src/ast/pltype.rs @@ -764,6 +764,7 @@ pub struct STType { } impl STType { pub fn check_impl_derives(&self, ctx: &Ctx, st: &STType, range: Range) { + debug_assert!(self.is_trait); let errnames = self .derives .iter() @@ -789,6 +790,7 @@ impl STType { } } pub fn get_trait_field(&self, k: &str) -> Option { + debug_assert!(self.is_trait); fn walk(st: &STType, k: &str) -> (Option, u32) { if let Some(f) = st.fields.get(k) { return (Some(f.clone()), f.index - 1); @@ -812,20 +814,21 @@ impl STType { } None } - fn merge_trait_field(&self) -> Vec { + fn merge_field(&self) -> Vec { self.fields .values() .map(Clone::clone) .chain(self.derives.iter().flat_map(|pltype| { if let PLType::Trait(t) = &*pltype.borrow() { - return t.merge_trait_field(); + return t.merge_field(); } unreachable!() })) .collect::>() } pub fn list_trait_fields(&self) -> Vec { - self.merge_trait_field() + debug_assert!(self.is_trait); + self.merge_field() .iter() .enumerate() .map(|(i, f)| { @@ -867,7 +870,7 @@ impl STType { modifier: None, }); } - self.merge_trait_field().iter().for_each(|f| { + self.merge_field().iter().for_each(|f| { fields.push(f.clone()); }); fields @@ -1097,7 +1100,8 @@ pub struct GenericType { pub name: String, pub range: Range, pub curpltype: Option>>, - pub trait_impl: Option>>, + pub trait_impl: Option>>>, + pub trait_place_holder: Option>>, pub refs: Arc>, } impl GenericType { @@ -1108,8 +1112,8 @@ impl GenericType { self.curpltype = None; } pub fn set_place_holder(&mut self, ctx: &mut Ctx) { - if let Some(trait_impl) = &self.trait_impl { - self.curpltype = Some(trait_impl.clone()); + if let Some(trait_place_holder) = &self.trait_place_holder { + self.curpltype = Some(trait_place_holder.clone()); return; } let range = self.range; diff --git a/src/ast/test.rs b/src/ast/test.rs index 903a3da21..06d148ec8 100644 --- a/src/ast/test.rs +++ b/src/ast/test.rs @@ -82,7 +82,7 @@ mod test { std::cmp::Ordering::Greater } }); - assert_eq!(diag.len(), 10); + assert_eq!(diag.len(), 11); assert_eq!( new_diag_range(10, 14, 10, 15), diag[0].get_range().to_diag_range() @@ -92,7 +92,7 @@ mod test { DiagCode::Err(crate::ast::diag::ErrorCode::TYPE_MISMATCH) ); assert_eq!( - new_diag_range(20, 16, 20, 19), + new_diag_range(19, 16, 19, 18), diag[1].get_range().to_diag_range() ); assert_eq!( @@ -100,7 +100,7 @@ mod test { DiagCode::Err(crate::ast::diag::ErrorCode::TYPE_MISMATCH) ); assert_eq!( - new_diag_range(22, 12, 22, 21), + new_diag_range(21, 12, 21, 21), diag[2].get_range().to_diag_range() ); assert_eq!( @@ -108,7 +108,7 @@ mod test { DiagCode::Err(crate::ast::diag::ErrorCode::INVALID_DIRECT_UNION_CAST) ); assert_eq!( - new_diag_range(23, 13, 23, 22), + new_diag_range(22, 13, 22, 22), diag[3].get_range().to_diag_range() ); assert_eq!( @@ -116,7 +116,7 @@ mod test { DiagCode::Err(crate::ast::diag::ErrorCode::INVALID_UNION_CAST) ); assert_eq!( - new_diag_range(24, 18, 24, 21), + new_diag_range(23, 18, 23, 21), diag[4].get_range().to_diag_range() ); assert_eq!( @@ -124,7 +124,7 @@ mod test { DiagCode::Err(crate::ast::diag::ErrorCode::UNION_DOES_NOT_CONTAIN_TYPE) ); assert_eq!( - new_diag_range(25, 13, 25, 21), + new_diag_range(24, 13, 24, 21), diag[5].get_range().to_diag_range() ); assert_eq!( @@ -132,7 +132,7 @@ mod test { DiagCode::Err(crate::ast::diag::ErrorCode::INVALID_IS_EXPR) ); assert_eq!( - new_diag_range(29, 11, 29, 11), + new_diag_range(28, 11, 28, 11), diag[6].get_range().to_diag_range() ); assert_eq!( @@ -140,7 +140,7 @@ mod test { DiagCode::Err(crate::ast::diag::ErrorCode::MISSING_SEMI) ); assert_eq!( - new_diag_range(32, 8, 32, 9), + new_diag_range(30, 8, 30, 9), diag[7].get_range().to_diag_range() ); assert_eq!( @@ -148,7 +148,7 @@ mod test { DiagCode::Warn(crate::ast::diag::WarnCode::UNUSED_VARIABLE) ); assert_eq!( - new_diag_range(32, 13, 32, 13), + new_diag_range(30, 13, 30, 13), diag[8].get_range().to_diag_range() ); assert_eq!( @@ -156,13 +156,21 @@ mod test { DiagCode::Err(crate::ast::diag::ErrorCode::MISSING_SEMI) ); assert_eq!( - new_diag_range(33, 15, 33, 15), + new_diag_range(31, 15, 31, 15), diag[9].get_range().to_diag_range() ); assert_eq!( diag[9].get_diag_code(), DiagCode::Err(crate::ast::diag::ErrorCode::MISSING_SEMI) ); + assert_eq!( + new_diag_range(41, 5, 41, 7), + diag[10].get_range().to_diag_range() + ); + assert_eq!( + diag[10].get_diag_code(), + DiagCode::Err(crate::ast::diag::ErrorCode::DERIVE_TRAIT_NOT_IMPL) + ); } #[test] fn test_memory_leak() { diff --git a/src/nomparser/function.rs b/src/nomparser/function.rs index b28272dec..cef5ffebc 100644 --- a/src/nomparser/function.rs +++ b/src/nomparser/function.rs @@ -6,11 +6,8 @@ use nom::{ IResult, }; -use crate::{ - ast::node::function::FuncDefNode, - ast::{node::interface::MultiTraitNode, tokens::TokenType}, -}; -use crate::{ast::node::interface::TraitBoundNode, nomparser::Span}; +use crate::nomparser::Span; +use crate::{ast::node::function::FuncDefNode, ast::tokens::TokenType}; use internal_macro::{test_parser, test_parser_error}; @@ -148,33 +145,3 @@ pub fn call_function_op(input: Span) -> IResult IResult> { - map_res( - tuple(( - identifier, - opt(preceded(tag_token_symbol(TokenType::COLON), multi_trait)), - )), - |(generic, impl_trait)| { - let range = if let Some(impl_trait) = &impl_trait { - generic.range.start.to(impl_trait.range().end) - } else { - generic.range - }; - res_box(Box::new(TraitBoundNode { - generic, - impl_trait, - range, - })) - }, - )(input) -} - -pub fn multi_trait(input: Span) -> IResult> { - map_res(type_name, |traits| { - res_box(Box::new(MultiTraitNode { - range: traits.range(), - traits, - })) - })(input) -} diff --git a/src/nomparser/types.rs b/src/nomparser/types.rs index 8a06c80dc..00978ddb6 100644 --- a/src/nomparser/types.rs +++ b/src/nomparser/types.rs @@ -1,3 +1,4 @@ +use crate::ast::node::interface::{MultiTraitNode, TraitBoundNode}; use crate::nomparser::Span; use crate::{ ast::node::types::{ArrayTypeNameNode, TypeNameNode}, @@ -171,7 +172,10 @@ pub fn trait_def(input: Span) -> IResult> { }) .collect(), range, - derives: de, + derives: MultiTraitNode { + traits: de, + range: Default::default(), + }, modifier, })) }, @@ -194,3 +198,38 @@ fn type_add(input: Span) -> IResult>> { type_name, )(input) } + +pub fn trait_bound(input: Span) -> IResult> { + map_res( + tuple(( + identifier, + opt(preceded(tag_token_symbol(TokenType::COLON), multi_trait)), + )), + |(generic, impl_trait)| { + let range = if let Some(impl_trait) = &impl_trait { + generic.range.start.to(impl_trait.range().end) + } else { + generic.range + }; + res_box(Box::new(TraitBoundNode { + generic, + impl_trait, + range, + })) + }, + )(input) +} + +pub fn multi_trait(input: Span) -> IResult> { + map_res(type_add, |traits| { + res_box(Box::new(MultiTraitNode { + range: traits + .first() + .unwrap() + .range() + .start + .to(traits.last().unwrap().range().end), + traits, + })) + })(input) +} diff --git a/test/fmt/test_fmt.pi b/test/fmt/test_fmt.pi index daf88842e..932d2d7d4 100644 --- a/test/fmt/test_fmt.pi +++ b/test/fmt/test_fmt.pi @@ -173,3 +173,15 @@ fn test_ret_union() Option { return 101; } +trait TA { +} + +trait TB { +} + +trait TC: TA+TB { +} + +trait TD: TA { +} + diff --git a/test/lsp_diag/test_diag.pi b/test/lsp_diag/test_diag.pi index 7d647263d..5aad66041 100644 --- a/test/lsp_diag/test_diag.pi +++ b/test/lsp_diag/test_diag.pi @@ -15,10 +15,9 @@ struct BInner { x: T; } -type AA = A|i64; - +type AA = A | i64; fn test_mismatch() void { - let a: AA = 1.0; + let a: AA = 1.; let aa: AA = 1 as i64; let b = aa as f32; let bb = aa as f32!; @@ -28,9 +27,18 @@ fn test_mismatch() void { } use std::io - fn test_semi() void { let a = 1 test_semi() return; -} \ No newline at end of file +} + +trait TA { +} + +trait TB: TA { +} + +impl TB for A { +} + diff --git a/test/test/multi_trait.pi b/test/test/multi_trait.pi index 6c4c2e3fb..945966438 100644 --- a/test/test/multi_trait.pi +++ b/test/test/multi_trait.pi @@ -9,7 +9,7 @@ trait B { } -trait C:A + B { +trait C: A+B { fn c() void; } @@ -22,6 +22,7 @@ pub fn test_multi_trait() void { let y = c.b(); panic::assert(x); panic::assert(y == 1000); + panic::assert(trait_with_generic(t) == 1000); return; } @@ -49,3 +50,9 @@ impl B for test_struct { } +fn trait_with_generic(x: T) i64 { + x.a(); + let i = x.b(); + return i; +} +