Commit b27d052676

Andrew Kelley <andrew@ziglang.org>
2021-04-01 06:36:32
stage2: finish source location reworkings in the branch
* remove the LazySrcLoc.todo tag * finish updating Sema and AstGen, remove the last of the `@panic("TODO")`.
1 parent e8143f6
lib/std/zig/ast.zig
@@ -1252,6 +1252,7 @@ pub const Tree = struct {
         buffer[0] = data.lhs;
         const params = if (data.lhs == 0) buffer[0..0] else buffer[0..1];
         return tree.fullFnProto(.{
+            .proto_node = node,
             .fn_token = tree.nodes.items(.main_token)[node],
             .return_type = data.rhs,
             .params = params,
@@ -1267,6 +1268,7 @@ pub const Tree = struct {
         const params_range = tree.extraData(data.lhs, Node.SubRange);
         const params = tree.extra_data[params_range.start..params_range.end];
         return tree.fullFnProto(.{
+            .proto_node = node,
             .fn_token = tree.nodes.items(.main_token)[node],
             .return_type = data.rhs,
             .params = params,
@@ -1283,6 +1285,7 @@ pub const Tree = struct {
         buffer[0] = extra.param;
         const params = if (extra.param == 0) buffer[0..0] else buffer[0..1];
         return tree.fullFnProto(.{
+            .proto_node = node,
             .fn_token = tree.nodes.items(.main_token)[node],
             .return_type = data.rhs,
             .params = params,
@@ -1298,6 +1301,7 @@ pub const Tree = struct {
         const extra = tree.extraData(data.lhs, Node.FnProto);
         const params = tree.extra_data[extra.params_start..extra.params_end];
         return tree.fullFnProto(.{
+            .proto_node = node,
             .fn_token = tree.nodes.items(.main_token)[node],
             .return_type = data.rhs,
             .params = params,
@@ -2120,6 +2124,7 @@ pub const full = struct {
         ast: Ast,
 
         pub const Ast = struct {
+            proto_node: Node.Index,
             fn_token: TokenIndex,
             return_type: Node.Index,
             params: []const Node.Index,
src/AstGen.zig
@@ -133,10 +133,6 @@ pub const ResultLoc = union(enum) {
     /// The result instruction from the expression must be ignored.
     /// Always an instruction with tag `alloc_inferred`.
     inferred_ptr: zir.Inst.Ref,
-    /// The expression must store its result into this pointer, which is a typed pointer that
-    /// has been bitcasted to whatever the expression's type is.
-    /// The result instruction from the expression must be ignored.
-    bitcasted_ptr: zir.Inst.Ref,
     /// There is a pointer for the expression to store its result into, however, its type
     /// is inferred based on peer type resolution for a `zir.Inst.Block`.
     /// The result instruction from the expression must be ignored.
@@ -172,7 +168,7 @@ pub const ResultLoc = union(enum) {
                 .tag = .break_void,
                 .elide_store_to_block_ptr_instructions = false,
             },
-            .inferred_ptr, .bitcasted_ptr, .block_ptr => {
+            .inferred_ptr, .block_ptr => {
                 if (block_scope.rvalue_rl_count == block_scope.break_count) {
                     // Neither prong of the if consumed the result location, so we can
                     // use break instructions to create an rvalue.
@@ -388,7 +384,7 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!zir.Ins
 }
 
 /// Turn Zig AST into untyped ZIR istructions.
-/// When `rl` is discard, ptr, inferred_ptr, bitcasted_ptr, or inferred_ptr, the
+/// When `rl` is discard, ptr, inferred_ptr, or inferred_ptr, the
 /// result instruction can be used to inspect whether it is isNoReturn() but that is it,
 /// it must otherwise not be used.
 pub fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref {
@@ -1155,7 +1151,6 @@ fn blockExprStmts(
                         .asm_volatile,
                         .bit_and,
                         .bitcast,
-                        .bitcast_ref,
                         .bitcast_result_ptr,
                         .bit_or,
                         .block,
@@ -1804,7 +1799,7 @@ fn orelseCatchExpr(
     // TODO handle catch
     const operand_rl: ResultLoc = switch (block_scope.break_result_loc) {
         .ref => .ref,
-        .discard, .none, .block_ptr, .inferred_ptr, .bitcasted_ptr => .none,
+        .discard, .none, .block_ptr, .inferred_ptr => .none,
         .ty => |elem_ty| blk: {
             const wrapped_ty = try block_scope.addUnNode(.optional_type, elem_ty, node);
             break :blk .{ .ty = wrapped_ty };
@@ -3519,7 +3514,6 @@ fn as(
     gz: *GenZir,
     scope: *Scope,
     rl: ResultLoc,
-    builtin_token: ast.TokenIndex,
     node: ast.Node.Index,
     lhs: ast.Node.Index,
     rhs: ast.Node.Index,
@@ -3538,13 +3532,9 @@ fn as(
             return asRlPtr(gz, scope, rl, block_scope.rl_ptr, rhs, dest_type);
         },
 
-        .bitcasted_ptr => |bitcasted_ptr| {
-            // TODO here we should be able to resolve the inference; we now have a type for the result.
-            return gz.astgen.mod.failTok(scope, builtin_token, "TODO implement @as with result location @bitCast", .{});
-        },
         .inferred_ptr => |result_alloc| {
             // TODO here we should be able to resolve the inference; we now have a type for the result.
-            return gz.astgen.mod.failTok(scope, builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
+            return gz.astgen.mod.failNode(scope, node, "TODO implement @as with inferred-type result location pointer", .{});
         },
     }
 }
@@ -3599,47 +3589,32 @@ fn bitCast(
     gz: *GenZir,
     scope: *Scope,
     rl: ResultLoc,
-    builtin_token: ast.TokenIndex,
     node: ast.Node.Index,
     lhs: ast.Node.Index,
     rhs: ast.Node.Index,
 ) InnerError!zir.Inst.Ref {
-    if (true) @panic("TODO update for zir-memory-layout");
+    const mod = gz.astgen.mod;
     const dest_type = try typeExpr(gz, scope, lhs);
     switch (rl) {
-        .none => {
+        .none, .discard, .ty => {
             const operand = try expr(gz, scope, .none, rhs);
-            return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
-        },
-        .discard => {
-            const operand = try expr(gz, scope, .none, rhs);
-            const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
-            _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
-            return result;
-        },
-        .ref => {
-            const operand = try expr(gz, scope, .ref, rhs);
-            const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand);
-            return result;
-        },
-        .ty => |result_ty| {
-            const result = try expr(gz, scope, .none, rhs);
-            const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result);
-            return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted);
+            const result = try gz.addPlNode(.bitcast, node, zir.Inst.Bin{
+                .lhs = dest_type,
+                .rhs = operand,
+            });
+            return rvalue(gz, scope, rl, result, node);
         },
+        .ref => unreachable, // `@bitCast` is not allowed as an r-value.
         .ptr => |result_ptr| {
-            const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr);
-            return expr(gz, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, rhs);
-        },
-        .bitcasted_ptr => |bitcasted_ptr| {
-            return mod.failTok(scope, builtin_token, "TODO implement @bitCast with result location another @bitCast", .{});
+            const casted_result_ptr = try gz.addUnNode(.bitcast_result_ptr, result_ptr, node);
+            return expr(gz, scope, .{ .ptr = casted_result_ptr }, rhs);
         },
         .block_ptr => |block_ptr| {
-            return mod.failTok(scope, builtin_token, "TODO implement @bitCast with result location inferred peer types", .{});
+            return mod.failNode(scope, node, "TODO implement @bitCast with result location inferred peer types", .{});
         },
         .inferred_ptr => |result_alloc| {
             // TODO here we should be able to resolve the inference; we now have a type for the result.
-            return mod.failTok(scope, builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{});
+            return mod.failNode(scope, node, "TODO implement @bitCast with inferred-type result location pointer", .{});
         },
     }
 }
@@ -3648,12 +3623,11 @@ fn typeOf(
     gz: *GenZir,
     scope: *Scope,
     rl: ResultLoc,
-    builtin_token: ast.TokenIndex,
     node: ast.Node.Index,
     params: []const ast.Node.Index,
 ) InnerError!zir.Inst.Ref {
     if (params.len < 1) {
-        return gz.astgen.mod.failTok(scope, builtin_token, "expected at least 1 argument, found 0", .{});
+        return gz.astgen.mod.failNode(scope, node, "expected at least 1 argument, found 0", .{});
     }
     if (params.len == 1) {
         const result = try gz.addUnNode(.typeof, try expr(gz, scope, .none, params[0]), node);
@@ -3693,14 +3667,14 @@ fn builtinCall(
     // Also, some builtins have a variable number of parameters.
 
     const info = BuiltinFn.list.get(builtin_name) orelse {
-        return mod.failTok(scope, builtin_token, "invalid builtin function: '{s}'", .{
+        return mod.failNode(scope, node, "invalid builtin function: '{s}'", .{
             builtin_name,
         });
     };
     if (info.param_count) |expected| {
         if (expected != params.len) {
             const s = if (expected == 1) "" else "s";
-            return mod.failTok(scope, builtin_token, "expected {d} parameter{s}, found {d}", .{
+            return mod.failNode(scope, node, "expected {d} parameter{s}, found {d}", .{
                 expected, s, params.len,
             });
         }
@@ -3788,9 +3762,9 @@ fn builtinCall(
             });
             return rvalue(gz, scope, rl, result, node);
         },
-        .as => return as(gz, scope, rl, builtin_token, node, params[0], params[1]),
-        .bit_cast => return bitCast(gz, scope, rl, builtin_token, node, params[0], params[1]),
-        .TypeOf => return typeOf(gz, scope, rl, builtin_token, node, params),
+        .as => return as(gz, scope, rl, node, params[0], params[1]),
+        .bit_cast => return bitCast(gz, scope, rl, node, params[0], params[1]),
+        .TypeOf => return typeOf(gz, scope, rl, node, params),
 
         .add_with_overflow,
         .align_cast,
@@ -3875,7 +3849,7 @@ fn builtinCall(
         .type_info,
         .type_name,
         .union_init,
-        => return mod.failTok(scope, builtin_token, "TODO: implement builtin function {s}", .{
+        => return mod.failNode(scope, node, "TODO: implement builtin function {s}", .{
             builtin_name,
         }),
 
@@ -3884,7 +3858,7 @@ fn builtinCall(
         .Frame,
         .frame_address,
         .frame_size,
-        => return mod.failTok(scope, builtin_token, "async and related features are not yet supported", .{}),
+        => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
     }
 }
 
@@ -4286,9 +4260,6 @@ fn rvalue(
             });
             return result;
         },
-        .bitcasted_ptr => |bitcasted_ptr| {
-            return gz.astgen.mod.failNode(scope, src_node, "TODO implement rvalue .bitcasted_ptr", .{});
-        },
         .inferred_ptr => |alloc| {
             _ = try gz.addBin(.store_to_inferred_ptr, alloc, result);
             return result;
src/main.zig
@@ -1487,7 +1487,7 @@ fn buildOutputType(
                 for (diags.arch.?.allCpuModels()) |cpu| {
                     help_text.writer().print(" {s}\n", .{cpu.name}) catch break :help;
                 }
-                std.log.info("Available CPUs for architecture '{s}': {s}", .{
+                std.log.info("Available CPUs for architecture '{s}':\n{s}", .{
                     @tagName(diags.arch.?), help_text.items,
                 });
             }
@@ -1499,7 +1499,7 @@ fn buildOutputType(
                 for (diags.arch.?.allFeaturesList()) |feature| {
                     help_text.writer().print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help;
                 }
-                std.log.info("Available CPU features for architecture '{s}': {s}", .{
+                std.log.info("Available CPU features for architecture '{s}':\n{s}", .{
                     @tagName(diags.arch.?), help_text.items,
                 });
             }
src/Module.zig
@@ -1016,11 +1016,6 @@ pub const Scope = struct {
                     gz.break_result_loc = .{ .block_ptr = gz };
                 },
 
-                .bitcasted_ptr => |ptr| {
-                    gz.rl_ptr = ptr;
-                    gz.break_result_loc = .{ .block_ptr = gz };
-                },
-
                 .block_ptr => |parent_block_scope| {
                     gz.rl_ty_inst = parent_block_scope.rl_ty_inst;
                     gz.rl_ptr = parent_block_scope.rl_ptr;
@@ -1052,10 +1047,12 @@ pub const Scope = struct {
         }
 
         pub fn addFnTypeCc(gz: *GenZir, tag: zir.Inst.Tag, args: struct {
+            src_node: ast.Node.Index,
             param_types: []const zir.Inst.Ref,
             ret_ty: zir.Inst.Ref,
             cc: zir.Inst.Ref,
         }) !zir.Inst.Ref {
+            assert(args.src_node != 0);
             assert(args.ret_ty != .none);
             assert(args.cc != .none);
             const gpa = gz.astgen.mod.gpa;
@@ -1065,6 +1062,7 @@ pub const Scope = struct {
                 @typeInfo(zir.Inst.FnTypeCc).Struct.fields.len + args.param_types.len);
 
             const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.FnTypeCc{
+                .return_type = args.ret_ty,
                 .cc = args.cc,
                 .param_types_len = @intCast(u32, args.param_types.len),
             });
@@ -1073,8 +1071,8 @@ pub const Scope = struct {
             const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
             gz.astgen.instructions.appendAssumeCapacity(.{
                 .tag = tag,
-                .data = .{ .fn_type = .{
-                    .return_type = args.ret_ty,
+                .data = .{ .pl_node = .{
+                    .src_node = gz.astgen.decl.nodeIndexToRelative(args.src_node),
                     .payload_index = payload_index,
                 } },
             });
@@ -1082,29 +1080,30 @@ pub const Scope = struct {
             return gz.astgen.indexToRef(new_index);
         }
 
-        pub fn addFnType(
-            gz: *GenZir,
-            tag: zir.Inst.Tag,
+        pub fn addFnType(gz: *GenZir, tag: zir.Inst.Tag, args: struct {
+            src_node: ast.Node.Index,
             ret_ty: zir.Inst.Ref,
             param_types: []const zir.Inst.Ref,
-        ) !zir.Inst.Ref {
-            assert(ret_ty != .none);
+        }) !zir.Inst.Ref {
+            assert(args.src_node != 0);
+            assert(args.ret_ty != .none);
             const gpa = gz.astgen.mod.gpa;
             try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
             try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
             try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
-                @typeInfo(zir.Inst.FnType).Struct.fields.len + param_types.len);
+                @typeInfo(zir.Inst.FnType).Struct.fields.len + args.param_types.len);
 
             const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.FnType{
-                .param_types_len = @intCast(u32, param_types.len),
+                .return_type = args.ret_ty,
+                .param_types_len = @intCast(u32, args.param_types.len),
             });
-            gz.astgen.appendRefsAssumeCapacity(param_types);
+            gz.astgen.appendRefsAssumeCapacity(args.param_types);
 
             const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
             gz.astgen.instructions.appendAssumeCapacity(.{
                 .tag = tag,
-                .data = .{ .fn_type = .{
-                    .return_type = ret_ty,
+                .data = .{ .pl_node = .{
+                    .src_node = gz.astgen.decl.nodeIndexToRelative(args.src_node),
                     .payload_index = payload_index,
                 } },
             });
@@ -1513,7 +1512,6 @@ pub const SrcLoc = struct {
     pub fn fileScope(src_loc: SrcLoc) *Scope.File {
         return switch (src_loc.lazy) {
             .unneeded => unreachable,
-            .todo => unreachable,
 
             .byte_abs,
             .token_abs,
@@ -1542,6 +1540,8 @@ pub const SrcLoc = struct {
             .node_offset_switch_operand,
             .node_offset_switch_special_prong,
             .node_offset_switch_range,
+            .node_offset_fn_type_cc,
+            .node_offset_fn_type_ret_ty,
             => src_loc.container.decl.container.file_scope,
         };
     }
@@ -1549,7 +1549,6 @@ pub const SrcLoc = struct {
     pub fn byteOffset(src_loc: SrcLoc) !u32 {
         switch (src_loc.lazy) {
             .unneeded => unreachable,
-            .todo => unreachable,
 
             .byte_abs => |byte_index| return byte_index,
 
@@ -1676,6 +1675,8 @@ pub const SrcLoc = struct {
             .node_offset_switch_operand => @panic("TODO"),
             .node_offset_switch_special_prong => @panic("TODO"),
             .node_offset_switch_range => @panic("TODO"),
+            .node_offset_fn_type_cc => @panic("TODO"),
+            .node_offset_fn_type_ret_ty => @panic("TODO"),
         }
     }
 };
@@ -1695,11 +1696,6 @@ pub const LazySrcLoc = union(enum) {
     /// look into using reverse-continue with a memory watchpoint to see where the
     /// value is being set to this tag.
     unneeded,
-    /// Same as `unneeded`, except the code setting up this tag knew that actually
-    /// the source location was needed, and I wanted to get other stuff compiling
-    /// and working before coming back to messing with source locations.
-    /// TODO delete this tag before merging the zir-memory-layout branch.
-    todo,
     /// The source location points to a byte offset within a source file,
     /// offset from 0. The source file is determined contextually.
     /// Inside a `SrcLoc`, the `file_scope` union field will be active.
@@ -1824,12 +1820,23 @@ pub const LazySrcLoc = union(enum) {
     /// range nodes. The error applies to all of them.
     /// The Decl is determined contextually.
     node_offset_switch_range: i32,
+    /// The source location points to the calling convention of a function type
+    /// expression, found by taking this AST node index offset from the containing
+    /// Decl AST node, which points to a function type AST node. Next, nagivate to
+    /// the calling convention node.
+    /// The Decl is determined contextually.
+    node_offset_fn_type_cc: i32,
+    /// The source location points to the return type of a function type
+    /// expression, found by taking this AST node index offset from the containing
+    /// Decl AST node, which points to a function type AST node. Next, nagivate to
+    /// the return type node.
+    /// The Decl is determined contextually.
+    node_offset_fn_type_ret_ty: i32,
 
     /// Upgrade to a `SrcLoc` based on the `Decl` or file in the provided scope.
     pub fn toSrcLoc(lazy: LazySrcLoc, scope: *Scope) SrcLoc {
         return switch (lazy) {
             .unneeded,
-            .todo,
             .byte_abs,
             .token_abs,
             .node_abs,
@@ -1860,6 +1867,8 @@ pub const LazySrcLoc = union(enum) {
             .node_offset_switch_operand,
             .node_offset_switch_special_prong,
             .node_offset_switch_range,
+            .node_offset_fn_type_cc,
+            .node_offset_fn_type_ret_ty,
             => .{
                 .container = .{ .decl = scope.srcDecl().? },
                 .lazy = lazy,
@@ -1871,7 +1880,6 @@ pub const LazySrcLoc = union(enum) {
     pub fn toSrcLocWithDecl(lazy: LazySrcLoc, decl: *Decl) SrcLoc {
         return switch (lazy) {
             .unneeded,
-            .todo,
             .byte_abs,
             .token_abs,
             .node_abs,
@@ -1902,6 +1910,8 @@ pub const LazySrcLoc = union(enum) {
             .node_offset_switch_operand,
             .node_offset_switch_special_prong,
             .node_offset_switch_range,
+            .node_offset_fn_type_cc,
+            .node_offset_fn_type_ret_ty,
             => .{
                 .container = .{ .decl = decl },
                 .lazy = lazy,
@@ -2340,13 +2350,18 @@ fn astgenAndSemaFn(
     const fn_type_inst: zir.Inst.Ref = if (cc != .none) fn_type: {
         const tag: zir.Inst.Tag = if (is_var_args) .fn_type_cc_var_args else .fn_type_cc;
         break :fn_type try fn_type_scope.addFnTypeCc(tag, .{
+            .src_node = fn_proto.ast.proto_node,
             .ret_ty = return_type_inst,
             .param_types = param_types,
             .cc = cc,
         });
     } else fn_type: {
         const tag: zir.Inst.Tag = if (is_var_args) .fn_type_var_args else .fn_type;
-        break :fn_type try fn_type_scope.addFnType(tag, return_type_inst, param_types);
+        break :fn_type try fn_type_scope.addFnType(tag, .{
+            .src_node = fn_proto.ast.proto_node,
+            .ret_ty = return_type_inst,
+            .param_types = param_types,
+        });
     };
     _ = try fn_type_scope.addBreak(.break_inline, 0, fn_type_inst);
 
src/Sema.zig
@@ -148,7 +148,6 @@ pub fn analyzeBody(
             .bit_not => try sema.zirBitNot(block, inst),
             .bit_or => try sema.zirBitwise(block, inst, .bit_or),
             .bitcast => try sema.zirBitcast(block, inst),
-            .bitcast_ref => try sema.zirBitcastRef(block, inst),
             .bitcast_result_ptr => try sema.zirBitcastResultPtr(block, inst),
             .block => try sema.zirBlock(block, inst),
             .bool_not => try sema.zirBoolNot(block, inst),
@@ -498,12 +497,6 @@ fn zirConst(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*
     return sema.mod.constInst(sema.arena, .unneeded, typed_value_copy);
 }
 
-fn zirBitcastRef(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
-    const tracy = trace(@src());
-    defer tracy.end();
-    return sema.mod.fail(&block.base, sema.src, "TODO implement zir_sema.zirBitcastRef", .{});
-}
-
 fn zirBitcastResultPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
     const tracy = trace(@src());
     defer tracy.end();
@@ -942,7 +935,7 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerE
     try child_block.instructions.append(sema.gpa, &loop_inst.base);
     loop_inst.body = .{ .instructions = try sema.arena.dupe(*Inst, loop_block.instructions.items) };
 
-    return sema.analyzeBlockBody(parent_block, &child_block, merges);
+    return sema.analyzeBlockBody(parent_block, src, &child_block, merges);
 }
 
 fn zirBlock(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
@@ -992,12 +985,13 @@ fn zirBlock(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) Inner
 
     _ = try sema.analyzeBody(&child_block, body);
 
-    return sema.analyzeBlockBody(parent_block, &child_block, merges);
+    return sema.analyzeBlockBody(parent_block, src, &child_block, merges);
 }
 
 fn analyzeBlockBody(
     sema: *Sema,
     parent_block: *Scope.Block,
+    src: LazySrcLoc,
     child_block: *Scope.Block,
     merges: *Scope.Block.Merges,
 ) InnerError!*Inst {
@@ -1034,7 +1028,7 @@ fn analyzeBlockBody(
     // Need to set the type and emit the Block instruction. This allows machine code generation
     // to emit a jump instruction to after the block when it encounters the break.
     try parent_block.instructions.append(sema.gpa, &merges.block_inst.base);
-    const resolved_ty = try sema.resolvePeerTypes(parent_block, .todo, merges.results.items);
+    const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items);
     merges.block_inst.base.ty = resolved_ty;
     merges.block_inst.body = .{
         .instructions = try sema.arena.dupe(*Inst, child_block.instructions.items),
@@ -1048,7 +1042,7 @@ fn analyzeBlockBody(
         }
         var coerce_block = parent_block.makeSubBlock();
         defer coerce_block.instructions.deinit(sema.gpa);
-        const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br.operand, .todo);
+        const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br.operand, br.operand.src);
         // If no instructions were produced, such as in the case of a coercion of a
         // constant value to a new type, we can simply point the br operand to it.
         if (coerce_block.instructions.items.len == 0) {
@@ -1334,7 +1328,7 @@ fn analyzeCall(
         // the block_inst above.
         _ = try inline_sema.root(&child_block);
 
-        const result = try inline_sema.analyzeBlockBody(block, &child_block, merges);
+        const result = try inline_sema.analyzeBlockBody(block, call_src, &child_block, merges);
 
         sema.branch_quota = inline_sema.branch_quota;
         sema.branch_count = inline_sema.branch_count;
@@ -1845,15 +1839,16 @@ fn zirFnType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index, var_args: b
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].fn_type;
+    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const src = inst_data.src();
     const extra = sema.code.extraData(zir.Inst.FnType, inst_data.payload_index);
     const param_types = sema.code.refSlice(extra.end, extra.data.param_types_len);
 
     return sema.fnTypeCommon(
         block,
-        .unneeded,
+        inst_data.src_node,
         param_types,
-        inst_data.return_type,
+        extra.data.return_type,
         .Unspecified,
         var_args,
     );
@@ -1863,21 +1858,23 @@ fn zirFnTypeCc(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index, var_args:
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].fn_type;
+    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const src = inst_data.src();
+    const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node };
     const extra = sema.code.extraData(zir.Inst.FnTypeCc, inst_data.payload_index);
     const param_types = sema.code.refSlice(extra.end, extra.data.param_types_len);
 
-    const cc_tv = try sema.resolveInstConst(block, .todo, extra.data.cc);
+    const cc_tv = try sema.resolveInstConst(block, cc_src, extra.data.cc);
     // TODO once we're capable of importing and analyzing decls from
     // std.builtin, this needs to change
     const cc_str = cc_tv.val.castTag(.enum_literal).?.data;
     const cc = std.meta.stringToEnum(std.builtin.CallingConvention, cc_str) orelse
-        return sema.mod.fail(&block.base, .todo, "Unknown calling convention {s}", .{cc_str});
+        return sema.mod.fail(&block.base, cc_src, "Unknown calling convention {s}", .{cc_str});
     return sema.fnTypeCommon(
         block,
-        .unneeded,
+        inst_data.src_node,
         param_types,
-        inst_data.return_type,
+        extra.data.return_type,
         cc,
         var_args,
     );
@@ -1886,13 +1883,15 @@ fn zirFnTypeCc(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index, var_args:
 fn fnTypeCommon(
     sema: *Sema,
     block: *Scope.Block,
-    src: LazySrcLoc,
+    src_node_offset: i32,
     zir_param_types: []const zir.Inst.Ref,
     zir_return_type: zir.Inst.Ref,
     cc: std.builtin.CallingConvention,
     var_args: bool,
 ) InnerError!*Inst {
-    const return_type = try sema.resolveType(block, src, zir_return_type);
+    const src: LazySrcLoc = .{ .node_offset = src_node_offset };
+    const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
+    const return_type = try sema.resolveType(block, ret_ty_src, zir_return_type);
 
     // Hot path for some common function types.
     if (zir_param_types.len == 0 and !var_args) {
@@ -1915,12 +1914,11 @@ fn fnTypeCommon(
 
     const param_types = try sema.arena.alloc(Type, zir_param_types.len);
     for (zir_param_types) |param_type, i| {
-        const resolved = try sema.resolveType(block, src, param_type);
-        // TODO skip for comptime params
-        if (!resolved.isValidVarType(false)) {
-            return sema.mod.fail(&block.base, .todo, "parameter of type '{}' must be declared comptime", .{resolved});
-        }
-        param_types[i] = resolved;
+        // TODO make a compile error from `resolveType` report the source location
+        // of the specific parameter. Will need to take a similar strategy as
+        // `resolveSwitchItemVal` to avoid resolving the source location unless
+        // we actually need to report an error.
+        param_types[i] = try sema.resolveType(block, src, param_type);
     }
 
     const fn_ty = try Type.Tag.function.create(sema.arena, .{
@@ -2082,9 +2080,14 @@ fn zirBitcast(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError
     const tracy = trace(@src());
     defer tracy.end();
 
-    const bin_inst = sema.code.instructions.items(.data)[inst].bin;
-    const dest_type = try sema.resolveType(block, .todo, bin_inst.lhs);
-    const operand = try sema.resolveInst(bin_inst.rhs);
+    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const src = inst_data.src();
+    const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+    const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+    const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
+
+    const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
+    const operand = try sema.resolveInst(extra.rhs);
     return sema.bitcast(block, dest_type, operand);
 }
 
@@ -2234,7 +2237,12 @@ fn zirSwitchCapture(
     const tracy = trace(@src());
     defer tracy.end();
 
-    @panic("TODO implement Sema for zirSwitchCapture");
+    const zir_datas = sema.code.instructions.items(.data);
+    const capture_info = zir_datas[inst].switch_capture;
+    const switch_info = zir_datas[capture_info.switch_inst].pl_node;
+    const src = switch_info.src();
+
+    return sema.mod.fail(&block.base, src, "TODO implement Sema for zirSwitchCapture", .{});
 }
 
 fn zirSwitchCaptureElse(
@@ -2246,7 +2254,12 @@ fn zirSwitchCaptureElse(
     const tracy = trace(@src());
     defer tracy.end();
 
-    @panic("TODO implement Sema for zirSwitchCaptureElse");
+    const zir_datas = sema.code.instructions.items(.data);
+    const capture_info = zir_datas[inst].switch_capture;
+    const switch_info = zir_datas[capture_info.switch_inst].pl_node;
+    const src = switch_info.src();
+
+    return sema.mod.fail(&block.base, src, "TODO implement Sema for zirSwitchCaptureElse", .{});
 }
 
 fn zirSwitchBlock(
@@ -2631,8 +2644,9 @@ fn analyzeSwitch(
                 const body = sema.code.extra[extra_index..][0..body_len];
                 extra_index += body_len;
 
-                const item = try sema.resolveInst(item_ref);
-                const item_val = try sema.resolveConstValue(block, item.src, item);
+                // Validation above ensured these will succeed.
+                const item = sema.resolveInst(item_ref) catch unreachable;
+                const item_val = sema.resolveConstValue(block, .unneeded, item) catch unreachable;
                 if (operand_val.eql(item_val)) {
                     return sema.resolveBody(block, body);
                 }
@@ -2652,8 +2666,9 @@ fn analyzeSwitch(
                 const body = sema.code.extra[extra_index + 2 * ranges_len ..][0..body_len];
 
                 for (items) |item_ref| {
-                    const item = try sema.resolveInst(item_ref);
-                    const item_val = try sema.resolveConstValue(block, item.src, item);
+                    // Validation above ensured these will succeed.
+                    const item = sema.resolveInst(item_ref) catch unreachable;
+                    const item_val = sema.resolveConstValue(block, item.src, item) catch unreachable;
                     if (operand_val.eql(item_val)) {
                         return sema.resolveBody(block, body);
                     }
@@ -2666,8 +2681,9 @@ fn analyzeSwitch(
                     const item_last = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
                     extra_index += 1;
 
-                    const first_tv = try sema.resolveInstConst(block, .todo, item_first);
-                    const last_tv = try sema.resolveInstConst(block, .todo, item_last);
+                    // Validation above ensured these will succeed.
+                    const first_tv = sema.resolveInstConst(block, .unneeded, item_first) catch unreachable;
+                    const last_tv = sema.resolveInstConst(block, .unneeded, item_last) catch unreachable;
                     if (Value.compare(operand_val, .gte, first_tv.val) and
                         Value.compare(operand_val, .lte, last_tv.val))
                     {
@@ -2876,7 +2892,7 @@ fn analyzeSwitch(
     };
 
     _ = try child_block.addSwitchBr(src, operand, cases, final_else_body);
-    return sema.analyzeBlockBody(block, &child_block, merges);
+    return sema.analyzeBlockBody(block, src, &child_block, merges);
 }
 
 fn resolveSwitchItemVal(
src/zir.zig
@@ -168,15 +168,12 @@ pub const Inst = struct {
         asm_volatile,
         /// Bitwise AND. `&`
         bit_and,
-        /// TODO delete this instruction, it has no purpose.
+        /// Bitcast a value to a different type.
+        /// Uses the pl_node field with payload `Bin`.
         bitcast,
-        /// An arbitrary typed pointer is pointer-casted to a new Pointer.
-        /// The destination type is given by LHS. The cast is to be evaluated
-        /// as if it were a bit-cast operation from the operand pointer element type to the
-        /// provided destination type.
-        bitcast_ref,
         /// A typed result location pointer is bitcasted to a new result location pointer.
         /// The new result location pointer has an inferred type.
+        /// Uses the un_node field.
         bitcast_result_ptr,
         /// Bitwise NOT. `~`
         /// Uses `un_node`.
@@ -338,12 +335,12 @@ pub const Inst = struct {
         /// Payload is `Bin` with lhs as the dest type, rhs the operand.
         floatcast,
         /// Returns a function type, assuming unspecified calling convention.
-        /// Uses the `fn_type` union field. `payload_index` points to a `FnType`.
+        /// Uses the `pl_node` union field. `payload_index` points to a `FnType`.
         fn_type,
         /// Same as `fn_type` but the function is variadic.
         fn_type_var_args,
         /// Returns a function type, with a calling convention instruction operand.
-        /// Uses the `fn_type` union field. `payload_index` points to a `FnTypeCc`.
+        /// Uses the `pl_node` union field. `payload_index` points to a `FnTypeCc`.
         fn_type_cc,
         /// Same as `fn_type_cc` but the function is variadic.
         fn_type_cc_var_args,
@@ -662,7 +659,6 @@ pub const Inst = struct {
                 .asm_volatile,
                 .bit_and,
                 .bitcast,
-                .bitcast_ref,
                 .bitcast_result_ptr,
                 .bit_or,
                 .block,
@@ -1212,12 +1208,6 @@ pub const Inst = struct {
             /// Index into extra. See `PtrType`.
             payload_index: u32,
         },
-        fn_type: struct {
-            return_type: Ref,
-            /// For `fn_type` this points to a `FnType` in `extra`.
-            /// For `fn_type_cc` this points to `FnTypeCc` in `extra`.
-            payload_index: u32,
-        },
         int_type: struct {
             /// Offset from Decl AST node index.
             /// `Tag` determines which kind of AST node this points to.
@@ -1289,6 +1279,7 @@ pub const Inst = struct {
     /// according to `param_types_len`.
     /// Each param type is a `Ref`.
     pub const FnTypeCc = struct {
+        return_type: Ref,
         cc: Ref,
         param_types_len: u32,
     };
@@ -1297,6 +1288,7 @@ pub const Inst = struct {
     /// according to `param_types_len`.
     /// Each param type is a `Ref`.
     pub const FnType = struct {
+        return_type: Ref,
         param_types_len: u32,
     };
 
@@ -1640,7 +1632,6 @@ const Writer = struct {
             => try self.writeSwitchCapture(stream, inst),
 
             .bitcast,
-            .bitcast_ref,
             .bitcast_result_ptr,
             .store_to_inferred_ptr,
             => try stream.writeAll("TODO)"),
@@ -2044,11 +2035,26 @@ const Writer = struct {
         stream: anytype,
         inst: Inst.Index,
         var_args: bool,
-    ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
-        const inst_data = self.code.instructions.items(.data)[inst].fn_type;
+    ) !void {
+        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const src = inst_data.src();
         const extra = self.code.extraData(Inst.FnType, inst_data.payload_index);
         const param_types = self.code.refSlice(extra.end, extra.data.param_types_len);
-        return self.writeFnTypeCommon(stream, param_types, inst_data.return_type, var_args, .none);
+        return self.writeFnTypeCommon(stream, param_types, extra.data.return_type, var_args, .none, src);
+    }
+
+    fn writeFnTypeCc(
+        self: *Writer,
+        stream: anytype,
+        inst: Inst.Index,
+        var_args: bool,
+    ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
+        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const src = inst_data.src();
+        const extra = self.code.extraData(Inst.FnTypeCc, inst_data.payload_index);
+        const param_types = self.code.refSlice(extra.end, extra.data.param_types_len);
+        const cc = extra.data.cc;
+        return self.writeFnTypeCommon(stream, param_types, extra.data.return_type, var_args, cc, src);
     }
 
     fn writeBoolBr(self: *Writer, stream: anytype, inst: Inst.Index) !void {
@@ -2064,19 +2070,6 @@ const Writer = struct {
         try stream.writeAll("})");
     }
 
-    fn writeFnTypeCc(
-        self: *Writer,
-        stream: anytype,
-        inst: Inst.Index,
-        var_args: bool,
-    ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
-        const inst_data = self.code.instructions.items(.data)[inst].fn_type;
-        const extra = self.code.extraData(Inst.FnTypeCc, inst_data.payload_index);
-        const param_types = self.code.refSlice(extra.end, extra.data.param_types_len);
-        const cc = extra.data.cc;
-        return self.writeFnTypeCommon(stream, param_types, inst_data.return_type, var_args, cc);
-    }
-
     fn writeIntType(self: *Writer, stream: anytype, inst: Inst.Index) !void {
         const int_type = self.code.instructions.items(.data)[inst].int_type;
         const prefix: u8 = switch (int_type.signedness) {
@@ -2110,7 +2103,8 @@ const Writer = struct {
         ret_ty: Inst.Ref,
         var_args: bool,
         cc: Inst.Ref,
-    ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
+        src: LazySrcLoc,
+    ) !void {
         try stream.writeAll("[");
         for (param_types) |param_type, i| {
             if (i != 0) try stream.writeAll(", ");
@@ -2120,7 +2114,8 @@ const Writer = struct {
         try self.writeInstRef(stream, ret_ty);
         try self.writeOptionalInstRef(stream, ", cc=", cc);
         try self.writeFlag(stream, ", var_args", var_args);
-        try stream.writeAll(")");
+        try stream.writeAll(") ");
+        try self.writeSrc(stream, src);
     }
 
     fn writeSmallStr(
BRANCH_TODO
@@ -1,9 +1,6 @@
 this is my WIP branch scratch pad, to be deleted before merging into master
 
 Merge TODO list:
- * remove the LazySrcLoc.todo tag
- * update astgen.zig
- * finish updating Sema.zig
  * finish implementing SrcLoc byteOffset function
  * audit all the .unneeded src locations
  * audit the calls in codegen toSrcLocWithDecl specifically if there is inlined function