Commit 00982f75e9

Dmitry Matveyev <greenfork.lists@yandex.com>
2021-06-20 20:04:14
stage2: Remove special double ampersand parsing case (#9114)
* Remove parser error on double ampersand * Add failing test for double ampersand case * Add error when encountering double ampersand in AstGen "Bit and" operator should not make sense when one of its operands is an address. * Check that 2 ampersands are adjacent to each other in source string * Remove cases of unused variables in tests
1 parent e4225ca
Changed files (7)
doc/docgen.zig
@@ -999,7 +999,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: anytype, source_token:
             .tilde,
             => try writeEscaped(out, src[token.loc.start..token.loc.end]),
 
-            .invalid, .invalid_ampersands, .invalid_periodasterisks => return parseError(
+            .invalid, .invalid_periodasterisks => return parseError(
                 docgen_tokenizer,
                 source_token,
                 "syntax error",
lib/std/zig/ast.zig
@@ -280,9 +280,6 @@ pub const Tree = struct {
                     token_tags[parse_error.token].symbol(),
                 });
             },
-            .invalid_and => {
-                return stream.writeAll("`&&` is invalid; note that `and` is boolean AND");
-            },
             .invalid_bit_range => {
                 return stream.writeAll("bit range not allowed on slices and arrays");
             },
@@ -2412,7 +2409,6 @@ pub const Error = struct {
         extra_const_qualifier,
         extra_volatile_qualifier,
         ptr_mod_on_array_child_type,
-        invalid_and,
         invalid_bit_range,
         invalid_token,
         same_line_doc_comment,
lib/std/zig/parse.zig
@@ -1333,7 +1333,6 @@ const Parser = struct {
         .keyword_or = .{ .prec = 10, .tag = .bool_or },
 
         .keyword_and = .{ .prec = 20, .tag = .bool_and },
-        .invalid_ampersands = .{ .prec = 20, .tag = .bool_and },
 
         .equal_equal = .{ .prec = 30, .tag = .equal_equal, .assoc = Assoc.none },
         .bang_equal = .{ .prec = 30, .tag = .bang_equal, .assoc = Assoc.none },
@@ -1385,9 +1384,6 @@ const Parser = struct {
                 .keyword_catch => {
                     _ = try p.parsePayload();
                 },
-                .invalid_ampersands => {
-                    try p.warn(.invalid_and);
-                },
                 else => {},
             }
             const rhs = try p.parseExprPrecedence(info.prec + 1);
lib/std/zig/parser_test.zig
@@ -4930,7 +4930,6 @@ test "recovery: missing comma" {
     , &[_]Error{
         .expected_token,
         .expected_token,
-        .invalid_and,
         .invalid_token,
     });
 }
@@ -4963,7 +4962,6 @@ test "recovery: missing return type" {
         \\test ""
     , &[_]Error{
         .expected_return_type,
-        .invalid_and,
         .expected_block,
     });
 }
@@ -4980,7 +4978,6 @@ test "recovery: continue after invalid decl" {
         .expected_token,
         .expected_pub_item,
         .expected_param_list,
-        .invalid_and,
     });
     try testError(
         \\threadlocal test "" {
@@ -4989,7 +4986,6 @@ test "recovery: continue after invalid decl" {
     , &[_]Error{
         .expected_var_decl,
         .expected_param_list,
-        .invalid_and,
     });
 }
 
@@ -4998,13 +4994,11 @@ test "recovery: invalid extern/inline" {
         \\inline test "" { a && b; }
     , &[_]Error{
         .expected_fn,
-        .invalid_and,
     });
     try testError(
         \\extern "" test "" { a && b; }
     , &[_]Error{
         .expected_var_decl_or_fn,
-        .invalid_and,
     });
 }
 
@@ -5016,9 +5010,7 @@ test "recovery: missing semicolon" {
         \\    @foo
         \\}
     , &[_]Error{
-        .invalid_and,
         .expected_token,
-        .invalid_and,
         .expected_token,
         .expected_param_list,
         .expected_token,
@@ -5038,7 +5030,6 @@ test "recovery: invalid container members" {
         .expected_expr,
         .expected_token,
         .expected_container_members,
-        .invalid_and,
         .expected_token,
     });
 }
@@ -5076,7 +5067,6 @@ test "recovery: invalid global error set access" {
     , &[_]Error{
         .expected_token,
         .expected_token,
-        .invalid_and,
     });
 }
 
@@ -5094,7 +5084,6 @@ test "recovery: invalid asterisk after pointer dereference" {
         \\}
     , &[_]Error{
         .asterisk_after_ptr_deref,
-        .invalid_and,
     });
 }
 
@@ -5110,7 +5099,6 @@ test "recovery: missing semicolon after if, for, while stmt" {
         .expected_semi_or_else,
         .expected_semi_or_else,
         .expected_semi_or_else,
-        .invalid_and,
     });
 }
 
lib/std/zig/tokenizer.zig
@@ -76,7 +76,6 @@ pub const Token = struct {
 
     pub const Tag = enum {
         invalid,
-        invalid_ampersands,
         invalid_periodasterisks,
         identifier,
         string_literal,
@@ -210,7 +209,6 @@ pub const Token = struct {
                 .container_doc_comment,
                 => null,
 
-                .invalid_ampersands => "&&",
                 .invalid_periodasterisks => ".**",
                 .bang => "!",
                 .pipe => "|",
@@ -579,11 +577,6 @@ pub const Tokenizer = struct {
                 },
 
                 .ampersand => switch (c) {
-                    '&' => {
-                        result.tag = .invalid_ampersands;
-                        self.index += 1;
-                        break;
-                    },
                     '=' => {
                         result.tag = .ampersand_equal;
                         self.index += 1;
src/AstGen.zig
@@ -551,7 +551,23 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
         .mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap),
         .div      => return simpleBinOp(gz, scope, rl, node, .div),
         .mod      => return simpleBinOp(gz, scope, rl, node, .mod_rem),
-        .bit_and  => return simpleBinOp(gz, scope, rl, node, .bit_and),
+        .bit_and  => {
+            const current_ampersand_token = main_tokens[node];
+            if (token_tags[current_ampersand_token + 1] == .ampersand) {
+                const token_starts = tree.tokens.items(.start);
+                const current_token_offset = token_starts[current_ampersand_token];
+                const next_token_offset = token_starts[current_ampersand_token + 1];
+                if (current_token_offset + 1 == next_token_offset) {
+                    return astgen.failTok(
+                        current_ampersand_token,
+                        "`&&` is invalid; note that `and` is boolean AND",
+                        .{},
+                    );
+                }
+            }
+
+            return simpleBinOp(gz, scope, rl, node, .bit_and);
+        },
         .bit_or   => return simpleBinOp(gz, scope, rl, node, .bit_or),
         .bit_xor  => return simpleBinOp(gz, scope, rl, node, .xor),
 
test/stage2/test.zig
@@ -1565,4 +1565,27 @@ pub fn addCases(ctx: *TestContext) !void {
             \\}
         , "HeHelHellHello");
     }
+
+    {
+        var case = ctx.exe("double ampersand", linux_x64);
+
+        case.addError(
+            \\pub const a = if (true && false) 1 else 2;
+        , &[_][]const u8{":1:24: error: `&&` is invalid; note that `and` is boolean AND"});
+
+        case.addError(
+            \\pub fn main() void {
+            \\    const a = true;
+            \\    const b = false;
+            \\    _ = a & &b;
+            \\}
+        , &[_][]const u8{":4:11: error: incompatible types: 'bool' and '*const bool'"});
+
+        case.addCompareOutput(
+            \\pub fn main() void {
+            \\    const b: u8 = 1;
+            \\    _ = &&b;
+            \\}
+        , "");
+    }
 }