Skip to content

Commit

Permalink
jule: add operator overloading support via reserved methods
Browse files Browse the repository at this point in the history
  • Loading branch information
mertcandav committed Feb 22, 2024
1 parent 310b4c3 commit 3d25afa
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 49 deletions.
48 changes: 36 additions & 12 deletions src/julec/obj/cxx/object.jule
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ use std::jule::lex::{
is_ignore_ident,
is_anon_ident,
}
use std::jule::pattern::{FuncPattern}
use std::jule::sema::{
FuncPattern,
Package,
SymbolTable,
Param,
Expand Down Expand Up @@ -451,7 +451,8 @@ impl ObjectCoder {

fn structure_destructor(mut self, mut &s: &StructIns): str {
const STATIC = false // Dispose method must be non-static
let mut disposed = FuncPattern.dispose(s.decl.find_method("dispose", STATIC))
let dispose_method = s.find_method("dispose", STATIC)
let mut disposed = FuncPattern.dispose(dispose_method)
let ref_access = s.decl.has_ref_accessible()
// Call destructor if implemented.
if !ref_access && !disposed {
Expand All @@ -463,7 +464,6 @@ impl ObjectCoder {
obj += "(void) { "

if disposed {
let dispose_method = s.find_method("dispose", false)
obj += "this->"
obj += IdentCoder.func(dispose_method)
obj += "(); "
Expand All @@ -477,17 +477,14 @@ impl ObjectCoder {
ret obj
}

fn structure_operators(mut self, mut &s: &StructIns): str {
let out_ident = IdentCoder.structure_ins(s)
let mut obj = ""

fn structure_operator_eq(mut self, mut &obj: str, &ident: str, mut &s: &StructIns) {
obj += self.indent()
if env::OPT_INLINE {
obj += "inline "
}
obj += "bool operator==(const "
obj += out_ident
obj += " &_Src) const {"
obj += ident
obj += " &_other) const {"
if s.fields.len > 0 {
self.add_indent()
obj += "\n"
Expand All @@ -508,7 +505,7 @@ impl ObjectCoder {
obj += "this->"
let f_ident = IdentCoder.field(f.decl)
obj += f_ident
obj += " == _Src."
obj += " == _other."
obj += f_ident
obj += " &&"
}
Expand All @@ -526,13 +523,40 @@ impl ObjectCoder {
obj += " return true; }"
}
obj += "\n\n"
}

fn structure_operator_not_eq(mut self, mut &obj: str, &ident: str, mut &s: &StructIns) {
obj += self.indent()
if env::OPT_INLINE {
obj += "inline "
}
obj += "bool operator!=(const "
obj += out_ident
obj += " &_Src) const { return !this->operator==(_Src); }"
obj += ident
obj += " &_other) const { return !this->operator==(_other); }\n\n"
}

fn structure_operator_gt(mut self, mut &obj: str, &ident: str, mut &s: &StructIns) {
let mut gt = s.find_method("gt", false)
if gt == nil || !FuncPattern.gt(gt) {
ret
}
obj += self.indent()
if env::OPT_INLINE {
obj += "inline "
}
obj += "bool operator>("
obj += ident
obj += " _other) { return this->"
obj += IdentCoder.func(gt)
obj += "(_other); }\n\n"
}

fn structure_operators(mut self, mut &s: &StructIns): str {
let ident = IdentCoder.structure_ins(s)
let mut obj = ""
self.structure_operator_eq(obj, ident, s)
self.structure_operator_not_eq(obj, ident, s)
self.structure_operator_gt(obj, ident, s)
ret obj
}

Expand Down
17 changes: 10 additions & 7 deletions std/jule/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ It is also used by the official reference compiler JuleC and is developed in par
- [`ast`](./ast): AST things.
- [`lex`](./lex): Lexical analyzer.
- [`parser`](./parser): Parser.
- [`pattern`](./pattern): Pattern checking for defines such as reserved functions.
- [`sema`](./sema): Semantic analyzer.
- [`types`](./types): Elementary package for type safety.

Expand All @@ -31,12 +30,16 @@ For example:
struct Test[T1, T2] {}
impl Test {
static fn new(): Test[T1, T2] {
type Y: T1
ret Test[T1, T2]{}
ret Test[T2, Y]{}
ret Test[Test[T1, T2], T2]{}
}
static fn new(): Test[T1, T2] {
type Y: T1
ret Test[T1, T2]{}
ret Test[T2, Y]{}
ret Test[Test[T1, T2], T2]{}
}
}
```
In this example above `Test[T]` and `Test[Y]` declaring same thing which is good. Also `Test[T2, Y]` (which is declares different generic instance for owner structure) is valid because there is plain usage of it's own generics. But latest return statement declares `Test[Test[T1, T2], T2]` type which is causes cycle. `T2` is plain usage, but `Test[T1, T2]` is nested usage not plain, causes cycle. Same rules are works for slices, pointers or etc. Plain usage is just the generic, not else.

- **(7)** Semantic analysis should analysis structures first. If methods have operator overloads, these will cause problems in the analysis because they are not analyzed and qualified accordingly. Therefore, structures need to be analyzed before functions.

- **(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.
6 changes: 6 additions & 0 deletions std/jule/sema/eval.jule
Original file line number Diff line number Diff line change
Expand Up @@ -3829,6 +3829,12 @@ impl BinaryEval {
}

match self.op.kind {
| TokenKind.Gt:
if self.l.kind.strct().operators.gt == nil {
self.e.push_err(self.op, LogMsg.OperatorNotForJuleType, self.op.kind, self.l.kind.to_str())
ret nil
}
fall
| TokenKind.Eqs
| TokenKind.NotEq:
ret &Data{
Expand Down
31 changes: 27 additions & 4 deletions std/jule/pattern/func.jule → std/jule/sema/pattern.jule
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,14 @@
// Use of this source code is governed by a BSD 3-Clause
// license that can be found in the LICENSE file.

use std::jule::sema::{
Fn,
}

// Pattern checker for functions and methods.
pub struct FuncPattern {}

impl FuncPattern {
// Reports whether function is the reserved dispose function.
pub static fn dispose(f: &Fn): bool {
ret f != nil &&
f.owner != nil &&
f.public &&
f.is_void() &&
f.generics.len == 0 &&
Expand All @@ -24,6 +21,7 @@ impl FuncPattern {
// Reports whether function is the reserved to_str function.
pub static fn to_str(f: &Fn): bool {
if f == nil ||
f.owner == nil ||
!f.public ||
f.is_void() ||
f.generics.len != 0 ||
Expand All @@ -40,4 +38,29 @@ impl FuncPattern {
}
ret prim.is_str()
}

// Reports whether function is the reserved gt function.
pub static fn gt(f: &Fn): bool {
if f == nil ||
f.owner == nil ||
!f.public ||
f.is_void() ||
f.generics.len != 0 ||
f.params.len != 2 ||
f.params[0].mutable ||
f.params[0].is_ref() {
ret false
}

let mut ins = unsafe { *(&f.instances[0]) }
let prim = ins.result.prim()
if prim == nil || !prim.is_bool() {
ret false
}
let mut y = ins.params[1]
if y.decl.mutable || y.decl.reference {
ret false
}
ret ins.owner == y.kind.strct()
}
}
76 changes: 54 additions & 22 deletions std/jule/sema/sema.jule
Original file line number Diff line number Diff line change
Expand Up @@ -2099,7 +2099,21 @@ impl Sema {
}
}

fn check_type_method(mut &self, mut &strct: &StructIns, mut &f: &Fn) {
// Returns new FnIns as ready-to-analysis.
// If function already has a instance, returns existing instance.
fn ready_to_check_fn(mut &self, mut &s: &StructIns, mut &f: &Fn): &FnIns {
let mut ins = f.instance()
if f.instances.len != 0 {
ret ins
}
ins.owner = s
f.append_instance(ins)
self.reload_fn_ins_types(ins)
ins.reloaded = true
ret ins
}

fn check_type_method(mut &self, mut &s: &StructIns, mut &f: &Fn) {
if f.cpp_linked {
ret
}
Expand All @@ -2109,14 +2123,7 @@ impl Sema {
ret
}

if f.instances.len == 0 {
let mut ins = f.instance()
ins.owner = strct
f.append_instance(ins)
self.reload_fn_ins_types(ins)
ins.reloaded = true
}

_ = self.ready_to_check_fn(s, f)
for (_, mut ins) in f.instances {
if ins.scope.stmts.len > 0 {
// Checked
Expand Down Expand Up @@ -2148,27 +2155,51 @@ impl Sema {
}
}

fn check_type_struct(mut &self, mut &strct: &Struct) {
if strct.cpp_linked {
fn check_struct_ins_op(mut &self, mut &s: &StructIns, method: str): &FnIns {
const STATIC = false // Operator methods cannot be static.
let mut op = s.find_method(method, STATIC)
if op == nil {
ret nil
}
let mut ins = self.ready_to_check_fn(s, op)
match method {
| "gt":
ret if FuncPattern.gt(op) { ins } else { nil }
|:
ret nil
}
}

fn check_struct_ins(mut &self, mut &s: &StructIns) {
self.check_field_defaults(s)

// Check operators first.
// See developer reference (7.1)
s.operators.gt = self.check_struct_ins_op(s, "gt")

for (_, mut f) in s.methods {
self.check_type_method(s, f)
}
}

fn check_type_struct(mut &self, mut &s: &Struct) {
if s.cpp_linked {
ret
}

// Generic instances are checked instantly.
if strct.generics.len > 0 {
if s.generics.len > 0 {
ret
}

if strct.instances.len == 0 {
let mut ins = strct.instance()
if s.instances.len == 0 {
let mut ins = s.instance()
ins.checked = true
strct.append_instance(ins)
s.append_instance(ins)
}

for (_, mut ins) in strct.instances {
self.check_field_defaults(ins)
for (_, mut f) in ins.methods {
self.check_type_method(ins, f)
}
for (_, mut ins) in s.instances {
self.check_struct_ins(ins)
}
}

Expand Down Expand Up @@ -2331,14 +2362,15 @@ impl Sema {
self.precheck_fn_types()
}

// See developer reference (7)
for (_, mut f) in self.files {
self.set_current_file(f)
self.check_fn_types()
self.check_struct_types()
}

for (_, mut f) in self.files {
self.set_current_file(f)
self.check_struct_types()
self.check_fn_types()
}
}

Expand Down
6 changes: 6 additions & 0 deletions std/jule/sema/struct.jule
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ pub struct FieldIns {
pub default: &Data // Nil if not given.
}

// Overloaded operators.
pub struct Operators {
pub gt: &FnIns
}

// Structure instance.
pub struct StructIns {
pub checked: bool
Expand All @@ -215,6 +220,7 @@ pub struct StructIns {
pub methods: []&Fn
pub mutable: bool // This structure has mutable defines.
pub references: &ReferenceStack
pub operators: Operators
}

impl Kind for StructIns {
Expand Down
5 changes: 1 addition & 4 deletions std/jule/sema/type.jule
Original file line number Diff line number Diff line change
Expand Up @@ -1001,10 +1001,7 @@ impl TypeChecker {
}

if ins.generics.len > 0 {
self.s.check_field_defaults(ins)
for (_, mut m) in ins.methods {
self.s.check_type_method(ins, m)
}
self.s.check_struct_ins(ins)
}

ret true
Expand Down

0 comments on commit 3d25afa

Please sign in to comment.