Skip to content

Commit

Permalink
add tests + remove some fixed todos
Browse files Browse the repository at this point in the history
  • Loading branch information
mustafaquraish committed Nov 17, 2024
1 parent d50f59e commit db44183
Show file tree
Hide file tree
Showing 9 changed files with 7,540 additions and 6,964 deletions.
14,320 changes: 7,359 additions & 6,961 deletions bootstrap/stage0.c

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions compiler/ast/nodes.oc
Original file line number Diff line number Diff line change
Expand Up @@ -189,13 +189,10 @@ enum FunctionKind {
// FIXME: Codegen is super hacky, need to clean up and fix:
// 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: Allow passing a normal function pointer to a closure (just wrap it in Closure struct with empty context)
// FIXME: More comprehensive postive / negative tests for closures.
struct Function {
kind: FunctionKind

Expand Down
127 changes: 127 additions & 0 deletions compiler/passes/reorder_symbols.oc
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
//* Topologically sort symbols
//*
//* This is needed because in C structs/enums/etc need to be declared before they are used
//* in others. The ordered structs are stored in `program.ordered_syms`

import std::mem
import @passes::generic_pass::GenericPass
import std::vector::Vector
import std::set::{ Set }
import @ast::program::{ Program, Namespace }
import @ast::nodes::{ Structure, Variable }
import @ast::scopes::{ TemplateInstance, Symbol }

struct ReorderSymbols {
o: &GenericPass
all_syms: &Vector<&Symbol>
done: &Set<untyped_ptr>
}

def ReorderSymbols::new(program: &Program): &ReorderSymbols {
let pass = mem::alloc<ReorderSymbols>()
*pass = ReorderSymbols(
o: GenericPass::new(program),
all_syms: Vector<&Symbol>::new(),
done: Set<untyped_ptr>::new(),
)
return pass
}

def ReorderSymbols::free(&this) {
.all_syms.free()
.done.free()
mem::free(.o)
mem::free(this)
}

def ReorderSymbols::collect_sym(&this, sym: &Symbol) {
if sym? and not sym.is_dead {
.all_syms += sym
}
}

// Collects all structs we have in the namespace into the vector
def ReorderSymbols::collect_all_symbols_ns(&this, ns: &Namespace) {
for struc : ns.structs.iter() {
if struc.sym.is_templated() {
for instance : struc.sym.template.instances.iter() {
let sym = instance.resolved
assert sym.type == Structure
.collect_sym(sym)
}

} else {
.collect_sym(struc.sym)
}
}
for enom in ns.enums.iter() {
.collect_sym(enom.sym)
}
for child : ns.namespaces.iter_values() {
.collect_all_symbols_ns(child)
}
}

def ReorderSymbols::collect_all_symbols(&this) {
.collect_all_symbols_ns(.o.program.global)
for cty in .o.program.closure_types.iter() {
.collect_sym(cty.sym)
}
for closure in .o.program.closures.iter() {
.collect_sym(closure.sym)
}
}

def ReorderSymbols::dfs(&this, sym: &Symbol) {
if not sym? or .done.contains(sym) return
.done.add(sym)

match sym.type {
Structure => {
for field : sym.u.struc.fields.iter() {
if field.type? {
.dfs(field.type.sym)
}
}
}
Enum => {
for field in sym.u.enom.shared_fields.iter() {
if field.type? {
.dfs(field.type.sym)
}
}
for variant in sym.u.enom.variants.iter() {
for field in variant.specific_fields.iter() {
if field.type? {
.dfs(field.type.sym)
}
}
}
}
Closure => {
let func = sym.u.func
for param in func.params.iter() {
.dfs(param.type.sym)
}
.dfs(func.return_type.sym)
}
ClosureType => .dfs(sym.u.type_def.sym)
else => return // Don't care about other types
}
.o.program.ordered_symbols += sym
}

// This function topologically sorts the structs based on depedencies, and stores
// the result inside `program.ordered_structs`
def ReorderSymbols::reorder_structs(&this) {
for sym : .all_syms.iter() {
.dfs(sym)
}
}

def ReorderSymbols::run(program: &Program) {
let pass = ReorderSymbols::new(program)
pass.collect_all_symbols()
pass.reorder_structs()
pass.free()
}
11 changes: 11 additions & 0 deletions tests/bad/closures/wrong_type_0.oc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/// fail: Expected `callback` with type @fn(i32): void, but got @fn(u32): void

def test(callback: @fn(i32), x: i32) {
callback(x)
}

def main() {
let x: u32 = 100
let cb = |a: u32| => x += a
test(cb, 10)
}
11 changes: 11 additions & 0 deletions tests/bad/closures/wrong_type_1.oc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/// fail: Variable type does not match assignment type, Expected type 'u32', got 'i32'

def test(callback: @fn(i32), x: i32) {
callback(x)
}

def main() {
let x: u32 = 100
let cb = |a: i32| => x = a
test(cb, 10)
}
8 changes: 8 additions & 0 deletions tests/lsp/hover/closure_body_captured.oc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/// lsp: -h 6 26
/// {"hover": "x: i32"}

def main() {
let x: i32 = 100
let cb = |a: i32| => x += a
cb(5)
}
8 changes: 8 additions & 0 deletions tests/lsp/hover/closure_call.oc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/// lsp: -h 6 10
/// {"hover": "cb: @fn(a: i32)"}

def main() {
let x: i32 = 100
let cb = |a: i32| => x += a
cb(5)
}
11 changes: 11 additions & 0 deletions tests/lsp/hover/closure_params.oc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/// lsp: -h 6 15
/// {"hover": "a: u32"}

def main() {
let x = 100
let cb = |a: u32| => x += a

for let i = 0; i < 5; i++ {
cb(i)
}
}
5 changes: 5 additions & 0 deletions tests/lsp/hover/closure_type.oc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// lsp: -h 4 24
/// {"hover": "i32"}

def foo(x: @fn(str): i32) {}
def main() => 0

0 comments on commit db44183

Please sign in to comment.