diff --git a/api/map.hpp b/api/map.hpp index de5a9118a..fd5feb6de 100644 --- a/api/map.hpp +++ b/api/map.hpp @@ -24,17 +24,49 @@ namespace jule class MapKeyHasher { + private: + class fnv1a + { + public: + mutable jule::U64 sum; + + fnv1a(void) noexcept + { + this->reset(); + } + + inline void reset(void) const noexcept + { + this->sum = 14695981039346656037LLU; + } + + void write(const jule::Slice &data) const noexcept + { + for (const jule::U8 &b : data) + { + this->sum ^= static_cast(b); + this->sum *= 1099511628211LLU; + } + } + }; + + private: + const jule::MapKeyHasher::fnv1a hasher; + public: - size_t operator()(const jule::Str &key) const + inline size_t operator()(const jule::Slice &key) const noexcept { + this->hasher.reset(); + this->hasher.write(key); + return this->hasher.sum; + } + + inline size_t operator()(const jule::Str &key) const noexcept { - size_t hash = 0; - for (jule::Int i = 0; i < key.len(); ++i) - hash += key.buffer[i] % 7; - return hash; + return this->operator()(key.fake_slice()); } template - inline size_t operator()(const T &obj) const + inline size_t operator()(const T &obj) const noexcept { return this->operator()(jule::to_str(obj)); } diff --git a/api/str.hpp b/api/str.hpp index cde441b78..ca0ca0e9d 100644 --- a/api/str.hpp +++ b/api/str.hpp @@ -37,7 +37,7 @@ namespace jule class Str { public: - std::basic_string buffer; + mutable std::basic_string buffer; static jule::Str alloc(const jule::Int &len) noexcept { if (len < 0) @@ -217,9 +217,9 @@ namespace jule return this->buffer.empty(); } - jule::Slice fake_slice(void) { + jule::Slice fake_slice(void) const { jule::Slice slice; - slice.data.alloc = this->begin(); + slice.data.alloc = const_cast(this->begin()); slice.data.ref = nullptr; slice._slice = slice.data.alloc; slice._len = this->len(); diff --git a/api/types.hpp b/api/types.hpp index 29cb37d9d..c492d8db7 100644 --- a/api/types.hpp +++ b/api/types.hpp @@ -21,6 +21,7 @@ namespace jule using U16 = std::uint16_t; using U32 = std::uint32_t; using U64 = std::uint64_t; + using Uintptr = std::uintptr_t; typedef float F32; typedef double F64; typedef bool Bool; @@ -28,11 +29,9 @@ namespace jule #ifdef ARCH_X32 using Uint = std::uint32_t; using Int = std::int32_t; - using Uintptr = std::uint32_t; #else using Uint = std::uint64_t; using Int = std::int64_t; - using Uintptr = std::uint64_t; #endif constexpr decltype(nullptr) nil = nullptr; diff --git a/src/julec/compile.jule b/src/julec/compile.jule index a9838486b..f33b0edf3 100644 --- a/src/julec/compile.jule +++ b/src/julec/compile.jule @@ -400,11 +400,6 @@ fn compile_command(mut &args: []str) { if main == nil { throw(logf(LogMsg.NoEntryPoint)) } - main.statically = true // Mark used for deadcode elimination. - } - let mut init = ir.main.find_fn(INIT_FN, CPP_LINKED) - if init != nil { - init.statically = true // Mark used for deadcode elimination. } apply_target_independent_optimizations(ir) diff --git a/src/julec/obj/cxx/ident.jule b/src/julec/obj/cxx/ident.jule index 17f071f5b..32f1f638e 100644 --- a/src/julec/obj/cxx/ident.jule +++ b/src/julec/obj/cxx/ident.jule @@ -25,30 +25,24 @@ struct IdentCoder {} impl IdentCoder { const Self = "_self_" - // Returns specified identifer as JuleC identifer. - // Equavalents: "JULEC_ID(" + ident + ")" of JuleC API. - static fn to_ident(ident: str): str { - ret "_" + ident - } - - // Returns cpp output identifier form of pointer address. - static fn addr(addr: uintptr): str { - ret "_" + conv::fmt_uint(u64(addr), 0xF) - } - // Returns cpp output identifier form of given identifier. // // Parameters: // - ident: Identifier. // - addr: Pointer address of package file handler. - static fn to_out(ident: str, addr: uintptr): str { + static fn to_out(&ident: str, addr: uintptr): str { if addr != 0 { - let mut obj = IdentCoder.addr(addr) + let mut obj = make(str, 40) + obj += "_" + obj += conv::fmt_uint(u64(addr), 0xF) obj += "_" obj += ident ret obj } - ret IdentCoder.to_ident(ident) + let mut obj = make(str, ident.len + 1) + obj += "_" + obj += ident + ret obj } // Returns cpp output local identifier form of fiven identifier. @@ -57,12 +51,14 @@ impl IdentCoder { // - row: Row of definition. // - col: Column of definition. // - ident: Identifier of definition. - static fn to_local(row: int, col: int, ident: str): str { - let mut obj = conv::itoa(row) + static fn to_local(row: int, col: int, &ident: str): str { + let mut obj = make(str, 40) + obj += "_" + obj += conv::itoa(row) obj += conv::itoa(col) obj += "_" obj += ident - ret IdentCoder.to_ident(obj) + ret obj } // Returns output identifier of function. @@ -85,22 +81,14 @@ impl IdentCoder { } // Returns output identifier of function instance. - static fn func_ins(mut &f: &FnIns): str { + static fn func_ins(&f: &FnIns): str { if f.is_builtin() { ret "jule::" + f.decl.ident } if f.decl.cpp_linked || f.generics.len == 0 { ret IdentCoder.func(f.decl) } - for (i, mut ins) in f.decl.instances { - if ins.same(f) { - let mut obj = IdentCoder.func(f.decl) - obj += "_" - obj += conv::itoa(i) - ret obj - } - } - ret "__?__" + ret IdentCoder.to_out(f.decl.ident, uintptr(f)) } // Returns output identifier of trait. @@ -137,19 +125,11 @@ impl IdentCoder { } // Returns output identifier of structure instance. - static fn structure_ins(mut &s: &StructIns): str { + static fn structure_ins(&s: &StructIns): str { if s.decl.cpp_linked || s.generics.len == 0 { ret IdentCoder.structure(s.decl) } - for (i, mut ins) in s.decl.instances { - if ins.same(s) { - let mut obj = IdentCoder.structure(s.decl) - obj += "_" - obj += conv::itoa(i) - ret obj - } - } - ret "__?__" + ret IdentCoder.to_out(s.decl.ident, uintptr(s)) } // Returns output identifier of field. @@ -181,31 +161,49 @@ impl IdentCoder { // Returns begin label identifier of iteration. static fn iter_begin(it: uintptr): str { - ret "_iter_begin_" + conv::fmt_uint(u64(it), 0xF) + let mut obj = make(str, 30) + obj += "_iter_begin_" + obj += conv::fmt_uint(u64(it), 0xF) + ret obj } // Returns end label identifier of iteration. static fn iter_end(it: uintptr): str { - ret "_iter_end_" + conv::fmt_uint(u64(it), 0xF) + let mut obj = make(str, 30) + obj += "_iter_end_" + obj += conv::fmt_uint(u64(it), 0xF) + ret obj } // Returns next label identifier of iteration. static fn iter_next(it: uintptr): str { - ret "_iter_next_" + conv::fmt_uint(u64(it), 0xF) + let mut obj = make(str, 30) + obj += "_iter_next_" + obj += conv::fmt_uint(u64(it), 0xF) + ret obj } // Returns label identifier. - static fn label(ident: str): str { - ret "_julec_label_" + ident + static fn label(&ident: str): str { + let mut obj = make(str, 30) + obj += "_julec_label_" + obj += ident + ret obj } // Returns end label identifier of match-case. static fn match_end(m: uintptr): str { - ret "_match_end_" + conv::fmt_uint(u64(m), 0xF) + let mut obj = make(str, 30) + obj += "_match_end_" + obj += conv::fmt_uint(u64(m), 0xF) + ret obj } // Returns begin label identifier of case. static fn case_begin(c: uintptr): str { - ret "_case_begin_" + conv::fmt_uint(u64(c), 0xF) + let mut obj = make(str, 30) + obj += "_case_begin_" + obj += conv::fmt_uint(u64(c), 0xF) + ret obj } } \ No newline at end of file diff --git a/src/julec/obj/cxx/type.jule b/src/julec/obj/cxx/type.jule index 84394085c..14a08f85c 100644 --- a/src/julec/obj/cxx/type.jule +++ b/src/julec/obj/cxx/type.jule @@ -56,7 +56,9 @@ impl TypeCoder { // Returns given identifier as Jule type identifier. static fn to_type(mut id: str): str { - id = types::real_kind_of(id) + if id != types::TypeKind.Uintptr { + id = types::real_kind_of(id) + } if 97 <= id[0] && id[0] <= 122 { id[0] -= 32 // To upper first byte. } diff --git a/src/julec/opt/deadcode/define.jule b/src/julec/opt/deadcode/define.jule index 5ee53f02a..cc93e4fd4 100644 --- a/src/julec/opt/deadcode/define.jule +++ b/src/julec/opt/deadcode/define.jule @@ -3,344 +3,224 @@ // license that can be found in the LICENSE file. use obj::{IR} -use env -use ast for std::jule::ast -use std::jule::build::{Directive} +use build for std::jule::build use std::jule::sema::{ Package, + SymbolTable, + ReferenceStack, + Var, Fn, FnIns, + Struct, StructIns, - Var, - ReferenceStack, Trait, - TypeKind, } -// Stores all dead definitions. -static mut deads: []any = nil - -// Stores currently checking definitions. -static mut checking: []any = nil - -// Currently checking reference. -static mut current: any = nil - -static mut main_current: any = nil - -// Returns directive if exist. -fn find_directive(mut &directives: []&ast::Directive, tag: str): &ast::Directive { - for (_, mut dr) in directives { - if dr.tag.kind == tag { - ret dr - } - } - ret nil +struct LiveTable { + vars: []&Var + fns: []&FnIns + structs: []&StructIns } -// Reports whether directive is exist. -fn has_directive(&directives: []&ast::Directive, tag: str): bool { - ret find_directive(unsafe { *(&directives) }, tag) != nil -} - -// Reports whether the reference tpye of t exist in arr. -fn is_exist(arr: []any, t: any): bool { - for _, ref in arr { - if ref == t { - ret true - } - } - ret false +pub struct ObjectDeadCode { + live: LiveTable + ir: &IR } -// Reports whether reference in dead. -fn is_dead(mut ref: any): (dead: bool) { - if is_exist(deads, ref) { - // Cycle, also already checked and marked as dead. - ret true - } - match type ref { - | &FnIns: - if (&FnIns)(ref).decl.is_init() { - break - } - fall - |: - if ref == current || ref == main_current { - ret true - } - if is_exist(checking, ref) { - // Cycle, also already checking and should be marked dead. - ret true +impl ObjectDeadCode { + static fn new(mut &ir: &IR): &ObjectDeadCode { + ret &ObjectDeadCode{ + ir: ir, } } - let mut old = current - current = ref - let len = checking.len - checking = append(checking, ref) - defer { - current = old - drop_ref(checking, len) - } - - match type ref { - | &FnIns: - ret is_fn_dead((&FnIns)(ref)) - | &Var: - ret is_var_dead((&Var)(ref)) - | &StructIns: - ret is_struct_dead((&StructIns)(ref)) - | &Trait: - ret is_trait_dead((&Trait)(ref)) - |: - ret false - } -} - -// Remove reference from refs by pos. -fn drop_ref(mut &refs: []any, pos: int) { - // Remove position, and following references. - // This is safe. - // References dropped after processed, so following - // references should be already processed. - refs = refs[:pos] -} - -fn is_all_references_are_dead(mut references: &ReferenceStack): bool { - let mut i = 0 - for i < references.len(); i++ { - let mut ref = references.at(i) - if current == ref { - continue - } - if main_current == ref { - continue + fn is_live[T](mut &self, &t: T): bool { + let mut live: []T = nil + match type T { + | &FnIns: + live = self.live.fns + | &Var: + live = self.live.vars + | &StructIns: + live = self.live.structs } - if !is_dead(ref) { - ret false + for i in live { + if live[i] == t { + ret true + } } - } - ret true -} - -fn is_fn_dead(mut f: &FnIns): bool { - if f.decl.cpp_linked || f.anon { ret false } - if f.decl.is_entry_point() || f.decl.is_init() { - ret !f.decl.statically - } - - if env::TEST && has_directive(f.decl.directives, Directive.Test) { - ret false - } - - if f.owner != nil { - if is_exist(checking, f.owner) { - // Cycle, also already checking and should be marked dead. - ret true - } - ret is_dead(f.owner) - } - - ret is_all_references_are_dead(f.references) -} - -fn is_var_dead(mut v: &Var): bool { - if v.constant { - ret true - } - - if v.cpp_linked { - ret false - } - - ret is_all_references_are_dead(v.references) -} - -fn kind_is_dead(mut k: &TypeKind): bool { - match { - | k.sptr() != nil: - ret kind_is_dead(k.sptr().elem) - | k.ptr() != nil: - ret kind_is_dead(k.ptr().elem) - | k.slc() != nil: - ret kind_is_dead(k.slc().elem) - | k.arr() != nil: - ret kind_is_dead(k.arr().elem) - | k.trt() != nil: - let mut t = k.trt() - if is_dead(t) { - ret true - } - | k.strct() != nil: - let mut s = k.strct() - if is_dead(s) { - ret true + fn push_live[T](mut &self, mut t: T) { + if !self.is_live[T](t) { + match type T { + | &Var: + self.live.vars = append(self.live.vars, t) + | &FnIns: + self.live.fns = append(self.live.fns, t) + | &StructIns: + self.live.structs = append(self.live.structs, t) + } } } - ret false -} -fn is_any_generic_is_dead(mut &generics: []&TypeKind): bool { - for (_, mut g) in generics { - if kind_is_dead(g) { - ret true + fn set_references_as_live(mut &self, mut &rs: &ReferenceStack) { + if rs == nil { + ret } - } - ret false -} - -fn is_struct_dead(mut s: &StructIns): bool { - // Do not eliminate structure. - ret false - - if s.decl.cpp_linked { - ret false - } - - ret is_all_references_are_dead(s.references) -} - -fn is_trait_dead(mut t: &Trait): bool { - // Do not eliminate trait. - ret false - - for (_, mut imp) in t.implemented { let mut i = 0 - for i < imp.instances.len; i++ { - let mut ins = imp.instances[i] - if !is_struct_dead(ins) { - ret false + for i < rs.len(); i++ { + let mut ref = rs.at(i) + match type ref { + | &FnIns: + let mut f = (&FnIns)(ref) + if self.is_live[&FnIns](f) { + break + } + if f.owner != nil && !f.decl.statically { + if !self.is_live[&StructIns](f.owner) { + self.push_live[&StructIns](f.owner) + self.set_references_as_live(f.owner.refers) + } + } + self.push_live[&FnIns](f) + self.set_references_as_live(f.refers) + | &Var: + let mut v = (&Var)(ref) + if self.is_live[&Var](v) { + break + } + self.push_live[&Var](v) + self.set_references_as_live(v.refers) + | &StructIns: + let mut s = (&StructIns)(ref) + if self.is_live[&StructIns](s) { + break + } + self.push_live[&StructIns](s) + self.set_references_as_live(s.refers) + for (_, mut ins) in s.methods { + for (_, mut mins) in ins.instances { + if self.is_live[&FnIns](mins) { + continue + } + self.push_live[&FnIns](mins) + self.set_references_as_live(mins.refers) + } + } } } } - ret true -} -// Eliminates dead functions. -fn eliminate_dead_functions(mut &funcs: []&Fn) { - let mut i = 0 - for i < funcs.len { - let mut f = funcs[i] - let mut j = 0 - for j < f.instances.len { - let mut ins = f.instances[j] - main_current = ins - if is_fn_dead(ins) { - deads = append(deads, ins) - f.instances = append(f.instances[:j], f.instances[j+1:]...) - } else { - j++ + fn inits(mut &self, mut &pkg: &Package) { + for (_, mut file) in pkg.files { + for (_, mut f) in file.funcs { + if f.ident == build::INIT_FN { + let mut ins = f.instances[0] + self.live.fns = append(self.live.fns, ins) + self.set_references_as_live(ins.refers) + } } } + } - // Remove function, no instances. - if f.instances.len == 0 { - funcs = append(funcs[:i], funcs[i+1:]...) - continue + fn collect_live(mut &self) { + for (_, mut used) in self.ir.used { + if !used.cpp_linked { + self.inits(used.package) + } } - i++ + self.inits(self.ir.main) + let mut main = self.ir.main.find_fn(build::ENTRY_POINT, false) + let mut ins = main.instances[0] + self.live.fns = append(self.live.fns, ins) + self.set_references_as_live(ins.refers) } -} -fn eliminate_dead_globals(mut &pkg: &Package) { - for (_, mut f) in pkg.files { + fn remove_dead_globals(mut &self, mut &vars: []&Var) { let mut i = 0 - for i < f.vars.len { - let mut v = f.vars[i] - main_current = v - if is_var_dead(v) { - deads = append(deads, v) - f.vars = append(f.vars[:i], f.vars[i+1:]...) - } else { + for i < vars.len { + let v = vars[i] + if self.is_live[&Var](v) { i++ + continue } + vars = append(vars[:i], vars[i+1:]...) } } -} -fn eliminate_dead_functions_package(mut &pkg: &Package) { - for (_, mut f) in pkg.files { - eliminate_dead_functions(f.funcs) + fn remove_dead_fns(mut &self, mut &funcs: []&Fn) { + let mut i = 0 + for i < funcs.len { + let mut f = funcs[i] + let mut j = 0 + for j < f.instances.len { + let ins = f.instances[j] + if self.is_live[&FnIns](ins) { + j++ + continue + } + f.instances = append(f.instances[:j], f.instances[j+1:]...) + } + if f.instances.len == 0 { + funcs = append(funcs[:i], funcs[i+1:]...) + continue + } + i++ + } } -} -fn eliminate_dead_structs(mut &pkg: &Package) { - for (_, mut f) in pkg.files { + fn remove_dead_structs(mut &self, mut &structs: []&Struct) { let mut i = 0 - for i < f.structs.len { - let mut s = f.structs[i] + for i < structs.len { + let mut s = structs[i] let mut j = 0 for j < s.instances.len { let mut ins = s.instances[j] - main_current = ins - if is_struct_dead(ins) { - deads = append(deads, ins) - s.instances = append(s.instances[:j], s.instances[j+1:]...) + self.remove_dead_fns(ins.methods) + if ins.methods.len != 0 || self.is_live[&StructIns](ins) { + j++ continue } - j++ + s.instances = append(s.instances[:j], s.instances[j+1:]...) } - if s.instances.len == 0 { - f.structs = append(f.structs[:i], f.structs[i+1:]...) + structs = append(structs[:i], structs[i+1:]...) continue } i++ } } -} -fn eliminate_dead_traits(mut &pkg: &Package) { - for (_, mut f) in pkg.files { - let mut i = 0 - for i < f.traits.len { - let mut t = f.traits[i] - main_current = t - if is_trait_dead(t) { - deads = append(deads, t) - f.traits = append(f.traits[:i], f.traits[i+1:]...) - } else { - i++ - } - } + fn remove_deads_file(mut &self, mut &file: &SymbolTable) { + self.remove_dead_fns(file.funcs) + self.remove_dead_structs(file.structs) + self.remove_dead_globals(file.vars) } -} -fn keep_alive_init(mut &pkg: &Package) { - const CPP_LINKED = false - const INIT_FN = "init" - let mut f = pkg.find_fn(INIT_FN, CPP_LINKED) - if f != nil { - f.statically = true + fn remove_deads_package(mut &self, mut &pkg: &Package) { + for (_, mut file) in pkg.files { + self.remove_deads_file(file) + } } -} - -fn eliminate_package(mut &pkg: &Package) { - deads = make([]any, 0, 1 << 10) - keep_alive_init(pkg) + fn remove_deads(mut &self) { + for (_, mut used) in self.ir.used { + if !used.cpp_linked { + self.remove_deads_package(used.package) + } + } + self.remove_deads_package(self.ir.main) + } - eliminate_dead_globals(pkg) - eliminate_dead_functions_package(pkg) + fn elimanate(mut &self) { + self.collect_live() + self.remove_deads() + } } -// Eliminate dead defines. pub fn eliminate_defines(mut &ir: &IR) { - let mut i = ir.used.len - 1 - for i >= 0; i-- { - let mut u = ir.used[i] - if !u.cpp_linked { - eliminate_package(u.package) - } - } - eliminate_package(ir.main) - - // Clear memory. - deads = nil - checking = nil - current = nil + let mut ocd = ObjectDeadCode.new(ir) + ocd.elimanate() } diff --git a/std/jule/README.md b/std/jule/README.md index 9364f027a..2ef7e2f0f 100644 --- a/std/jule/README.md +++ b/std/jule/README.md @@ -46,3 +46,5 @@ For example: - **(7.1)** When analyzing structures, operator overload methods must first be made ready for control and qualified. If an operator tries to use overloading, one of whose construction methods has not yet been analyzed, it will cause analysis errors. - **(7.2)** Operators must be checkec and assigned to structures before analysis of methods and others. Because problems can occur if you analysis operators structure by structure. Semantic will analyze structures file by file. Therefore if an operator tries to use overloading from other file, one of whose construction methods has not yet been assigned, it will cause analysis errors. + +- **(8)** Check `enum` declarations first before using them or any using possibility appears. Enum fields should be evaluated before evaluate algorithms executed. Otherwise, probably program will panic because of unevaluated enum field(s) when tried to use. \ No newline at end of file diff --git a/std/jule/sema/eval.jule b/std/jule/sema/eval.jule index f8af03cfa..e69263ac7 100644 --- a/std/jule/sema/eval.jule +++ b/std/jule/sema/eval.jule @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD 3-Clause // license that can be found in the LICENSE file. -use integ for std::jule::integrated use conv for std::conv use path for std::fs::path use std::jule::ast::{ @@ -425,34 +424,20 @@ impl Eval { ret self.find_builtins(ident) } - fn push_reference(mut self, mut &references: &ReferenceStack) { + fn push_reference[T](mut self, mut &ref: T) { match type self.lookup { | &ScopeChecker: - let mut sc = (&ScopeChecker)(self.lookup).get_root() - if !references.exist[FnIns](sc.owner) { - references.push(sc.owner) + let mut sc = (&ScopeChecker)(self.lookup).get_hard_root() + if !sc.owner.refers.exist[T](ref) { + sc.owner.refers.push(ref) } |: - if self.owner != nil && !references.exist[Var](self.owner) { - references.push(self.owner) + if self.owner != nil && !self.owner.refers.exist[T](ref) { + self.owner.refers.push(ref) } } } - fn push_reference_to_fn(mut self, mut &f: &FnIns) { - self.push_reference(f.references) - } - - fn push_reference_to_var(mut self, mut &v: &Var) { - if v.references != nil { - self.push_reference(v.references) - } - } - - fn push_reference_to_struct(mut self, mut &s: &StructIns) { - self.push_reference(s.references) - } - fn __eval_enum(self, mut enm: &Enum): &Data { ret &Data{ lvalue: false, @@ -539,7 +524,7 @@ impl Eval { self.check_deprecated(f.directives, error_token) let mut ins = f.instance() - self.push_reference_to_fn(ins) + self.push_reference[&FnIns](ins) ret self.eval_fn_ins(ins) } @@ -627,7 +612,7 @@ impl Eval { } } - self.push_reference_to_var(v) + self.push_reference[&Var](v) if !v.cpp_linked && (v.value == nil || v.value.data == nil) { if v.constant { @@ -1349,7 +1334,7 @@ impl Eval { } let mut s = t.strct() - self.push_reference_to_struct(s) + self.push_reference[&StructIns](s) if !s.decl.is_implements(tr) { self.push_err(error_token, LogMsg.TypeNotSupportsCastingTo, d.kind.to_str(), t.to_str()) @@ -1630,7 +1615,7 @@ impl Eval { self.push_err(error_token, LogMsg.IllegalCycleRefersItself, s.decl.ident) } - self.push_reference_to_struct(s) + self.push_reference[&StructIns](s) self.check_deprecated(s.decl.directives, error_token) let mut slc = StructLitChecker{ @@ -1845,7 +1830,7 @@ impl Eval { } f.reloaded = true } - } else { + } else if f.generics.len > 0 { self.s.build_fn_non_generic_type_kinds(f) } @@ -1869,6 +1854,15 @@ impl Eval { ret } + if dynamic_annotation { + ok = self.s.reload_fn_ins_types(f) + if !ok { + d = nil + ret + } + f.reloaded = true + } + let mut exist_instance = f.decl.append_instance(f) if exist_instance != nil { f = exist_instance @@ -1879,26 +1873,18 @@ impl Eval { if f.decl.is_void() { d = build_void_data() } else { - if dynamic_annotation { - ok = self.s.reload_fn_ins_types(f) - if !ok { - d = nil - ret - } - f.reloaded = true - } - d.kind = f.result d.lvalue = false } - d.mutable = true - d.model = &FnCallExprModel{ + let mut model = &FnCallExprModel{ token: fc.token, func: f, expr: call_model, args: fcac.arg_models, } + d.model = model + d.mutable = true if f.decl.exceptional { match { @@ -1916,9 +1902,17 @@ impl Eval { self.push_err(fc.token, LogMsg.HandledUnexceptional) } - if f.generics.len > 0 && exist_instance == nil { - // Check generic function instance instantly. - self.s.check_fn_ins_caller(f, fc.token) + if f.generics.len > 0 { + if exist_instance != nil { + // Update model by exist function instance. + // Because generic functions returns always new instance. + // So, if this absolute instance is already exist, update model. + // In other words, model's instance will be a dangling instance. + update_model_to_generic_ins(model, f) + } else { + // Check generic function instance instantly. + self.s.check_fn_ins_caller(f, fc.token) + } } } @@ -1995,7 +1989,7 @@ impl Eval { let mut ins = method.instance() ins.owner = s - self.push_reference_to_fn(ins) + self.push_reference[&FnIns](ins) d.model = &StructStaticIdentExprModel{ structure: s, expr: d.model, @@ -2088,7 +2082,7 @@ impl Eval { let mut ins = m.instance() ins.owner = s - self.push_reference_to_fn(ins) + self.push_reference[&FnIns](ins) let mut model = new(Data, *d) d.model = &StructSubIdentExprModel{ token: si.ident, @@ -4320,4 +4314,15 @@ fn cast_const_by_type(&t: str, mut &d: &Data) { d.constant.set_f64(f64(f32(d.constant.as_f64()))) } } -} \ No newline at end of file +} + +fn update_model_to_generic_ins(mut &m: &FnCallExprModel, mut &f: &FnIns) { + match type m.expr { + | &FnIns: + m.expr = f + | &StructSubIdentExprModel: + (&StructSubIdentExprModel)(m.expr).method = f + | &StructStaticIdentExprModel: + (&StructStaticIdentExprModel)(m.expr).method = f + } +} diff --git a/std/jule/sema/fn.jule b/std/jule/sema/fn.jule index e594eb32b..f3e2ef020 100644 --- a/std/jule/sema/fn.jule +++ b/std/jule/sema/fn.jule @@ -4,8 +4,7 @@ use std::jule::ast::{Directive, GenericDecl, ScopeTree} use std::jule::build::{ENTRY_POINT, INIT_FN} -use std::jule::lex::{Token, TokenKind, is_anon_ident} -use strings for std::strings +use std::jule::lex::{Token, is_anon_ident} // Return type. pub struct RetType { @@ -96,38 +95,12 @@ impl Fn { ret self.result != nil && self.result.idents.len > 0 } - // Reports whether any parameter uses generic types. - pub fn parameters_uses_generics(self): bool { - if self.generics.len == 0 { - ret false - } - - for _, p in self.params { - if parameter_uses_generics(p, self.generics) { - ret true - } - } - - ret false - } - - // Reports whether result type uses generic types. - pub fn result_uses_generics(self): bool { - if self.is_void() { - ret false - } else if self.result.kind == nil || self.result.kind.kind == nil { - ret false - } - - ret kind_uses_generics(self.result.kind.kind, self.generics) - } - // Force to new instance. fn instance_force(mut &self): &FnIns { let mut ins = &FnIns{ decl: self, scope: new(Scope), - references: ReferenceStack.new(), + refers: ReferenceStack.new(), } ins.params = make([]&ParamIns, 0, self.params.len) @@ -219,14 +192,14 @@ impl ParamIns { // Function instance. pub struct FnIns { - pub owner: &StructIns - pub decl: &Fn - pub generics: []&TypeKind - pub params: []&ParamIns - pub result: &TypeKind - pub scope: &Scope - pub references: &ReferenceStack - pub anon: bool + pub owner: &StructIns + pub decl: &Fn + pub generics: []&TypeKind + pub params: []&ParamIns + pub result: &TypeKind + pub scope: &Scope + pub refers: &ReferenceStack + pub anon: bool caller: BuiltinCaller reloaded: bool @@ -417,19 +390,9 @@ impl FnIns { } } -fn kind_uses_generics(k: &TypeKind, &generics: []&GenericDecl): bool { - let pk = k.to_str() - for _, g in generics { - if strings::contains(pk, g.ident) { - ret true - } - } - ret false -} - fn parameter_uses_generics(&p: &Param, &generics: []&GenericDecl): bool { if p.is_self() { ret false } - ret kind_uses_generics(p.kind.kind, generics) + ret kind_uses_generics(p.kind.decl.kind, generics) } \ No newline at end of file diff --git a/std/jule/sema/scope.jule b/std/jule/sema/scope.jule index b78815ae4..4bd11c947 100644 --- a/std/jule/sema/scope.jule +++ b/std/jule/sema/scope.jule @@ -41,7 +41,7 @@ fn new_scope_checker_base(mut &s: &Sema, mut owner: &FnIns): &ScopeChecker { ret &ScopeChecker{ s: s, owner: owner, - table: &SymbolTable{}, + table: new(SymbolTable), } } @@ -1889,6 +1889,7 @@ impl ScopeChecker { | &BuiltinErrorCallExprModel: let mut m = (&BuiltinErrorCallExprModel)(d.model) self.process_error_call(m, expr.token) + self.scope.stmts = append(self.scope.stmts, d) |: ret } @@ -1908,7 +1909,14 @@ impl ScopeChecker { fn check_tree(mut &self) { self.i = 0 - for self.i < self.tree.stmts.len; self.i++ { + let mut n = self.tree.stmts.len + if self.result != nil { + // Skip last statement if result is exist. + // Algorithm will check last statement for result. + // So, if you check last statement also here, it will duplicate. + n-- + } + for self.i < n; self.i++ { let mut stmt = self.tree.stmts[self.i] self.check_node(stmt.data) if self.stopped() { diff --git a/std/jule/sema/sema.jule b/std/jule/sema/sema.jule index 22a5186f5..4d1fd4740 100644 --- a/std/jule/sema/sema.jule +++ b/std/jule/sema/sema.jule @@ -673,18 +673,19 @@ impl Sema { // Builds type with type aliases for generics. // Returns nil if error occur or failed. fn build_type_with_generics(mut &self, mut &t: &ast::TypeDecl, - mut generics: []&TypeAlias): &TypeKind { + mut generics: []&TypeAlias, mut refers: &ReferenceStack): &TypeKind { let mut tc = &TypeChecker{ s: self, lookup: self, use_generics: generics, + refers: refers, } ret tc.check_decl(t) } // Same as self.build_type_with_generics but not uses any generics. fn build_type(mut &self, mut &t: &ast::TypeDecl): &TypeKind { - ret self.build_type_with_generics(t, nil) + ret self.build_type_with_generics(t, nil, nil) } // Select type with name selection. @@ -833,8 +834,24 @@ impl Sema { } fn get_trait_check_fn_kind(mut &self, mut &f: &Fn): &FnIns { + if f.instances.len == 1 { + ret f.instances[0] + } let mut ins = f.instance_force() - self.build_fn_non_generic_type_kinds(ins) + + let mut tc = &TypeChecker{ + s: self, + lookup: self, + } + for (_, mut p) in ins.params { + if !p.decl.is_self() { + p.kind = tc.check_decl(p.decl.kind.decl) + } + } + if !f.is_void() { + ins.result = tc.check_decl(f.result.kind.decl) + } + ret ins } @@ -897,7 +914,7 @@ impl Sema { } } } else { - p.kind = sema.build_type_with_generics(p.decl.kind.decl, generics) + p.kind = sema.build_type_with_generics(p.decl.kind.decl, generics, f.refers) if p.kind != nil { p.kind.variadic = p.decl.variadic if p.decl.reference && !is_valid_for_ref(p.kind) { @@ -910,7 +927,7 @@ impl Sema { } if !f.decl.is_void() { - f.result = sema.build_type_with_generics(f.decl.result.kind.decl, generics) + f.result = sema.build_type_with_generics(f.decl.result.kind.decl, generics, f.refers) ok = f.result != nil && ok } @@ -1278,25 +1295,6 @@ impl Sema { ret } - fn check_fn_decl_types(mut &self, mut &f: &Fn) { - let mut generics = f.generics - if f.owner != nil && f.owner.generics.len != 0 { - generics = append(generics, f.owner.generics...) - } - - for (_, mut p) in f.params { - if !p.is_self() { - let mut kind = self.build_non_generic_type_kind(p.kind.decl, generics) - p.kind.kind = kind - } - } - - if !f.is_void() { - let mut kind = self.build_non_generic_type_kind(f.result.kind.decl, generics) - f.result.kind.kind = kind - } - } - // Checks generics, parameters and return type. // Not checks scope, and other things. fn check_fn_decl_prototype(mut &self, mut &f: &Fn) { @@ -1318,8 +1316,6 @@ impl Sema { | !self.check_fn_decl_params_dup(f): | !self.check_fn_decl_result_dup(f): } - - self.check_fn_decl_types(f) } fn check_trait_decl_method(mut &self, mut &f: &Fn) { @@ -1327,8 +1323,12 @@ impl Sema { self.push_err(f.token, LogMsg.IgnoreIdent) } - self.check_fn_decl_prototype(f) f.sema = self + self.check_fn_decl_prototype(f) + let mut ins = f.instance() + _ = self.reload_fn_ins_types(ins) + ins.reloaded = true + f.append_instance(ins) } fn check_trait_decl_methods(mut &self, mut &t: &Trait) { @@ -1628,23 +1628,8 @@ impl Sema { fn check_struct_fields(mut &self, mut &st: &Struct): (ok: bool) { ok = true - - let mut tc = &TypeChecker{ - s: self, - lookup: self, - ignore_generics: st.generics, - referencer: &Referencer{ - ident: st.ident, - owner: st, - }, - } - - let n = st.instances.len for (_, mut f) in st.fields { f.owner = st - f.kind.kind = tc.check_decl(f.kind.decl) - ok = f.kind.kind != nil && ok - for _, cf in st.fields { if f == cf { break @@ -1655,29 +1640,9 @@ impl Sema { } } } - - // Save itself for legal cycles like *Struct, or &Struct. - if ok && n != st.instances.len { - tc.referencer = nil - st.instances = st.instances[:n] - for (_, mut f) in st.fields { - f.kind.kind = tc.check_decl(f.kind.decl) - } - } - ret ok } - fn check_struct_decl_methods(mut &self, mut &s: &Struct): (ok: bool) { - for (_, mut m) in s.methods { - self.check_fn_decl_types(m) - if self.errors.len > 0 { - ret false - } - } - ret true - } - fn check_struct_decl(mut &self, mut &s: &Struct) { if is_ignore_ident(s.ident) { self.push_err(s.token, LogMsg.IgnoreIdent) @@ -1688,11 +1653,9 @@ impl Sema { self.check_directives(s.directives, s) - s.sema = self match { | !self.check_decl_generics(s.generics): | !self.check_struct_fields(s): - | !self.check_struct_decl_methods(s): | !self.check_struct_impls(s): } } @@ -1819,71 +1782,22 @@ impl Sema { ret true } - fn check_struct_derive_clone(mut self, mut &st: &Struct): bool { - ret self.check_struct_ins_derive_clone(st.instance()) - } - - fn check_file_derives(mut self): (ok: bool) { - // Check derives. - for (_, mut st) in self.file.structs { - ok = self.check_struct_derive_clone(st) - if !ok { - break - } - } - - ret ok - } - - fn check_package_derives(mut self) { - for (_, mut f) in self.files { - self.set_current_file(f) - let ok = self.check_file_derives() - if !ok { - ret - } - } - } - // Checks declarations of all package files. // Breaks checking if checked file failed. fn check_package_decls(mut &self) { for (_, mut f) in self.files { self.set_current_file(f) - if !self.check_type_alias_decls() { - ret - } - } - - for (_, mut f) in self.files { - self.set_current_file(f) - if !self.check_trait_decls() { - ret - } - } - - for (_, mut f) in self.files { - self.set_current_file(f) - if !self.check_global_decls() { - ret - } - } - - for (_, mut f) in self.files { - self.set_current_file(f) - if !self.check_fn_decls() { - ret - } - } - - for (_, mut f) in self.files { - self.set_current_file(f) - if !self.check_struct_decls() { - ret + match { + | !self.check_type_alias_decls(): + | !self.check_trait_decls(): + | !self.check_global_decls(): + | !self.check_fn_decls(): + | !self.check_struct_decls(): + |: + continue } + ret } - - self.check_package_derives() } fn check_data_for_type_inference(mut self, &d: &Data, &err_token: &Token) { @@ -2010,9 +1924,65 @@ impl Sema { } } - fn check_field_defaults(mut &self, mut &ins: &StructIns) { + fn check_struct_ins_op(mut &self, mut &s: &StructIns, mut &f: &Fn, p: fn(f: &Fn): bool): &FnIns { + if f == nil || f.generics.len > 0 { + ret nil + } + let mut ins = self.ready_to_check_fn(s, f) + if p(f) { + ret ins + } + ret nil + } + + fn check_fields(mut &self, mut &s: &StructIns): (ok: bool) { + let mut tc = TypeChecker{ + s: s.decl.sema, + lookup: s.decl.sema, + referencer: &Referencer{ + ident: s.decl.ident, + owner: s.decl, + }, + refers: s.refers, + } + + if s.generics.len > 0 { + tc.use_generics = make([]&TypeAlias, 0, s.generics.len) + for (i, mut g) in s.generics { + tc.use_generics = append(tc.use_generics, &TypeAlias{ + ident: s.decl.generics[i].ident, + kind: &TypeSymbol{ + kind: g, + }, + }) + } + } + + let mut old_file = self.file + defer { self.set_current_file(old_file) } + + if self.file.file != s.decl.token.file { + let mut file = find_file(self.files, s.decl.token.file) + if file != nil { + self.set_current_file(file) + } + } + let mut eval = self.eval(self) - for (_, mut f) in ins.fields { + for (_, mut f) in s.fields { + let mut kind = tc.check_decl(f.decl.kind.decl) + ok = kind != nil && ok + if kind == nil { + if self != s.decl.sema && s.decl.sema.errors.len > 0 { + self.errors = append(self.errors, s.decl.sema.errors...) + s.decl.sema.errors = nil + } + continue + } + f.kind = kind + s.mutable = s.mutable || (!f.decl.mutable && f.kind.mutable()) + _ = self.check_struct_ins_derive_clone(s) + // Skip this field if not has default value. if f.decl.default == nil { continue @@ -2030,21 +2000,14 @@ impl Sema { const REFERENCE = false // Fields cannot be reference. _ = self.check_assign_type(REFERENCE, f.kind, f.default, f.decl.default.token) } + ret } - fn check_struct_ins_op(mut &self, mut &s: &StructIns, mut &f: &Fn, p: fn(f: &Fn): bool): &FnIns { - if f == nil || f.generics.len > 0 { - ret nil - } - let mut ins = self.ready_to_check_fn(s, f) - if p(f) { - ret ins - } - ret nil + fn precheck_struct_ins(mut &self, mut &s: &StructIns) { + self.check_fields(s) } fn check_struct_ins(mut &self, mut &s: &StructIns) { - self.check_field_defaults(s) for (_, mut f) in s.methods { self.check_type_method(s, f) } @@ -2060,12 +2023,9 @@ impl Sema { ret } - if s.instances.len == 0 { - let mut ins = s.instance() - ins.checked = true - s.append_instance(ins) - } - + // NOTICE: + // Do not check zero instance case, operator overloading + // checker will create an instance if not exist. for (_, mut ins) in s.instances { self.check_struct_ins(ins) } @@ -2140,7 +2100,7 @@ impl Sema { } } - fn check_type_struct_operators(mut &self, mut &s: &Struct) { + fn precheck_struct_type(mut &self, mut &s: &Struct) { if s.cpp_linked { ret } @@ -2153,15 +2113,16 @@ impl Sema { if s.instances.len == 0 { let mut ins = s.instance() ins.checked = true - s.append_instance(ins) + s.append_instance(ins) // Append instance before precheck. + self.precheck_struct_ins(ins) } self.check_struct_ins_operators(s.instances[0]) } - fn check_struct_types_operators(mut &self) { + fn precheck_struct_types(mut &self) { for (_, mut s) in self.file.structs { - self.check_type_struct_operators(s) + self.precheck_struct_type(s) } } @@ -2306,29 +2267,18 @@ impl Sema { // Checks all types of all package files. // Breaks checking if checked file failed. fn check_package_types(mut &self) { - for (_, mut f) in self.files { - self.set_current_file(f) - self.check_global_types() - } - for (_, mut f) in self.files { self.set_current_file(f) self.precheck_fn_types() + // Check structures first, see developer reference (7). + // Check operators first, see developer reference (7.1), and (7.2). + self.precheck_struct_types() } - // Check structures first, see developer reference (7). - // Check operators first, see developer reference (7.1), and (7.2). - for (_, mut f) in self.files { - self.set_current_file(f) - self.check_struct_types_operators() - } for (_, mut f) in self.files { self.set_current_file(f) + self.check_global_types() self.check_struct_types() - } - - for (_, mut f) in self.files { - self.set_current_file(f) self.check_fn_types() } } @@ -2359,6 +2309,8 @@ impl Sema { ret } + // Check enums here. + // See developer reference (8). self.check_enums() // Break checking if enums has error. if self.errors.len != 0 { diff --git a/std/jule/sema/struct.jule b/std/jule/sema/struct.jule index e6601ab1e..e6c54f42d 100644 --- a/std/jule/sema/struct.jule +++ b/std/jule/sema/struct.jule @@ -21,7 +21,6 @@ impl Field { fn instance(mut &self): &FieldIns { ret &FieldIns{ decl: self, - kind: self.kind.kind, } } } @@ -100,7 +99,7 @@ impl Struct { let mut ins = &StructIns{ decl: self, fields: make([]&FieldIns, 0, self.fields.len), - references: ReferenceStack.new(), + refers: ReferenceStack.new(), } for (_, mut f) in self.fields { @@ -227,14 +226,14 @@ pub struct FieldIns { // Structure instance. pub struct StructIns { - pub checked: bool - pub decl: &Struct - pub generics: []&TypeKind - pub fields: []&FieldIns - pub methods: []&Fn - pub mutable: bool // This structure has mutable defines. - pub references: &ReferenceStack - pub operators: Operators + pub checked: bool + pub decl: &Struct + pub generics: []&TypeKind + pub fields: []&FieldIns + pub methods: []&Fn + pub mutable: bool // This structure has mutable defines. + pub refers: &ReferenceStack + pub operators: Operators } impl Kind for StructIns { diff --git a/std/jule/sema/symbol.jule b/std/jule/sema/symbol.jule index d70812c21..b962cc917 100644 --- a/std/jule/sema/symbol.jule +++ b/std/jule/sema/symbol.jule @@ -44,7 +44,9 @@ pub struct ReferenceStack { impl ReferenceStack { // Returns new reference stack instance. static fn new(): &ReferenceStack { - ret new(ReferenceStack) + ret &ReferenceStack{ + buffer: make([]any, 0, 10) + } } } @@ -65,7 +67,7 @@ impl ReferenceStack { } // Reports whether reference is exist. - pub fn exist[T](self, t: &T): bool { + pub fn exist[T](self, t: T): bool { for _, ref in self.buffer { if ref == t { ret true @@ -242,7 +244,7 @@ fn build_var(mut decl: &VarDecl): &Var { statically: decl.statically, reference: decl.reference, directives: decl.directives, - references: ReferenceStack.new(), + refers: ReferenceStack.new(), kind: build_type(decl.kind), value: build_expr(decl.expr), } diff --git a/std/jule/sema/type.jule b/std/jule/sema/type.jule index 444ac1c02..a8814e27a 100644 --- a/std/jule/sema/type.jule +++ b/std/jule/sema/type.jule @@ -29,16 +29,6 @@ use strings for std::strings type PrimKind: types::TypeKind -// Reports whether directive is exist. -fn has_directive(mut &directives: []&ast::Directive, tag: str): bool { - for (_, mut dr) in directives { - if dr.tag.kind == tag { - ret true - } - } - ret false -} - // Type alias. pub struct TypeAlias { pub scope: &ScopeTree @@ -568,93 +558,6 @@ impl Ptr { pub fn is_unsafe(self): bool { ret self.elem == nil } } -fn can_get_ptr(mut &d: &Data): bool { - if !d.lvalue || d.is_const() { - ret false - } - - match { - | d.kind.fnc() != nil || d.kind.enm() != nil: - ret false - |: - ret true - } -} - -// Reports kind is valid for smart pointer type such as &T. -fn is_valid_for_sptr_type(mut &t: &TypeKind): bool { - let mut s = t.strct() - if s != nil && s.decl != nil && s.decl.cpp_linked { - ret false - } - ret true -} - -// Reports kind is valid for reference such as reference variables. -fn is_valid_for_ref(mut &t: &TypeKind): bool { ret t.fnc() == nil } - -// Reports whether type has built-in string conversion support. -fn is_builtin_str_convertable(mut &t: &TypeKind): bool { - ret !t.void() && t.fnc() == nil && t.tup() == nil -} - -fn build_link_path_by_tokens(&tokens: []&Token): str { - let mut s = tokens[0].kind - for _, token in tokens[1:] { - s += "::" - s += token.kind - } - ret s -} - -fn build_prim_type(kind: str): &Prim { - ret &Prim{ - kind: kind, - } -} - -fn get_struct_from_kind(mut k: &TypeKind): &Struct { - match { - | k == nil: - ret nil - | k.strct() != nil: - ret k.strct().decl - | k.sptr() != nil: - ret get_struct_from_kind(k.sptr().elem) - | k.slc() != nil: - ret get_struct_from_kind(k.slc().elem) - | k.arr() != nil: - ret get_struct_from_kind(k.arr().elem) - | k.ptr() != nil: - // Pass pointers. - // Cloning just copies pointer address. - // There is no any illegal cycle risk. - ret nil - |: - ret nil - } -} - -// Reports whether kind is primitive type. -fn is_prim(kind: str): bool { - ret kind == TokenKind.I8 || - kind == TokenKind.I16 || - kind == TokenKind.I32 || - kind == TokenKind.I64 || - kind == TokenKind.U8 || - kind == TokenKind.U16 || - kind == TokenKind.U32 || - kind == TokenKind.U64 || - kind == TokenKind.F32 || - kind == TokenKind.F64 || - kind == TokenKind.Int || - kind == TokenKind.Uint || - kind == TokenKind.Uintptr || - kind == TokenKind.Bool || - kind == TokenKind.Str || - kind == TokenKind.Any -} - struct Referencer { ident: str owner: any @@ -677,6 +580,9 @@ struct TypeChecker { // Also used as checker owner. referencer: &Referencer + // If this not nil, type dependencies will push into stack. + refers: &ReferenceStack + error_token: &Token // This identifiers ignored and @@ -742,6 +648,16 @@ impl TypeChecker { ret build_prim_type(decl.ident) } + fn push_reference[T](mut self, mut &t: T) { + if self.refers == nil { + ret + } + if self.refers.exist[T](t) { + ret + } + self.refers.push(t) + } + fn push_cycle_error(self, def1: any, def2: any, mut &message: str) { const PADDING = 4 @@ -947,64 +863,16 @@ impl TypeChecker { if ins.checked { ret true } - ins.checked = true - - if self.referencer != nil && self.referencer.owner == ins.decl { - // Break algorithm cycle. - ret true - } - - let mut old_file = self.s.file - defer { self.s.set_current_file(old_file) } - - let mut file = find_file(self.s.files, ins.decl.token.file) - if file != nil { - self.s.set_current_file(file) - } - - let mut referencer = &Referencer{ - ident: ins.decl.ident, - owner: ins.decl, - } - - let mut generics = make([]&TypeAlias, 0, ins.generics.len) - for (i, mut g) in ins.generics { - generics = append(generics, &TypeAlias{ - ident: ins.decl.generics[i].ident, - kind: &TypeSymbol{ - kind: g, - }, - }) - } - - let mut old_sema_file = ins.decl.sema.file defer { - ins.decl.sema.set_current_file(old_sema_file) + ins.checked = true } - let mut tc = TypeChecker{ - s: ins.decl.sema, - lookup: ins.decl.sema, - referencer: referencer, - use_generics: generics, - } - - // Check field types. - for (_, mut field) in ins.fields { - let mut kind = tc.check_decl(field.decl.kind.decl) - ok = kind != nil - - if ins.decl.sema != nil && self.s != ins.decl.sema && ins.decl.sema.errors.len > 0 { - self.s.errors = append(self.s.errors, ins.decl.sema.errors...) - ins.decl.sema.errors = nil - } - if ok { - field.kind = kind - ins.mutable = ins.mutable || (!field.decl.mutable && field.kind.mutable()) - _ = self.s.check_struct_ins_derive_clone(ins) - } + // Break algorithm cycle. + if self.referencer != nil && self.referencer.owner == ins.decl { + ret true } + self.s.precheck_struct_ins(ins) if ins.generics.len > 0 { self.s.check_struct_ins_operators(ins) self.s.check_struct_ins(ins) @@ -1088,11 +956,6 @@ impl TypeChecker { } } - if s.generics.len > 0 && self.ignore_generics.len > 0 { - // Ignore prototypes. - ret nil - } - let mut ok = self.not_plain || self.check_illegal_cycles(decl, s) if !ok { ret nil @@ -1112,7 +975,8 @@ impl TypeChecker { let mut exist_instance = s.append_instance(ins) if exist_instance != nil { // Already checked instance, did not appended. - // So, this instance is not unqiue. + // So, this instance is not unique. + self.push_reference[&StructIns](exist_instance) ret exist_instance } @@ -1121,6 +985,7 @@ impl TypeChecker { ret nil } + self.push_reference[&StructIns](ins) ret ins } @@ -1422,8 +1287,6 @@ impl TypeChecker { fn build(mut self, mut &decl_kind: TypeDeclKind): &TypeKind { let mut kind: Kind = nil match type decl_kind { - | &TypeKind: - kind = (&TypeKind)(decl_kind) | &IdentTypeDecl: let mut t = self.build_ident((&IdentTypeDecl)(decl_kind)) if t != nil { @@ -1518,6 +1381,166 @@ impl TypeChecker { } } +struct IdentTypeLookup {} + +impl IdentTypeLookup { + static fn exist(&ident: str, &k: TypeDeclKind): bool { + match type k { + | &IdentTypeDecl: + ret (&IdentTypeDecl)(k).ident == ident + | &SptrTypeDecl: + let sptr = (&SptrTypeDecl)(k) + ret IdentTypeLookup.exist(ident, sptr.elem.kind) + | &PtrTypeDecl: + let ptr = (&PtrTypeDecl)(k) + ret IdentTypeLookup.exist(ident, ptr.elem.kind) + | &SlcTypeDecl: + let slc = (&SlcTypeDecl)(k) + ret IdentTypeLookup.exist(ident, slc.elem.kind) + | &ArrTypeDecl: + let arr = (&ArrTypeDecl)(k) + ret IdentTypeLookup.exist(ident, arr.elem.kind) + | &MapTypeDecl: + let map = (&MapTypeDecl)(k) + ret IdentTypeLookup.exist(ident, map.key.kind) || + IdentTypeLookup.exist(ident, map.val.kind) + | &FnDecl: + let f = (&FnDecl)(k) + for _, p in f.params { + if p.is_self() { + continue + } + if IdentTypeLookup.exist(ident, p.kind.kind) { + ret true + } + } + if f.result != nil { + ret IdentTypeLookup.exist(ident, f.result.kind.kind) + } + ret false + | &TupleTypeDecl: + let tup = (&TupleTypeDecl)(k) + for _, t in tup.types { + if IdentTypeLookup.exist(ident, t.kind) { + ret true + } + } + ret false + | &NamespaceTypeDecl: + let ns = (&NamespaceTypeDecl)(k) + ret ns.kind.ident == ident + |: + ret false + } + } +} + +fn kind_uses_generics(&k: TypeDeclKind, &generics: []&GenericDecl): bool { + for _, g in generics { + if IdentTypeLookup.exist(g.ident, k) { + ret true + } + } + ret false +} + +// Reports whether directive is exist. +fn has_directive(mut &directives: []&ast::Directive, tag: str): bool { + for (_, mut dr) in directives { + if dr.tag.kind == tag { + ret true + } + } + ret false +} + +fn can_get_ptr(mut &d: &Data): bool { + if !d.lvalue || d.is_const() { + ret false + } + + match { + | d.kind.fnc() != nil || d.kind.enm() != nil: + ret false + |: + ret true + } +} + +// Reports kind is valid for smart pointer type such as &T. +fn is_valid_for_sptr_type(mut &t: &TypeKind): bool { + let mut s = t.strct() + if s != nil && s.decl != nil && s.decl.cpp_linked { + ret false + } + ret true +} + +// Reports kind is valid for reference such as reference variables. +fn is_valid_for_ref(mut &t: &TypeKind): bool { ret t.fnc() == nil } + +// Reports whether type has built-in string conversion support. +fn is_builtin_str_convertable(mut &t: &TypeKind): bool { + ret !t.void() && t.fnc() == nil && t.tup() == nil +} + +fn build_link_path_by_tokens(&tokens: []&Token): str { + let mut s = tokens[0].kind + for _, token in tokens[1:] { + s += "::" + s += token.kind + } + ret s +} + +fn build_prim_type(kind: str): &Prim { + ret &Prim{ + kind: kind, + } +} + +fn get_struct_from_kind(mut k: &TypeKind): &Struct { + match { + | k == nil: + ret nil + | k.strct() != nil: + ret k.strct().decl + | k.sptr() != nil: + ret get_struct_from_kind(k.sptr().elem) + | k.slc() != nil: + ret get_struct_from_kind(k.slc().elem) + | k.arr() != nil: + ret get_struct_from_kind(k.arr().elem) + | k.ptr() != nil: + // Pass pointers. + // Cloning just copies pointer address. + // There is no any illegal cycle risk. + ret nil + |: + ret nil + } +} + +// Reports whether kind is primitive type. +fn is_prim(kind: str): bool { + ret kind == TokenKind.I8 || + kind == TokenKind.I16 || + kind == TokenKind.I32 || + kind == TokenKind.I64 || + kind == TokenKind.U8 || + kind == TokenKind.U16 || + kind == TokenKind.U32 || + kind == TokenKind.U64 || + kind == TokenKind.F32 || + kind == TokenKind.F64 || + kind == TokenKind.Int || + kind == TokenKind.Uint || + kind == TokenKind.Uintptr || + kind == TokenKind.Bool || + kind == TokenKind.Str || + kind == TokenKind.Any +} + fn apply_implicit_cast(mut &dest: &TypeKind, mut &d: &Data) { if d.kind.is_nil() { ret diff --git a/std/jule/sema/type2.jule b/std/jule/sema/type2.jule index cbf8969f0..e5abda702 100644 --- a/std/jule/sema/type2.jule +++ b/std/jule/sema/type2.jule @@ -686,13 +686,14 @@ impl FnCallArgChecker { ok = true let mut i = 0 + if params.len > 0 && params[0].decl.is_self() { + // Ignore self. + i++ + } iter: for i < params.len { let mut p = params[i] match { - | p.decl.is_self(): - // Ignore self. - break | p.decl.variadic: ok = self.push_variadic(p, i) && ok // Variadiced parameters always last. diff --git a/std/jule/sema/var.jule b/std/jule/sema/var.jule index 5c5acc927..1c4f96a6f 100644 --- a/std/jule/sema/var.jule +++ b/std/jule/sema/var.jule @@ -26,7 +26,7 @@ pub struct Var { pub reference: bool pub kind: &TypeSymbol pub value: &Value - pub references: &ReferenceStack + pub refers: &ReferenceStack pub directives: []&Directive pub iter_relation: &IterRelation