From 0a8a0fdb0e0182b551025294c97792a8aef7c9d9 Mon Sep 17 00:00:00 2001 From: Swastik Baranwal Date: Sat, 25 Mar 2023 18:08:47 +0530 Subject: [PATCH] checker, cgen: fix check and cgen for `match x { StructName {} }` and `match x { y {} }`, where `x` is a mutable receiver (#17755) --- vlib/v/checker/match.v | 4 ++ .../tests/struct_match_same_type_node.out | 7 ++ .../tests/struct_match_same_type_node.vv | 25 +++++++ vlib/v/gen/c/match.v | 4 +- vlib/v/tests/match_struct_type_test.v | 69 +++++++++++++++++++ 5 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 vlib/v/checker/tests/struct_match_same_type_node.out create mode 100644 vlib/v/checker/tests/struct_match_same_type_node.vv create mode 100644 vlib/v/tests/match_struct_type_test.v diff --git a/vlib/v/checker/match.v b/vlib/v/checker/match.v index 4e00b1742..10c61a623 100644 --- a/vlib/v/checker/match.v +++ b/vlib/v/checker/match.v @@ -194,6 +194,10 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym // ensure that the sub expressions of the branch are actually checked, before anything else: _ := c.expr(expr) } + if expr is ast.TypeNode && cond_sym.kind == .struct_ { + c.error('struct instances cannot be matched by type name, they can only be matched to other instances of the same struct type', + branch.pos) + } if mut expr is ast.RangeExpr { // Allow for `match enum_value { 4..5 { } }`, even though usually int and enum values, // are considered incompatible outside unsafe{}, and are not allowed to be compared directly diff --git a/vlib/v/checker/tests/struct_match_same_type_node.out b/vlib/v/checker/tests/struct_match_same_type_node.out new file mode 100644 index 000000000..83e6accbd --- /dev/null +++ b/vlib/v/checker/tests/struct_match_same_type_node.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/struct_match_same_type_node.vv:10:3: error: struct instances cannot be matched by type name, they can only be matched to other instances of the same struct type + 8 | fn (mut t Type) returns() Union { + 9 | match t { + 10 | Type { + | ~~~~ + 11 | t.something = 'else' + 12 | } diff --git a/vlib/v/checker/tests/struct_match_same_type_node.vv b/vlib/v/checker/tests/struct_match_same_type_node.vv new file mode 100644 index 000000000..0d6b17f0e --- /dev/null +++ b/vlib/v/checker/tests/struct_match_same_type_node.vv @@ -0,0 +1,25 @@ +struct Type { +mut: + something string +} + +type Union = Type + +fn (mut t Type) returns() Union { + match t { + Type { + t.something = 'else' + } + else {} + } + + return t +} + +fn main() { + mut t := &Type{ + something: 'something' + } + t.returns() + println(t) +} diff --git a/vlib/v/gen/c/match.v b/vlib/v/gen/c/match.v index 9c445c074..9273913c2 100644 --- a/vlib/v/gen/c/match.v +++ b/vlib/v/gen/c/match.v @@ -472,8 +472,10 @@ fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var str g.write(')') } .struct_ { + derefs_expr := '*'.repeat(g.get_expr_type(expr).nr_muls()) + derefs_ctype := '*'.repeat(node.cond_type.nr_muls()) ptr_typ := g.equality_fn(node.cond_type) - g.write('${ptr_typ}_struct_eq(${cond_var}, ') + g.write('${ptr_typ}_struct_eq(${derefs_ctype}${cond_var}, ${derefs_expr}') g.expr(expr) g.write(')') } diff --git a/vlib/v/tests/match_struct_type_test.v b/vlib/v/tests/match_struct_type_test.v new file mode 100644 index 000000000..cbb75afab --- /dev/null +++ b/vlib/v/tests/match_struct_type_test.v @@ -0,0 +1,69 @@ +struct Type { +mut: + something string +} + +fn (mut t Type) match_mut_receiver_against_zero_struct_value() { + zero := Type{} + match t { + zero { + t.something = 'else' + } + else {} + } +} + +fn (t &Type) match_ref_receiver_against_zero_struct_value() bool { + zero := Type{} + match t { + zero { + return true + } + else {} + } + return false +} + +// + +fn test_match_struct_type_mut_receiver() { + mut a := &Type{} + a.match_mut_receiver_against_zero_struct_value() + assert a.something == 'else' + mut b := &Type{ + something: 'another' + } + b.match_mut_receiver_against_zero_struct_value() + assert b.something == 'another' +} + +fn test_match_struct_type_mut_receiver_with_non_ref_receiver_in_caller() { + mut a := Type{} + a.match_mut_receiver_against_zero_struct_value() + assert a.something == 'else' + mut b := Type{ + something: 'another' + } + b.match_mut_receiver_against_zero_struct_value() + assert b.something == 'another' +} + +// + +fn test_match_struct_type_ref_receiver() { + a := &Type{} + assert a.match_ref_receiver_against_zero_struct_value() + b := &Type{ + something: 'another' + } + assert !b.match_ref_receiver_against_zero_struct_value() +} + +fn test_match_struct_type_ref_receiver_with_non_ref_receiver_in_caller() { + a := Type{} + assert a.match_ref_receiver_against_zero_struct_value() + b := Type{ + something: 'another' + } + assert !b.match_ref_receiver_against_zero_struct_value() +} -- 2.39.5