From 9989453673ee8cb6a3324abb16f551669d9c4768 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 11 Mar 2026 12:41:13 +0300 Subject: [PATCH] checker: C error: cannot convert 'struct _option_string' to 'unsigned char *' (fixes #26347) --- vlib/v/checker/checker.v | 40 ++++++++++++++++++++- vlib/v/checker/tests/map_option_key_err.out | 20 +++++++++++ vlib/v/checker/tests/map_option_key_err.vv | 10 ++++++ 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 vlib/v/checker/tests/map_option_key_err.out create mode 100644 vlib/v/checker/tests/map_option_key_err.vv diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index b4cfb4af8..f04cca20e 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1795,6 +1795,36 @@ fn (mut c Checker) check_expr_option_or_result_call(expr ast.Expr, ret_type ast. return ret_type } +fn (mut c Checker) expr_unhandled_option_type(expr ast.Expr) ast.Type { + match expr { + ast.Ident { + if expr.or_expr.kind == .absent && expr.info is ast.IdentVar { + return c.type_resolver.get_type_or_default(expr, expr.info.typ) + } + } + ast.CallExpr { + if expr.or_block.kind == .absent { + return expr.return_type + } + } + ast.SelectorExpr { + if expr.or_block.kind == .absent { + return expr.typ + } + } + ast.IndexExpr { + if expr.or_expr.kind == .absent { + return expr.typ + } + } + ast.ParExpr { + return c.expr_unhandled_option_type(expr.expr) + } + else {} + } + return ast.void_type +} + fn (mut c Checker) check_or_expr(node ast.OrExpr, ret_type ast.Type, expr_return_type ast.Type, expr ast.Expr) { if node.kind == .propagate_option { if c.table.cur_fn != unsafe { nil } && !c.table.cur_fn.return_type.has_flag(.option) @@ -6034,10 +6064,18 @@ fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type { } else { // [1] if typ_sym.kind == .map { info := typ_sym.info as ast.Map + old_expected_type := c.expected_type c.expected_type = info.key_type index_type := c.expr(mut node.index) + c.expected_type = old_expected_type key_type := c.unwrap_generic(info.key_type) - if !c.check_types(index_type, key_type) { + actual_index_type := c.expr_unhandled_option_type(node.index) + if actual_index_type.has_flag(.option) && !key_type.has_flag(.option) { + got_typ_str, expected_typ_str := c.get_string_names_of(actual_index_type, + key_type) + c.error('invalid key: cannot use `${got_typ_str}` as `${expected_typ_str}`, it must be unwrapped first', + node.index.pos()) + } else if !c.check_types(index_type, key_type) { err := c.expected_msg(index_type, key_type) c.error('invalid key: ${err}', node.pos) } diff --git a/vlib/v/checker/tests/map_option_key_err.out b/vlib/v/checker/tests/map_option_key_err.out new file mode 100644 index 000000000..44c671346 --- /dev/null +++ b/vlib/v/checker/tests/map_option_key_err.out @@ -0,0 +1,20 @@ +vlib/v/checker/tests/map_option_key_err.vv:7:10: error: invalid key: cannot use `?string` as `string`, it must be unwrapped first + 5 | mut c := cuid2.new() + 6 | key := c.next() + 7 | _ = m[c.next()] + | ~~~~~~ + 8 | m[c.next()] = 1 + 9 | m[key]++ +vlib/v/checker/tests/map_option_key_err.vv:8:6: error: invalid key: cannot use `?string` as `string`, it must be unwrapped first + 6 | key := c.next() + 7 | _ = m[c.next()] + 8 | m[c.next()] = 1 + | ~~~~~~ + 9 | m[key]++ + 10 | } +vlib/v/checker/tests/map_option_key_err.vv:9:4: error: invalid key: cannot use `?string` as `string`, it must be unwrapped first + 7 | _ = m[c.next()] + 8 | m[c.next()] = 1 + 9 | m[key]++ + | ~~~ + 10 | } diff --git a/vlib/v/checker/tests/map_option_key_err.vv b/vlib/v/checker/tests/map_option_key_err.vv new file mode 100644 index 000000000..981e1462c --- /dev/null +++ b/vlib/v/checker/tests/map_option_key_err.vv @@ -0,0 +1,10 @@ +import rand.cuid2 + +fn main() { + mut m := map[string]int{} + mut c := cuid2.new() + key := c.next() + _ = m[c.next()] + m[c.next()] = 1 + m[key]++ +} -- 2.39.5