Merge pull request #9882 from mattbork/astgen-cursor

astgen.zig: keep source cursor increasing monotonically as much as possible
This commit is contained in:
Andrew Kelley 2021-10-04 14:16:33 -04:00 committed by GitHub
commit 2454459ef5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 140 additions and 136 deletions

View File

@ -1683,7 +1683,7 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
const defer_scope = scope.cast(Scope.Defer).?;
scope = defer_scope.parent;
const expr_node = node_datas[defer_scope.defer_node].rhs;
_ = try unusedResultExpr(parent_gz, defer_scope.parent, expr_node);
try unusedResultDeferExpr(parent_gz, defer_scope, defer_scope.parent, expr_node);
},
.defer_error => scope = scope.cast(Scope.Defer).?.parent,
.top => unreachable,
@ -1736,7 +1736,7 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
const defer_scope = scope.cast(Scope.Defer).?;
scope = defer_scope.parent;
const expr_node = node_datas[defer_scope.defer_node].rhs;
_ = try unusedResultExpr(parent_gz, defer_scope.parent, expr_node);
try unusedResultDeferExpr(parent_gz, defer_scope, defer_scope.parent, expr_node);
},
.defer_error => scope = scope.cast(Scope.Defer).?.parent,
.namespace => break,
@ -1922,8 +1922,8 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
.simple_var_decl => scope = try varDecl(gz, scope, statement, &block_arena.allocator, tree.simpleVarDecl(statement)),
.aligned_var_decl => scope = try varDecl(gz, scope, statement, &block_arena.allocator, tree.alignedVarDecl(statement)),
.@"defer" => scope = try makeDeferScope(scope, statement, &block_arena.allocator, .defer_normal),
.@"errdefer" => scope = try makeDeferScope(scope, statement, &block_arena.allocator, .defer_error),
.@"defer" => scope = try makeDeferScope(gz.astgen, scope, statement, &block_arena.allocator, .defer_normal),
.@"errdefer" => scope = try makeDeferScope(gz.astgen, scope, statement, &block_arena.allocator, .defer_error),
.assign => try assign(gz, scope, statement),
@ -1951,6 +1951,22 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
try checkUsed(gz, parent_scope, scope);
}
fn unusedResultDeferExpr(gz: *GenZir, defer_scope: *Scope.Defer, expr_scope: *Scope, expr_node: Ast.Node.Index) InnerError!void {
const astgen = gz.astgen;
const prev_offset = astgen.source_offset;
const prev_line = astgen.source_line;
const prev_column = astgen.source_column;
defer {
astgen.source_offset = prev_offset;
astgen.source_line = prev_line;
astgen.source_column = prev_column;
}
astgen.source_offset = defer_scope.source_offset;
astgen.source_line = defer_scope.source_line;
astgen.source_column = defer_scope.source_column;
_ = try unusedResultExpr(gz, expr_scope, expr_node);
}
/// Returns AST source node of the thing that is noreturn if the statement is definitely `noreturn`.
/// Otherwise returns 0.
fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
@ -2333,7 +2349,7 @@ fn genDefers(
const prev_in_defer = gz.in_defer;
gz.in_defer = true;
defer gz.in_defer = prev_in_defer;
_ = try unusedResultExpr(gz, defer_scope.parent, expr_node);
try unusedResultDeferExpr(gz, defer_scope, defer_scope.parent, expr_node);
},
.defer_error => {
const defer_scope = scope.cast(Scope.Defer).?;
@ -2344,7 +2360,7 @@ fn genDefers(
const prev_in_defer = gz.in_defer;
gz.in_defer = true;
defer gz.in_defer = prev_in_defer;
_ = try unusedResultExpr(gz, defer_scope.parent, expr_node);
try unusedResultDeferExpr(gz, defer_scope, defer_scope.parent, expr_node);
},
.both => |err_code| {
const expr_node = node_datas[defer_scope.defer_node].rhs;
@ -2365,7 +2381,7 @@ fn genDefers(
};
break :blk &local_val_scope.base;
};
_ = try unusedResultExpr(gz, sub_scope, expr_node);
try unusedResultDeferExpr(gz, defer_scope, sub_scope, expr_node);
},
.normal_only => continue,
}
@ -2409,16 +2425,27 @@ fn checkUsed(
}
fn makeDeferScope(
astgen: *AstGen,
scope: *Scope,
node: Ast.Node.Index,
block_arena: *Allocator,
scope_tag: Scope.Tag,
) InnerError!*Scope {
const tree = astgen.tree;
const node_datas = tree.nodes.items(.data);
const expr_node = node_datas[node].rhs;
const token_starts = tree.tokens.items(.start);
const node_start = token_starts[tree.firstToken(expr_node)];
const defer_scope = try block_arena.create(Scope.Defer);
astgen.advanceSourceCursor(tree.source, node_start);
defer_scope.* = .{
.base = .{ .tag = scope_tag },
.parent = scope,
.defer_node = node,
.source_offset = astgen.source_offset,
.source_line = astgen.source_line,
.source_column = astgen.source_column,
};
return &defer_scope.base;
}
@ -3184,6 +3211,12 @@ fn fnDecl(
astgen.fn_block = &fn_gz;
defer astgen.fn_block = prev_fn_block;
const token_starts = tree.tokens.items(.start);
const lbrace_start = token_starts[tree.firstToken(body_node)];
astgen.advanceSourceCursor(tree.source, lbrace_start);
const lbrace_line = @intCast(u32, astgen.source_line);
const lbrace_column = @intCast(u32, astgen.source_column);
_ = try expr(&fn_gz, params_scope, .none, body_node);
try checkUsed(gz, &fn_gz.base, params_scope);
@ -3202,6 +3235,8 @@ fn fnDecl(
break :func try decl_gz.addFunc(.{
.src_node = decl_node,
.lbrace_line = lbrace_line,
.lbrace_column = lbrace_column,
.param_block = block_inst,
.ret_ty = ret_gz.instructions.items,
.ret_br = ret_br,
@ -3544,6 +3579,12 @@ fn testDecl(
astgen.fn_block = &fn_block;
defer astgen.fn_block = prev_fn_block;
const token_starts = tree.tokens.items(.start);
const lbrace_start = token_starts[tree.firstToken(body_node)];
astgen.advanceSourceCursor(tree.source, lbrace_start);
const lbrace_line = @intCast(u32, astgen.source_line);
const lbrace_column = @intCast(u32, astgen.source_column);
const block_result = try expr(&fn_block, &fn_block.base, .none, body_node);
if (fn_block.instructions.items.len == 0 or !fn_block.refIsNoReturn(block_result)) {
// Since we are adding the return instruction here, we must handle the coercion.
@ -3553,6 +3594,8 @@ fn testDecl(
const func_inst = try decl_block.addFunc(.{
.src_node = node,
.lbrace_line = lbrace_line,
.lbrace_column = lbrace_column,
.param_block = block_inst,
.ret_ty = &.{},
.ret_br = 0,
@ -5935,10 +5978,11 @@ fn switchExpr(
const operand_ty_inst = try parent_gz.addUnNode(typeof_tag, operand, operand_node);
const item_rl: ResultLoc = .{ .ty = operand_ty_inst };
// Contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti.
// This is the header as well as the optional else prong body, as well as all the
// scalar cases.
// At the end we will memcpy this into place.
// These contain the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti.
// This is the optional else prong body.
var special_case_payload = ArrayListUnmanaged(u32){};
defer special_case_payload.deinit(gpa);
// This is all the scalar cases.
var scalar_cases_payload = ArrayListUnmanaged(u32){};
defer scalar_cases_payload.deinit(gpa);
// Same deal, but this is only the `extra` data for the multi cases.
@ -5956,86 +6000,10 @@ fn switchExpr(
var case_scope = parent_gz.makeSubBlock(&block_scope.base);
defer case_scope.instructions.deinit(gpa);
// Do the else/`_` first because it goes first in the payload.
var capture_val_scope: Scope.LocalVal = undefined;
if (special_node != 0) {
const case = switch (node_tags[special_node]) {
.switch_case_one => tree.switchCaseOne(special_node),
.switch_case => tree.switchCase(special_node),
else => unreachable,
};
const sub_scope = blk: {
const payload_token = case.payload_token orelse break :blk &case_scope.base;
const ident = if (token_tags[payload_token] == .asterisk)
payload_token + 1
else
payload_token;
const is_ptr = ident != payload_token;
if (mem.eql(u8, tree.tokenSlice(ident), "_")) {
if (is_ptr) {
return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
}
break :blk &case_scope.base;
}
const capture_tag: Zir.Inst.Tag = if (is_ptr)
.switch_capture_else_ref
else
.switch_capture_else;
const capture = try case_scope.add(.{
.tag = capture_tag,
.data = .{ .switch_capture = .{
.switch_inst = switch_block,
.prong_index = undefined,
} },
});
const capture_name = try astgen.identAsString(payload_token);
capture_val_scope = .{
.parent = &case_scope.base,
.gen_zir = &case_scope,
.name = capture_name,
.inst = capture,
.token_src = payload_token,
.id_cat = .@"capture",
};
break :blk &capture_val_scope.base;
};
const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
try checkUsed(parent_gz, &case_scope.base, sub_scope);
if (!parent_gz.refIsNoReturn(case_result)) {
block_scope.break_count += 1;
_ = try case_scope.addBreak(.@"break", switch_block, case_result);
}
// Documentation for this: `Zir.Inst.SwitchBlock` and `Zir.Inst.SwitchBlockMulti`.
try scalar_cases_payload.ensureUnusedCapacity(gpa, case_scope.instructions.items.len +
3 + // operand, scalar_cases_len, else body len
@boolToInt(multi_cases_len != 0));
scalar_cases_payload.appendAssumeCapacity(@enumToInt(operand));
scalar_cases_payload.appendAssumeCapacity(scalar_cases_len);
if (multi_cases_len != 0) {
scalar_cases_payload.appendAssumeCapacity(multi_cases_len);
}
scalar_cases_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len));
scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items);
} else {
// Documentation for this: `Zir.Inst.SwitchBlock` and `Zir.Inst.SwitchBlockMulti`.
try scalar_cases_payload.ensureUnusedCapacity(
gpa,
@as(usize, 2) + // operand, scalar_cases_len
@boolToInt(multi_cases_len != 0),
);
scalar_cases_payload.appendAssumeCapacity(@enumToInt(operand));
scalar_cases_payload.appendAssumeCapacity(scalar_cases_len);
if (multi_cases_len != 0) {
scalar_cases_payload.appendAssumeCapacity(multi_cases_len);
}
}
// In this pass we generate all the item and prong expressions except the special case.
// In this pass we generate all the item and prong expressions.
var multi_case_index: u32 = 0;
var scalar_case_index: u32 = 0;
for (case_nodes) |case_node| {
if (case_node == special_node)
continue;
const case = switch (node_tags[case_node]) {
.switch_case_one => tree.switchCaseOne(case_node),
.switch_case => tree.switchCase(case_node),
@ -6045,9 +6013,10 @@ fn switchExpr(
// Reset the scope.
case_scope.instructions.shrinkRetainingCapacity(0);
const is_multi_case = case.ast.values.len != 1 or
node_tags[case.ast.values[0]] == .switch_range;
const is_multi_case = case.ast.values.len > 1 or
(case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .switch_range);
var capture_val_scope: Scope.LocalVal = undefined;
const sub_scope = blk: {
const payload_token = case.payload_token orelse break :blk &case_scope.base;
const ident = if (token_tags[payload_token] == .asterisk)
@ -6061,28 +6030,42 @@ fn switchExpr(
}
break :blk &case_scope.base;
}
const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
const is_ptr_bits: u2 = @boolToInt(is_ptr);
const capture_tag: Zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
0b00 => .switch_capture,
0b01 => .switch_capture_ref,
0b10 => .switch_capture_multi,
0b11 => .switch_capture_multi_ref,
const capture = if (case_node == special_node) capture: {
const capture_tag: Zir.Inst.Tag = if (is_ptr)
.switch_capture_else_ref
else
.switch_capture_else;
break :capture try case_scope.add(.{
.tag = capture_tag,
.data = .{ .switch_capture = .{
.switch_inst = switch_block,
.prong_index = undefined,
} },
});
} else capture: {
const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
const is_ptr_bits: u2 = @boolToInt(is_ptr);
const capture_tag: Zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
0b00 => .switch_capture,
0b01 => .switch_capture_ref,
0b10 => .switch_capture_multi,
0b11 => .switch_capture_multi_ref,
};
const capture_index = if (is_multi_case) ci: {
multi_case_index += 1;
break :ci multi_case_index - 1;
} else ci: {
scalar_case_index += 1;
break :ci scalar_case_index - 1;
};
break :capture try case_scope.add(.{
.tag = capture_tag,
.data = .{ .switch_capture = .{
.switch_inst = switch_block,
.prong_index = capture_index,
} },
});
};
const capture_index = if (is_multi_case) ci: {
multi_case_index += 1;
break :ci multi_case_index - 1;
} else ci: {
scalar_case_index += 1;
break :ci scalar_case_index - 1;
};
const capture = try case_scope.add(.{
.tag = capture_tag,
.data = .{ .switch_capture = .{
.switch_inst = switch_block,
.prong_index = capture_index,
} },
});
const capture_name = try astgen.identAsString(ident);
capture_val_scope = .{
.parent = &case_scope.base,
@ -6134,6 +6117,17 @@ fn switchExpr(
multi_cases_payload.items[header_index + 1] = ranges_len;
multi_cases_payload.items[header_index + 2] = @intCast(u32, case_scope.instructions.items.len);
try multi_cases_payload.appendSlice(gpa, case_scope.instructions.items);
} else if (case_node == special_node) {
const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
try checkUsed(parent_gz, &case_scope.base, sub_scope);
if (!parent_gz.refIsNoReturn(case_result)) {
block_scope.break_count += 1;
_ = try case_scope.addBreak(.@"break", switch_block, case_result);
}
try special_case_payload.ensureUnusedCapacity(gpa, 1 + // body_len
case_scope.instructions.items.len);
special_case_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len));
special_case_payload.appendSliceAssumeCapacity(case_scope.instructions.items);
} else {
const item_node = case.ast.values[0];
const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node);
@ -6143,7 +6137,7 @@ fn switchExpr(
block_scope.break_count += 1;
_ = try case_scope.addBreak(.@"break", switch_block, case_result);
}
try scalar_cases_payload.ensureUnusedCapacity(gpa, 2 +
try scalar_cases_payload.ensureUnusedCapacity(gpa, 2 + // item + body_len
case_scope.instructions.items.len);
scalar_cases_payload.appendAssumeCapacity(@enumToInt(item_inst));
scalar_cases_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len));
@ -6180,8 +6174,17 @@ fn switchExpr(
const payload_index = astgen.extra.items.len;
const zir_datas = astgen.instructions.items(.data);
zir_datas[switch_block].pl_node.payload_index = @intCast(u32, payload_index);
try astgen.extra.ensureUnusedCapacity(gpa, scalar_cases_payload.items.len +
// Documentation for this: `Zir.Inst.SwitchBlock` and `Zir.Inst.SwitchBlockMulti`.
try astgen.extra.ensureUnusedCapacity(gpa, @as(usize, 2) + // operand, scalar_cases_len
@boolToInt(multi_cases_len != 0) +
special_case_payload.items.len +
scalar_cases_payload.items.len +
multi_cases_payload.items.len);
astgen.extra.appendAssumeCapacity(@enumToInt(operand));
astgen.extra.appendAssumeCapacity(scalar_cases_len);
if (multi_cases_len != 0) {
astgen.extra.appendAssumeCapacity(multi_cases_len);
}
const strat = rl.strategy(&block_scope);
switch (strat.tag) {
.break_operand => {
@ -6189,6 +6192,7 @@ fn switchExpr(
// `elide_store_to_block_ptr_instructions` will either be true,
// or all prongs are noreturn.
if (!strat.elide_store_to_block_ptr_instructions) {
astgen.extra.appendSliceAssumeCapacity(special_case_payload.items);
astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items);
astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items);
return indexToRef(switch_block);
@ -6204,32 +6208,30 @@ fn switchExpr(
// it as the break operand.
var extra_index: usize = 0;
extra_index += 2;
extra_index += @boolToInt(multi_cases_len != 0);
if (special_prong != .none) special_prong: {
const body_len_index = extra_index;
const body_len = scalar_cases_payload.items[extra_index];
const body_len = special_case_payload.items[extra_index];
extra_index += 1;
if (body_len < 2) {
extra_index += body_len;
astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
astgen.extra.appendSliceAssumeCapacity(special_case_payload.items[0..extra_index]);
break :special_prong;
}
extra_index += body_len - 2;
const store_inst = scalar_cases_payload.items[extra_index];
const store_inst = special_case_payload.items[extra_index];
if (zir_tags[store_inst] != .store_to_block_ptr or
zir_datas[store_inst].bin.lhs != block_scope.rl_ptr)
{
extra_index += 2;
astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
astgen.extra.appendSliceAssumeCapacity(special_case_payload.items[0..extra_index]);
break :special_prong;
}
assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
if (block_scope.rl_ty_inst != .none) {
extra_index += 1;
const break_inst = scalar_cases_payload.items[extra_index];
const break_inst = special_case_payload.items[extra_index];
extra_index += 1;
astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
astgen.extra.appendSliceAssumeCapacity(special_case_payload.items[0..extra_index]);
zir_tags[store_inst] = .as;
zir_datas[store_inst].bin = .{
.lhs = block_scope.rl_ty_inst,
@ -6237,15 +6239,16 @@ fn switchExpr(
};
zir_datas[break_inst].@"break".operand = indexToRef(store_inst);
} else {
scalar_cases_payload.items[body_len_index] -= 1;
astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
special_case_payload.items[body_len_index] -= 1;
astgen.extra.appendSliceAssumeCapacity(special_case_payload.items[0..extra_index]);
extra_index += 1;
astgen.extra.appendAssumeCapacity(scalar_cases_payload.items[extra_index]);
astgen.extra.appendAssumeCapacity(special_case_payload.items[extra_index]);
extra_index += 1;
}
} else {
astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
astgen.extra.appendSliceAssumeCapacity(special_case_payload.items[0..extra_index]);
}
extra_index = 0;
var scalar_i: u32 = 0;
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const start_index = extra_index;
@ -6342,6 +6345,7 @@ fn switchExpr(
},
.break_void => {
assert(!strat.elide_store_to_block_ptr_instructions);
astgen.extra.appendSliceAssumeCapacity(special_case_payload.items);
astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items);
astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items);
// Modify all the terminating instruction tags to become `break` variants.
@ -9282,6 +9286,9 @@ const Scope = struct {
/// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
parent: *Scope,
defer_node: Ast.Node.Index,
source_offset: u32,
source_line: u32,
source_column: u32,
};
/// Represents a global scope that has any number of declarations in it.
@ -9563,6 +9570,8 @@ const GenZir = struct {
fn addFunc(gz: *GenZir, args: struct {
src_node: Ast.Node.Index,
lbrace_line: u32 = 0,
lbrace_column: u32 = 0,
body: []const Zir.Inst.Index,
param_block: Zir.Inst.Index,
ret_ty: []const Zir.Inst.Index,
@ -9592,19 +9601,13 @@ const GenZir = struct {
const fn_decl = args.src_node;
assert(node_tags[fn_decl] == .fn_decl or node_tags[fn_decl] == .test_decl);
const block = node_datas[fn_decl].rhs;
const lbrace_start = token_starts[tree.firstToken(block)];
const rbrace_start = token_starts[tree.lastToken(block)];
astgen.advanceSourceCursor(tree.source, lbrace_start);
const lbrace_line = @intCast(u32, astgen.source_line);
const lbrace_column = @intCast(u32, astgen.source_column);
astgen.advanceSourceCursor(tree.source, rbrace_start);
const rbrace_line = @intCast(u32, astgen.source_line);
const rbrace_column = @intCast(u32, astgen.source_column);
const columns = lbrace_column | (rbrace_column << 16);
src_locs_buffer[0] = lbrace_line;
const columns = args.lbrace_column | (rbrace_column << 16);
src_locs_buffer[0] = args.lbrace_line;
src_locs_buffer[1] = rbrace_line;
src_locs_buffer[2] = columns;
src_locs = &src_locs_buffer;
@ -10577,6 +10580,7 @@ fn advanceSourceCursor(astgen: *AstGen, source: []const u8, end: usize) void {
var i = astgen.source_offset;
var line = astgen.source_line;
var column = astgen.source_column;
assert(i <= end);
while (i < end) : (i += 1) {
if (source[i] == '\n') {
line += 1;

View File

@ -1913,8 +1913,8 @@ const Writer = struct {
try stream.writeAll(") ");
if (body.len != 0) {
try stream.print("(lbrace={d}:{d},rbrace={d}:{d}) ", .{
src_locs.lbrace_line, @truncate(u16, src_locs.columns),
src_locs.rbrace_line, @truncate(u16, src_locs.columns >> 16),
src_locs.lbrace_line + 1, @truncate(u16, src_locs.columns) + 1,
src_locs.rbrace_line + 1, @truncate(u16, src_locs.columns >> 16) + 1,
});
}
try self.writeSrc(stream, src);
@ -1928,7 +1928,7 @@ const Writer = struct {
fn writeDbgStmt(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
const inst_data = self.code.instructions.items(.data)[inst].dbg_stmt;
try stream.print("{d}, {d})", .{ inst_data.line, inst_data.column });
try stream.print("{d}, {d})", .{ inst_data.line + 1, inst_data.column + 1 });
}
fn writeInstRef(self: *Writer, stream: anytype, ref: Zir.Inst.Ref) !void {