From 0b2b06df3c144e02a87ca73813e795454c86ff9f Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Wed, 30 Oct 2024 16:56:52 -0300 Subject: [PATCH] checker: fix missing check for fn var with generic return inherited to anon fn (fix #19045) (#22683) --- vlib/v/checker/fn.v | 16 +++++++++ .../tests/anon_missing_generic_err.out | 21 ++++++++++++ .../checker/tests/anon_missing_generic_err.vv | 33 +++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 vlib/v/checker/tests/anon_missing_generic_err.out create mode 100644 vlib/v/checker/tests/anon_missing_generic_err.vv diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 8c5c7d89f..7e5e21db6 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -608,6 +608,22 @@ fn (mut c Checker) anon_fn(mut node ast.AnonFn) ast.Type { c.error('original `${parent_var.name}` is immutable, declare it with `mut` to make it mutable', var.pos) } + if parent_var.typ != ast.no_type { + parent_var_sym := c.table.final_sym(parent_var.typ) + if parent_var_sym.info is ast.FnType { + ret_typ := c.unwrap_generic(parent_var_sym.info.func.return_type) + if ret_typ.has_flag(.generic) { + generic_names := c.table.generic_type_names(ret_typ) + curr_list := c.table.cur_fn.generic_names.join(', ') + for name in generic_names { + if name !in c.table.cur_fn.generic_names { + c.error('Add the generic type `${name}` to the anon fn generic list type, that is currently `[${curr_list}]`', + var.pos) + } + } + } + } + } if parent_var.expr is ast.IfGuardExpr { sym := c.table.sym(parent_var.expr.expr_type) if sym.info is ast.MultiReturn { diff --git a/vlib/v/checker/tests/anon_missing_generic_err.out b/vlib/v/checker/tests/anon_missing_generic_err.out new file mode 100644 index 000000000..76e88e30e --- /dev/null +++ b/vlib/v/checker/tests/anon_missing_generic_err.out @@ -0,0 +1,21 @@ +vlib/v/checker/tests/anon_missing_generic_err.vv:8:2: notice: uninitialized `fn` struct fields are not allowed, since they can result in segfaults; use `?fn` or `@[required]` or initialize the field with `=` (if you absolutely want to have unsafe function pointers, use `= unsafe { nil }`) + 6 | + 7 | struct Factory { + 8 | build fn () ! + | ~~~~~~~~~~~~~ + 9 | } + 10 | +vlib/v/checker/tests/anon_missing_generic_err.vv:21:14: error: Add the generic type `E` to the anon fn generic list type, that is currently `[F]` + 19 | fn use_factory[F, E](factory fn (dep F) !E) Factory { + 20 | return Factory{ + 21 | build: fn [factory] [F]() ! { + | ~~~~~~~ + 22 | mut dep := F{} + 23 | inject[F](mut dep)! +vlib/v/checker/tests/anon_missing_generic_err.vv:12:16: error: $for expects a type name or variable name to be used here, but F is not a type or variable name + 10 | + 11 | fn inject[T](mut serv T) ! { + 12 | $for field in T.fields { + | ^ + 13 | if field.typ is string { + 14 | serv.$(field.name) = 'Hello world!' diff --git a/vlib/v/checker/tests/anon_missing_generic_err.vv b/vlib/v/checker/tests/anon_missing_generic_err.vv new file mode 100644 index 000000000..0ba34f24e --- /dev/null +++ b/vlib/v/checker/tests/anon_missing_generic_err.vv @@ -0,0 +1,33 @@ +struct Test { + something string +} + +struct Dependency {} + +struct Factory { + build fn () ! +} + +fn inject[T](mut serv T) ! { + $for field in T.fields { + if field.typ is string { + serv.$(field.name) = 'Hello world!' + } + } +} + +fn use_factory[F, E](factory fn (dep F) !E) Factory { + return Factory{ + build: fn [factory] [F]() ! { + mut dep := F{} + inject[F](mut dep)! + dump(factory(dep)!) + } + } +} + +use_factory[Dependency, Test](fn (dep Dependency) !Test { + return Test{ + something: 'daleks!' + } +}).build()! -- 2.39.5