Skip to content

Commit

Permalink
Add switch case return logic
Browse files Browse the repository at this point in the history
Adds logic for switch case to be the last statement in a function and be detected for return values.
  • Loading branch information
Vurv78 committed Oct 13, 2023
1 parent eec505d commit 0694f56
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,13 @@ function number deadcase() {
} else {
return 2321515
}
}

function number switchcase() {
switch (5) {
case 5,
return 2
default,
return 5
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## SHOULD_FAIL:COMPILE

function string failure() {
switch (5) {
case 2,
break
default,
break

# 'break' does not return a value or cause a runtime error, just early returns switch.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## SHOULD_FAIL:COMPILE

function string failure() {
switch (5) {
case 2,
return "boowomp"
# no default case, compiler can't guarantee that this always runs, fails to compile.
}
}
25 changes: 18 additions & 7 deletions lua/entities/gmod_wire_expression2/base/compiler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ cvars.AddChangeCallback("wire_expression2_quotatick", function(_, old, new)
end, "compiler_quota_check")

---@class ScopeData
---@field dead boolean?
---@field dead "ret"|true?
---@field loop boolean?
---@field switch_case boolean?
---@field function { [1]: string, [2]: EnvFunction}?
Expand Down Expand Up @@ -313,7 +313,7 @@ local CompileVisitors = {
end

if els and dead then -- if (0) { return } else { return } mark any code after as dead
self.scope.data.dead = true
self.scope.data.dead = "ret"
end

return function(state) ---@param state RuntimeContext
Expand Down Expand Up @@ -522,14 +522,16 @@ local CompileVisitors = {
---@param data { [1]: Node, [2]: {[1]: Node, [2]: Node}[], [3]: Node? }
[NodeVariant.Switch] = function (self, trace, data)
local expr, expr_ty = self:CompileExpr(data[1])
local dead = true

local cases = {} ---@type { [1]: RuntimeOperator, [2]: RuntimeOperator }[]
for i, case in ipairs(data[2]) do
local cond, cond_ty = self:CompileExpr(case[1])
local block
self:Scope(function(scope)
local block = self:Scope(function(scope)
scope.data.switch_case = true
block = self:CompileStmt(case[2])
local b = self:CompileStmt(case[2])
dead = dead and scope.data.dead == "ret"
return b
end)

local eq = self:GetOperator("eq", { expr_ty, cond_ty }, case[1].trace)
Expand All @@ -541,7 +543,16 @@ local CompileVisitors = {
}
end

local default = data[3] and self:Scope(function() return self:CompileStmt(data[3]) end)
local default = data[3] and self:Scope(function(scope)
local b = self:CompileStmt(data[3])
dead = dead and scope.data.dead == "ret"
return b
end)

if dead and default then -- if all cases dead and has default case, mark scope as dead.
self.scope.data.dead = true
end

local ncases = #cases

return function(state) ---@param state RuntimeContext
Expand Down Expand Up @@ -890,7 +901,7 @@ local CompileVisitors = {
local fn = self.scope:ResolveData("function")
self:Assert(fn, "Cannot use `return` outside of a function", trace)

self.scope.data.dead = true
self.scope.data.dead = "ret"

local retval, ret_ty
if data then
Expand Down

0 comments on commit 0694f56

Please sign in to comment.