zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 72d954e7d3cfb8b46a26d5a199a9a5ecd36d87be (tree)
parent fdac89d6cd65fa19bd5c6d381b62d980d97e5852
Author: Matthew Lugg <mlugg@mlugg.co.uk>
Date:   Wed, 29 Apr 2026 19:58:39 +0100

compiler: remove array multiplication from the language

Resolves: https://github.com/ziglang/zig/issues/24738

Diffstat:
Mlib/compiler/reduce/Walk.zig | 1-
Mlib/docs/wasm/Walk.zig | 1-
Mlib/docs/wasm/html_render.zig | 3+--
Mlib/std/crypto/Certificate.zig | 2+-
Mlib/std/zig.zig | 2--
Mlib/std/zig/Ast.zig | 22+---------------------
Mlib/std/zig/Ast/Render.zig | 12------------
Mlib/std/zig/AstGen.zig | 16----------------
Mlib/std/zig/AstRlAnnotate.zig | 6------
Mlib/std/zig/AstSmith.zig | 14++++----------
Mlib/std/zig/Parse.zig | 66++----------------------------------------------------------------
Mlib/std/zig/TokenSmith.zig | 6------
Mlib/std/zig/Zir.zig | 16----------------
Mlib/std/zig/ZonGen.zig | 1-
Mlib/std/zig/llvm/Builder.zig | 4++--
Mlib/std/zig/parser_test.zig | 33+++------------------------------
Mlib/std/zig/tokenizer.zig | 47++++-------------------------------------------
Msrc/Sema.zig | 169-------------------------------------------------------------------------------
Msrc/print_zir.zig | 14--------------
Mtools/docgen.zig | 3+--
Mtools/doctest.zig | 3+--
21 files changed, 20 insertions(+), 421 deletions(-)

diff --git a/lib/compiler/reduce/Walk.zig b/lib/compiler/reduce/Walk.zig @@ -248,7 +248,6 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { .add_wrap, .add_sat, .array_cat, - .array_mult, .assign, .assign_bit_and, .assign_bit_or, diff --git a/lib/docs/wasm/Walk.zig b/lib/docs/wasm/Walk.zig @@ -709,7 +709,6 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index) .less_or_equal, .array_cat, - .array_mult, .error_union, .merge_error_sets, .bool_and, diff --git a/lib/docs/wasm/html_render.zig b/lib/docs/wasm/html_render.zig @@ -302,7 +302,6 @@ pub fn fileSourceHtml( .minus_pipe_equal, .asterisk, .asterisk_equal, - .asterisk_asterisk, .asterisk_percent, .asterisk_percent_equal, .asterisk_pipe, @@ -328,7 +327,7 @@ pub fn fileSourceHtml( .tilde, => try appendEscaped(out, slice), - .invalid, .invalid_periodasterisks => return error.InvalidToken, + .invalid => return error.InvalidToken, } } } diff --git a/lib/std/crypto/Certificate.zig b/lib/std/crypto/Certificate.zig @@ -1092,7 +1092,7 @@ pub const rsa = struct { } var m_p_buf: [8 + Hash.digest_length + Hash.digest_length]u8 = undefined; var m_p = m_p_buf[0 .. 8 + Hash.digest_length + sLen]; - std.mem.copyForwards(u8, m_p, @as(*const [8]u8, @splat(0))); + std.mem.copyForwards(u8, m_p, @as(*const [8]u8, &@splat(0))); std.mem.copyForwards(u8, m_p[8..], &mHash); std.mem.copyForwards(u8, m_p[(8 + Hash.digest_length)..], salt); diff --git a/lib/std/zig.zig b/lib/std/zig.zig @@ -811,7 +811,6 @@ pub const SimpleComptimeReason = enum(u32) { compile_error_string, inline_assembly_code, atomic_order, - array_mul_factor, slice_cat_operand, inline_call_target, generic_call_target, @@ -899,7 +898,6 @@ pub const SimpleComptimeReason = enum(u32) { .compile_error_string => "compile error string must be comptime-known", .inline_assembly_code => "inline assembly code must be comptime-known", .atomic_order => "atomic order must be comptime-known", - .array_mul_factor => "array multiplication factor must be comptime-known", .slice_cat_operand => "slice being concatenated must be comptime-known", .inline_call_target => "function being called inline must be comptime-known", .generic_call_target => "generic function being called must be comptime-known", diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig @@ -331,11 +331,6 @@ pub fn rootDecls(tree: Ast) []const Node.Index { pub fn renderError(tree: Ast, parse_error: Error, w: *Writer) Writer.Error!void { switch (parse_error.tag) { - .asterisk_after_ptr_deref => { - // Note that the token will point at the `.*` but ideally the source - // location would point to the `*` after the `.*`. - return w.writeAll("'.*' cannot be followed by '*'; are you missing a space?"); - }, .chained_comparison_operators => { return w.writeAll("comparison operators cannot be chained"); }, @@ -687,7 +682,6 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex { .mul, .div, .mod, - .array_mult, .mul_wrap, .mul_sat, .add, @@ -924,7 +918,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex { .mul, .div, .mod, - .array_mult, .mul_wrap, .mul_sat, .add, @@ -2111,9 +2104,7 @@ fn fullFnProtoComponents(tree: Ast, info: full.FnProto.Components) full.FnProto fn fullPtrTypeComponents(tree: Ast, info: full.PtrType.Components) full.PtrType { const size: std.builtin.Type.Pointer.Size = switch (tree.tokenTag(info.main_token)) { - .asterisk, - .asterisk_asterisk, - => .one, + .asterisk => .one, .l_bracket => switch (tree.tokenTag(info.main_token + 1)) { .asterisk => if (tree.tokenTag(info.main_token + 2) == .identifier) .c else .many, else => .slice, @@ -2842,7 +2833,6 @@ pub const Error = struct { } = .{ .none = {} }, pub const Tag = enum { - asterisk_after_ptr_deref, chained_comparison_operators, decl_between_fields, expected_block, @@ -3178,8 +3168,6 @@ pub const Node = struct { div, /// `lhs % rhs`. The `main_token` field is the `%` token. mod, - /// `lhs ** rhs`. The `main_token` field is the `**` token. - array_mult, /// `lhs *% rhs`. The `main_token` field is the `*%` token. mul_wrap, /// `lhs *| rhs`. The `main_token` field is the `*|` token. @@ -3250,8 +3238,6 @@ pub const Node = struct { /// /// The `main_token` is the asterisk if a single item pointer or the /// lbracket if a slice, many-item pointer, or C-pointer. - /// The `main_token` might be a ** token, which is shared with a - /// parent/child pointer type and may require special handling. ptr_type_aligned, /// `[*:lhs]rhs`, /// `*rhs`, @@ -3263,8 +3249,6 @@ pub const Node = struct { /// /// The `main_token` is the asterisk if a single item pointer or the /// lbracket if a slice, many-item pointer, or C-pointer. - /// The `main_token` might be a ** token, which is shared with a - /// parent/child pointer type and may require special handling. ptr_type_sentinel, /// The `data` field is a `.extra_and_node`: /// 1. a `ExtraIndex` to `PtrType`. @@ -3272,8 +3256,6 @@ pub const Node = struct { /// /// The `main_token` is the asterisk if a single item pointer or the /// lbracket if a slice, many-item pointer, or C-pointer. - /// The `main_token` might be a ** token, which is shared with a - /// parent/child pointer type and may require special handling. ptr_type, /// The `data` field is a `.extra_and_node`: /// 1. a `ExtraIndex` to `PtrTypeBitRange`. @@ -3281,8 +3263,6 @@ pub const Node = struct { /// /// The `main_token` is the asterisk if a single item pointer or the /// lbracket if a slice, many-item pointer, or C-pointer. - /// The `main_token` might be a ** token, which is shared with a - /// parent/child pointer type and may require special handling. ptr_type_bit_range, /// `lhs[rhs..]` /// diff --git a/lib/std/zig/Ast/Render.zig b/lib/std/zig/Ast/Render.zig @@ -508,7 +508,6 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { .add_wrap, .add_sat, .array_cat, - .array_mult, .bang_equal, .bit_and, .bit_or, @@ -1028,16 +1027,6 @@ fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!voi switch (ptr_type.size) { .one => { - // Since ** tokens exist and the same token is shared by two - // nested pointer types, we check to see if we are the parent - // in such a relationship. If so, skip rendering anything for - // this pointer type and rely on the child to render our asterisk - // as well when it renders the ** token. - if (tree.tokenTag(main_token) == .asterisk_asterisk and - main_token == tree.nodeMainToken(ptr_type.ast.child_type)) - { - return renderExpression(r, ptr_type.ast.child_type, space); - } try renderToken(r, main_token, .none); // asterisk }, .many => { @@ -3223,7 +3212,6 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool { .add, .add_wrap, .array_cat, - .array_mult, .assign, .assign_bit_and, .assign_bit_or, diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig @@ -507,7 +507,6 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins .less_than, .less_or_equal, .array_cat, - .array_mult, .bool_and, .bool_or, .@"asm", @@ -777,19 +776,6 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE .less_or_equal => return simpleBinOp(gz, scope, ri, node, .cmp_lte), .array_cat => return simpleBinOp(gz, scope, ri, node, .array_cat), - .array_mult => { - // This syntax form does not currently use the result type in the language specification. - // However, the result type can be used to emit more optimal code for large multiplications by - // having Sema perform a coercion before the multiplication operation. - const lhs_node, const rhs_node = tree.nodeData(node).node_and_node; - const result = try gz.addPlNode(.array_mul, node, Zir.Inst.ArrayMul{ - .res_ty = if (try ri.rl.resultType(gz, node)) |t| t else .none, - .lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node), - .rhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, rhs_node, .array_mul_factor), - }); - return rvalue(gz, ri, result, node); - }, - .error_union, .merge_error_sets => |tag| { const inst_tag: Zir.Inst.Tag = switch (tag) { .error_union => .error_union_type, @@ -2713,7 +2699,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .alloc_inferred_comptime_mut, .make_ptr_const, .array_cat, - .array_mul, .array_type, .array_type_sentinel, .elem_type, @@ -10307,7 +10292,6 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev .add_wrap, .add_sat, .array_cat, - .array_mult, .assign, .assign_destructure, .assign_bit_and, diff --git a/lib/std/zig/AstRlAnnotate.zig b/lib/std/zig/AstRlAnnotate.zig @@ -269,12 +269,6 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI return false; }, - .array_mult => { - const lhs, const rhs = tree.nodeData(node).node_and_node; - _ = try astrl.expr(lhs, block, ResultInfo.none); - _ = try astrl.expr(rhs, block, ResultInfo.type_only); - return false; - }, .error_union, .merge_error_sets => { const lhs, const rhs = tree.nodeData(node).node_and_node; _ = try astrl.expr(lhs, block, ResultInfo.none); diff --git a/lib/std/zig/AstSmith.zig b/lib/std/zig/AstSmith.zig @@ -18,7 +18,6 @@ token_tag_buf: [2048]Token.Tag, token_start_buf: [2048]std.zig.Ast.ByteOffset, tokens_len: usize, -/// For `.asterisk`, this also includes `.asterisk2` not_token: ?Token.Tag, not_token_comptime: bool, /// ExprSuffix @@ -196,7 +195,6 @@ fn preservePegEndOfWord(a: *AstSmith) SourceError!void { /// Assumes the token has not been written yet fn addTokenTag(a: *AstSmith, tag: Token.Tag) SourceError!void { assert(tag != a.not_token); - if (a.not_token == .asterisk) assert(tag != .asterisk_asterisk); a.not_token = null; if (a.not_token_comptime) assert(tag != .keyword_comptime); @@ -240,9 +238,7 @@ fn pegToken(a: *AstSmith, tag: Token.Tag) SourceError!void { switch (lexeme[0]) { '_', 'a'...'z', 'A'...'Z', '0'...'9' => try a.preservePegEndOfWord(), - '*' => if (a.tokens_len > 0 and a.source_buf[a.source_len - 1] == '*' and - a.token_tag_buf[a.tokens_len - 1] != .asterisk_asterisk) - { + '*' => if (a.tokens_len > 0 and a.source_buf[a.source_len - 1] == '*') { try a.addSourceByte(' '); }, '.' => if (a.tokens_len > 0 and switch (a.source_buf[a.source_len - 1]) { @@ -1723,13 +1719,11 @@ fn pegAdditionOp(a: *AstSmith) SourceError!void { /// / ASTERISK /// / SLASH /// / PERCENT -/// / ASTERISK2 /// / ASTERISKPERCENT /// / ASTERISKPIPE fn pegMultiplyOp(a: *AstSmith) SourceError!void { const tags = [_]Token.Tag{ .asterisk, - .asterisk_asterisk, .pipe_pipe, .slash, .percent, @@ -1865,9 +1859,9 @@ fn pegSliceTypeStart(a: *AstSmith) SourceError!void { try a.pegToken(.r_bracket); } -/// SinglePtrTypeStart <- ASTERISK / ASTERISK2 +/// SinglePtrTypeStart <- ASTERISK fn pegSinglePtrTypeStart(a: *AstSmith) SourceError!void { - try a.pegToken(if (!a.smith.value(bool)) .asterisk else .asterisk_asterisk); + try a.pegToken(.asterisk); } /// ManyPtrTypeStart <- LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET @@ -1889,7 +1883,7 @@ fn pegManyPtrTypeStart(a: *AstSmith) SourceError!void { try a.pegToken(.r_bracket); } -/// ArrayTypeStart <- LBRACKET !(ASTERISK / ASTERISK2) Expr (COLON Expr)? RBRACKET +/// ArrayTypeStart <- LBRACKET !ASTERISK Expr (COLON Expr)? RBRACKET fn pegArrayTypeStart(a: *AstSmith) SourceError!void { try a.pegToken(.l_bracket); a.not_token = .asterisk; diff --git a/lib/std/zig/Parse.zig b/lib/std/zig/Parse.zig @@ -1609,7 +1609,6 @@ const operTable = std.enums.directEnumArrayDefault(Token.Tag, OperInfo, .{ .prec .asterisk = .{ .prec = 70, .tag = .mul }, .slash = .{ .prec = 70, .tag = .div }, .percent = .{ .prec = 70, .tag = .mod }, - .asterisk_asterisk = .{ .prec = 70, .tag = .array_mult }, .asterisk_percent = .{ .prec = 70, .tag = .mul_wrap }, .asterisk_pipe = .{ .prec = 70, .tag = .mul_sat }, }); @@ -1709,11 +1708,11 @@ fn expectPrefixExpr(p: *Parse) Error!Node.Index { /// /// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET /// -/// SinglePtrTypeStart <- ASTERISK / ASTERISK2 +/// SinglePtrTypeStart <- ASTERISK /// /// ManyPtrTypeStart <- LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET /// -/// ArrayTypeStart <- LBRACKET Expr !(ASTERISK / ASTERISK2) (COLON Expr)? RBRACKET +/// ArrayTypeStart <- LBRACKET Expr !ASTERISK (COLON Expr)? RBRACKET /// /// BitAlign <- KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN fn parseTypeExpr(p: *Parse) Error!?Node.Index { @@ -1777,59 +1776,6 @@ fn parseTypeExpr(p: *Parse) Error!?Node.Index { }); } }, - .asterisk_asterisk => { - const asterisk = p.nextToken(); - const mods = try p.parsePtrModifiers(); - const elem_type = try p.expectTypeExpr(); - const inner: Node.Index = inner: { - if (mods.bit_range_start != .none) { - break :inner try p.addNode(.{ - .tag = .ptr_type_bit_range, - .main_token = asterisk, - .data = .{ .extra_and_node = .{ - try p.addExtra(Node.PtrTypeBitRange{ - .sentinel = .none, - .align_node = mods.align_node.unwrap().?, - .addrspace_node = mods.addrspace_node, - .bit_range_start = mods.bit_range_start.unwrap().?, - .bit_range_end = mods.bit_range_end.unwrap().?, - }), - elem_type, - } }, - }); - } else if (mods.addrspace_node != .none) { - break :inner try p.addNode(.{ - .tag = .ptr_type, - .main_token = asterisk, - .data = .{ .extra_and_node = .{ - try p.addExtra(Node.PtrType{ - .sentinel = .none, - .align_node = mods.align_node, - .addrspace_node = mods.addrspace_node, - }), - elem_type, - } }, - }); - } else { - break :inner try p.addNode(.{ - .tag = .ptr_type_aligned, - .main_token = asterisk, - .data = .{ .opt_node_and_node = .{ - mods.align_node, - elem_type, - } }, - }); - } - }; - return try p.addNode(.{ - .tag = .ptr_type_aligned, - .main_token = asterisk, - .data = .{ .opt_node_and_node = .{ - .none, - inner, - } }, - }); - }, .l_bracket => switch (p.tokenTag(p.tok_i + 1)) { .asterisk => { const l_bracket = p.nextToken(); @@ -3257,14 +3203,6 @@ fn parseSuffixOp(p: *Parse, lhs: Node.Index) !?Node.Index { .main_token = p.nextToken(), .data = .{ .node = lhs }, }), - .invalid_periodasterisks => { - try p.warn(.asterisk_after_ptr_deref); - return try p.addNode(.{ - .tag = .deref, - .main_token = p.nextToken(), - .data = .{ .node = lhs }, - }); - }, .period => switch (p.tokenTag(p.tok_i + 1)) { .identifier => return try p.addNode(.{ .tag = .field_access, diff --git a/lib/std/zig/TokenSmith.zig b/lib/std/zig/TokenSmith.zig @@ -57,12 +57,6 @@ pub fn gen(smith: *Smith) TokenSmith { @memcpy(t.source_buf[t.source_len..][0..lexeme.len], lexeme); t.source_len += @intCast(lexeme.len); - if (tag == .invalid_periodasterisks) { - t.tag_buf[t.tags_len] = .asterisk; - t.start_buf[t.tags_len] = t.source_len - 1; - t.tags_len += 1; - } - t.source_buf[t.source_len] = '\n'; t.source_len += 1; } else sw: switch (tag) { diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig @@ -250,9 +250,6 @@ pub const Inst = struct { /// Array concatenation. `a ++ b` /// Uses the `pl_node` union field. Payload is `Bin`. array_cat, - /// Array multiplication `a ** b` - /// Uses the `pl_node` union field. Payload is `ArrayMul`. - array_mul, /// `[N]T` syntax. No source location provided. /// Uses the `pl_node` union field. Payload is `Bin`. lhs is length, rhs is element type. array_type, @@ -1102,7 +1099,6 @@ pub const Inst = struct { .alloc_inferred_comptime_mut, .make_ptr_const, .array_cat, - .array_mul, .array_type, .array_type_sentinel, .reify_int, @@ -1396,7 +1392,6 @@ pub const Inst = struct { .resolve_inferred_alloc, .make_ptr_const, .array_cat, - .array_mul, .array_type, .array_type_sentinel, .reify_int, @@ -1630,7 +1625,6 @@ pub const Inst = struct { .param_anytype = .str_tok, .param_anytype_comptime = .str_tok, .array_cat = .pl_node, - .array_mul = .pl_node, .array_type = .pl_node, .array_type_sentinel = .pl_node, .reify_int = .pl_node, @@ -3961,15 +3955,6 @@ pub const Inst = struct { expect_len: u32, }; - pub const ArrayMul = struct { - /// The result type of the array multiplication operation, or `.none` if none was available. - res_ty: Ref, - /// The LHS of the array multiplication. - lhs: Ref, - /// The RHS of the array multiplication. - rhs: Ref, - }; - pub const RestoreErrRetIndex = struct { src_node: Ast.Node.Offset, /// If `.none`, restore the trace to its state upon function entry. @@ -4121,7 +4106,6 @@ fn findTrackableInner( .param_anytype, .param_anytype_comptime, .array_cat, - .array_mul, .array_type, .array_type_sentinel, .reify_int, diff --git a/lib/std/zig/ZonGen.zig b/lib/std/zig/ZonGen.zig @@ -166,7 +166,6 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator .less_than, .less_or_equal, .array_cat, - .array_mult, .bool_and, .bool_or, .bool_not, diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig @@ -7628,7 +7628,7 @@ pub const Constant = enum(u32) { const expected_limbs = @divExact(512, @bitSizeOf(std.math.big.Limb)); string: [ (std.math.big.int.Const{ - .limbs = &@splat(maxInt(std.math.big.Limb)), + .limbs = &@as([expected_limbs]std.math.big.Limb, @splat(maxInt(std.math.big.Limb))), .positive = false, }).sizeInBaseUpperBound(10) ]u8, @@ -10595,7 +10595,7 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void const expected_limbs = @divExact(512, @bitSizeOf(std.math.big.Limb)); string: [ (std.math.big.int.Const{ - .limbs = &@splat(maxInt(std.math.big.Limb)), + .limbs = &@as([expected_limbs]std.math.big.Limb, @splat(maxInt(std.math.big.Limb))), .positive = false, }).sizeInBaseUpperBound(10) ]u8, diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig @@ -2764,13 +2764,6 @@ test "zig fmt: comments before var decl in struct" { ); } -test "zig fmt: array literal with 1 item on 1 line" { - try testCanonical( - \\var s = []const u64{0} ** 25; - \\ - ); -} - test "zig fmt: comments before global variables" { try testCanonical( \\/// Foo copies keys and values before they go into the map, and @@ -2911,7 +2904,7 @@ test "zig fmt: function attributes" { ); } -test "zig fmt: nested pointers with ** tokens" { +test "zig fmt: deeply nested pointers" { try testCanonical( \\const x: *u32 = undefined; \\const x: **u32 = undefined; @@ -3001,7 +2994,6 @@ test "zig fmt: infix operators" { \\ _ = i.i; \\ _ = i || i; \\ _ = i!i; - \\ _ = i ** i; \\ _ = i ++ i; \\ _ = i orelse i; \\ _ = i % i; @@ -4565,7 +4557,7 @@ test "zig fmt: integer literals with underscore separators" { test "zig fmt: hex literals with underscore separators" { try testTransform( \\pub fn orMask(a: [ 1_000 ]u64, b: [ 1_000] u64) [1_000]u64 { - \\ var c: [1_000]u64 = [1]u64{ 0xFFFF_FFFF_FFFF_FFFF}**1_000; + \\ var c: [1_000]u64 = @splat(0xFFFF_FFFF_FFFF_FFFF); \\ for (c [ 1_0 .. ], 0..) |_, i| { \\ c[i] = (a[i] | b[i]) & 0xCCAA_CCAA_CCAA_CCAA; \\ } @@ -4575,7 +4567,7 @@ test "zig fmt: hex literals with underscore separators" { \\ , \\pub fn orMask(a: [1_000]u64, b: [1_000]u64) [1_000]u64 { - \\ var c: [1_000]u64 = [1]u64{0xFFFF_FFFF_FFFF_FFFF} ** 1_000; + \\ var c: [1_000]u64 = @splat(0xFFFF_FFFF_FFFF_FFFF); \\ for (c[1_0..], 0..) |_, i| { \\ c[i] = (a[i] | b[i]) & 0xCCAA_CCAA_CCAA_CCAA; \\ } @@ -7083,25 +7075,6 @@ test "recovery: invalid global error set access" { }); } -test "recovery: invalid asterisk after pointer dereference" { - try testError( - \\test "" { - \\ var sequence = "repeat".*** 10; - \\} - , &[_]Error{ - .asterisk_after_ptr_deref, - .mismatched_binary_op_whitespace, - }); - try testError( - \\test "" { - \\ var sequence = "repeat".** 10&a; - \\} - , &[_]Error{ - .asterisk_after_ptr_deref, - .mismatched_binary_op_whitespace, - }); -} - test "recovery: missing semicolon after if, for, while stmt" { try testError( \\test "" { diff --git a/lib/std/zig/tokenizer.zig b/lib/std/zig/tokenizer.zig @@ -64,7 +64,6 @@ pub const Token = struct { pub const Tag = enum { invalid, - invalid_periodasterisks, identifier, string_literal, multiline_string_literal_line, @@ -109,7 +108,6 @@ pub const Token = struct { minus_pipe_equal, asterisk, asterisk_equal, - asterisk_asterisk, asterisk_percent, asterisk_percent_equal, asterisk_pipe, @@ -197,7 +195,6 @@ pub const Token = struct { .container_doc_comment, => null, - .invalid_periodasterisks => ".**", .bang => "!", .pipe => "|", .pipe_pipe => "||", @@ -236,7 +233,6 @@ pub const Token = struct { .minus_pipe_equal => "-|=", .asterisk => "*", .asterisk_equal => "*=", - .asterisk_asterisk => "**", .asterisk_percent => "*%", .asterisk_percent_equal => "*%=", .asterisk_pipe => "*|", @@ -386,7 +382,6 @@ pub const Tokenizer = struct { angle_bracket_angle_bracket_right, period, period_2, - period_asterisk, saw_at_sign, invalid, }; @@ -569,10 +564,6 @@ pub const Tokenizer = struct { result.tag = .asterisk_equal; self.index += 1; }, - '*' => { - result.tag = .asterisk_asterisk; - self.index += 1; - }, '%' => continue :state .asterisk_percent, '|' => continue :state .asterisk_pipe, else => result.tag = .asterisk, @@ -915,7 +906,10 @@ pub const Tokenizer = struct { self.index += 1; switch (self.buffer[self.index]) { '.' => continue :state .period_2, - '*' => continue :state .period_asterisk, + '*' => { + result.tag = .period_asterisk; + self.index += 1; + }, else => result.tag = .period, } }, @@ -931,14 +925,6 @@ pub const Tokenizer = struct { } }, - .period_asterisk => { - self.index += 1; - switch (self.buffer[self.index]) { - '*' => result.tag = .invalid_periodasterisks, - else => result.tag = .period_asterisk, - } - }, - .slash => { self.index += 1; switch (self.buffer[self.index]) { @@ -1343,31 +1329,6 @@ test "correctly parse pointer assignment" { }); } -test "correctly parse pointer dereference followed by asterisk" { - try testTokenize("\"b\".* ** 10", &.{ - .string_literal, - .period_asterisk, - .asterisk_asterisk, - .number_literal, - }); - - try testTokenize("(\"b\".*)** 10", &.{ - .l_paren, - .string_literal, - .period_asterisk, - .r_paren, - .asterisk_asterisk, - .number_literal, - }); - - try testTokenize("\"b\".*** 10", &.{ - .string_literal, - .invalid_periodasterisks, - .asterisk_asterisk, - .number_literal, - }); -} - test "range literals" { try testTokenize("0...9", &.{ .number_literal, .ellipsis3, .number_literal }); try testTokenize("'0'...'9'", &.{ .char_literal, .ellipsis3, .char_literal }); diff --git a/src/Sema.zig b/src/Sema.zig @@ -1171,7 +1171,6 @@ fn analyzeBodyInner( .make_ptr_const => try sema.zirMakePtrConst(block, inst), .anyframe_type => try sema.zirAnyframeType(block, inst), .array_cat => try sema.zirArrayCat(block, inst), - .array_mul => try sema.zirArrayMul(block, inst), .array_type => try sema.zirArrayType(block, inst), .array_type_sentinel => try sema.zirArrayTypeSentinel(block, inst), .reify_int => try sema.zirReifyInt(block, inst), @@ -13861,174 +13860,6 @@ fn analyzeTupleMul( return block.addAggregateInit(tuple_ty, element_refs); } -fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const tracy = trace(@src()); - defer tracy.end(); - - const pt = sema.pt; - const zcu = pt.zcu; - const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; - const extra = sema.code.extraData(Zir.Inst.ArrayMul, inst_data.payload_index).data; - const uncoerced_lhs = sema.resolveInst(extra.lhs); - const uncoerced_lhs_ty = sema.typeOf(uncoerced_lhs); - const src: LazySrcLoc = block.nodeOffset(inst_data.src_node); - const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); - const operator_src = block.src(.{ .node_offset_main_token = inst_data.src_node }); - const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); - - const lhs, const lhs_ty = coerced_lhs: { - // If we have a result type, we might be able to do this more efficiently - // by coercing the LHS first. Specifically, if we want an array or vector - // and have a tuple, coerce the tuple immediately. - no_coerce: { - if (extra.res_ty == .none) break :no_coerce; - const res_ty = try sema.resolveTypeOrPoison(block, src, extra.res_ty) orelse break :no_coerce; - if (!uncoerced_lhs_ty.isTuple(zcu)) break :no_coerce; - const lhs_len = uncoerced_lhs_ty.structFieldCount(zcu); - const lhs_dest_ty = switch (res_ty.zigTypeTag(zcu)) { - else => break :no_coerce, - .array => try pt.arrayType(.{ - .child = res_ty.childType(zcu).toIntern(), - .len = lhs_len, - .sentinel = if (res_ty.sentinel(zcu)) |s| s.toIntern() else .none, - }), - .vector => try pt.vectorType(.{ - .child = res_ty.childType(zcu).toIntern(), - .len = lhs_len, - }), - }; - // Attempt to coerce to this type, but don't emit an error if it fails. Instead, - // just exit out of this path and let the usual error happen later, so that error - // messages are consistent. - const coerced = sema.coerceExtra(block, lhs_dest_ty, uncoerced_lhs, lhs_src, .{ .report_err = false }) catch |err| switch (err) { - error.NotCoercible => break :no_coerce, - else => |e| return e, - }; - break :coerced_lhs .{ coerced, lhs_dest_ty }; - } - break :coerced_lhs .{ uncoerced_lhs, uncoerced_lhs_ty }; - }; - - if (lhs_ty.isTuple(zcu)) { - // In `**` rhs must be comptime-known, but lhs can be runtime-known - const factor = try sema.resolveInt(block, rhs_src, extra.rhs, .usize, .{ .simple = .array_mul_factor }); - const factor_casted = try sema.usizeCast(block, rhs_src, factor); - return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor_casted); - } - - // Analyze the lhs first, to catch the case that someone tried to do exponentiation - const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, lhs_ty) orelse { - const msg = msg: { - const msg = try sema.errMsg(lhs_src, "expected indexable; found '{f}'", .{lhs_ty.fmt(pt)}); - errdefer msg.destroy(sema.gpa); - switch (lhs_ty.zigTypeTag(zcu)) { - .int, .float, .comptime_float, .comptime_int, .vector => { - try sema.errNote(operator_src, msg, "this operator multiplies arrays; use std.math.pow for exponentiation", .{}); - }, - else => {}, - } - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - }; - - // In `**` rhs must be comptime-known, but lhs can be runtime-known - const factor = try sema.resolveInt(block, rhs_src, extra.rhs, .usize, .{ .simple = .array_mul_factor }); - - const result_len_u64 = std.math.mul(u64, lhs_info.len, factor) catch - return sema.fail(block, rhs_src, "operation results in overflow", .{}); - const result_len = try sema.usizeCast(block, src, result_len_u64); - - const result_ty = try pt.arrayType(.{ - .len = result_len, - .sentinel = if (lhs_info.sentinel) |s| s.toIntern() else .none, - .child = lhs_info.elem_type.toIntern(), - }); - - const ptr_addrspace = if (lhs_ty.zigTypeTag(zcu) == .pointer) lhs_ty.ptrAddressSpace(zcu) else null; - const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); - - if (sema.resolveValue(lhs)) |lhs_val| ct: { - const lhs_sub_val = if (lhs_ty.isSinglePointer(zcu)) - try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty) orelse break :ct - else if (lhs_ty.isSlice(zcu)) - try sema.maybeDerefSliceAsArray(block, lhs_src, lhs_val) orelse break :ct - else - lhs_val; - - const val = v: { - // Optimization for the common pattern of a single element repeated N times, such - // as zero-filling a byte array. - if (lhs_len == 1 and lhs_info.sentinel == null) { - const elem_val = try lhs_sub_val.elemValue(pt, 0); - break :v try pt.aggregateSplatValue(result_ty, elem_val); - } - - const element_vals = try sema.arena.alloc(InternPool.Index, result_len); - var elem_i: usize = 0; - while (elem_i < result_len) { - var lhs_i: usize = 0; - while (lhs_i < lhs_len) : (lhs_i += 1) { - const elem_val = try lhs_sub_val.elemValue(pt, lhs_i); - element_vals[elem_i] = elem_val.toIntern(); - elem_i += 1; - } - } - break :v try pt.aggregateValue(result_ty, element_vals); - }; - return sema.addConstantMaybeRef(val, ptr_addrspace != null); - } - - try sema.requireRuntimeBlock(block, src, lhs_src); - - // Grab all the LHS values ahead of time, rather than repeatedly emitting instructions - // to get the same elem values. - const lhs_vals = try sema.arena.alloc(Air.Inst.Ref, lhs_len); - for (lhs_vals, 0..) |*lhs_val, idx| { - const idx_ref = try pt.intRef(.usize, idx); - lhs_val.* = try sema.elemVal(block, lhs_src, lhs, idx_ref, src, false); - } - - if (ptr_addrspace) |ptr_as| { - const alloc_ty = try pt.ptrType(.{ - .child = result_ty.toIntern(), - .flags = .{ - .address_space = ptr_as, - .is_const = true, - }, - }); - const alloc = try block.addTy(.alloc, alloc_ty); - const elem_ptr_ty = try pt.ptrType(.{ - .child = lhs_info.elem_type.toIntern(), - .flags = .{ .address_space = ptr_as }, - }); - - var elem_i: usize = 0; - while (elem_i < result_len) { - for (lhs_vals) |lhs_val| { - const elem_index = try pt.intRef(.usize, elem_i); - const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); - try sema.storePtr2(block, src, elem_ptr, src, lhs_val, lhs_src, .store); - elem_i += 1; - } - } - if (lhs_info.sentinel) |sent_val| { - const elem_index = try pt.intRef(.usize, result_len); - const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); - const init = Air.internedToRef(sent_val.toIntern()); - try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); - } - - return alloc; - } - - const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len); - for (0..try sema.usizeCast(block, rhs_src, factor)) |i| { - @memcpy(element_refs[i * lhs_len ..][0..lhs_len], lhs_vals); - } - return block.addAggregateInit(result_ty, element_refs); -} - fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; diff --git a/src/print_zir.zig b/src/print_zir.zig @@ -416,8 +416,6 @@ const Writer = struct { .for_len => try self.writePlNodeMultiOp(stream, inst), - .array_mul => try self.writeArrayMul(stream, inst), - .elem_val_imm => try self.writeElemValImm(stream, inst), .@"export" => try self.writePlNodeExport(stream, inst), @@ -1046,18 +1044,6 @@ const Writer = struct { try self.writeSrcNode(stream, inst_data.src_node); } - fn writeArrayMul(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; - const extra = self.code.extraData(Zir.Inst.ArrayMul, inst_data.payload_index).data; - try self.writeInstRef(stream, extra.res_ty); - try stream.writeAll(", "); - try self.writeInstRef(stream, extra.lhs); - try stream.writeAll(", "); - try self.writeInstRef(stream, extra.rhs); - try stream.writeAll(") "); - try self.writeSrcNode(stream, inst_data.src_node); - } - fn writeElemValImm(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].elem_val_imm; try self.writeInstRef(stream, inst_data.operand); diff --git a/tools/docgen.zig b/tools/docgen.zig @@ -871,7 +871,6 @@ fn tokenizeAndPrintRaw( .minus_pipe_equal, .asterisk, .asterisk_equal, - .asterisk_asterisk, .asterisk_percent, .asterisk_percent_equal, .asterisk_pipe, @@ -897,7 +896,7 @@ fn tokenizeAndPrintRaw( .tilde, => try writeEscaped(out, src[token.loc.start..token.loc.end]), - .invalid, .invalid_periodasterisks => return parseError( + .invalid => return parseError( docgen_tokenizer, source_token, "syntax error", diff --git a/tools/doctest.zig b/tools/doctest.zig @@ -800,7 +800,6 @@ fn tokenizeAndPrint(arena: Allocator, out: *Writer, raw_src: []const u8) !void { .minus_pipe_equal, .asterisk, .asterisk_equal, - .asterisk_asterisk, .asterisk_percent, .asterisk_percent_equal, .asterisk_pipe, @@ -826,7 +825,7 @@ fn tokenizeAndPrint(arena: Allocator, out: *Writer, raw_src: []const u8) !void { .tilde, => try writeEscaped(out, src[token.loc.start..token.loc.end]), - .invalid, .invalid_periodasterisks => fatal("syntax error", .{}), + .invalid => fatal("syntax error", .{}), } index = token.loc.end; }