From b1fbc00092d7c71cc9db00397b195607afa50fe4 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Sun, 7 Apr 2024 14:09:30 +0300 Subject: [PATCH 01/12] sema: fix generic function instance modeling --- src/julec/obj/cxx/ident.jule | 4 ++-- std/jule/sema/eval.jule | 31 ++++++++++++++++++++++++------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/julec/obj/cxx/ident.jule b/src/julec/obj/cxx/ident.jule index 17f071f5b..9de064070 100644 --- a/src/julec/obj/cxx/ident.jule +++ b/src/julec/obj/cxx/ident.jule @@ -93,7 +93,7 @@ impl IdentCoder { ret IdentCoder.func(f.decl) } for (i, mut ins) in f.decl.instances { - if ins.same(f) { + if ins == f { let mut obj = IdentCoder.func(f.decl) obj += "_" obj += conv::itoa(i) @@ -142,7 +142,7 @@ impl IdentCoder { ret IdentCoder.structure(s.decl) } for (i, mut ins) in s.decl.instances { - if ins.same(s) { + if ins == s { let mut obj = IdentCoder.structure(s.decl) obj += "_" obj += conv::itoa(i) diff --git a/std/jule/sema/eval.jule b/std/jule/sema/eval.jule index f8af03cfa..c4eba180f 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::{ @@ -1892,13 +1891,14 @@ impl Eval { 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 +1916,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) + } } } @@ -4320,4 +4328,13 @@ 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 + } +} From 6f967b0edfef9465ee8d00c2e9b3b7f3c9c08b25 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Sun, 7 Apr 2024 15:01:16 +0300 Subject: [PATCH 02/12] compiler: minor optimizations --- src/julec/obj/cxx/ident.jule | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/julec/obj/cxx/ident.jule b/src/julec/obj/cxx/ident.jule index 9de064070..33274da4f 100644 --- a/src/julec/obj/cxx/ident.jule +++ b/src/julec/obj/cxx/ident.jule @@ -92,15 +92,7 @@ impl IdentCoder { if f.decl.cpp_linked || f.generics.len == 0 { ret IdentCoder.func(f.decl) } - for (i, mut ins) in f.decl.instances { - if ins == 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. @@ -141,15 +133,7 @@ impl IdentCoder { if s.decl.cpp_linked || s.generics.len == 0 { ret IdentCoder.structure(s.decl) } - for (i, mut ins) in s.decl.instances { - if ins == 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. From 81b97af0c3e9ad103735c237596127baef0c2fad Mon Sep 17 00:00:00 2001 From: mertcandav Date: Sun, 7 Apr 2024 16:57:23 +0300 Subject: [PATCH 03/12] api: use FNV-1A algorithm for hashing of the jule::Map's keys --- api/map.hpp | 44 ++++++++++++++++++++++++++++++++++++++------ api/str.hpp | 6 +++--- 2 files changed, 41 insertions(+), 9 deletions(-) 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(); From 6f09e29da15a5b8ca2a7ebd9377f227c106addd7 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Sun, 7 Apr 2024 17:06:19 +0300 Subject: [PATCH 04/12] compiler: improve identifier selection for object --- src/julec/obj/cxx/ident.jule | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/julec/obj/cxx/ident.jule b/src/julec/obj/cxx/ident.jule index 33274da4f..3cbd6b090 100644 --- a/src/julec/obj/cxx/ident.jule +++ b/src/julec/obj/cxx/ident.jule @@ -33,7 +33,16 @@ impl IdentCoder { // Returns cpp output identifier form of pointer address. static fn addr(addr: uintptr): str { - ret "_" + conv::fmt_uint(u64(addr), 0xF) + static mut hash: [uintptr:str] = nil + static mut n = u64(0) + let z = hash[addr] + if z != "" { + ret z + } + let s = "_" + conv::fmt_uint(n, 0xF) + hash[addr] = s + n++ + ret s } // Returns cpp output identifier form of given identifier. From a5e80428d7000c34d3304c003c809ebf1386f23c Mon Sep 17 00:00:00 2001 From: mertcandav Date: Sun, 7 Apr 2024 17:39:45 +0300 Subject: [PATCH 05/12] compiler: minor optimizations --- src/julec/obj/cxx/ident.jule | 75 +++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/src/julec/obj/cxx/ident.jule b/src/julec/obj/cxx/ident.jule index 3cbd6b090..32f1f638e 100644 --- a/src/julec/obj/cxx/ident.jule +++ b/src/julec/obj/cxx/ident.jule @@ -25,39 +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 { - static mut hash: [uintptr:str] = nil - static mut n = u64(0) - let z = hash[addr] - if z != "" { - ret z - } - let s = "_" + conv::fmt_uint(n, 0xF) - hash[addr] = s - n++ - ret s - } - // 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. @@ -66,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. @@ -94,7 +81,7 @@ 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 } @@ -138,7 +125,7 @@ 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) } @@ -174,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 From 093622c40c1f35843c0a04a1ce39335f1ed7cee7 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Sun, 7 Apr 2024 18:22:24 +0300 Subject: [PATCH 06/12] sema: optimize result checking for scopes --- std/jule/sema/scope.jule | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/std/jule/sema/scope.jule b/std/jule/sema/scope.jule index b78815ae4..a7a7e1915 100644 --- a/std/jule/sema/scope.jule +++ b/std/jule/sema/scope.jule @@ -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() { From 282010cbf8de05f8bae2b70593bf27394842cefc Mon Sep 17 00:00:00 2001 From: mertcandav Date: Mon, 8 Apr 2024 00:16:12 +0300 Subject: [PATCH 07/12] compiler, sema: improve and reimplement --opt-deadcode and reference stack --- src/julec/compile.jule | 5 - src/julec/opt/deadcode/define.jule | 430 +++++++++++------------------ std/jule/sema/eval.jule | 38 +-- std/jule/sema/fn.jule | 18 +- std/jule/sema/scope.jule | 2 +- std/jule/sema/sema.jule | 9 +- std/jule/sema/struct.jule | 18 +- std/jule/sema/symbol.jule | 8 +- std/jule/sema/type.jule | 16 ++ std/jule/sema/var.jule | 2 +- 10 files changed, 213 insertions(+), 333 deletions(-) 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/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/sema/eval.jule b/std/jule/sema/eval.jule index c4eba180f..439e1760b 100644 --- a/std/jule/sema/eval.jule +++ b/std/jule/sema/eval.jule @@ -424,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, @@ -538,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) } @@ -626,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 { @@ -1348,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()) @@ -1629,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{ @@ -2003,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, @@ -2096,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, diff --git a/std/jule/sema/fn.jule b/std/jule/sema/fn.jule index e594eb32b..aa64aeaae 100644 --- a/std/jule/sema/fn.jule +++ b/std/jule/sema/fn.jule @@ -127,7 +127,7 @@ impl Fn { 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 +219,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 diff --git a/std/jule/sema/scope.jule b/std/jule/sema/scope.jule index a7a7e1915..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), } } diff --git a/std/jule/sema/sema.jule b/std/jule/sema/sema.jule index 22a5186f5..cc5c920b5 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. @@ -897,7 +898,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 +911,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 } diff --git a/std/jule/sema/struct.jule b/std/jule/sema/struct.jule index e6601ab1e..e00d14d11 100644 --- a/std/jule/sema/struct.jule +++ b/std/jule/sema/struct.jule @@ -100,7 +100,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 +227,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..c31208fdb 100644 --- a/std/jule/sema/type.jule +++ b/std/jule/sema/type.jule @@ -677,6 +677,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 +745,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 @@ -987,6 +1000,7 @@ impl TypeChecker { lookup: ins.decl.sema, referencer: referencer, use_generics: generics, + refers: ins.refers, } // Check field types. @@ -1113,6 +1127,7 @@ impl TypeChecker { if exist_instance != nil { // Already checked instance, did not appended. // So, this instance is not unqiue. + self.push_reference[&StructIns](exist_instance) ret exist_instance } @@ -1121,6 +1136,7 @@ impl TypeChecker { ret nil } + self.push_reference[&StructIns](ins) ret ins } 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 From 1409c949f3cac83cf7fdf6623f8166e90a310f55 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Mon, 8 Apr 2024 00:36:58 +0300 Subject: [PATCH 08/12] sema: fix generic function analysis --- std/jule/sema/eval.jule | 18 +++++++++--------- std/jule/sema/type2.jule | 7 ++++--- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/std/jule/sema/eval.jule b/std/jule/sema/eval.jule index 439e1760b..dba26e18d 100644 --- a/std/jule/sema/eval.jule +++ b/std/jule/sema/eval.jule @@ -1854,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 @@ -1864,15 +1873,6 @@ 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 } 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. From 55364d404accded8950ea62e231a87ed01eb42e9 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Mon, 8 Apr 2024 18:25:50 +0300 Subject: [PATCH 09/12] sema: refactoring and minor fixes/optimizations --- std/jule/sema/eval.jule | 2 +- std/jule/sema/fn.jule | 41 +---- std/jule/sema/sema.jule | 194 +++++++++++------------ std/jule/sema/struct.jule | 1 - std/jule/sema/type.jule | 325 +++++++++++++++++++------------------- 5 files changed, 257 insertions(+), 306 deletions(-) diff --git a/std/jule/sema/eval.jule b/std/jule/sema/eval.jule index dba26e18d..c8555528a 100644 --- a/std/jule/sema/eval.jule +++ b/std/jule/sema/eval.jule @@ -1830,7 +1830,7 @@ impl Eval { } f.reloaded = true } - } else { + } else if f.generics.len > 0 { self.s.build_fn_non_generic_type_kinds(f) } diff --git a/std/jule/sema/fn.jule b/std/jule/sema/fn.jule index aa64aeaae..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,32 +95,6 @@ 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{ @@ -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/sema.jule b/std/jule/sema/sema.jule index cc5c920b5..5d53cd502 100644 --- a/std/jule/sema/sema.jule +++ b/std/jule/sema/sema.jule @@ -834,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 } @@ -1279,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) { @@ -1319,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) { @@ -1328,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) { @@ -1629,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 @@ -1656,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) @@ -1693,7 +1657,6 @@ impl Sema { match { | !self.check_decl_generics(s.generics): | !self.check_struct_fields(s): - | !self.check_struct_decl_methods(s): | !self.check_struct_impls(s): } } @@ -1820,32 +1783,6 @@ 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) { @@ -1883,8 +1820,6 @@ impl Sema { ret } } - - self.check_package_derives() } fn check_data_for_type_inference(mut self, &d: &Data, &err_token: &Token) { @@ -2011,9 +1946,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 @@ -2031,21 +2022,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) } @@ -2061,12 +2045,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) } @@ -2154,7 +2135,8 @@ 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]) diff --git a/std/jule/sema/struct.jule b/std/jule/sema/struct.jule index e00d14d11..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, } } } diff --git a/std/jule/sema/type.jule b/std/jule/sema/type.jule index c31208fdb..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 @@ -960,65 +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, - refers: ins.refers, - } - - // 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) @@ -1102,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 @@ -1126,7 +975,7 @@ 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 } @@ -1438,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 { @@ -1534,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 From b34478004aebb65f52348339a8b77a87ffa4d634 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Mon, 8 Apr 2024 19:00:10 +0300 Subject: [PATCH 10/12] sema: minor optimizations --- std/jule/README.md | 2 ++ std/jule/sema/sema.jule | 67 +++++++++++------------------------------ 2 files changed, 20 insertions(+), 49 deletions(-) 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/sema.jule b/std/jule/sema/sema.jule index 5d53cd502..4d1fd4740 100644 --- a/std/jule/sema/sema.jule +++ b/std/jule/sema/sema.jule @@ -1653,7 +1653,6 @@ impl Sema { self.check_directives(s.directives, s) - s.sema = self match { | !self.check_decl_generics(s.generics): | !self.check_struct_fields(s): @@ -1788,37 +1787,16 @@ impl Sema { 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 } } @@ -2122,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 } @@ -2142,9 +2120,9 @@ impl Sema { 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) } } @@ -2289,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() } } @@ -2342,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 { From 3b233b9a9f430988ba40e0b6b95c31f31bea7d1f Mon Sep 17 00:00:00 2001 From: mertcandav Date: Mon, 8 Apr 2024 20:15:18 +0300 Subject: [PATCH 11/12] sema: fix generic static method function instance modeling --- std/jule/sema/eval.jule | 2 ++ 1 file changed, 2 insertions(+) diff --git a/std/jule/sema/eval.jule b/std/jule/sema/eval.jule index c8555528a..e69263ac7 100644 --- a/std/jule/sema/eval.jule +++ b/std/jule/sema/eval.jule @@ -4322,5 +4322,7 @@ fn update_model_to_generic_ins(mut &m: &FnCallExprModel, mut &f: &FnIns) { m.expr = f | &StructSubIdentExprModel: (&StructSubIdentExprModel)(m.expr).method = f + | &StructStaticIdentExprModel: + (&StructStaticIdentExprModel)(m.expr).method = f } } From 1187d37b02914084e35d008351bd5d86dda0c57b Mon Sep 17 00:00:00 2001 From: mertcandav Date: Tue, 9 Apr 2024 00:01:34 +0300 Subject: [PATCH 12/12] api: use std::uinptr_t for jule::Uinptr and code generation will generate jule::Uintptr instead of platform bitsize-unsigned integer type --- api/types.hpp | 3 +-- src/julec/obj/cxx/type.jule | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) 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/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. }