From a0a847f2e40046387e2e2b8a0b8dae9cb27ca22a Mon Sep 17 00:00:00 2001 From: Martin Wickham Date: Thu, 23 Sep 2021 12:17:06 -0500 Subject: Stage2: Implement comptime closures and the This builtin (#9823) --- src/Sema.zig | 263 ++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 196 insertions(+), 67 deletions(-) (limited to 'src/Sema.zig') diff --git a/src/Sema.zig b/src/Sema.zig index a0e3250e56..cc7a227ca6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -8,8 +8,12 @@ mod: *Module, /// Alias to `mod.gpa`. gpa: *Allocator, -/// Points to the arena allocator of the Decl. +/// Points to the temporary arena allocator of the Sema. +/// This arena will be cleared when the sema is destroyed. arena: *Allocator, +/// Points to the arena allocator for the owner_decl. +/// This arena will persist until the decl is invalidated. +perm_arena: *Allocator, code: Zir, air_instructions: std.MultiArrayList(Air.Inst) = .{}, air_extra: std.ArrayListUnmanaged(u32) = .{}, @@ -80,6 +84,8 @@ const Scope = Module.Scope; const CompileError = Module.CompileError; const SemaError = Module.SemaError; const Decl = Module.Decl; +const CaptureScope = Module.CaptureScope; +const WipCaptureScope = Module.WipCaptureScope; const LazySrcLoc = Module.LazySrcLoc; const RangeSet = @import("RangeSet.zig"); const target_util = @import("target.zig"); @@ -129,15 +135,29 @@ pub fn analyzeBody( ) CompileError!Zir.Inst.Index { // No tracy calls here, to avoid interfering with the tail call mechanism. + const parent_capture_scope = block.wip_capture_scope; + + var wip_captures = WipCaptureScope{ + .finalized = true, + .scope = parent_capture_scope, + .perm_arena = sema.perm_arena, + .gpa = sema.gpa, + }; + defer if (wip_captures.scope != parent_capture_scope) { + wip_captures.deinit(); + }; + const map = &block.sema.inst_map; const tags = block.sema.code.instructions.items(.tag); const datas = block.sema.code.instructions.items(.data); + var orig_captures: usize = parent_capture_scope.captures.count(); + // We use a while(true) loop here to avoid a redundant way of breaking out of // the loop. The only way to break out of the loop is with a `noreturn` // instruction. var i: usize = 0; - while (true) { + const result = while (true) { const inst = body[i]; const air_inst: Air.Inst.Ref = switch (tags[inst]) { // zig fmt: off @@ -170,6 +190,7 @@ pub fn analyzeBody( .call_compile_time => try sema.zirCall(block, inst, .compile_time, false), .call_nosuspend => try sema.zirCall(block, inst, .no_async, false), .call_async => try sema.zirCall(block, inst, .async_kw, false), + .closure_get => try sema.zirClosureGet(block, inst), .cmp_lt => try sema.zirCmp(block, inst, .lt), .cmp_lte => try sema.zirCmp(block, inst, .lte), .cmp_eq => try sema.zirCmpEq(block, inst, .eq, .cmp_eq), @@ -343,9 +364,6 @@ pub fn analyzeBody( .trunc => try sema.zirUnaryMath(block, inst), .round => try sema.zirUnaryMath(block, inst), - .opaque_decl => try sema.zirOpaqueDecl(block, inst, .parent), - .opaque_decl_anon => try sema.zirOpaqueDecl(block, inst, .anon), - .opaque_decl_func => try sema.zirOpaqueDecl(block, inst, .func), .error_set_decl => try sema.zirErrorSetDecl(block, inst, .parent), .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon), .error_set_decl_func => try sema.zirErrorSetDecl(block, inst, .func), @@ -362,13 +380,13 @@ pub fn analyzeBody( // Instructions that we know to *always* be noreturn based solely on their tag. // These functions match the return type of analyzeBody so that we can // tail call them here. - .compile_error => return sema.zirCompileError(block, inst), - .ret_coerce => return sema.zirRetCoerce(block, inst), - .ret_node => return sema.zirRetNode(block, inst), - .ret_load => return sema.zirRetLoad(block, inst), - .ret_err_value => return sema.zirRetErrValue(block, inst), - .@"unreachable" => return sema.zirUnreachable(block, inst), - .panic => return sema.zirPanic(block, inst), + .compile_error => break sema.zirCompileError(block, inst), + .ret_coerce => break sema.zirRetCoerce(block, inst), + .ret_node => break sema.zirRetNode(block, inst), + .ret_load => break sema.zirRetLoad(block, inst), + .ret_err_value => break sema.zirRetErrValue(block, inst), + .@"unreachable" => break sema.zirUnreachable(block, inst), + .panic => break sema.zirPanic(block, inst), // zig fmt: on // Instructions that we know can *never* be noreturn based solely on @@ -503,34 +521,49 @@ pub fn analyzeBody( i += 1; continue; }, + .closure_capture => { + try sema.zirClosureCapture(block, inst); + i += 1; + continue; + }, // Special case instructions to handle comptime control flow. .@"break" => { if (block.is_comptime) { - return inst; // same as break_inline + break inst; // same as break_inline } else { - return sema.zirBreak(block, inst); + break sema.zirBreak(block, inst); } }, - .break_inline => return inst, + .break_inline => break inst, .repeat => { if (block.is_comptime) { // Send comptime control flow back to the beginning of this block. const src: LazySrcLoc = .{ .node_offset = datas[inst].node }; try sema.emitBackwardBranch(block, src); + if (wip_captures.scope.captures.count() != orig_captures) { + try wip_captures.reset(parent_capture_scope); + block.wip_capture_scope = wip_captures.scope; + orig_captures = 0; + } i = 0; continue; } else { const src_node = sema.code.instructions.items(.data)[inst].node; const src: LazySrcLoc = .{ .node_offset = src_node }; try sema.requireRuntimeBlock(block, src); - return always_noreturn; + break always_noreturn; } }, .repeat_inline => { // Send comptime control flow back to the beginning of this block. const src: LazySrcLoc = .{ .node_offset = datas[inst].node }; try sema.emitBackwardBranch(block, src); + if (wip_captures.scope.captures.count() != orig_captures) { + try wip_captures.reset(parent_capture_scope); + block.wip_capture_scope = wip_captures.scope; + orig_captures = 0; + } i = 0; continue; }, @@ -545,7 +578,7 @@ pub fn analyzeBody( if (inst == break_data.block_inst) { break :blk sema.resolveInst(break_data.operand); } else { - return break_inst; + break break_inst; } }, .block => blk: { @@ -559,7 +592,7 @@ pub fn analyzeBody( if (inst == break_data.block_inst) { break :blk sema.resolveInst(break_data.operand); } else { - return break_inst; + break break_inst; } }, .block_inline => blk: { @@ -572,11 +605,11 @@ pub fn analyzeBody( if (inst == break_data.block_inst) { break :blk sema.resolveInst(break_data.operand); } else { - return break_inst; + break break_inst; } }, .condbr => blk: { - if (!block.is_comptime) return sema.zirCondbr(block, inst); + if (!block.is_comptime) break sema.zirCondbr(block, inst); // Same as condbr_inline. TODO https://github.com/ziglang/zig/issues/8220 const inst_data = datas[inst].pl_node; const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node }; @@ -590,7 +623,7 @@ pub fn analyzeBody( if (inst == break_data.block_inst) { break :blk sema.resolveInst(break_data.operand); } else { - return break_inst; + break break_inst; } }, .condbr_inline => blk: { @@ -606,15 +639,22 @@ pub fn analyzeBody( if (inst == break_data.block_inst) { break :blk sema.resolveInst(break_data.operand); } else { - return break_inst; + break break_inst; } }, }; if (sema.typeOf(air_inst).isNoReturn()) - return always_noreturn; + break always_noreturn; try map.put(sema.gpa, inst, air_inst); i += 1; + } else unreachable; + + if (!wip_captures.finalized) { + try wip_captures.finalize(); + block.wip_capture_scope = parent_capture_scope; } + + return result; } fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -626,6 +666,7 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr .struct_decl => return sema.zirStructDecl( block, extended, inst), .enum_decl => return sema.zirEnumDecl( block, extended), .union_decl => return sema.zirUnionDecl( block, extended, inst), + .opaque_decl => return sema.zirOpaqueDecl( block, extended, inst), .ret_ptr => return sema.zirRetPtr( block, extended), .ret_type => return sema.zirRetType( block, extended), .this => return sema.zirThis( block, extended), @@ -1011,7 +1052,6 @@ fn zirStructDecl( } fn createTypeName(sema: *Sema, block: *Scope.Block, name_strategy: Zir.Inst.NameStrategy) ![:0]u8 { - _ = block; switch (name_strategy) { .anon => { // It would be neat to have "struct:line:column" but this name has @@ -1020,14 +1060,14 @@ fn createTypeName(sema: *Sema, block: *Scope.Block, name_strategy: Zir.Inst.Name // semantically analyzed. const name_index = sema.mod.getNextAnonNameIndex(); return std.fmt.allocPrintZ(sema.gpa, "{s}__anon_{d}", .{ - sema.owner_decl.name, name_index, + block.src_decl.name, name_index, }); }, - .parent => return sema.gpa.dupeZ(u8, mem.spanZ(sema.owner_decl.name)), + .parent => return sema.gpa.dupeZ(u8, mem.spanZ(block.src_decl.name)), .func => { const name_index = sema.mod.getNextAnonNameIndex(); const name = try std.fmt.allocPrintZ(sema.gpa, "{s}__anon_{d}", .{ - sema.owner_decl.name, name_index, + block.src_decl.name, name_index, }); log.warn("TODO: handle NameStrategy.func correctly instead of using anon name '{s}'", .{ name, @@ -1083,17 +1123,6 @@ fn zirEnumDecl( var new_decl_arena = std.heap.ArenaAllocator.init(gpa); errdefer new_decl_arena.deinit(); - const tag_ty = blk: { - if (tag_type_ref != .none) { - // TODO better source location - // TODO (needs AstGen fix too) move this eval to the block so it gets allocated - // in the new decl arena. - break :blk try sema.resolveType(block, src, tag_type_ref); - } - const bits = std.math.log2_int_ceil(usize, fields_len); - break :blk try Type.Tag.int_unsigned.create(&new_decl_arena.allocator, bits); - }; - const enum_obj = try new_decl_arena.allocator.create(Module.EnumFull); const enum_ty_payload = try new_decl_arena.allocator.create(Type.Payload.EnumFull); enum_ty_payload.* = .{ @@ -1112,7 +1141,7 @@ fn zirEnumDecl( enum_obj.* = .{ .owner_decl = new_decl, - .tag_ty = tag_ty, + .tag_ty = Type.initTag(.@"null"), .fields = .{}, .values = .{}, .node_offset = src.node_offset, @@ -1140,16 +1169,6 @@ fn zirEnumDecl( const body_end = extra_index; extra_index += bit_bags_count; - try enum_obj.fields.ensureTotalCapacity(&new_decl_arena.allocator, fields_len); - const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { - if (bag != 0) break true; - } else false; - if (any_values) { - try enum_obj.values.ensureTotalCapacityContext(&new_decl_arena.allocator, fields_len, .{ - .ty = tag_ty, - }); - } - { // We create a block for the field type instructions because they // may need to reference Decls from inside the enum namespace. @@ -1172,10 +1191,14 @@ fn zirEnumDecl( sema.func = null; defer sema.func = prev_func; + var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, new_decl.src_scope); + defer wip_captures.deinit(); + var enum_block: Scope.Block = .{ .parent = null, .sema = sema, .src_decl = new_decl, + .wip_capture_scope = wip_captures.scope, .instructions = .{}, .inlining = null, .is_comptime = true, @@ -1185,7 +1208,30 @@ fn zirEnumDecl( if (body.len != 0) { _ = try sema.analyzeBody(&enum_block, body); } + + try wip_captures.finalize(); + + const tag_ty = blk: { + if (tag_type_ref != .none) { + // TODO better source location + break :blk try sema.resolveType(block, src, tag_type_ref); + } + const bits = std.math.log2_int_ceil(usize, fields_len); + break :blk try Type.Tag.int_unsigned.create(&new_decl_arena.allocator, bits); + }; + enum_obj.tag_ty = tag_ty; } + + try enum_obj.fields.ensureTotalCapacity(&new_decl_arena.allocator, fields_len); + const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { + if (bag != 0) break true; + } else false; + if (any_values) { + try enum_obj.values.ensureTotalCapacityContext(&new_decl_arena.allocator, fields_len, .{ + .ty = enum_obj.tag_ty, + }); + } + var bit_bag_index: usize = body_end; var cur_bit_bag: u32 = undefined; var field_i: u32 = 0; @@ -1224,10 +1270,10 @@ fn zirEnumDecl( // that points to this default value expression rather than the struct. // But only resolve the source location if we need to emit a compile error. const tag_val = (try sema.resolveInstConst(block, src, tag_val_ref)).val; - enum_obj.values.putAssumeCapacityNoClobberContext(tag_val, {}, .{ .ty = tag_ty }); + enum_obj.values.putAssumeCapacityNoClobberContext(tag_val, {}, .{ .ty = enum_obj.tag_ty }); } else if (any_values) { const tag_val = try Value.Tag.int_u64.create(&new_decl_arena.allocator, field_i); - enum_obj.values.putAssumeCapacityNoClobberContext(tag_val, {}, .{ .ty = tag_ty }); + enum_obj.values.putAssumeCapacityNoClobberContext(tag_val, {}, .{ .ty = enum_obj.tag_ty }); } } @@ -1305,20 +1351,14 @@ fn zirUnionDecl( fn zirOpaqueDecl( sema: *Sema, block: *Scope.Block, + extended: Zir.Inst.Extended.InstData, inst: Zir.Inst.Index, - name_strategy: Zir.Inst.NameStrategy, ) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); - - _ = name_strategy; - _ = inst_data; - _ = src; - _ = extra; + _ = extended; + _ = inst; return sema.mod.fail(&block.base, sema.src, "TODO implement zirOpaqueDecl", .{}); } @@ -2160,6 +2200,7 @@ fn zirCImport(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Com .parent = parent_block, .sema = sema, .src_decl = parent_block.src_decl, + .wip_capture_scope = parent_block.wip_capture_scope, .instructions = .{}, .inlining = parent_block.inlining, .is_comptime = parent_block.is_comptime, @@ -2214,7 +2255,7 @@ fn zirCImport(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Com try sema.mod.semaFile(result.file); const file_root_decl = result.file.root_decl.?; try sema.mod.declareDeclDependency(sema.owner_decl, file_root_decl); - return sema.addType(file_root_decl.ty); + return sema.addConstant(file_root_decl.ty, file_root_decl.val); } fn zirSuspendBlock(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -2259,6 +2300,7 @@ fn zirBlock( .parent = parent_block, .sema = sema, .src_decl = parent_block.src_decl, + .wip_capture_scope = parent_block.wip_capture_scope, .instructions = .{}, .label = &label, .inlining = parent_block.inlining, @@ -2866,10 +2908,14 @@ fn analyzeCall( sema.func = module_fn; defer sema.func = parent_func; + var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, module_fn.owner_decl.src_scope); + defer wip_captures.deinit(); + var child_block: Scope.Block = .{ .parent = null, .sema = sema, .src_decl = module_fn.owner_decl, + .wip_capture_scope = wip_captures.scope, .instructions = .{}, .label = null, .inlining = &inlining, @@ -3034,6 +3080,9 @@ fn analyzeCall( break :res2 result; }; + + try wip_captures.finalize(); + break :res res2; } else if (func_ty_info.is_generic) res: { const func_val = try sema.resolveConstValue(block, func_src, func); @@ -3116,7 +3165,8 @@ fn analyzeCall( try namespace.anon_decls.ensureUnusedCapacity(gpa, 1); // Create a Decl for the new function. - const new_decl = try mod.allocateNewDecl(namespace, module_fn.owner_decl.src_node); + const src_decl = namespace.getDecl(); + const new_decl = try mod.allocateNewDecl(namespace, module_fn.owner_decl.src_node, src_decl.src_scope); // TODO better names for generic function instantiations const name_index = mod.getNextAnonNameIndex(); new_decl.name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{ @@ -3147,6 +3197,7 @@ fn analyzeCall( .mod = mod, .gpa = gpa, .arena = sema.arena, + .perm_arena = &new_decl_arena.allocator, .code = fn_zir, .owner_decl = new_decl, .namespace = namespace, @@ -3159,10 +3210,14 @@ fn analyzeCall( }; defer child_sema.deinit(); + var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, new_decl.src_scope); + defer wip_captures.deinit(); + var child_block: Scope.Block = .{ .parent = null, .sema = &child_sema, .src_decl = new_decl, + .wip_capture_scope = wip_captures.scope, .instructions = .{}, .inlining = null, .is_comptime = true, @@ -3250,6 +3305,8 @@ fn analyzeCall( arg_i += 1; } + try wip_captures.finalize(); + // Populate the Decl ty/val with the function and its type. new_decl.ty = try child_sema.typeOf(new_func_inst).copy(&new_decl_arena.allocator); new_decl.val = try Value.Tag.function.create(&new_decl_arena.allocator, new_func); @@ -5164,6 +5221,7 @@ fn analyzeSwitch( .parent = block, .sema = sema, .src_decl = block.src_decl, + .wip_capture_scope = block.wip_capture_scope, .instructions = .{}, .label = &label, .inlining = block.inlining, @@ -5268,12 +5326,19 @@ fn analyzeSwitch( const body = sema.code.extra[extra_index..][0..body_len]; extra_index += body_len; + var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope); + defer wip_captures.deinit(); + case_block.instructions.shrinkRetainingCapacity(0); + case_block.wip_capture_scope = wip_captures.scope; + const item = sema.resolveInst(item_ref); // `item` is already guaranteed to be constant known. _ = try sema.analyzeBody(&case_block, body); + try wip_captures.finalize(); + try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); cases_extra.appendAssumeCapacity(1); // items_len cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len)); @@ -5301,6 +5366,7 @@ fn analyzeSwitch( extra_index += items_len; case_block.instructions.shrinkRetainingCapacity(0); + case_block.wip_capture_scope = child_block.wip_capture_scope; var any_ok: Air.Inst.Ref = .none; @@ -5379,11 +5445,18 @@ fn analyzeSwitch( var cond_body = case_block.instructions.toOwnedSlice(gpa); defer gpa.free(cond_body); + var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope); + defer wip_captures.deinit(); + case_block.instructions.shrinkRetainingCapacity(0); + case_block.wip_capture_scope = wip_captures.scope; + const body = sema.code.extra[extra_index..][0..body_len]; extra_index += body_len; _ = try sema.analyzeBody(&case_block, body); + try wip_captures.finalize(); + if (is_first) { is_first = false; first_else_body = cond_body; @@ -5409,9 +5482,16 @@ fn analyzeSwitch( var final_else_body: []const Air.Inst.Index = &.{}; if (special.body.len != 0) { + var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope); + defer wip_captures.deinit(); + case_block.instructions.shrinkRetainingCapacity(0); + case_block.wip_capture_scope = wip_captures.scope; + _ = try sema.analyzeBody(&case_block, special.body); + try wip_captures.finalize(); + if (is_first) { final_else_body = case_block.instructions.items; } else { @@ -5693,7 +5773,7 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErro try mod.semaFile(result.file); const file_root_decl = result.file.root_decl.?; try sema.mod.declareDeclDependency(sema.owner_decl, file_root_decl); - return sema.addType(file_root_decl.ty); + return sema.addConstant(file_root_decl.ty, file_root_decl.val); } fn zirRetErrValueCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -6536,8 +6616,45 @@ fn zirThis( block: *Scope.Block, extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { + const this_decl = block.base.namespace().getDecl(); const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; - return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirThis", .{}); + return sema.analyzeDeclVal(block, src, this_decl); +} + +fn zirClosureCapture( + sema: *Sema, + block: *Scope.Block, + inst: Zir.Inst.Index, +) CompileError!void { + // TODO: Compile error when closed over values are modified + const inst_data = sema.code.instructions.items(.data)[inst].un_tok; + const tv = try sema.resolveInstConst(block, inst_data.src(), inst_data.operand); + try block.wip_capture_scope.captures.putNoClobber(sema.gpa, inst, .{ + .ty = try tv.ty.copy(sema.perm_arena), + .val = try tv.val.copy(sema.perm_arena), + }); +} + +fn zirClosureGet( + sema: *Sema, + block: *Scope.Block, + inst: Zir.Inst.Index, +) CompileError!Air.Inst.Ref { + // TODO CLOSURE: Test this with inline functions + const inst_data = sema.code.instructions.items(.data)[inst].inst_node; + var scope: *CaptureScope = block.src_decl.src_scope.?; + // Note: The target closure must be in this scope list. + // If it's not here, the zir is invalid, or the list is broken. + const tv = while (true) { + // Note: We don't need to add a dependency here, because + // decls always depend on their lexical parents. + if (scope.captures.getPtr(inst_data.inst)) |tv| { + break tv; + } + scope = scope.parent.?; + } else unreachable; + + return sema.addConstant(tv.ty, tv.val); } fn zirRetAddr( @@ -8615,6 +8732,7 @@ fn addSafetyCheck( var fail_block: Scope.Block = .{ .parent = parent_block, .sema = sema, + .wip_capture_scope = parent_block.wip_capture_scope, .src_decl = parent_block.src_decl, .instructions = .{}, .inlining = parent_block.inlining, @@ -8714,7 +8832,7 @@ fn safetyPanic( block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId, -) !Zir.Inst.Index { +) CompileError!Zir.Inst.Index { const msg = switch (panic_id) { .unreach => "reached unreachable code", .unwrap_null => "attempt to use null value", @@ -10666,6 +10784,10 @@ pub fn resolveDeclFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: sema.namespace = &struct_obj.namespace; defer sema.namespace = prev_namespace; + const old_src = block.src_decl; + defer block.src_decl = old_src; + block.src_decl = struct_obj.owner_decl; + struct_obj.status = .field_types_wip; try sema.analyzeStructFields(block, struct_obj); struct_obj.status = .have_field_types; @@ -10684,6 +10806,10 @@ pub fn resolveDeclFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: sema.namespace = &union_obj.namespace; defer sema.namespace = prev_namespace; + const old_src = block.src_decl; + defer block.src_decl = old_src; + block.src_decl = union_obj.owner_decl; + union_obj.status = .field_types_wip; try sema.analyzeUnionFields(block, union_obj); union_obj.status = .have_field_types; @@ -10885,9 +11011,11 @@ fn analyzeUnionFields( const src: LazySrcLoc = .{ .node_offset = union_obj.node_offset }; extra_index += @boolToInt(small.has_src_node); - if (small.has_tag_type) { + const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: { + const ty_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); extra_index += 1; - } + break :blk ty_ref; + } else .none; const body_len = if (small.has_body_len) blk: { const body_len = zir.extra[extra_index]; @@ -10996,6 +11124,7 @@ fn analyzeUnionFields( } // TODO resolve the union tag_type_ref + _ = tag_type_ref; } fn getBuiltin( -- cgit v1.2.3