aboutsummaryrefslogtreecommitdiff
path: root/src/js_parser/js_parser.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/js_parser/js_parser.zig')
-rw-r--r--src/js_parser/js_parser.zig295
1 files changed, 161 insertions, 134 deletions
diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig
index cb48f08e3..39860b42e 100644
--- a/src/js_parser/js_parser.zig
+++ b/src/js_parser/js_parser.zig
@@ -3,7 +3,7 @@ usingnamespace @import("imports.zig");
const TemplatePartTuple = std.meta.Tuple(&[_]type{ []E.TemplatePart, logger.Loc });
const ScopeOrderList = std.ArrayListUnmanaged(?ScopeOrder);
-var panic_buffer = std.mem.zeros(32 * 1024);
+var panic_buffer = std.mem.zeroes([32 * 1024]u8);
var panic_stream = std.io.fixedBufferStream(&panic_buffer);
pub fn ExpressionTransposer(comptime ctx: type, visitor: fn (ptr: *ctx, arg: Expr, state: anytype) Expr) type {
@@ -1771,6 +1771,8 @@ pub const P = struct {
// This is a general place to put lots of Expr objects
expr_list: List(Expr),
+ scope_order_to_visit: []ScopeOrder = &([_]ScopeOrder{}),
+
const TransposeState = struct {
is_await_target: bool = false,
is_then_catch_target: bool = false,
@@ -1827,7 +1829,7 @@ pub const P = struct {
p.import_records_for_current_part.append(import_record_index) catch unreachable;
p.ignoreUsage(p.require_ref);
- return p.e(E.Require{ .import_record_index = import_record_index }, arg.loc);
+ return p.e(E.Require{ .import_record_index = Ref.toInt(import_record_index) }, arg.loc);
},
else => {},
}
@@ -1845,13 +1847,16 @@ pub const P = struct {
};
pub fn s(p: *P, t: anytype, loc: logger.Loc) Stmt {
+ // Output.print("\nStmt: {s} - {d}\n", .{ @typeName(@TypeOf(t)), loc.start });
if (@typeInfo(@TypeOf(t)) == .Pointer) {
return Stmt.init(t, loc);
} else {
return Stmt.alloc(p.allocator, t, loc);
}
}
+
pub fn e(p: *P, t: anytype, loc: logger.Loc) Expr {
+ // Output.print("\nExpr: {s} - {d}\n", .{ @typeName(@TypeOf(t)), loc.start });
if (@typeInfo(@TypeOf(t)) == .Pointer) {
return Expr.init(t, loc);
} else {
@@ -2222,6 +2227,23 @@ pub const P = struct {
}
pub fn prepareForVisitPass(p: *P) !void {
+ {
+ var count: usize = 0;
+ for (p.scopes_in_order.items) |item| {
+ if (item != null) {
+ count += 1;
+ }
+ }
+ var i: usize = 0;
+ p.scope_order_to_visit = try p.allocator.alloc(ScopeOrder, p.scopes_in_order.items.len);
+ for (p.scopes_in_order.items) |item| {
+ if (item) |_item| {
+ p.scope_order_to_visit[i] = _item;
+ i += 1;
+ }
+ }
+ }
+
try p.pushScopeForVisitPass(js_ast.Scope.Kind.entry, locModuleScope);
p.fn_or_arrow_data_visit.is_outside_fn_or_arrow = true;
p.module_scope = p.current_scope;
@@ -2266,20 +2288,18 @@ pub const P = struct {
}
pub fn nextScopeInOrderForVisitPass(p: *P) ScopeOrder {
- while (p.scopes_in_order.items[p.scopes_in_order_visitor_index] == null) {
- p.scopes_in_order_visitor_index += 1;
- }
- const scope_order = p.scopes_in_order.items[p.scopes_in_order_visitor_index].?;
- p.scopes_in_order_visitor_index += 1;
- return scope_order;
+ const head = p.scope_order_to_visit[0];
+ p.scope_order_to_visit = p.scope_order_to_visit[1..p.scope_order_to_visit.len];
+ return head;
}
pub fn pushScopeForVisitPass(p: *P, kind: js_ast.Scope.Kind, loc: logger.Loc) !void {
- for (p.scopes_in_order.items[p.scopes_in_order_visitor_index..p.scopes_in_order.items.len]) |scope_order, i| {
- if (scope_order) |ord| {
- Output.print("Scope ({d}, {d})\n", .{ @enumToInt(ord.scope.kind), ord.loc.start });
- }
- }
+ // Output.print("\n+Loc: {d}\n", .{loc.start});
+ // for (p.scopes_in_order.items[p.scopes_in_order_visitor_index..p.scopes_in_order.items.len]) |scope_order, i| {
+ // if (scope_order) |ord| {
+ // Output.print("Scope ({d}, {d})\n", .{ @enumToInt(ord.scope.kind), ord.loc.start });
+ // }
+ // }
const order = p.nextScopeInOrderForVisitPass();
// Sanity-check that the scopes generated by the first and second passes match
@@ -2296,7 +2316,6 @@ pub const P = struct {
debugl("<pushScopeForParsePass>");
defer debugl("</pushScopeForParsePass>");
var parent: *Scope = p.current_scope;
- p.scope_id += 1;
var scope = try p.allocator.create(Scope);
scope.* = Scope{
@@ -2308,7 +2327,6 @@ pub const P = struct {
.kind = kind,
.label_ref = null,
.parent = parent,
- .id = p.scope_id,
};
try parent.children.append(scope);
@@ -2351,7 +2369,7 @@ pub const P = struct {
// Remember the length in case we call popAndDiscardScope() later
const scope_index = p.scopes_in_order.items.len;
try p.scopes_in_order.append(p.allocator, ScopeOrder{ .loc = loc, .scope = scope });
-
+ // Output.print("\nLoc: {d}\n", .{loc.start});
return scope_index;
}
@@ -3525,6 +3543,7 @@ pub const P = struct {
}
stmtOpts = ParseStatementOptions{};
try p.declareBinding(kind, value, &stmtOpts);
+ binding = value;
}
try p.lexer.expect(.t_open_brace);
@@ -5008,23 +5027,27 @@ pub const P = struct {
var name: ?js_ast.LocRef = null;
_ = p.pushScopeForParsePass(.function_args, loc) catch unreachable;
- defer p.popScope();
+ // The name is optional
if (p.lexer.token == .t_identifier) {
- name = js_ast.LocRef{
- .loc = loc,
+ // Don't declare the name "arguments" since it's shadowed and inaccessible
+ var _name = js_ast.LocRef{
+ .loc = p.lexer.loc(),
.ref = null,
};
- if (p.lexer.identifier.len > 0 and !strings.eql(p.lexer.identifier, "arguments")) {
- (name orelse unreachable).ref = try p.declareSymbol(.hoisted_function, (name orelse unreachable).loc, p.lexer.identifier);
+ const text = p.lexer.identifier;
+ if (text.len > 0 and !strings.eql(text, "arguments")) {
+ _name.ref = try p.declareSymbol(.hoisted_function, _name.loc, text);
} else {
- (name orelse unreachable).ref = try p.newSymbol(.hoisted_function, p.lexer.identifier);
+ _name.ref = try p.newSymbol(.hoisted_function, text);
}
debug("FUNC NAME {s}", .{p.lexer.identifier});
+ name = _name;
try p.lexer.next();
}
+ // Even anonymous functions can have TypeScript type parameters
if (p.options.ts) {
p.skipTypescriptTypeParameters();
}
@@ -5036,6 +5059,7 @@ pub const P = struct {
});
p.validateFunctionName(func, .expr);
+ p.popScope();
return p.e(js_ast.E.Function{
.func = func,
@@ -6281,6 +6305,7 @@ pub const P = struct {
const yes = try p.parseExpr(.comma);
p.allow_in = old_allow_in;
+
try p.lexer.expect(.t_colon);
const no = try p.parseExpr(.comma);
@@ -7069,6 +7094,7 @@ pub const P = struct {
const class = try p.parseClass(classKeyword, name, ParseClassOptions{});
p.popScope();
+
return p.e(class, loc);
},
.t_new => {
@@ -7906,9 +7932,7 @@ pub const P = struct {
pub fn visitFunc(p: *P, func: *G.Fn, open_parens_loc: logger.Loc) void {
const old_fn_or_arrow_data = p.fn_or_arrow_data_visit;
- defer p.fn_or_arrow_data_visit = old_fn_or_arrow_data;
const old_fn_only_data = p.fn_only_data_visit;
- defer p.fn_only_data_visit = old_fn_only_data;
p.fn_or_arrow_data_visit = FnOrArrowDataVisit{ .is_async = func.flags.is_async };
p.fn_only_data_visit = FnOnlyDataVisit{ .is_this_nested = true, .arguments_ref = func.arguments_ref };
@@ -7923,7 +7947,6 @@ pub const P = struct {
}
p.pushScopeForVisitPass(.function_args, open_parens_loc) catch unreachable;
- defer p.popScope();
p.visitArgs(
func.args,
VisitArgsOpts{
@@ -7933,16 +7956,18 @@ pub const P = struct {
},
);
- var body = func.body orelse p.panic("Expected visitFunc to have body {s}", .{func});
- p.pushScopeForVisitPass(.function_body, body.loc) catch unreachable;
- defer p.popScope();
- var stmts = List(Stmt).fromOwnedSlice(p.allocator, body.stmts);
- var temp_opts = PrependTempRefsOpts{ .kind = StmtsKind.fn_body, .fn_body_loc = body.loc };
+ assert(func.body != null);
+ p.pushScopeForVisitPass(.function_body, func.body.?.loc) catch unreachable;
+ var stmts = List(Stmt).fromOwnedSlice(p.allocator, func.body.?.stmts);
+ var temp_opts = PrependTempRefsOpts{ .kind = StmtsKind.fn_body, .fn_body_loc = func.body.?.loc };
p.visitStmtsAndPrependTempRefs(&stmts, &temp_opts) catch unreachable;
+ func.body.?.stmts = stmts.toOwnedSlice();
- body.stmts = stmts.toOwnedSlice();
+ p.popScope();
+ p.popScope();
- func.body = body;
+ p.fn_or_arrow_data_visit = old_fn_or_arrow_data;
+ p.fn_only_data_visit = old_fn_only_data;
}
pub fn maybeKeepExprSymbolName(p: *P, expr: Expr, original_name: string, was_anonymous_named_expr: bool) Expr {
@@ -7976,6 +8001,7 @@ pub const P = struct {
}
pub fn visitExprInOut(p: *P, expr: Expr, in: ExprIn) Expr {
+ // Output.print("\nVisit: {s} - {d}\n", .{ @tagName(expr.data), expr.loc.start });
switch (expr.data) {
.e_null, .e_super, .e_boolean, .e_big_int, .e_reg_exp, .e_new_target, .e_undefined => {},
.e_string => |e_| {
@@ -8713,7 +8739,10 @@ pub const P = struct {
const side_effects = SideEffects.toBoolean(e_.test_.data);
- if (side_effects.ok) {
+ if (!side_effects.ok) {
+ e_.yes = p.visitExpr(e_.yes);
+ e_.no = p.visitExpr(e_.no);
+ } else {
// Mark the control flow as dead if the branch is never taken
if (side_effects.value) {
// "true ? live : dead"
@@ -8730,9 +8759,6 @@ pub const P = struct {
p.is_control_flow_dead = old;
e_.no = p.visitExpr(e_.no);
}
- } else {
- e_.yes = p.visitExpr(e_.yes);
- e_.no = p.visitExpr(e_.no);
}
},
.e_await => |e_| {
@@ -8788,68 +8814,68 @@ pub const P = struct {
.e_object => |e_| {
if (in.assign_target != .none) {
p.maybeCommaSpreadError(e_.comma_after_spread);
- var has_spread = false;
- var has_proto = false;
+ }
- var i: usize = 0;
- while (i < e_.properties.len) : (i += 1) {
- var property = e_.properties[i];
-
- if (property.kind != .spread) {
- const key = p.visitExpr(property.key orelse Global.panic("Expected property key", .{}));
- e_.properties[i].key = key;
-
- // Forbid duplicate "__proto__" properties according to the specification
- if (!property.flags.is_computed and !property.flags.was_shorthand and !property.flags.is_method and in.assign_target == .none and key.data.isStringValue() and strings.eqlComptime(
- // __proto__ is utf8, assume it lives in refs
- key.data.e_string.utf8,
- "__proto__",
- )) {
- if (has_proto) {
- const r = js_lexer.rangeOfIdentifier(p.source, key.loc);
- p.log.addRangeError(p.source, r, "Cannot specify the \"__proto__\" property more than once per object") catch unreachable;
- }
- has_proto = true;
- }
- } else {
- has_spread = true;
- }
+ var has_spread = false;
+ var has_proto = false;
+ var i: usize = 0;
+ while (i < e_.properties.len) : (i += 1) {
+ var property = e_.properties[i];
- // Extract the initializer for expressions like "({ a: b = c } = d)"
- if (in.assign_target != .none and property.initializer != null and property.value != null) {
- switch (property.value.?.data) {
- .e_binary => |bin| {
- if (bin.op == .bin_assign) {
- property.initializer = bin.right;
- property.value = bin.left;
- }
- },
- else => {},
+ if (property.kind != .spread) {
+ const key = p.visitExpr(property.key orelse Global.panic("Expected property key", .{}));
+ e_.properties[i].key = key;
+
+ // Forbid duplicate "__proto__" properties according to the specification
+ if (!property.flags.is_computed and !property.flags.was_shorthand and !property.flags.is_method and in.assign_target == .none and key.data.isStringValue() and strings.eqlComptime(
+ // __proto__ is utf8, assume it lives in refs
+ key.data.e_string.utf8,
+ "__proto__",
+ )) {
+ if (has_proto) {
+ const r = js_lexer.rangeOfIdentifier(p.source, key.loc);
+ p.log.addRangeError(p.source, r, "Cannot specify the \"__proto__\" property more than once per object") catch unreachable;
}
+ has_proto = true;
}
+ } else {
+ has_spread = true;
+ }
- if (property.value != null) {
- property.value = p.visitExprInOut(property.value.?, ExprIn{ .assign_target = in.assign_target });
+ // Extract the initializer for expressions like "({ a: b = c } = d)"
+ if (in.assign_target != .none and property.initializer != null and property.value != null) {
+ switch (property.value.?.data) {
+ .e_binary => |bin| {
+ if (bin.op == .bin_assign) {
+ property.initializer = bin.right;
+ property.value = bin.left;
+ }
+ },
+ else => {},
}
+ }
- if (property.initializer != null) {
- const was_anonymous_named_expr = p.isAnonymousNamedExpr(property.initializer orelse unreachable);
- property.initializer = p.visitExprInOut(property.initializer.?, ExprIn{ .assign_target = in.assign_target });
+ if (property.value != null) {
+ property.value = p.visitExprInOut(property.value.?, ExprIn{ .assign_target = in.assign_target });
+ }
- if (property.value) |val| {
- if (@as(Expr.Tag, val.data) == .e_identifier) {
- property.initializer = p.maybeKeepExprSymbolName(
- property.initializer orelse unreachable,
- p.symbols.items[val.data.e_identifier.ref.inner_index].original_name,
- was_anonymous_named_expr,
- );
- }
+ if (property.initializer != null) {
+ const was_anonymous_named_expr = p.isAnonymousNamedExpr(property.initializer orelse unreachable);
+ property.initializer = p.visitExprInOut(property.initializer.?, ExprIn{ .assign_target = in.assign_target });
+
+ if (property.value) |val| {
+ if (@as(Expr.Tag, val.data) == .e_identifier) {
+ property.initializer = p.maybeKeepExprSymbolName(
+ property.initializer orelse unreachable,
+ p.symbols.items[val.data.e_identifier.ref.inner_index].original_name,
+ was_anonymous_named_expr,
+ );
}
}
-
- // TODO: can we avoid htis copy
- e_.properties[i] = property;
}
+
+ // TODO: can we avoid htis copy
+ e_.properties[i] = property;
}
},
.e_import => |e_| {
@@ -8931,16 +8957,19 @@ pub const P = struct {
p.fn_only_data_visit.is_inside_async_arrow_fn = e_.is_async or p.fn_only_data_visit.is_inside_async_arrow_fn;
p.pushScopeForVisitPass(.function_args, expr.loc) catch unreachable;
+ var dupe = p.allocator.dupe(Stmt, e_.body.stmts) catch unreachable;
+
p.visitArgs(e_.args, VisitArgsOpts{
.has_rest_arg = e_.has_rest_arg,
- .body = e_.body.stmts,
+ .body = dupe,
.is_unique_formal_parameters = true,
});
p.pushScopeForVisitPass(.function_body, e_.body.loc) catch unreachable;
- var stmts_list = List(Stmt).fromOwnedSlice(p.allocator, e_.body.stmts);
+ var stmts_list = List(Stmt).fromOwnedSlice(p.allocator, dupe);
var temp_opts = PrependTempRefsOpts{ .kind = StmtsKind.fn_body };
p.visitStmtsAndPrependTempRefs(&stmts_list, &temp_opts) catch unreachable;
+ p.allocator.free(e_.body.stmts);
e_.body.stmts = stmts_list.toOwnedSlice();
p.popScope();
p.popScope();
@@ -9015,17 +9044,18 @@ pub const P = struct {
}
pub fn keepExprSymbolName(p: *P, _value: Expr, name: string) Expr {
- var start = p.expr_list.items.len;
- p.expr_list.ensureUnusedCapacity(2) catch unreachable;
- p.expr_list.appendAssumeCapacity(_value);
- p.expr_list.appendAssumeCapacity(p.e(E.String{
- .utf8 = name,
- }, _value.loc));
+ return _value;
+ // var start = p.expr_list.items.len;
+ // p.expr_list.ensureUnusedCapacity(2) catch unreachable;
+ // p.expr_list.appendAssumeCapacity(_value);
+ // p.expr_list.appendAssumeCapacity(p.e(E.String{
+ // .utf8 = name,
+ // }, _value.loc));
- var value = p.callRuntime(_value.loc, "ℹ", p.expr_list.items[start..p.expr_list.items.len]);
- // Make sure tree shaking removes this if the function is never used
- value.data.e_call.can_be_unwrapped_if_unused = true;
- return value;
+ // var value = p.callRuntime(_value.loc, "ℹ", p.expr_list.items[start..p.expr_list.items.len]);
+ // // Make sure tree shaking removes this if the function is never used
+ // value.data.e_call.can_be_unwrapped_if_unused = true;
+ // return value;
}
pub fn fnBodyContainsUseStrict(body: []Stmt) ?logger.Loc {
@@ -9719,9 +9749,9 @@ pub const P = struct {
.s_for => |data| {
{
p.pushScopeForVisitPass(.block, stmt.loc) catch unreachable;
- defer p.popScope();
+
if (data.init) |initst| {
- _ = p.visitForLoopInit(initst, false);
+ data.init = p.visitForLoopInit(initst, false);
}
if (data.test_) |test_| {
@@ -9735,6 +9765,7 @@ pub const P = struct {
}
data.body = p.visitLoopBody(data.body);
+ p.popScope();
}
// TODO: Potentially relocate "var" declarations to the top level
@@ -9782,32 +9813,36 @@ pub const P = struct {
// p.lowerObjectRestInForLoopInit(s.Init, &s.Body)
},
.s_try => |data| {
+ p.pushScopeForVisitPass(.block, stmt.loc) catch unreachable;
{
- p.pushScopeForVisitPass(.block, stmt.loc) catch unreachable;
- defer p.popScope();
- p.fn_or_arrow_data_visit.try_body_count += 1;
- defer p.fn_or_arrow_data_visit.try_body_count -= 1;
var _stmts = List(Stmt).fromOwnedSlice(p.allocator, data.body);
+ p.fn_or_arrow_data_visit.try_body_count += 1;
p.visitStmts(&_stmts, StmtsKind.none) catch unreachable;
+ p.fn_or_arrow_data_visit.try_body_count -= 1;
data.body = _stmts.toOwnedSlice();
}
+ p.popScope();
if (data.catch_) |*catch_| {
p.pushScopeForVisitPass(.block, catch_.loc) catch unreachable;
- defer p.popScope();
- if (catch_.binding != null and @as(Binding.Tag, catch_.binding.?.data) != .b_missing) {
- p.visitBinding(catch_.binding.?, null);
+ {
+ if (catch_.binding != null) {
+ p.visitBinding(catch_.binding.?, null);
+ }
+ var _stmts = List(Stmt).fromOwnedSlice(p.allocator, catch_.body);
+ p.visitStmts(&_stmts, StmtsKind.none) catch unreachable;
+ catch_.body = _stmts.toOwnedSlice();
}
- var _stmts = List(Stmt).fromOwnedSlice(p.allocator, data.body);
- p.visitStmts(&_stmts, StmtsKind.none) catch unreachable;
- catch_.body = _stmts.toOwnedSlice();
+ p.popScope();
}
if (data.finally) |*finally| {
p.pushScopeForVisitPass(.block, finally.loc) catch unreachable;
- var _stmts = List(Stmt).fromOwnedSlice(p.allocator, data.body);
- p.visitStmts(&_stmts, StmtsKind.none) catch unreachable;
- finally.stmts = _stmts.toOwnedSlice();
+ {
+ var _stmts = List(Stmt).fromOwnedSlice(p.allocator, finally.stmts);
+ p.visitStmts(&_stmts, StmtsKind.none) catch unreachable;
+ finally.stmts = _stmts.toOwnedSlice();
+ }
p.popScope();
}
},
@@ -10289,9 +10324,10 @@ pub const P = struct {
pub fn visitLoopBody(p: *P, stmt: StmtNodeIndex) StmtNodeIndex {
const old_is_inside_loop = p.fn_or_arrow_data_visit.is_inside_loop;
p.fn_or_arrow_data_visit.is_inside_loop = true;
- defer p.fn_or_arrow_data_visit.is_inside_loop = old_is_inside_loop;
p.loop_body = stmt.data;
- return p.visitSingleStmt(stmt, .loop_body);
+ const res = p.visitSingleStmt(stmt, .loop_body);
+ p.fn_or_arrow_data_visit.is_inside_loop = old_is_inside_loop;
+ return res;
}
pub fn visitSingleStmt(p: *P, stmt: Stmt, kind: StmtsKind) Stmt {
@@ -10328,19 +10364,9 @@ pub const P = struct {
return Stmt{ .data = Prefill.Data.SEmpty, .loc = loc };
}
- if (stmts.len == 1) {
- switch (stmts[0].data) {
- .s_local => |local| {
- // "let" and "const" must be put in a block when in a single-statement context
-
- if (local.kind == .k_var) {
- return stmts[0];
- }
- },
- else => {
- return stmts[0];
- },
- }
+ if (stmts.len == 1 and std.meta.activeTag(stmts[0].data) != .s_local or (std.meta.activeTag(stmts[0].data) == .s_local and stmts[0].data.s_local.kind == S.Local.Kind.k_var)) {
+ // "let" and "const" must be put in a block when in a single-statement context
+ return stmts[0];
}
return p.s(S.Block{ .stmts = stmts }, loc);
@@ -10622,7 +10648,7 @@ pub const P = struct {
// values that introduce new scopes and declare new symbols. If this is an
// arrow function, then those new scopes will need to be parented under the
// scope of the arrow function itself.
- const scopeIndex = try p.pushScopeForParsePass(.function_args, loc);
+ const scope_index = try p.pushScopeForParsePass(.function_args, loc);
// Allow "in" inside parentheses
var oldAllowIn = p.allow_in;
@@ -10754,12 +10780,12 @@ pub const P = struct {
// If we get here, it's not an arrow function so undo the pushing of the
// scope we did earlier. This needs to flatten any child scopes into the
// parent scope as if the scope was never pushed in the first place.
- p.popAndFlattenScope(scopeIndex);
+ p.popAndFlattenScope(scope_index);
// If this isn't an arrow function, then types aren't allowed
if (type_colon_range.len > 0) {
try p.log.addRangeError(p.source, type_colon_range, "Unexpected \":\"");
- p.panic("", .{});
+ return error.ParseError;
}
// Are these arguments for a call to a function named "async"?
@@ -10774,7 +10800,7 @@ pub const P = struct {
p.logExprErrors(&errors);
if (spread_range.len > 0) {
try p.log.addRangeError(p.source, type_colon_range, "Unexpected \"...\"");
- p.panic("", .{});
+ return error.ParseError;
}
var value = Expr.joinAllWithComma(items, p.allocator);
@@ -10784,7 +10810,7 @@ pub const P = struct {
// Indicate that we expected an arrow function
try p.lexer.expected(.t_equals_greater_than);
- p.panic("", .{});
+ return error.ParseError;
}
// This code is tricky.
@@ -10793,6 +10819,7 @@ pub const P = struct {
// The key is in how we remove scopes from the list
// If we do an orderedRemove, it gets very slow.
// swapRemove is fast. But a little more dangerous.
+ // Instead, we just tombstone it.
pub fn popAndFlattenScope(p: *P, scope_index: usize) void {
// Move up to the parent scope
var to_flatten = p.current_scope;