Skip to content

Commit

Permalink
Add print functions to type system
Browse files Browse the repository at this point in the history
  • Loading branch information
mustafaquraish committed Nov 16, 2024
1 parent d4e21dc commit 7a6c795
Show file tree
Hide file tree
Showing 19 changed files with 8,714 additions and 7,460 deletions.
15,211 changes: 7,999 additions & 7,212 deletions bootstrap/stage0.c

Large diffs are not rendered by default.

29 changes: 28 additions & 1 deletion compiler/ast/nodes.oc
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ enum ASTType {
Defer
Specialization
ArrayLiteral
CreateClosure

// Used for heap-allocating the result of an expression
// @new XYZ => { let a = mem::alloc<typeof(XYZ)>(); *a = XYZ; yield a }
Expand Down Expand Up @@ -179,13 +180,38 @@ def EnumVariant::new(span: Span): &EnumVariant {
return variant
}

// Calling this FunctionKind instead of FunctionType to
// avoid confusion with the `struct FunctionType` used in &Type
enum FunctionKind {
Normal
Method
Closure
}


// FIXME: Can we do something about `alloca()` in codegen?
// FIXME: Codegen is super hacky, need to clean up and fix:
// FIXME: Have a "ground truth" function for getting names of closure fields/etc. Currently hardcoded in many places
// FIXME: Check to see if any existing fields clash with implicit closure fields, and throw an error/rename
// FIXME: Checking lifetimes: closures can't outlive their parent function
// FIXME: Allow capturing things other than variables
// FIXME: Allow nested closures (in resolve_scoped_identifier) we only ever look up one level
// FIXME: Don't treat global variables/functions as captured variables, they should be looked up normally
// FIXME: Almost ALL the details are hardcoded in codegen. Make type-checker aware of extra implicit fields/params etc.
// FIXME: - Essentially, "unroll" the closures into structs+fields at type-checker level
// FIXME: We do not correctly account for ordering of used types in closure types
struct Function {
kind: FunctionKind

sym: &Symbol
params: &Vector<&Variable>
//! (Eventually) resolved return type of the function
return_type: &Type
body: &AST

scope: &Scope
closure_scope: &Scope
closed_vars: &Map<str, &Symbol>

span: Span
//! Used for LSP, to know span of (unresolved) return type
Expand All @@ -201,7 +227,6 @@ struct Function {
operator_overloads: &Vector<Operator>

exits: bool
is_method: bool
is_static: bool
parent_type: &Type

Expand Down Expand Up @@ -251,6 +276,7 @@ def Argument::new(expr: &AST, label_token: &Token = null): &Argument {

enum CallType {
Normal
Closure
StructConstructor
EnumConstructor
}
Expand Down Expand Up @@ -480,6 +506,7 @@ union ASTUnion {
array_literal: ArrayLiteral
child: &AST
ret: Return
closure: &Function
}

struct AST {
Expand Down
37 changes: 35 additions & 2 deletions compiler/ast/program.oc
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import std::mem
import std::setjmp::{ ErrorContext }
import std::sv::{ SV }
import @ast::scopes::{ Symbol, Scope }
import @ast::nodes::{ AST, Function, Structure, Enum }
import @ast::nodes::{ AST, Function, Structure, Enum, Variable }
import @ast::operators::{ OperatorOverload }
import @errors::Error
import @types::{ Type, BaseType }
Expand Down Expand Up @@ -121,7 +121,7 @@ def Namespace::find_importable_symbol(&this, name: str): &Symbol {
}
for func : .functions.iter() {
if func.is_method then continue
if func.kind == Method then continue
if func.sym.name.eq(name) return func.sym
}
Expand Down Expand Up @@ -181,11 +181,17 @@ struct Program {

explicit_alive_symbols: &Vector<&Symbol>

closure_types: &Vector<&Type>
closures: &Vector<&Function> // FIXME: what about closed args?

// Configs
check_doc_links: bool
gen_debug_info: bool
keep_all_code: bool
include_stdlib: bool

// State
uid: u32 // For generating unique IDs
}

def Program::new(): &Program {
Expand All @@ -212,6 +218,9 @@ def Program::new(): &Program {
prog.operator_overloads = Map<OperatorOverload, &Function>::new()
prog.err_jmp_stack = Vector<ErrorContext>::new()
prog.explicit_alive_symbols = Vector<&Symbol>::new()
prog.closure_types = Vector<&Type>::new()
prog.closures = Vector<&Function>::new()
prog.uid = 0
return prog
}

Expand Down Expand Up @@ -268,6 +277,30 @@ def Program::error(&this, err: &Error): &Error {
return err
}

// FIXME: This logic looks too similar to Type::eq() for 2 functions, however we want to
// try not to create a new dummy Type object before we call this function to avoid
// memory allocations. There is probably a way to refactor this to avoid the duplication.
def Program::find_closure_type(&this, params: &Vector<&Variable>, ret: &Type): &Type {

// FIXME: Can we do a non-linear search here?
for c in .closure_types.iter() {
let clos = c.u.func
if clos.params.size != params.size then continue
if not clos.return_type.eq(ret) then continue
let all_match = true
for let i = 0; i < params.size; i += 1 {
if not clos.params.at(i).type.eq(params.at(i).type) {
all_match = false
break
}
}
if not all_match then continue
// FIXME: Variadic? Other attributes?
return c
}
return null
}

def Program::get_function_deep_copy(&this, old: &Function, ns: &Namespace): &Function {
// FIXME: This is a hack to get around the fact that we can't deep-copy
// functions yet. We're just going to get the source code of the function
Expand Down
11 changes: 10 additions & 1 deletion compiler/ast/scopes.oc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ enum SymbolType {
Namespace
Variable
Constant
Closure
ClosedVariable

Enum
EnumVariant
Expand Down Expand Up @@ -78,6 +80,11 @@ struct EnumField {
idx: u32
}

struct ClosedVariable {
orig: &Variable
closure: &Function
}

union SymbolUnion {
func: &Function
struc: &Structure
Expand All @@ -87,6 +94,8 @@ union SymbolUnion {
enom: &Enum
enum_var: &EnumVariant
enum_field: EnumField
closure: &Function
closed_var: ClosedVariable
}

struct Symbol {
Expand Down Expand Up @@ -148,7 +157,7 @@ def Symbol::new(type: SymbolType, ns: &Namespace, name: str, display: str, full_
def Symbol::join_display(a: str, b: str): str => if a.len() == 0 then b else `{a}::{b}`
def Symbol::join_full_name(a: str, b: str): str => if a.len() == 0 then b else `{a}_{b}`

def Symbol::is_templated(&this): bool => .template?
def Symbol::is_templated(&this): bool => this? and .template?

def Symbol::new_with_parent(type: SymbolType, ns: &Namespace, parent: &Symbol, name: str, span: Span): &Symbol {
let display = Symbol::join_display(parent.display, name)
Expand Down
6 changes: 3 additions & 3 deletions compiler/docgen.oc
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def DocGenerator::gen_typename_str(&this, type: &Type): str {
sub.replace_with(`[{sub}]`)
return sub
}
Function => {
FunctionPtr => {
let buf = Buffer::make()
buf += "fn("
let first = true
Expand Down Expand Up @@ -203,7 +203,7 @@ def DocGenerator::gen_function(&this, func: &Function): &Value {
if func.sym.comment? {
func_doc["description"] = func.sym.comment
}
if func.is_method {
if func.kind == Method {
func_doc["kind"] = "method"
func_doc["parent"] = .gen_typename(func.parent_type)
} else {
Expand Down Expand Up @@ -376,7 +376,7 @@ def DocGenerator::gen_ns(&this, ns: &Namespace): &Value {
if not ns.functions.is_empty() {
let funcs_doc = Value::new(Dictionary)
for func : ns.functions.iter() {
if func.is_method {
if func.kind == Method {
continue
}
let func_doc = .gen_function(func)
Expand Down
2 changes: 1 addition & 1 deletion compiler/lsp/finder.oc
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def Finder::find_signature_help(&this, node: &AST, args: &Vector<&Argument>, par
Structure => func.u.struc.fields
Variable => {
let var_type = func.u.var.type
if not var_type? or var_type.base != Function return false
if not var_type? or var_type.base != FunctionPtr return false
yield func.u.var.type.u.func.params
}
else => {
Expand Down
24 changes: 18 additions & 6 deletions compiler/lsp/utils.oc
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,13 @@ def gen_type_string(type: &Type, full: bool = true): str => match type.base {
sb <<= gen_template_params(instance.args)
yield sb.str()
}
Function => {
Closure | FunctionPtr => {
let sb = Buffer::make()
let func_type = type.u.func
let is_non_static_method = false

if func_type.orig? {
if func_type.orig.is_method {
if func_type.orig.kind == Method {
is_non_static_method = not func_type.orig.is_static
}
sb += "def "
Expand Down Expand Up @@ -110,6 +110,7 @@ def gen_type_string(type: &Type, full: bool = true): str => match type.base {
}

} else {
if type.base == Closure then sb += "@"
sb += "fn"
}
sb += "("
Expand Down Expand Up @@ -216,6 +217,15 @@ def gen_hover_string(sym: &Symbol): str => match sym.type {
let field = sym.u.enum_field
yield `enum {field.variant.sym.display}.{field.idx}`
}
Closure => `closure {sym.display}`
ClosedVariable => {
let var = sym.u.closed_var.orig
let buf = Buffer::make()
buf += var.sym.display
buf += ": "
buf += gen_type_string(var.type)
yield buf.str()
}
}

def get_symbol_typedef(sym: &Symbol): &Type => match sym.type {
Expand All @@ -228,6 +238,8 @@ def get_symbol_typedef(sym: &Symbol): &Type => match sym.type {
Enum => sym.u.enom.type
EnumVariant => sym.u.enum_var.parent.type
EnumField => sym.u.enum_field.variant.parent.type
Closure => sym.u.func.type
ClosedVariable => sym.u.closed_var.orig.type
// else => std::panic(f"get_symbol_typedef: unhandled symbol type: {sym.type}")
}

Expand Down Expand Up @@ -387,7 +399,7 @@ def gen_namespace_json(ns: &Namespace): &Value {
}

for func : ns.functions.iter() {
if func.is_method continue
if func.kind == Method continue
children += gen_function_json(func)
}

Expand Down Expand Up @@ -474,12 +486,12 @@ def gen_signature_help(node: &AST, active_param: u32): &Value {
Function => {
let func = callee_sym.u.func
obj["label"] = gen_type_string(func.type)
is_non_static_method = func.is_method and not func.is_static
is_non_static_method = func.kind == Method and not func.is_static
yield func.params
}
Variable => {
let var = callee_sym.u.var
if var.type.base != Function {
if var.type.base != FunctionPtr {
if verbose then println(f"gen_signature_help: variable is not a function: {var.type}")
return obj
}
Expand Down Expand Up @@ -592,7 +604,7 @@ def gen_completion_items_from_ns(completions: &Value, ns: &Namespace, seen: &Set
}
}
for it : ns.functions.iter() {
if not it.is_method {
if it.kind != Method {
seen += it.sym.name
insert_completion_item(completions, it.sym, seen)
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/main.oc
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ def run_executable(argc: i32, argv: &str) {
}

log(Info, f"{cmd}")
let exit_code = system(cmd.str())
let ret = system(cmd.str())
let exit_code = (ret >> 8) & 0xFF // Effectively WEXITSTATUS(ret)
log(Info, f"Exited with code: {exit_code}")
std::exit(exit_code)
}
Expand All @@ -109,7 +110,6 @@ def parse_args(argc: &i32, argv: &&str, program: &Program) {
"-d" => debug = true
"-n" => {
compile_c = false
program.keep_all_code = true
}
"--no-dce" => program.keep_all_code = true
"-o" => exec_path = shift_args(argc, argv)
Expand Down
Loading

0 comments on commit 7a6c795

Please sign in to comment.