From 0bef271e75657195145a519199ce12b97db246f4 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 19 Jun 2021 17:16:24 +0800 Subject: [PATCH 1/3] stage2 Sema: Add error notes to unresolvable peer types --- src/Sema.zig | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 753ef8fb9c..6d5480c53d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -8398,8 +8398,15 @@ fn resolvePeerTypes( continue; } - // TODO error notes pointing out each type - return sema.mod.fail(&block.base, src, "incompatible types: '{}' and '{}'", .{ chosen_ty, candidate_ty }); + const msg = msg: { + const msg = try sema.mod.errMsg(&block.base, src, "incompatible types: '{}' and '{}'", .{ chosen_ty, candidate_ty }); + errdefer msg.destroy(sema.gpa); + // TODO add error notes + // try sema.mod.errNote(&block.base, chosen.src, msg, "type '{}' here", .{chosen_ty}); + // try sema.mod.errNote(&block.base, candidate.src, msg, "type '{}' here", .{candidate_ty}); + break :msg msg; + }; + return sema.mod.failWithOwnedErrorMsg(&block.base, msg); } return sema.typeOf(chosen); From fcdc5c6b3ccca5cc4e1c2353f280068d1c427153 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Wed, 23 Jun 2021 23:52:46 +0800 Subject: [PATCH 2/3] stage2 Sema: Resolve source locations of @TypeOf parameters --- src/Sema.zig | 67 ++++++++++++++++++++++++++++++++++++++++++-------- test/cases.zig | 6 ++++- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 6d5480c53d..4471d76d44 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1516,7 +1516,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde if (ptr_val.castTag(.inferred_alloc)) |inferred_alloc| { const peer_inst_list = inferred_alloc.data.stored_inst_list.items; - const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list); + const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, null); if (var_is_mut) { try sema.validateVarType(block, ty_src, final_elem_ty); } @@ -2123,7 +2123,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(gpa, merges.block_inst); - const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items); + const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items, null); try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + child_block.instructions.items.len); sema.air_instructions.items(.data)[merges.block_inst] = .{ .ty_pl = .{ @@ -4768,7 +4768,7 @@ fn zirBitwise( const rhs_ty = sema.typeOf(rhs); const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; - const resolved_type = try sema.resolvePeerTypes(block, src, instructions); + const resolved_type = try sema.resolvePeerTypes(block, src, instructions, null); const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); @@ -4914,7 +4914,7 @@ fn analyzeArithmetic( } const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; - const resolved_type = try sema.resolvePeerTypes(block, src, instructions); + const resolved_type = try sema.resolvePeerTypes(block, src, instructions, null); const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); @@ -5223,7 +5223,7 @@ fn analyzeCmp( return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src); } const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; - const resolved_type = try sema.resolvePeerTypes(block, src, instructions); + const resolved_type = try sema.resolvePeerTypes(block, src, instructions, null); if (!resolved_type.isSelfComparable(is_equality_cmp)) { return sema.mod.fail(&block.base, src, "{s} operator not allowed for type '{}'", .{ @tagName(op), resolved_type, @@ -5433,7 +5433,7 @@ fn zirTypeofPeer( inst_list[i] = sema.resolveInst(arg_ref); } - const result_type = try sema.resolvePeerTypes(block, src, inst_list); + const result_type = try sema.resolvePeerTypes(block, src, inst_list, extra.data.src_node); return sema.addType(result_type); } @@ -8324,11 +8324,42 @@ fn wrapErrorUnion( } } +fn resolveTypeOfArgSrcLoc( + gpa: *Allocator, + decl: *Decl, + typeof_builtin_call_node_offset: i32, + candidates: usize, + candidate_i: usize, +) LazySrcLoc { + @setCold(true); + if (candidates <= 2) { + switch (candidate_i) { + 0 => return LazySrcLoc{ .node_offset_builtin_call_arg0 = typeof_builtin_call_node_offset }, + 1 => return LazySrcLoc{ .node_offset_builtin_call_arg1 = typeof_builtin_call_node_offset }, + else => unreachable, + } + } + + const tree = decl.namespace.file_scope.getTree(gpa) catch |err| { + // In this case we emit a warning + a less precise source location. + log.warn("unable to load {s}: {s}", .{ + decl.namespace.file_scope.sub_file_path, @errorName(err), + }); + return LazySrcLoc{ .node_offset = 0 }; + }; + const node = decl.relativeToNodeIndex(typeof_builtin_call_node_offset); + const node_datas = tree.nodes.items(.data); + const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; + + return LazySrcLoc{ .node_abs = params[candidate_i] }; +} + fn resolvePeerTypes( sema: *Sema, block: *Scope.Block, src: LazySrcLoc, instructions: []Air.Inst.Ref, + typeof_builtin_call_node_offset: ?i32, ) !Type { if (instructions.len == 0) return Type.initTag(.noreturn); @@ -8339,7 +8370,8 @@ fn resolvePeerTypes( const target = sema.mod.getTarget(); var chosen = instructions[0]; - for (instructions[1..]) |candidate| { + var chosen_i: usize = 0; + for (instructions[1..]) |candidate, candidate_i| { const candidate_ty = sema.typeOf(candidate); const chosen_ty = sema.typeOf(chosen); if (candidate_ty.eql(chosen_ty)) @@ -8348,12 +8380,14 @@ fn resolvePeerTypes( continue; if (chosen_ty.zigTypeTag() == .NoReturn) { chosen = candidate; + chosen_i = candidate_i + 1; continue; } if (candidate_ty.zigTypeTag() == .Undefined) continue; if (chosen_ty.zigTypeTag() == .Undefined) { chosen = candidate; + chosen_i = candidate_i + 1; continue; } if (chosen_ty.isInt() and @@ -8362,18 +8396,21 @@ fn resolvePeerTypes( { if (chosen_ty.intInfo(target).bits < candidate_ty.intInfo(target).bits) { chosen = candidate; + chosen_i = candidate_i + 1; } continue; } if (chosen_ty.isFloat() and candidate_ty.isFloat()) { if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) { chosen = candidate; + chosen_i = candidate_i + 1; } continue; } if (chosen_ty.zigTypeTag() == .ComptimeInt and candidate_ty.isInt()) { chosen = candidate; + chosen_i = candidate_i + 1; continue; } @@ -8383,6 +8420,7 @@ fn resolvePeerTypes( if (chosen_ty.zigTypeTag() == .ComptimeFloat and candidate_ty.isFloat()) { chosen = candidate; + chosen_i = candidate_i + 1; continue; } @@ -8395,15 +8433,24 @@ fn resolvePeerTypes( } if (chosen_ty.zigTypeTag() == .EnumLiteral and candidate_ty.zigTypeTag() == .Enum) { chosen = candidate; + chosen_i = candidate_i + 1; continue; } + // At this point, we hit a compile error. If the call to + // resolvePeerTypes originated from the @TypeOf builtin, we + // need to recover the source locations const msg = msg: { const msg = try sema.mod.errMsg(&block.base, src, "incompatible types: '{}' and '{}'", .{ chosen_ty, candidate_ty }); errdefer msg.destroy(sema.gpa); - // TODO add error notes - // try sema.mod.errNote(&block.base, chosen.src, msg, "type '{}' here", .{chosen_ty}); - // try sema.mod.errNote(&block.base, candidate.src, msg, "type '{}' here", .{candidate_ty}); + // TODO add error notes for other scenarios + if (typeof_builtin_call_node_offset) |node_offset| { + const chosen_src = resolveTypeOfArgSrcLoc(sema.gpa, block.src_decl, node_offset, instructions.len, chosen_i); + const candidate_src = resolveTypeOfArgSrcLoc(sema.gpa, block.src_decl, node_offset, instructions.len, candidate_i + 1); + + try sema.mod.errNote(&block.base, chosen_src, msg, "type '{}' here", .{chosen_ty}); + try sema.mod.errNote(&block.base, candidate_src, msg, "type '{}' here", .{candidate_ty}); + } break :msg msg; }; return sema.mod.failWithOwnedErrorMsg(&block.base, msg); diff --git a/test/cases.zig b/test/cases.zig index 840ee7a4ac..0f28c2fe63 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -288,7 +288,11 @@ pub fn addCases(ctx: *TestContext) !void { \\pub fn main() void { \\ _ = @TypeOf(true, 1); \\} - , &[_][]const u8{":2:9: error: incompatible types: 'bool' and 'comptime_int'"}); + , &[_][]const u8{ + ":2:9: error: incompatible types: 'bool' and 'comptime_int'", + ":2:17: note: type 'bool' here", + ":2:23: note: type 'comptime_int' here", + }); } { From 16c11988587ad78cd47ec571e1120c052abd47ed Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Fri, 9 Jul 2021 20:43:19 +0800 Subject: [PATCH 3/3] stage2 Sema: Resolve LazySrcLocs for bitwise and arithmetic exprs --- src/Module.zig | 51 ++++++++++++++++++++++++++++++++++ src/Sema.zig | 75 +++++++++++++++++++------------------------------- test/cases.zig | 6 +++- 3 files changed, 84 insertions(+), 48 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 84b721369d..e007a3c8ae 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4268,6 +4268,57 @@ pub const SwitchProngSrc = union(enum) { } }; +pub const PeerTypeCandidateSrc = union(enum) { + /// Do not print out error notes for candidate sources + none: void, + /// When we want to know the the src of candidate i, look up at + /// index i in this slice + override: []LazySrcLoc, + /// resolvePeerTypes originates from a @TypeOf(...) call + typeof_builtin_call_node_offset: i32, + + pub fn resolve( + self: PeerTypeCandidateSrc, + gpa: *Allocator, + decl: *Decl, + candidates: usize, + candidate_i: usize, + ) ?LazySrcLoc { + @setCold(true); + + switch (self) { + .none => { + return null; + }, + .override => |candidate_srcs| { + return candidate_srcs[candidate_i]; + }, + .typeof_builtin_call_node_offset => |node_offset| { + if (candidates <= 2) { + switch (candidate_i) { + 0 => return LazySrcLoc{ .node_offset_builtin_call_arg0 = node_offset }, + 1 => return LazySrcLoc{ .node_offset_builtin_call_arg1 = node_offset }, + else => unreachable, + } + } + + const tree = decl.namespace.file_scope.getTree(gpa) catch |err| { + // In this case we emit a warning + a less precise source location. + log.warn("unable to load {s}: {s}", .{ + decl.namespace.file_scope.sub_file_path, @errorName(err), + }); + return LazySrcLoc{ .node_offset = 0 }; + }; + const node = decl.relativeToNodeIndex(node_offset); + const node_datas = tree.nodes.items(.data); + const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; + + return LazySrcLoc{ .node_abs = params[candidate_i] }; + }, + } + } +}; + pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) CompileError!void { const tracy = trace(@src()); defer tracy.end(); diff --git a/src/Sema.zig b/src/Sema.zig index 4471d76d44..46424c9f2b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1516,7 +1516,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde if (ptr_val.castTag(.inferred_alloc)) |inferred_alloc| { const peer_inst_list = inferred_alloc.data.stored_inst_list.items; - const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, null); + const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none); if (var_is_mut) { try sema.validateVarType(block, ty_src, final_elem_ty); } @@ -2123,7 +2123,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(gpa, merges.block_inst); - const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items, null); + const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items, .none); try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + child_block.instructions.items.len); sema.air_instructions.items(.data)[merges.block_inst] = .{ .ty_pl = .{ @@ -4768,7 +4768,7 @@ fn zirBitwise( const rhs_ty = sema.typeOf(rhs); const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; - const resolved_type = try sema.resolvePeerTypes(block, src, instructions, null); + const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]LazySrcLoc{ lhs_src, rhs_src } }); const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); @@ -4914,7 +4914,7 @@ fn analyzeArithmetic( } const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; - const resolved_type = try sema.resolvePeerTypes(block, src, instructions, null); + const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]LazySrcLoc{ lhs_src, rhs_src } }); const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); @@ -5223,7 +5223,7 @@ fn analyzeCmp( return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src); } const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; - const resolved_type = try sema.resolvePeerTypes(block, src, instructions, null); + const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]LazySrcLoc{ lhs_src, rhs_src } }); if (!resolved_type.isSelfComparable(is_equality_cmp)) { return sema.mod.fail(&block.base, src, "{s} operator not allowed for type '{}'", .{ @tagName(op), resolved_type, @@ -5433,7 +5433,7 @@ fn zirTypeofPeer( inst_list[i] = sema.resolveInst(arg_ref); } - const result_type = try sema.resolvePeerTypes(block, src, inst_list, extra.data.src_node); + const result_type = try sema.resolvePeerTypes(block, src, inst_list, .{ .typeof_builtin_call_node_offset = extra.data.src_node }); return sema.addType(result_type); } @@ -8324,42 +8324,12 @@ fn wrapErrorUnion( } } -fn resolveTypeOfArgSrcLoc( - gpa: *Allocator, - decl: *Decl, - typeof_builtin_call_node_offset: i32, - candidates: usize, - candidate_i: usize, -) LazySrcLoc { - @setCold(true); - if (candidates <= 2) { - switch (candidate_i) { - 0 => return LazySrcLoc{ .node_offset_builtin_call_arg0 = typeof_builtin_call_node_offset }, - 1 => return LazySrcLoc{ .node_offset_builtin_call_arg1 = typeof_builtin_call_node_offset }, - else => unreachable, - } - } - - const tree = decl.namespace.file_scope.getTree(gpa) catch |err| { - // In this case we emit a warning + a less precise source location. - log.warn("unable to load {s}: {s}", .{ - decl.namespace.file_scope.sub_file_path, @errorName(err), - }); - return LazySrcLoc{ .node_offset = 0 }; - }; - const node = decl.relativeToNodeIndex(typeof_builtin_call_node_offset); - const node_datas = tree.nodes.items(.data); - const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; - - return LazySrcLoc{ .node_abs = params[candidate_i] }; -} - fn resolvePeerTypes( sema: *Sema, block: *Scope.Block, src: LazySrcLoc, instructions: []Air.Inst.Ref, - typeof_builtin_call_node_offset: ?i32, + candidate_srcs: Module.PeerTypeCandidateSrc, ) !Type { if (instructions.len == 0) return Type.initTag(.noreturn); @@ -8437,20 +8407,31 @@ fn resolvePeerTypes( continue; } - // At this point, we hit a compile error. If the call to - // resolvePeerTypes originated from the @TypeOf builtin, we - // need to recover the source locations + // At this point, we hit a compile error. We need to recover + // the source locations. + const chosen_src = candidate_srcs.resolve( + sema.gpa, + block.src_decl, + instructions.len, + chosen_i, + ); + const candidate_src = candidate_srcs.resolve( + sema.gpa, + block.src_decl, + instructions.len, + candidate_i + 1, + ); + const msg = msg: { const msg = try sema.mod.errMsg(&block.base, src, "incompatible types: '{}' and '{}'", .{ chosen_ty, candidate_ty }); errdefer msg.destroy(sema.gpa); - // TODO add error notes for other scenarios - if (typeof_builtin_call_node_offset) |node_offset| { - const chosen_src = resolveTypeOfArgSrcLoc(sema.gpa, block.src_decl, node_offset, instructions.len, chosen_i); - const candidate_src = resolveTypeOfArgSrcLoc(sema.gpa, block.src_decl, node_offset, instructions.len, candidate_i + 1); - try sema.mod.errNote(&block.base, chosen_src, msg, "type '{}' here", .{chosen_ty}); - try sema.mod.errNote(&block.base, candidate_src, msg, "type '{}' here", .{candidate_ty}); - } + if (chosen_src) |src_loc| + try sema.mod.errNote(&block.base, src_loc, msg, "type '{}' here", .{chosen_ty}); + + if (candidate_src) |src_loc| + try sema.mod.errNote(&block.base, src_loc, msg, "type '{}' here", .{candidate_ty}); + break :msg msg; }; return sema.mod.failWithOwnedErrorMsg(&block.base, msg); diff --git a/test/cases.zig b/test/cases.zig index 0f28c2fe63..31460a3fa7 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -1733,7 +1733,11 @@ pub fn addCases(ctx: *TestContext) !void { \\ const b = false; \\ _ = a & &b; \\} - , &[_][]const u8{":4:11: error: incompatible types: 'bool' and '*const bool'"}); + , &[_][]const u8{ + ":4:11: error: incompatible types: 'bool' and '*const bool'", + ":4:9: note: type 'bool' here", + ":4:13: note: type '*const bool' here", + }); case.addCompareOutput( \\pub fn main() void {