diff --git a/vlib/v/checker/assign.v b/vlib/v/checker/assign.v index ee874193581e4a..4d3fee67d17813 100644 --- a/vlib/v/checker/assign.v +++ b/vlib/v/checker/assign.v @@ -673,6 +673,15 @@ or use an explicit `unsafe{ a[..] }`, if you do not want a copy of the slice.', right.pos()) } } + .boolean_and_assign, .boolean_or_assign { + if c.table.final_sym(left_type_unwrapped).kind != .bool { + c.error('operator ${node.op.str()} not defined on left operand type `${left_sym.name}`', + left.pos()) + } else if c.table.final_sym(right_type_unwrapped).kind != .bool { + c.error('operator ${node.op.str()} not defined on right operand type `${right_sym.name}`', + right.pos()) + } + } .unsigned_right_shift_assign { if node.left.len != 1 || node.right.len != 1 { c.error('unsupported operation: unable to lower expression for unsigned shift assignment.', diff --git a/vlib/v/checker/tests/mismatch_bool_assign_err.out b/vlib/v/checker/tests/mismatch_bool_assign_err.out new file mode 100644 index 00000000000000..bfd9ef3422290e --- /dev/null +++ b/vlib/v/checker/tests/mismatch_bool_assign_err.out @@ -0,0 +1,12 @@ +vlib/v/checker/tests/mismatch_bool_assign_err.vv:2:1: error: operator ||= not defined on left operand type `int` + 1 | mut b := 12 + 2 | b ||= 34 + | ^ + 3 | println(b) + 4 | +vlib/v/checker/tests/mismatch_bool_assign_err.vv:6:1: error: operator &&= not defined on left operand type `string` + 4 | + 5 | mut c := 'str' + 6 | c &&= 's' + | ^ + 7 | println(c) diff --git a/vlib/v/checker/tests/mismatch_bool_assign_err.vv b/vlib/v/checker/tests/mismatch_bool_assign_err.vv new file mode 100644 index 00000000000000..71030d696e1a43 --- /dev/null +++ b/vlib/v/checker/tests/mismatch_bool_assign_err.vv @@ -0,0 +1,7 @@ +mut b := 12 +b ||= 34 +println(b) + +mut c := 'str' +c &&= 's' +println(c) diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index 310de68b41deb7..76703bc6e06c21 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -535,6 +535,28 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { op_overloaded = true } } + + if left_sym.kind == .bool && right_sym.kind == .bool + && node.op in [.boolean_or_assign, .boolean_and_assign] { + extracted_op := match node.op { + .boolean_or_assign { + '||' + } + .boolean_and_assign { + '&&' + } + else { + 'unknown op' + } + } + g.expr(left) + g.write(' = ') + g.expr(left) + g.write(' ${extracted_op} ') + g.expr(val) + g.writeln(';') + return + } if right_sym.info is ast.FnType && is_decl { if is_inside_ternary { g.out.write_string(util.tabs(g.indent - g.inside_ternary)) diff --git a/vlib/v/scanner/scanner.v b/vlib/v/scanner/scanner.v index e7daf8f3f435c2..ff4f65c7bd5931 100644 --- a/vlib/v/scanner/scanner.v +++ b/vlib/v/scanner/scanner.v @@ -872,6 +872,12 @@ pub fn (mut s Scanner) text_scan() token.Token { } } `&` { + if nextc == `&` { + if s.look_ahead(2) == `=` { + s.pos += 2 + return s.new_token(.boolean_and_assign, '', 3) + } + } if nextc == `=` { s.pos++ return s.new_token(.and_assign, '', 2) @@ -885,6 +891,10 @@ pub fn (mut s Scanner) text_scan() token.Token { } `|` { if nextc == `|` { + if s.look_ahead(2) == `=` { + s.pos += 2 + return s.new_token(.boolean_or_assign, '', 3) + } s.pos++ return s.new_token(.logical_or, '', 2) } diff --git a/vlib/v/tests/bool_assign_operator_test.v b/vlib/v/tests/bool_assign_operator_test.v new file mode 100644 index 00000000000000..c82aab699b23e2 --- /dev/null +++ b/vlib/v/tests/bool_assign_operator_test.v @@ -0,0 +1,8 @@ +fn test_bool_assign_operator() { + mut flag := true + flag ||= false + assert flag == true + + flag &&= false + assert flag == false +} diff --git a/vlib/v/token/token.v b/vlib/v/token/token.v index f8230f07cf9245..a0eb80688b120c 100644 --- a/vlib/v/token/token.v +++ b/vlib/v/token/token.v @@ -67,6 +67,8 @@ pub enum Kind { right_shift_assign // <<= left_shift_assign // >>= unsigned_right_shift_assign // >>>= + boolean_and_assign // &&= + boolean_or_assign // ||= lcbr // { rcbr // } lpar // ( @@ -186,7 +188,7 @@ pub enum AtKind { pub const assign_tokens = [Kind.assign, .plus_assign, .minus_assign, .mult_assign, .div_assign, .xor_assign, .mod_assign, .or_assign, .and_assign, .right_shift_assign, .left_shift_assign, - .unsigned_right_shift_assign] + .unsigned_right_shift_assign, .boolean_and_assign, .boolean_or_assign] pub const valid_at_tokens = ['@VROOT', '@VMODROOT', '@VEXEROOT', '@FN', '@METHOD', '@MOD', '@STRUCT', '@VEXE', '@FILE', '@LINE', '@COLUMN', '@VHASH', '@VCURRENTHASH', '@VMOD_FILE', '@VMODHASH', @@ -261,6 +263,8 @@ fn build_token_str() []string { s[Kind.right_shift_assign] = '>>=' s[Kind.unsigned_right_shift_assign] = '>>>=' s[Kind.left_shift_assign] = '<<=' + s[Kind.boolean_or_assign] = '||=' + s[Kind.boolean_and_assign] = '&&=' s[Kind.lcbr] = '{' s[Kind.rcbr] = '}' s[Kind.lpar] = '(' @@ -468,6 +472,8 @@ pub fn build_precedences() []Precedence { p[Kind.unsigned_right_shift_assign] = .assign p[Kind.mult_assign] = .assign p[Kind.xor_assign] = .assign + p[Kind.boolean_or_assign] = .assign + p[Kind.boolean_and_assign] = .assign p[Kind.key_in] = .in_as p[Kind.not_in] = .in_as p[Kind.key_as] = .in_as @@ -583,6 +589,8 @@ pub fn kind_to_string(k Kind) string { .right_shift_assign { 'right_shift_assign' } .left_shift_assign { 'left_shift_assign' } .unsigned_right_shift_assign { 'unsigned_right_shift_assign' } + .boolean_and_assign { 'boolean_and_assign' } + .boolean_or_assign { 'boolean_or_assign' } .lcbr { 'lcbr' } .rcbr { 'rcbr' } .lpar { 'lpar' } @@ -707,6 +715,8 @@ pub fn kind_from_string(s string) !Kind { 'right_shift_assign' { .right_shift_assign } 'left_shift_assign' { .left_shift_assign } 'unsigned_right_shift_assign' { .unsigned_right_shift_assign } + 'boolean_and_assign' { .boolean_and_assign } + 'boolean_or_assign' { .boolean_or_assign } 'lcbr' { .lcbr } 'rcbr' { .rcbr } 'lpar' { .lpar } @@ -793,6 +803,8 @@ pub fn assign_op_to_infix_op(op Kind) Kind { .right_shift_assign { .right_shift } .unsigned_right_shift_assign { .unsigned_right_shift } .left_shift_assign { .left_shift } + .boolean_and_assign { .and } + .boolean_or_assign { .logical_or } else { ._end_ } } }