Skip to content

Commit

Permalink
Disallow locals aliasing globals
Browse files Browse the repository at this point in the history
If a global reference is used inside the definition
of a local with the same name, the global is aliased by the local.
Previous attempts to get around this do not work generally, but we can
detect when such a situation occurs and emit a compiler error. This
'fix' could possibly be better addressed with a VERY large compiler
refactor, but such code is usually bad style anyways.
  • Loading branch information
bakpakin committed Jan 9, 2020
1 parent 669a072 commit 59de14c
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 4 deletions.
14 changes: 11 additions & 3 deletions fennel.lua
Expand Up @@ -482,6 +482,9 @@ local function makeScope(parent)
includes = setmetatable({}, {
__index = parent and parent.includes
}),
refedglobals = setmetatable({}, {
__index = parent and parent.refedglobals
}),
autogensyms = {},
parent = parent,
vararg = parent and parent.vararg,
Expand Down Expand Up @@ -639,8 +642,10 @@ end
-- Calling this function will mean that further
-- compilation in scope will use these new manglings
-- instead of the current manglings.
local function applyManglings(scope, newManglings)
local function applyManglings(scope, newManglings, ast)
for raw, mangled in pairs(newManglings) do
assertCompile(not scope.refedglobals[mangled],
"use of global " .. raw .. " is aliased by a local", ast)
scope.manglings[raw] = mangled
end
end
Expand Down Expand Up @@ -727,6 +732,9 @@ local function symbolToExpression(symbol, scope, isReference)
-- then we need to check for allowed globals
assertCompile(not isReference or isLocal or globalAllowed(parts[1]),
'unknown global in strict mode: ' .. parts[1], symbol)
if not isLocal then
rootScope.refedglobals[parts[1]] = true
end
return expr(combineParts(parts, scope), etype)
end

Expand Down Expand Up @@ -1241,7 +1249,7 @@ local function destructure(to, from, ast, scope, parent, opts)
end

local ret = destructure1(to, nil, ast, true)
applyManglings(scope, newManglings)
applyManglings(scope, newManglings, ast)
return ret
end

Expand Down Expand Up @@ -1752,7 +1760,7 @@ SPECIALS['each'] = function(ast, scope, parent)
destructure(args, raw, ast, scope, chunk,
{ declaration = true, nomulti = true })
end
applyManglings(scope, newManglings)
applyManglings(scope, newManglings, ast)
compileDo(ast, scope, chunk, 3)
emit(parent, chunk, ast)
emit(parent, 'end', ast)
Expand Down
6 changes: 5 additions & 1 deletion test.lua
Expand Up @@ -572,6 +572,10 @@ local compile_failures = {
-- macros loaded in function scope shouldn't leak to other functions
["((fn [] (require-macros \"test-macros\") (global x1 (->1 99 (+ 31)))))\
(->1 23 (+ 1))"]="unknown global in strict mode",
-- Locals aliasing globals
["(local ipairs #(ipairs $))"]="aliased by a local",
["(let [next #(next $)] print)"]="aliased by a local",
["(let [pairs #(pairs $)] pairs)"]="aliased by a local",
-- other
["(match [1 2 3] [a & b c] nil)"]="rest argument in final position",
["(x(y))"]="expected whitespace before opening delimiter %(",
Expand All @@ -585,7 +589,7 @@ local compile_failures = {
print("Running tests for compile errors...")
for code, expected_msg in pairs(compile_failures) do
local ok, msg = pcall(fennel.compileString, code,
{allowedGlobals = {"pairs"}, checkUnusedLocals = true})
{allowedGlobals = {"pairs", "next", "ipairs"}, checkUnusedLocals = true})
if(ok) then
fail = fail + 1
print(" Expected failure when compiling " .. code .. ": " .. msg)
Expand Down

0 comments on commit 59de14c

Please sign in to comment.