Skip to content

Commit

Permalink
Merge pull request #200 from myuon/issue-199
Browse files Browse the repository at this point in the history
fix: introduce env for typecheck to capture correct variables
  • Loading branch information
myuon authored Nov 23, 2023
2 parents 5d38341 + 063e389 commit b7390d5
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 39 deletions.
16 changes: 13 additions & 3 deletions quartz/std.qz
Original file line number Diff line number Diff line change
Expand Up @@ -1512,6 +1512,11 @@ module derive {
indent = " ".repeat(depth * 4);
}

let indent_prev = " ";
if pretty {
indent_prev = " ".repeat((depth - 1) * 4);
}

let rep = reflection::get_type_rep(t);
if rep.name.equal("string") {
return format(`"{}"`, t as string);
Expand Down Expand Up @@ -1573,9 +1578,14 @@ module derive {
let tag_name = rep.fields.at(tag);

let v = (t as ptr[any]).at(1);
s = s.concat(format(" {}: {} }", tag_name, derive::to_string_internal(v, pretty, depth + 1)));
s = s.concat(format(
"{}{}: {}",
indent,
tag_name,
derive::to_string_internal(v, pretty, depth + 1),
));

return s;
return s.concat("{}{}}".format(sep, indent_prev));
}

let s = format("{} {{}", rep.name, sep);
Expand All @@ -1589,7 +1599,7 @@ module derive {
s = s.concat(format("{}{}: {}", indent, f, derive::to_string_internal(v, pretty, depth + 1)));
}

return s.concat(" }");
return s.concat("{}{}}".format(sep, indent_prev));
}

fun to_string(t: any): string {
Expand Down
62 changes: 30 additions & 32 deletions quartz/typecheck.qz
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ struct Typechecker {
}],
current_function_name: string?,
captured: vec[string],
env: map[string, LType],
}

module Typechecker {
Expand Down Expand Up @@ -370,6 +371,7 @@ module Typechecker {
}]](),
current_function_name: nil,
captured: make[vec[string]](),
env: make[map[string, LType]](),
};
}

Expand Down Expand Up @@ -527,7 +529,7 @@ module Typechecker {
},
);

self.function(d.t_func!, make[map[string, LType]]()).try;
self.function(d.t_func!).try;

self.globals.insert(
self.path_to(d.t_func!.name.data),
Expand Down Expand Up @@ -642,12 +644,13 @@ module Typechecker {
return _ or error::new(format("unimplemented: decl, {}", d.to_string()));
}

fun function(self, f: Function, locals_init: map[string, LType]): Type or error {
let locals = self.locals;
self.locals = make[map[string, LType]]();
for k in locals_init.list_keys() {
self.locals.insert(k, locals_init.(k));
fun function(self, f: Function): Type or error {
let prev_locals = self.locals;
let prev_env = self.env;
for l in self.locals.list_keys() {
self.env.insert(l, self.locals.at(l));
}
self.locals = make[map[string, LType]]();

self.captured = make[vec[string]]();

Expand Down Expand Up @@ -678,28 +681,9 @@ module Typechecker {
self.result_type = f.result_type?;
self.block(f.body).try;

let captured_local = make[map[string, bool]]();
for c in self.captured {
captured_local.insert(c, true);
}
for p in f.params {
captured_local.insert(p.name, false);
}
for l in self.locals.list_keys() {
if !locals_init.has(l) {
captured_local.insert(l, false);
}
}

self.captured = make[vec[string]]();
for c in captured_local.list_keys() {
if captured_local.(c) {
self.captured.push(c);
}
}

self.locals = locals;
self.current_function_name = nil;
self.locals = prev_locals;
self.env = prev_env;

let ps = make[vec[Type]]();
for param in f.params {
Expand Down Expand Up @@ -1049,14 +1033,22 @@ module Typechecker {
return _ or error::new("unimplemented: binop, {}".format(expr.to_string()));
} else if expr.t_ident != nil {
if self.locals.has(expr.t_ident!.name) {
self.captured.push(expr.t_ident!.name);
let t = self.locals.at(expr.t_ident!.name);
self.set_search_node_type(t.data, lexpr.location);
self.set_search_node_definition(self.current_path, t.location, lexpr.location);
self.set_completion(t.data, lexpr.location).try;

return t.data;
}
if self.env.has(expr.t_ident!.name) {
self.captured.push(expr.t_ident!.name);
let t = self.env.at(expr.t_ident!.name);
self.set_search_node_type(t.data, lexpr.location);
self.set_search_node_definition(self.current_path, t.location, lexpr.location);
self.set_completion(t.data, lexpr.location).try;

return t.data;
}

let result or _err = self.resolve_path(Path::new(expr.t_ident!.name));
if result != nil {
Expand Down Expand Up @@ -1782,9 +1774,10 @@ module Typechecker {
lexpr.location.start!.to_string(),
);

let prev = self.current_function_name;
let func_type = self.function(expr.t_closure!.func, self.locals).try;
self.current_function_name = prev;
let prev_name = self.current_function_name;
let prev_captured = self.captured;
let func_type = self.function(expr.t_closure!.func).try;
self.current_function_name = prev_name;

let captures = make[vec[struct {
name: string,
Expand All @@ -1793,11 +1786,16 @@ module Typechecker {
for c in self.captured {
captures.push(struct {
name: c,
type_: self.locals.(c).data,
type_: self.env.(c).data,
});
}
expr.t_closure!.captures = captures;

// merge with previous captures
for p in prev_captured {
self.captured.push(p);
}

return Type {
t_closure: struct {
params: func_type.t_func!.params,
Expand Down
9 changes: 6 additions & 3 deletions tests/cases/closures/test10.qz
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
fun f(a: i32, p: any) {
fun f(a: i32, p: fun[(i32), nil]) {
println("{}", a.to_string());
p(7);
}

fun main() {
Expand All @@ -10,11 +11,13 @@ fun main() {
fun (resp_id: i32) {
f(
a,
fun () {
panic("Image failed to load");
fun (arg: i32) {
println("called with {}", arg.to_string());
},
);
},
);

println("ok");
}

4 changes: 4 additions & 0 deletions tests/cases/closures/test10.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
5
10
called with 7
ok
45 changes: 45 additions & 0 deletions tests/cases/closures/test11.qz
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
fun js_fetch(url: string, call: fun[(i32), nil]) {
call(10);
}

fun js_image_set_onload(image: any, call: fun[(), nil]) {
call();
}

fun js_image_set_onerror(image: any, call: fun[(), nil]) {
call();
}

fun js_context_draw_image(context: string, image: i32, x: i32, y: i32) {
println(context);
println("{}", image.to_string());
println("{}", x.to_string());
println("{}", y.to_string());
}

fun main() {
let rhb = 200;
let context = "context";
let image = "image";

js_fetch(
"assets/rhb.json",
fun (resp_id: i32) {
js_image_set_onload(
rhb,
fun () {
js_context_draw_image(context, rhb, 0, 0);
},
);
js_image_set_onerror(
image,
fun () {
println("Image failed to load");
},
);
},
);

println("ok");
}

6 changes: 6 additions & 0 deletions tests/cases/closures/test11.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
context
200
0
0
Image failed to load
ok
1 change: 0 additions & 1 deletion tests/cases/closures/test4.qz
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ fun main() {
let outer = fun () {
let x = "outer";
let inner = fun () {
let x = "inner";
println(x);
};
inner();
Expand Down

0 comments on commit b7390d5

Please sign in to comment.