Skip to content

Commit

Permalink
big code reorganization: TypeChecker record
Browse files Browse the repository at this point in the history
Several big changes, that were done in tandem, and which
would be too troublesome to break into separate commits.
The goal here is to ultimately be able to break tl.tl
into multiple files (because its size started hitting
limits in both Lua 5.1 (number of upvalues) and Lua 5.4
(number of locals). Here's a high-level summary of the
changes:

* new Errors record, encapsulating error-reporting concerns;

* all Type occurrences have unique objects reporting their
  locations (no more singletons for base types such as BOOLEAN
  and INVALID);

* some enums renamed for more consistency across Gen and Feat
  options;

* TypeCheckOptions and EnvOptions tables reorganized for
  easier forwarding of options across them;

* simplifications in the various function signatures
  of the public API;

* all Types and Nodes store filename, line and column
  location (`f`, `y`, `x`);

* Scope is now a record containing the variables map and
  unresolved items -- no more "@unresolved" pseudo-variable
  and `unresolved` pseudo-type for storing this data in
  the symbols table;

* `type_check` now uses a TypeChecker object for storing all state, instead of
  relying on closures and function nesting (that's a bit sad is it ended up
  spreading `self:` and extra function arguments everywhere, but I guess state
  management will be more explicit for others reading the code now...);

* all Fact objects have a Where location as well, and supressions of
  inference data in error messages for widened-back types is marked
  explicitly with `no_infer` instead of missing a `w` field;

* general simplification of the sourcing of error locations (though
  I would still like to improve that further);
  • Loading branch information
hishamhm committed Jan 26, 2024
1 parent fd5bcab commit d885f90
Show file tree
Hide file tree
Showing 14 changed files with 6,465 additions and 6,168 deletions.
4 changes: 2 additions & 2 deletions spec/api/gen_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ describe("tl.gen", function()
print(math.floor(2))
]]

local env = tl.init_env(true, true)
local env = tl.init_env(true, false)
local output, result = tl.gen(input, env)

assert.equal('print(math.floor(2))', output)
Expand All @@ -83,7 +83,7 @@ describe("tl.gen", function()
print(math.floor(2))]]

local env = tl.init_env(true, true)
local env = tl.init_env(true, false)
local output, result = tl.gen(input, env)

assert.equal(input, output)
Expand Down
4 changes: 2 additions & 2 deletions spec/api/get_types_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe("tl.get_types", function()
local function a()
::continue::
end
]], false, env))
]], env))

local tr, trenv = tl.get_types(result)
assert(tr)
Expand All @@ -25,7 +25,7 @@ describe("tl.get_types", function()
end
R.f("hello")
]], false, env))
]], env))

local tr, trenv = tl.get_types(result)
local y = 6
Expand Down
2 changes: 1 addition & 1 deletion spec/api/pretty_print_ast.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ local util = require("spec.util")
describe("tl.pretty_print_ast", function()
it("returns error for <close> attribute on non 5.4 target", function()
local input = [[local x <close> = io.open("foobar", "r")]]
local result = tl.process_string(input, false, tl.init_env(false, "off", "5.4"), "foo.tl")
local result = tl.process_string(input, tl.init_env(false, "off", "5.4"), "foo.tl")
local output, err = tl.pretty_print_ast(result.ast, "5.3")

assert.is_nil(output)
Expand Down
2 changes: 1 addition & 1 deletion spec/call/generic_function_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ describe("generic function", function()
recurse_node(ast, visit_node, visit_type)
end
]], {
{ x = 40, msg = "argument 3: in map value: type parameter <T>: got number, expected string" }
{ y = 16, x = 40, msg = "argument 3: in map value: type parameter <T>: got number, expected string" }
}))

it("inference trickles down to function arguments, pass", util.check([[
Expand Down
20 changes: 9 additions & 11 deletions spec/cli/types_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,6 @@ describe("tl types works like check", function()
local by_pos = types.by_pos[next(types.by_pos)]
assert(by_pos["1"])
assert(by_pos["1"]["13"]) -- require
assert(by_pos["1"]["20"]) -- (
assert(by_pos["1"]["21"]) -- "os"
assert(by_pos["1"]["26"]) -- .
end)
Expand All @@ -217,18 +216,17 @@ describe("tl types works like check", function()
assert(types.by_pos)
local by_pos = types.by_pos[next(types.by_pos)]
assert.same({
["19"] = 2,
["20"] = 5,
["22"] = 2,
["39"] = 6,
["41"] = 2,
["19"] = 8,
["22"] = 8,
["23"] = 6,
["30"] = 2,
["41"] = 8,
}, by_pos["1"])
assert.same({
["17"] = 3,
["20"] = 4,
["25"] = 17,
["30"] = 16,
["31"] = 2,
["17"] = 6,
["20"] = 2,
["25"] = 9,
["31"] = 8,
}, by_pos["2"])
end)
end)
Expand Down
4 changes: 2 additions & 2 deletions spec/declaration/record_method_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,8 @@ describe("record method", function()
return "hello"
end
]], {
{ msg = "in assignment: incompatible number of returns: got 0 (), expected 1 (string)" },
{ msg = "excess return values, expected 0 (), got 1 (string \"hello\")" },
{ y = 5, msg = "in assignment: incompatible number of returns: got 0 (), expected 1 (string)" },
{ y = 6, msg = "excess return values, expected 0 (), got 1 (string \"hello\")" },
}))

it("allows functions declared on method tables (#27)", function()
Expand Down
4 changes: 2 additions & 2 deletions spec/parser/parser_error_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ local tl = require("tl")

describe("parser errors", function()
it("parse errors include filename", function ()
local result = tl.process_string("local x 1", false, nil, "foo.tl")
local result = tl.process_string("local x 1", nil, "foo.tl")
assert.same("foo.tl", result.syntax_errors[1].filename, "parse errors should contain .filename property")
end)

Expand Down Expand Up @@ -30,7 +30,7 @@ describe("parser errors", function()
local code = [[
local bar = require "bar"
]]
local result = tl.process_string(code, true, nil, "foo.tl")
local result = tl.process_string(code, nil, "foo.tl")
assert.is_not_nil(string.match(result.env.loaded["./bar.tl"].syntax_errors[1].filename, "bar.tl$"), "errors should contain .filename property")
end)
end)
1 change: 1 addition & 0 deletions spec/parser/parser_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ describe("parser", function()
assert.same({
kind = "statements",
tk = "$EOF$",
f = "",
x = 1,
y = 1,
xend = 5,
Expand Down
2 changes: 1 addition & 1 deletion spec/stdlib/require_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ describe("require", function()
local result, err = tl.process("foo.tl")

assert.same(0, #result.syntax_errors)
assert.same(0, #result.env.loaded["foo.tl"].type_errors)
assert.same({}, result.env.loaded["foo.tl"].type_errors)
assert.same(1, #result.env.loaded["./box.tl"].type_errors)
assert.match("cannot use operator ..", result.env.loaded["./box.tl"].type_errors[1].msg)
end)
Expand Down
2 changes: 1 addition & 1 deletion spec/stdlib/xpcall_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ describe("xpcall", function()
{ msg = "xyz: got boolean, expected number" }
}))

it("type checks the message handler", util.check_type_error([[
it("#only type checks the message handler", util.check_type_error([[
local function f(a: string, b: number)
end
Expand Down
10 changes: 5 additions & 5 deletions spec/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ local function check(lax, code, unknowns, gen_target)
if gen_target == "5.4" then
gen_compat = "off"
end
local result = tl.type_check(ast, { filename = "foo.lua", lax = lax, gen_target = gen_target, gen_compat = gen_compat })
local result = tl.type_check(ast, "foo.lua", { feat_lax = lax and "on" or "off", gen_target = gen_target, gen_compat = gen_compat })
batch:add(assert.same, {}, result.type_errors)

if unknowns then
Expand All @@ -456,7 +456,7 @@ local function check_type_error(lax, code, type_errors, gen_target)
if gen_target == "5.4" then
gen_compat = "off"
end
local result = tl.type_check(ast, { filename = "foo.tl", lax = lax, gen_target = gen_target, gen_compat = gen_compat })
local result = tl.type_check(ast, "foo.tl", { feat_lax = lax and "on" or "off", gen_target = gen_target, gen_compat = gen_compat })
local result_type_errors = combine_result(result, "type_errors")

batch_compare(batch, "type errors", type_errors, result_type_errors)
Expand Down Expand Up @@ -525,7 +525,7 @@ function util.check_syntax_error(code, syntax_errors)
local batch = batch_assertions()
batch_compare(batch, "syntax errors", syntax_errors, errors)
batch:assert()
tl.type_check(ast, { filename = "foo.tl", lax = false })
tl.type_check(ast, "foo.tl", { feat_lax = "off" })
end
end

Expand Down Expand Up @@ -564,7 +564,7 @@ function util.check_types(code, types)
local batch = batch_assertions()
local env = tl.init_env()
env.report_types = true
local result = tl.type_check(ast, { filename = "foo.tl", env = env, lax = false })
local result = tl.type_check(ast, "foo.tl", { feat_lax = "off" }, env)
batch:add(assert.same, {}, result.type_errors, "Code was not expected to have type errors")

local tr = env.reporter:get_report()
Expand Down Expand Up @@ -596,7 +596,7 @@ local function gen(lax, code, expected, gen_target)
return function()
local ast, syntax_errors = tl.parse(code, "foo.tl")
assert.same({}, syntax_errors, "Code was not expected to have syntax errors")
local result = tl.type_check(ast, { filename = "foo.tl", lax = lax, gen_target = gen_target })
local result = tl.type_check(ast, "foo.tl", { feat_lax = lax and "on" or "off", gen_target = gen_target })
assert.same({}, result.type_errors)
local output_code = tl.pretty_print_ast(ast)

Expand Down
10 changes: 6 additions & 4 deletions tl
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,12 @@ local function setup_env(tlconfig, filename)
end

local opts = {
lax_mode = lax_mode,
feat_arity = tlconfig["feat_arity"],
gen_compat = tlconfig["gen_compat"],
gen_target = tlconfig["gen_target"],
defaults = {
feat_lax = lax_mode and "on" or "off",
feat_arity = tlconfig["feat_arity"],
gen_compat = tlconfig["gen_compat"],
gen_target = tlconfig["gen_target"],
},
predefined_modules = tlconfig._init_env_modules,
}

Expand Down
Loading

0 comments on commit d885f90

Please sign in to comment.