Skip to content

Commit

Permalink
Throw compile error when trying to use closures
Browse files Browse the repository at this point in the history
  • Loading branch information
edubart committed Oct 1, 2020
1 parent fa5b215 commit 9b954de
Show file tree
Hide file tree
Showing 11 changed files with 144 additions and 96 deletions.
53 changes: 34 additions & 19 deletions nelua/analyzer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,10 @@ function visitors.Id(context, node)
symbol = node.attr.forcesymbol
end
symbol:link_node(node)
if context.generator ~= 'lua' and symbol.scope ~= context.rootscope and
not symbol:is_directly_accesible_from_scope(context.scope) then
node:raisef("attempt to access upvalue '%s', but closures are not supported", name)
end
node.done = symbol
return symbol
end
Expand Down Expand Up @@ -1370,7 +1374,8 @@ end

function visitors.Block(context, node)
if node.preprocess then
local scope = context:push_forked_cleaned_scope('block', node)
local scope = context:push_forked_cleaned_scope(node)
scope.is_block = true

local ok, err = except.trycall(node.preprocess, node)
if not ok then
Expand All @@ -1395,7 +1400,8 @@ function visitors.Block(context, node)
if #statnodes > 0 or not node.scope then
local scope
repeat
scope = context:push_forked_cleaned_scope('block', node)
scope = context:push_forked_cleaned_scope(node)
scope.is_block = true
context:traverse_nodes(statnodes)
local resolutions_count = scope:resolve()
context:pop_scope()
Expand Down Expand Up @@ -1468,7 +1474,8 @@ function visitors.While(context, node)
condnode.desiredtype = primtypes.boolean
condnode.attr.inconditional = true
context:traverse_node(condnode)
context:push_forked_cleaned_scope('loop', node)
local scope = context:push_forked_cleaned_scope(node)
scope.is_loop = true
context:traverse_node(blocknode)
context:pop_scope()
end
Expand All @@ -1477,7 +1484,8 @@ function visitors.Repeat(context, node)
local blocknode, condnode = node[1], node[2]
condnode.desiredtype = primtypes.boolean
condnode.attr.inconditional = true
context:push_forked_cleaned_scope('loop', node)
local scope = context:push_forked_cleaned_scope(node)
scope.is_loop = true
context:traverse_node(blocknode)
context:push_scope(blocknode.scope)
context:traverse_node(condnode)
Expand All @@ -1501,7 +1509,8 @@ function visitors.ForNum(context, node)
end
local ittype
repeat
local scope = context:push_forked_cleaned_scope('loop', node)
local scope = context:push_forked_cleaned_scope(node)
scope.is_loop = true

local itsymbol = context:traverse_node(itvarnode)
itsymbol.scope:add_symbol(itsymbol)
Expand Down Expand Up @@ -1608,7 +1617,8 @@ function visitors.ForIn(context, node)
if context.generator == 'lua' then -- lua backend
context:traverse_nodes(inexpnodes)
repeat
local scope = context:push_forked_cleaned_scope('loop', node)
local scope = context:push_forked_cleaned_scope(node)
scope.is_loop = true
context:traverse_node(blocknode)
local resolutions_count = scope:resolve()
context:pop_scope()
Expand Down Expand Up @@ -1659,14 +1669,14 @@ function visitors.ForIn(context, node)
end

function visitors.Break(context, node)
if not context.scope:get_parent_of_kind('loop') then
if not context.scope:get_up_scope_of_kind('is_loop') then
node:raisef("`break` statement is not inside a loop")
end
node.done = true
end

function visitors.Continue(context, node)
if not context.scope:get_parent_of_kind('loop') then
if not context.scope:get_up_scope_of_kind('is_loop') then
node:raisef("`continue` statement is not inside a loop")
end
node.done = true
Expand All @@ -1692,7 +1702,7 @@ function visitors.Goto(context, node)
local labelname = node[1]
local label = context.scope:find_label(labelname)
if not label then
local funcscope = context.scope:get_parent_of_kind('function') or context.rootscope
local funcscope = context.scope:get_up_return_scope() or context.rootscope
if not funcscope.resolved_once then
-- we should find it in the next traversal
funcscope:delay_resolution()
Expand All @@ -1716,12 +1726,12 @@ function visitors.VarDecl(context, node)
assert(varnode.tag == 'IdDecl')
varnode.attr.vardecl = true
if varscope == 'global' then
if not context.scope:is_topscope() then
if not context.scope.is_topscope then
varnode:raisef("global variables can only be declared in top scope")
end
varnode.attr.global = true
end
if varscope == 'global' or context.scope:is_topscope() then
if varscope == 'global' or context.scope.is_topscope then
varnode.attr.staticstorage = true
end
if context.pragmas.nostatic then
Expand Down Expand Up @@ -1881,7 +1891,7 @@ end
function visitors.Return(context, node)
local retnodes = node[1]
context:traverse_nodes(retnodes)
local funcscope = context.scope:get_parent_of_kind('function') or context.rootscope
local funcscope = context.scope:get_up_return_scope() or context.rootscope
if funcscope.rettypes then
for i,funcrettype,retnode,rettype in izipargnodes(funcscope.rettypes, retnodes) do
if rettype then
Expand Down Expand Up @@ -1915,7 +1925,7 @@ function visitors.Return(context, node)
end
end

local function resolve_function_argtypes(symbol, varnode, argnodes, scope, checkpoly)
local function resolve_function_argtypes(funcscope, symbol, varnode, argnodes, scope, checkpoly)
local ispolyparent = false
local argattrs = {}
local argtypes = {}
Expand Down Expand Up @@ -1950,6 +1960,7 @@ local function resolve_function_argtypes(symbol, varnode, argnodes, scope, check
selfsym.codename = 'self'
selfsym.lvalue = true
selfsym.type = symbol.metafuncselftype
selfsym.scope = funcscope
symbol.selfsym = selfsym
end
table.insert(argtypes, 1, symbol.metafuncselftype)
Expand Down Expand Up @@ -1990,8 +2001,9 @@ function visitors.DoExpr(context, node)
local blocknode = node[1]
local exprscope
repeat
exprscope = context:push_forked_cleaned_scope('function', node)
exprscope.doexpr = true
exprscope = context:push_forked_cleaned_scope(node)
exprscope.is_doexpr = true
exprscope.is_returnbreak = true
context:traverse_node(blocknode)
local resolutions_count = exprscope:resolve()
context:pop_scope()
Expand Down Expand Up @@ -2035,12 +2047,12 @@ end
local function visitor_FuncDef_variable(context, varscope, varnode)
local decl = varscope ~= nil
if varscope == 'global' then
if not context.scope:is_topscope() then
if not context.scope.is_topscope then
varnode:raisef("global function can only be declared in top scope")
end
varnode.attr.global = true
end
if varscope == 'global' or context.scope:is_topscope() then
if varscope == 'global' or context.scope.is_topscope then
varnode.attr.staticstorage = true
end
if decl then
Expand Down Expand Up @@ -2104,7 +2116,9 @@ function visitors.FuncDef(context, node, polysymbol)

local funcscope
repeat
funcscope = context:push_forked_cleaned_scope('function', node)
funcscope = context:push_forked_cleaned_scope(node)
funcscope.is_function = true
funcscope.is_returnbreak = true

funcscope.rettypes = rettypes
context:traverse_nodes(argnodes)
Expand All @@ -2114,7 +2128,8 @@ function visitors.FuncDef(context, node, polysymbol)
argnode.attr.scope:add_symbol(argnode.attr)
end
end
argattrs, argtypes, ispolyparent = resolve_function_argtypes(symbol, varnode, argnodes, funcscope, not polysymbol)
argattrs, argtypes, ispolyparent =
resolve_function_argtypes(funcscope, symbol, varnode, argnodes, funcscope,not polysymbol)

if not ispolyparent then
-- poly functions never traverse the blocknode by itself
Expand Down
12 changes: 6 additions & 6 deletions nelua/analyzercontext.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ local AnalyzerContext = class(VisitorContext)
function AnalyzerContext:_init(visitors, parser, ast, generator)
VisitorContext._init(self, visitors)
self.parser = parser
self.rootscope = Scope(self, 'root', ast)
self.rootscope = Scope(self, ast)
self.ast = ast
self.scope = self.rootscope
self.usedbuiltins = {}
Expand Down Expand Up @@ -47,21 +47,21 @@ function AnalyzerContext:push_scope(scope)
self.scope = scope
end

function AnalyzerContext:push_forked_scope(kind, node)
function AnalyzerContext:push_forked_scope(node)
local scope
if node.scope then
scope = node.scope
assert(scope.kind == kind and scope.parent == self.scope and scope.node == node)
assert(scope.parent == self.scope and scope.node == node)
else
scope = self.scope:fork(kind, node)
scope = self.scope:fork(node)
node.scope = scope
end
self:push_scope(scope)
return scope
end

function AnalyzerContext:push_forked_cleaned_scope(kind, node)
local scope = self:push_forked_scope(kind, node)
function AnalyzerContext:push_forked_cleaned_scope(node)
local scope = self:push_forked_scope(node)
scope:clear_symbols()
return scope
end
Expand Down
2 changes: 1 addition & 1 deletion nelua/builtins.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function builtins.require(context, node)
local argnode = node[1][1]
if not (argnode and
argnode.attr.type and argnode.attr.type.is_stringview and
argnode.attr.comptime) or not context.scope:is_topscope() then
argnode.attr.comptime) or not context.scope.is_topscope then
-- not a compile time require
if canloadatruntime then
attr.runtime_require = true
Expand Down
22 changes: 11 additions & 11 deletions nelua/cgenerator.lua
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ local function destroy_upscopes_variables(context, emitter, kind)
repeat
destroy_scope_variables(context, emitter, scope)
scope = scope.parent
until (scope.kind == kind or scope == context.rootscope)
until (scope[kind] or scope == context.rootscope)
destroy_scope_variables(context, emitter, scope)
end

Expand Down Expand Up @@ -883,7 +883,7 @@ end
function visitors.Block(context, node, emitter)
local statnodes = node:args()
emitter:inc_indent()
local scope = context:push_forked_scope('block', node)
local scope = context:push_forked_scope(node)
do
emitter:add_traversal_list(statnodes, '')
end
Expand All @@ -901,7 +901,7 @@ function visitors.Return(context, node, emitter)
-- destroy parent blocks
local defemitter = CEmitter(context, emitter.depth)
local desemitter = CEmitter(context, emitter.depth)
local funcscope = context.scope:get_parent_of_kind('function') or context.rootscope
local funcscope = context.scope:get_up_return_scope() or context.rootscope
context.scope.alreadydestroyed = true
funcscope.has_return = true
if funcscope == context.rootscope then
Expand All @@ -917,7 +917,7 @@ function visitors.Return(context, node, emitter)
defemitter:add_val2type(primtypes.cint, retnode)
defemitter:add_ln(';')
end
elseif funcscope.doexpr then
elseif funcscope.is_doexpr then
defemitter:add_indent_ln('__expr = ', retnodes[1], ';')
else
local functype = funcscope.functype
Expand Down Expand Up @@ -1053,7 +1053,7 @@ function visitors.DoExpr(context, node, emitter)
emitter:inc_indent()
emitter:add_indent_ln(node.attr.type, ' __expr;')
emitter:dec_indent()
context:push_forked_scope('function', node)
context:push_forked_scope(node)
emitter:add(blocknode)
context:pop_scope()
emitter:inc_indent()
Expand All @@ -1078,7 +1078,7 @@ function visitors.While(context, node, emitter)
emitter:add_indent("while(")
emitter:add_val2type(primtypes.boolean, condnode)
emitter:add_ln(') {')
context:push_forked_scope('loop', node)
context:push_forked_scope(node)
emitter:add(blocknode)
context:pop_scope()
emitter:add_indent_ln("}")
Expand All @@ -1087,7 +1087,7 @@ end
function visitors.Repeat(context, node, emitter)
local blocknode, condnode = node:args()
emitter:add_indent_ln("while(true) {")
context:push_forked_scope('loop', node)
context:push_forked_scope(node)
emitter:add(blocknode)
emitter:inc_indent()
emitter:add_indent('if(')
Expand All @@ -1109,7 +1109,7 @@ function visitors.ForNum(context, node, emitter)
local fixedend = node.attr.fixedend
local itvarattr = itvarnode.attr
local itmutate = itvarattr.mutate
context:push_forked_scope('loop', node)
context:push_forked_scope(node)
do
local ccompop = cdefs.compare_ops[compop]
local ittype = itvarattr.type
Expand Down Expand Up @@ -1164,13 +1164,13 @@ function visitors.ForIn() --luacov:disable
end --luacov:enable

function visitors.Break(context, _, emitter)
destroy_upscopes_variables(context, emitter, 'loop')
destroy_upscopes_variables(context, emitter, 'is_loop')
context.scope.alreadydestroyed = true
emitter:add_indent_ln('break;')
end

function visitors.Continue(context, _, emitter)
destroy_upscopes_variables(context, emitter, 'loop')
destroy_upscopes_variables(context, emitter, 'is_loop')
context.scope.alreadydestroyed = true
emitter:add_indent_ln('continue;')
end
Expand Down Expand Up @@ -1251,7 +1251,7 @@ function visitors.FuncDef(context, node, emitter)

decemitter:add(varnode)
defemitter:add(varnode)
local funcscope = context:push_forked_scope('function', node)
local funcscope = context:push_forked_scope(node)
funcscope.functype = type
do
decemitter:add('(')
Expand Down
4 changes: 2 additions & 2 deletions nelua/configer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,9 @@ local function init_default_configs()
load_config(fs.getuserconfpath(fs.join('nelua', 'neluacfg.lua')))

-- load project plugins configs
for f in fs.dirmatch('.', '%.neluacfg.[-_%w]+.lua') do
for f in fs.dirmatch('.', '%.neluacfg.[-_%w]+.lua') do --luacov:disable
load_config(f)
end
end --luacov:enable

-- load project config
load_config('.neluacfg.lua')
Expand Down

0 comments on commit 9b954de

Please sign in to comment.