From f0abc452fa9ab3844bd443eaa49d80880e5058d0 Mon Sep 17 00:00:00 2001 From: Turiiya <34311583+ttytm@users.noreply.github.com> Date: Sat, 4 May 2024 16:09:45 +0200 Subject: [PATCH] checker: disallow structs with `@[params]` attribute as mutable function parameters (#21206) --- vlib/v/checker/fn.v | 11 +++++++-- .../tests/mut_parms_struct_param_err.out | 7 ++++++ .../tests/mut_parms_struct_param_err.vv | 12 ++++++++++ vlib/v/tests/struct_test.v | 24 ++++++++----------- 4 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 vlib/v/checker/tests/mut_parms_struct_param_err.out create mode 100644 vlib/v/checker/tests/mut_parms_struct_param_err.vv diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 898afc1bd9..266f431693 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -254,6 +254,10 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { c.error('generic struct `${pure_sym_name}` in fn declaration must specify the generic type names, e.g. ${pure_sym_name}[T]', param.type_pos) } + if param.is_mut && arg_typ_sym.info.attrs.any(it.name == 'params') { + c.error('declaring a mutable parameter that accepts a struct with the `@[params]` attribute is not allowed', + param.type_pos) + } } else if arg_typ_sym.info is ast.Interface { if arg_typ_sym.info.generic_types.len > 0 && !param.typ.has_flag(.generic) && arg_typ_sym.info.concrete_types.len == 0 { @@ -1278,8 +1282,11 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. } else { if param.is_mut { tok := param.specifier() - c.error('function `${node.name}` parameter `${param.name}` is `${tok}`, so use `${tok} ${call_arg.expr}` instead', - call_arg.expr.pos()) + param_sym := c.table.sym(param.typ) + if !(param_sym.info is ast.Struct && param_sym.info.attrs.any(it.name == 'params')) { + c.error('function `${node.name}` parameter `${param.name}` is `${tok}`, so use `${tok} ${call_arg.expr}` instead', + call_arg.expr.pos()) + } } else { c.fail_if_unreadable(call_arg.expr, arg_typ, 'argument') } diff --git a/vlib/v/checker/tests/mut_parms_struct_param_err.out b/vlib/v/checker/tests/mut_parms_struct_param_err.out new file mode 100644 index 0000000000..fb9a39581c --- /dev/null +++ b/vlib/v/checker/tests/mut_parms_struct_param_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/mut_parms_struct_param_err.vv:8:17: error: declaring a mutable parameter that accepts a struct with the `@[params]` attribute is not allowed + 6 | } + 7 | + 8 | fn foo(mut opts Params) bool { + | ~~~~~~ + 9 | return opts.a + 10 | } diff --git a/vlib/v/checker/tests/mut_parms_struct_param_err.vv b/vlib/v/checker/tests/mut_parms_struct_param_err.vv new file mode 100644 index 0000000000..064499f454 --- /dev/null +++ b/vlib/v/checker/tests/mut_parms_struct_param_err.vv @@ -0,0 +1,12 @@ +@[params] +struct Params { +mut: + a bool + x int +} + +fn foo(mut opts Params) bool { + return opts.a +} + +foo(a: true) diff --git a/vlib/v/tests/struct_test.v b/vlib/v/tests/struct_test.v index 12077e3909..f5ec3aabf9 100644 --- a/vlib/v/tests/struct_test.v +++ b/vlib/v/tests/struct_test.v @@ -25,7 +25,8 @@ struct Lol { struct User { name string - age int +mut: + age int } struct Foo { @@ -257,14 +258,12 @@ fn bar_config(c Config, def int) { assert c.def == def } -fn mut_bar_config(mut c Config, def int) &Config { - c.n = c.def - assert c.n == def - return unsafe { c } -} - fn foo_user(u User) {} +fn foo_mut_user(mut u User) { + u.age++ +} + fn test_struct_literal_args() { foo_config(20, n: 10 @@ -277,17 +276,14 @@ fn test_struct_literal_args() { bar_config(Config{}, 10) bar_config(Config{ def: 4 }, 4) - mut c_ := Config{ - def: 10 - } - c := mut_bar_config(mut c_, 10) - assert c.n == 10 - assert c.def == 10 - foo_user(name: 'Peter') foo_user(name: 'Peter') foo_user(age: 7) foo_user(name: 'Stew', age: 50) + + mut user := User{'Stew', 50} + foo_mut_user(mut user) + assert user.age == 51 } struct City {