Skip to content

Commit 436c3e6

Browse files
gh-76: Ban RETURN from outside FUNC.
1 parent 78a9a7f commit 436c3e6

File tree

3 files changed

+50
-2
lines changed

3 files changed

+50
-2
lines changed

src/builtins.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7229,8 +7229,9 @@ static int parallel_worker(void* arg) {
72297229
// Prepare a call environment from the function's closure
72307230
Env* call_env = env_create(ps->func->closure);
72317231

7232-
// Execute the function body as a program block
7233-
ExecResult res = exec_program_in_env(thr_interp, ps->func->body, call_env);
7232+
// Execute the function body as if it were a function call so RETURN
7233+
// statements inside the function body are allowed.
7234+
ExecResult res = exec_program_in_env_as_function(thr_interp, ps->func->body, call_env, ps->func->name);
72347235

72357236
if (res.status == EXEC_ERROR && res.error) {
72367237
ps->errors[ps->index] = res.error; // transfer ownership

src/interpreter.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2414,6 +2414,14 @@ static ExecResult exec_stmt(Interpreter* interp, Stmt* stmt, Env* env, LabelMap*
24142414
}
24152415

24162416
case STMT_RETURN: {
2417+
// RETURN is valid only inside a function. Detect function
2418+
// execution context by inspecting the trace stack: function
2419+
// call frames are pushed with has_call_location == 1.
2420+
if (interp->trace_stack_count == 0 ||
2421+
interp->trace_stack[interp->trace_stack_count - 1].has_call_location == 0) {
2422+
return make_error("RETURN used outside function", stmt->line, stmt->column);
2423+
}
2424+
24172425
// Evaluate return expression and propagate as EXEC_RETURN
24182426
Value v = eval_expr(interp, stmt->as.return_stmt.value, env);
24192427
if (interp->error) {
@@ -3308,3 +3316,37 @@ ExecResult exec_program_in_env(Interpreter* interp, Stmt* program, Env* env) {
33083316

33093317
return res;
33103318
}
3319+
3320+
// Execute a parsed function body (`program`) within an existing Interpreter
3321+
// and Env treating the execution as a function call frame so `RETURN` is
3322+
// permitted. `func_name` is used for traceback entries.
3323+
ExecResult exec_program_in_env_as_function(Interpreter* interp, Stmt* program, Env* env, const char* func_name) {
3324+
if (!interp || !program || !env) {
3325+
ExecResult r = make_error("Internal: invalid args to exec_program_in_env_as_function", 0, 0);
3326+
return r;
3327+
}
3328+
3329+
// Ensure there's a call-like trace frame so RETURN is valid.
3330+
if (interp->trace_stack_count == 0) {
3331+
(void)trace_push_frame(interp, func_name ? func_name : "<lambda>", env, 0, 0, 1);
3332+
} else {
3333+
if (trace_push_frame(interp, func_name ? func_name : "<lambda>", env, 0, 0, 1) != 0) {
3334+
ExecResult r = make_error("Out of memory", 0, 0);
3335+
return r;
3336+
}
3337+
}
3338+
3339+
LabelMap labels = {0};
3340+
ExecResult res = exec_stmt_list(interp, &program->as.block, env, &labels);
3341+
3342+
if (res.status == EXEC_ERROR) {
3343+
char* tb = interpreter_format_traceback(interp, res.error, res.error_line, res.error_column);
3344+
free(res.error);
3345+
res.error = tb;
3346+
}
3347+
3348+
for (size_t i = 0; i < labels.count; i++) value_free(labels.items[i].key);
3349+
free(labels.items);
3350+
3351+
return res;
3352+
}

src/interpreter.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ ExecResult exec_program(Stmt* program, const char* source_path);
119119
// `exec_program`.
120120
ExecResult exec_program_in_env(Interpreter* interp, Stmt* program, Env* env);
121121

122+
// Execute a parsed function body (`program`) within an interpreter and
123+
// environment while treating the execution as a function call. This pushes
124+
// a call-frame so `RETURN` is valid inside `program`.
125+
ExecResult exec_program_in_env_as_function(Interpreter* interp, Stmt* program, Env* env, const char* func_name);
126+
122127
// Build and return a traceback string for the current interpreter call stack.
123128
// Caller owns the returned string.
124129
char* interpreter_format_traceback(Interpreter* interp, const char* error_msg, int line, int col);

0 commit comments

Comments
 (0)