Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/other_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,10 @@ jobs:

# NB: this does not mean it runs, but at least keeps it from regressing
- name: Ensure V can be compiled with -autofree
run: ./v -autofree -o v2 cmd/v
run: ./v -autofree cmd/v

- name: Ensure V can be compiled with -no-closures
run: ./v -no-closures cmd/v

- name: Shader examples can be built
run: |
Expand Down
3 changes: 3 additions & 0 deletions vlib/v/compiler_errors_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ fn test_all() {
global_run_dir := '${checker_dir}/globals_run'
run_dir := '${checker_dir}/run'
skip_unused_dir := 'vlib/v/tests/skip_unused'
no_closures_dir := 'vlib/v/tests/no_closures'

checker_tests := get_tests_in_dir(checker_dir, false).filter(!it.contains('with_check_option'))
parser_tests := get_tests_in_dir(parser_dir, false)
Expand All @@ -100,6 +101,7 @@ fn test_all() {
module_tests := get_tests_in_dir(module_dir, true)
run_tests := get_tests_in_dir(run_dir, false)
skip_unused_dir_tests := get_tests_in_dir(skip_unused_dir, false)
no_closures_tests := get_tests_in_dir(no_closures_dir, false)
checker_with_check_option_tests := get_tests_in_dir(checker_with_check_option_dir,
false)
mut tasks := Tasks{
Expand All @@ -118,6 +120,7 @@ fn test_all() {
tasks.add('', run_dir, 'run', '.run.out', run_tests, false)
tasks.add('', checker_with_check_option_dir, '-check', '.out', checker_with_check_option_tests,
false)
tasks.add('', no_closures_dir, '-no-closures run', '.out', no_closures_tests, false)
tasks.run()

if os.user_os() == 'linux' {
Expand Down
3 changes: 3 additions & 0 deletions vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -4465,6 +4465,9 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
if name !in g.anon_fns {
g.anon_fns << name
g.gen_closure_fn(expr_styp, m, name)
if g.pref.no_closures {
g.error('a closure was generated for m.name: ${m.name}', node.pos)
}
}
}
g.write('builtin__closure__closure_create(${name}, ')
Expand Down
3 changes: 3 additions & 0 deletions vlib/v/gen/c/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,9 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
node.is_c_variadic)
if is_closure {
g.nr_closures++
if g.pref.no_closures {
g.error('a closure was generated for function', node.pos)
}
}
arg_str := g.out.after(arg_start_pos)
if node.no_body || ((g.pref.use_cache && g.pref.build_mode != .build_module) && node.is_builtin
Expand Down
9 changes: 9 additions & 0 deletions vlib/v/help/build/build-c.txt
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,15 @@ see also `v help build`.
user,gcboehm,eval
user,gg_record_trace,skip

-no-closures
Produce a compile time error early, if V generates a closure. That happens implicitly,
if you try to treat methods as values, or explicitly through `fn [captures] (){}`.
This option is useful to prevent accidental introduction of closures for environments,
that do not support closures well (projects targeting wasm, or projects that have to be
ported for less supported platforms, that do not have implementations for the closure thunks).
Note: the CI for the V compiler, checks that V itself, can be compiled with `-no-closures`,
to ease porting.

-no-rsp
By default, V passes all C compiler options to the backend C compiler
in so called "response files" (https://gcc.gnu.org/wiki/Response_Files).
Expand Down
4 changes: 4 additions & 0 deletions vlib/v/pref/pref.v
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ pub mut:
bare_builtin_dir string // Set by -bare-builtin-dir xyz/ . The xyz/ module should contain implementations of malloc, memset, etc, that are used by the rest of V's `builtin` module. That option is only useful with -freestanding (i.e. when is_bare is true).
no_preludes bool // Prevents V from generating preludes in resulting .c files
custom_prelude string // Contents of custom V prelude that will be prepended before code in resulting .c files
no_closures bool // Produce a compile time error, if a closure was generated for any reason (an implicit receiver method was stored, or an explicit `fn [captured]()`).
cmain string // The name of the generated C main function. Useful with framework like code, that uses macros to re-define `main`, like SDL2 does. When set, V will always generate `int THE_NAME(int ___argc, char** ___argv){`, *no matter* the platform.
lookup_path []string
output_cross_c bool // true, when the user passed `-os cross` or `-cross`
Expand Down Expand Up @@ -809,6 +810,9 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
res.skip_notes = true
res.notes_are_errors = false
}
'-no-closures' {
res.no_closures = true
}
'-no-rsp' {
res.no_rsp = true
}
Expand Down
7 changes: 7 additions & 0 deletions vlib/v/tests/no_closures/method_closure.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
vlib/v/tests/no_closures/method_closure.vv:9:15: cgen error: a closure was generated for m.name: member
7 |
8 | fn main() {
9 | x := UInt(4).member // generate an implicit closure that captures the receiver 4
| ~~~~~~
10 | res := x()
11 | assert res == 40
13 changes: 13 additions & 0 deletions vlib/v/tests/no_closures/method_closure.vv
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
type UInt = u32

fn (me UInt) member() u32 {
println('member called')
return me * 10
}

fn main() {
x := UInt(4).member // generate an implicit closure that captures the receiver 4
res := x()
assert res == 40
println('ok')
}
7 changes: 7 additions & 0 deletions vlib/v/tests/no_closures/simple_closure.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
vlib/v/tests/no_closures/simple_closure.vv:3:8: cgen error: a closure was generated for function
1 | fn main() {
2 | my_var := 12
3 | c1 := fn [my_var] () int {
| ~~~~~~~~~~~~~~~~~~~~
4 | return my_var
5 | }
8 changes: 8 additions & 0 deletions vlib/v/tests/no_closures/simple_closure.vv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fn main() {
my_var := 12
c1 := fn [my_var] () int {
return my_var
}
assert c1() == 12
println('ok')
}
Loading