Merge pull request #14257 from Vexu/compile-errors

compile error improvements and bug fixes
This commit is contained in:
Andrew Kelley 2023-01-12 03:39:01 -05:00 committed by GitHub
commit 2a92b04b9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 216 additions and 40 deletions

View File

@ -362,6 +362,9 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
.wrong_equal_var_decl => {
return stream.writeAll("variable initialized with '==' instead of '='");
},
.var_const_decl => {
return stream.writeAll("use 'var' or 'const' to declare variable");
},
.expected_token => {
const found_tag = token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)];
@ -2743,6 +2746,7 @@ pub const Error = struct {
c_style_container,
expected_var_const,
wrong_equal_var_decl,
var_const_decl,
zig_style_container,
previous_field,

View File

@ -471,6 +471,13 @@ const Parser = struct {
// There is not allowed to be a decl after a field with no comma.
// Report error but recover parser.
try p.warn(.expected_comma_after_field);
if (p.token_tags[p.tok_i] == .semicolon and p.token_tags[identifier] == .identifier) {
try p.warnMsg(.{
.tag = .var_const_decl,
.is_note = true,
.token = identifier,
});
}
p.findNextContainerMember();
continue;
},

View File

@ -5238,6 +5238,18 @@ test "zig fmt: missing const/var before local variable" {
});
}
test "zig fmt: missing const/var before local variable" {
try testError(
\\std = foo,
\\std = foo;
\\*u32 = foo;
, &.{
.expected_comma_after_field,
.var_const_decl,
.expected_comma_after_field,
});
}
test "zig fmt: while continue expr" {
try testCanonical(
\\test {

View File

@ -2463,6 +2463,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.is_non_null_ptr,
.is_non_err,
.is_non_err_ptr,
.ret_is_non_err,
.mod_rem,
.mul,
.mulwrap,
@ -4486,9 +4487,24 @@ fn structDeclInner(
.container_field_align,
.container_field,
.@"comptime",
.test_decl,
=> continue,
else => {
return astgen.failNode(member_node, "tuple declarations cannot contain declarations", .{});
const tuple_member = for (container_decl.ast.members) |maybe_tuple| switch (node_tags[maybe_tuple]) {
.container_field_init,
.container_field_align,
.container_field,
=> break maybe_tuple,
else => {},
} else unreachable;
return astgen.failNodeNotes(
member_node,
"tuple declarations cannot contain declarations",
.{},
&[_]u32{
try astgen.errNoteNode(tuple_member, "tuple field here", .{}),
},
);
},
}
};
@ -7012,7 +7028,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
// Emit conditional branch for generating errdefers.
const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
const is_non_err = try gz.addUnNode(.is_non_err, result, node);
const is_non_err = try gz.addUnNode(.ret_is_non_err, result, node);
const condbr = try gz.addCondBr(.condbr, node);
var then_scope = gz.makeSubBlock(scope);

View File

@ -528,10 +528,10 @@ pub const Decl = struct {
/// Decl is marked alive, then it sends the Decl to the linker. Otherwise it
/// deletes the Decl on the spot.
alive: bool,
/// Whether the Decl is a `usingnamespace` declaration.
is_usingnamespace: bool,
/// If true `name` is already fully qualified.
name_fully_qualified: bool = false,
/// What kind of a declaration is this.
kind: Kind,
/// Represents the position of the code in the output file.
/// This is populated regardless of semantic analysis and code generation.
@ -551,6 +551,14 @@ pub const Decl = struct {
/// typed_value may need to be regenerated.
dependencies: DepsTable = .{},
pub const Kind = enum {
@"usingnamespace",
@"test",
@"comptime",
named,
anon,
};
pub const Index = enum(u32) {
_,
@ -4438,7 +4446,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
// not the struct itself.
try sema.resolveTypeLayout(decl_tv.ty);
if (decl.is_usingnamespace) {
if (decl.kind == .@"usingnamespace") {
if (!decl_tv.ty.eql(Type.type, mod)) {
return sema.fail(&block_scope, ty_src, "expected type, found {}", .{
decl_tv.ty.fmt(mod),
@ -4964,26 +4972,31 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
// Every Decl needs a name.
var is_named_test = false;
var kind: Decl.Kind = .named;
const decl_name: [:0]const u8 = switch (decl_name_index) {
0 => name: {
if (export_bit) {
const i = iter.usingnamespace_index;
iter.usingnamespace_index += 1;
kind = .@"usingnamespace";
break :name try std.fmt.allocPrintZ(gpa, "usingnamespace_{d}", .{i});
} else {
const i = iter.comptime_index;
iter.comptime_index += 1;
kind = .@"comptime";
break :name try std.fmt.allocPrintZ(gpa, "comptime_{d}", .{i});
}
},
1 => name: {
const i = iter.unnamed_test_index;
iter.unnamed_test_index += 1;
kind = .@"test";
break :name try std.fmt.allocPrintZ(gpa, "test_{d}", .{i});
},
2 => name: {
is_named_test = true;
const test_name = zir.nullTerminatedString(decl_doccomment_index);
kind = .@"test";
break :name try std.fmt.allocPrintZ(gpa, "decltest.{s}", .{test_name});
},
else => name: {
@ -4991,6 +5004,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
if (raw_name.len == 0) {
is_named_test = true;
const test_name = zir.nullTerminatedString(decl_name_index + 1);
kind = .@"test";
break :name try std.fmt.allocPrintZ(gpa, "test.{s}", .{test_name});
} else {
break :name try gpa.dupeZ(u8, raw_name);
@ -4998,8 +5012,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
},
};
const is_exported = export_bit and decl_name_index != 0;
const is_usingnamespace = export_bit and decl_name_index == 0;
if (is_usingnamespace) try namespace.usingnamespace_set.ensureUnusedCapacity(gpa, 1);
if (kind == .@"usingnamespace") try namespace.usingnamespace_set.ensureUnusedCapacity(gpa, 1);
// We create a Decl for it regardless of analysis status.
const gop = try namespace.decls.getOrPutContextAdapted(
@ -5012,8 +5025,9 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
if (!gop.found_existing) {
const new_decl_index = try mod.allocateNewDecl(namespace, decl_node, iter.parent_decl.src_scope);
const new_decl = mod.declPtr(new_decl_index);
new_decl.kind = kind;
new_decl.name = decl_name;
if (is_usingnamespace) {
if (kind == .@"usingnamespace") {
namespace.usingnamespace_set.putAssumeCapacity(new_decl_index, is_pub);
}
log.debug("scan new {*} ({s}) into {*}", .{ new_decl, decl_name, namespace });
@ -5058,7 +5072,6 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
}
new_decl.is_pub = is_pub;
new_decl.is_exported = is_exported;
new_decl.is_usingnamespace = is_usingnamespace;
new_decl.has_align = has_align;
new_decl.has_linksection_or_addrspace = has_linksection_or_addrspace;
new_decl.zir_decl_index = @intCast(u32, decl_sub_index);
@ -5076,7 +5089,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
decl.is_pub = is_pub;
decl.is_exported = is_exported;
decl.is_usingnamespace = is_usingnamespace;
decl.kind = kind;
decl.has_align = has_align;
decl.has_linksection_or_addrspace = has_linksection_or_addrspace;
decl.zir_decl_index = @intCast(u32, decl_sub_index);
@ -5635,7 +5648,7 @@ pub fn allocateNewDecl(
.has_linksection_or_addrspace = false,
.has_align = false,
.alive = false,
.is_usingnamespace = false,
.kind = .anon,
};
return decl_and_index.decl_index;

View File

@ -953,6 +953,7 @@ fn analyzeBodyInner(
.int_type => try sema.zirIntType(block, inst),
.is_non_err => try sema.zirIsNonErr(block, inst),
.is_non_err_ptr => try sema.zirIsNonErrPtr(block, inst),
.ret_is_non_err => try sema.zirRetIsNonErr(block, inst),
.is_non_null => try sema.zirIsNonNull(block, inst),
.is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst),
.merge_error_sets => try sema.zirMergeErrorSets(block, inst),
@ -9004,7 +9005,7 @@ fn zirParam(
else => |e| return e,
} or comptime_syntax;
if (sema.inst_map.get(inst)) |arg| {
if (is_comptime) {
if (is_comptime and sema.preallocated_new_func != null) {
// We have a comptime value for this parameter so it should be elided from the
// function type of the function instruction in this block.
const coerced_arg = try sema.coerce(block, param_ty, arg, src);
@ -10288,6 +10289,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
.ret_err_value_code,
.restore_err_ret_index,
.is_non_err,
.ret_is_non_err,
.condbr,
=> {},
else => break,
@ -16215,11 +16217,54 @@ fn typeInfoDecls(
};
try sema.queueFullTypeResolution(try declaration_ty.copy(sema.arena));
const decls_len = if (opt_namespace) |ns| ns.decls.count() else 0;
const decls_vals = try decls_anon_decl.arena().alloc(Value, decls_len);
for (decls_vals) |*decls_val, i| {
const decl_index = opt_namespace.?.decls.keys()[i];
var decl_vals = std.ArrayList(Value).init(sema.gpa);
defer decl_vals.deinit();
var seen_namespaces = std.AutoHashMap(*Namespace, void).init(sema.gpa);
defer seen_namespaces.deinit();
if (opt_namespace) |some| {
try sema.typeInfoNamespaceDecls(block, decls_anon_decl.arena(), some, &decl_vals, &seen_namespaces);
}
const new_decl = try decls_anon_decl.finish(
try Type.Tag.array.create(decls_anon_decl.arena(), .{
.len = decl_vals.items.len,
.elem_type = declaration_ty,
}),
try Value.Tag.aggregate.create(
decls_anon_decl.arena(),
try decls_anon_decl.arena().dupe(Value, decl_vals.items),
),
0, // default alignment
);
return try Value.Tag.slice.create(sema.arena, .{
.ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl),
.len = try Value.Tag.int_u64.create(sema.arena, decl_vals.items.len),
});
}
fn typeInfoNamespaceDecls(
sema: *Sema,
block: *Block,
decls_anon_decl: Allocator,
namespace: *Namespace,
decl_vals: *std.ArrayList(Value),
seen_namespaces: *std.AutoHashMap(*Namespace, void),
) !void {
const gop = try seen_namespaces.getOrPut(namespace);
if (gop.found_existing) return;
const decls = namespace.decls.keys();
for (decls) |decl_index| {
const decl = sema.mod.declPtr(decl_index);
if (decl.kind == .@"usingnamespace") {
try sema.mod.ensureDeclAnalyzed(decl_index);
var buf: Value.ToTypeBuffer = undefined;
const new_ns = decl.val.toType(&buf).getNamespace().?;
try sema.typeInfoNamespaceDecls(block, decls_anon_decl, new_ns, decl_vals, seen_namespaces);
continue;
}
if (decl.kind != .named) continue;
const name_val = v: {
var anon_decl = try block.startAnonDecl();
defer anon_decl.deinit();
@ -16229,37 +16274,21 @@ fn typeInfoDecls(
try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]),
0, // default alignment
);
break :v try Value.Tag.slice.create(decls_anon_decl.arena(), .{
.ptr = try Value.Tag.decl_ref.create(decls_anon_decl.arena(), new_decl),
.len = try Value.Tag.int_u64.create(decls_anon_decl.arena(), bytes.len),
break :v try Value.Tag.slice.create(decls_anon_decl, .{
.ptr = try Value.Tag.decl_ref.create(decls_anon_decl, new_decl),
.len = try Value.Tag.int_u64.create(decls_anon_decl, bytes.len),
});
};
const fields = try decls_anon_decl.arena().create([2]Value);
const fields = try decls_anon_decl.create([2]Value);
fields.* = .{
//name: []const u8,
name_val,
//is_pub: bool,
Value.makeBool(decl.is_pub),
};
decls_val.* = try Value.Tag.aggregate.create(decls_anon_decl.arena(), fields);
try decl_vals.append(try Value.Tag.aggregate.create(decls_anon_decl, fields));
}
const new_decl = try decls_anon_decl.finish(
try Type.Tag.array.create(decls_anon_decl.arena(), .{
.len = decls_vals.len,
.elem_type = declaration_ty,
}),
try Value.Tag.aggregate.create(
decls_anon_decl.arena(),
try decls_anon_decl.arena().dupe(Value, decls_vals),
),
0, // default alignment
);
return try Value.Tag.slice.create(sema.arena, .{
.ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl),
.len = try Value.Tag.int_u64.create(sema.arena, decls_vals.len),
});
}
fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@ -16577,7 +16606,7 @@ fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const src = inst_data.src();
const operand = try sema.resolveInst(inst_data.operand);
try sema.checkErrorType(block, src, sema.typeOf(operand));
return sema.analyzeIsNonErr(block, inst_data.src(), operand);
return sema.analyzeIsNonErr(block, src, operand);
}
fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@ -16592,6 +16621,16 @@ fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
return sema.analyzeIsNonErr(block, src, loaded);
}
fn zirRetIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const operand = try sema.resolveInst(inst_data.operand);
return sema.analyzeIsNonErr(block, src, operand);
}
fn zirCondbr(
sema: *Sema,
parent_block: *Block,
@ -29847,6 +29886,11 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
},
.have_layout, .fully_resolved_wip, .fully_resolved => return,
}
const prev_status = struct_obj.status;
errdefer if (struct_obj.status == .layout_wip) {
struct_obj.status = prev_status;
};
struct_obj.status = .layout_wip;
for (struct_obj.fields.values()) |field, i| {
sema.resolveTypeLayout(field.ty) catch |err| switch (err) {
@ -30026,6 +30070,11 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void {
},
.have_layout, .fully_resolved_wip, .fully_resolved => return,
}
const prev_status = union_obj.status;
errdefer if (union_obj.status == .layout_wip) {
union_obj.status = prev_status;
};
union_obj.status = .layout_wip;
for (union_obj.fields.values()) |field, i| {
sema.resolveTypeLayout(field.ty) catch |err| switch (err) {

View File

@ -481,6 +481,9 @@ pub const Inst = struct {
/// Return a boolean false if dereferenced pointer is an error
/// Uses the `un_node` field.
is_non_err_ptr,
/// Same as `is_non_er` but doesn't validate that the type can be an error.
/// Uses the `un_node` field.
ret_is_non_err,
/// A labeled block of code that loops forever. At the end of the body will have either
/// a `repeat` instruction or a `repeat_inline` instruction.
/// Uses the `pl_node` field. The AST node is either a for loop or while loop.
@ -1083,6 +1086,7 @@ pub const Inst = struct {
.is_non_null_ptr,
.is_non_err,
.is_non_err_ptr,
.ret_is_non_err,
.mod_rem,
.mul,
.mulwrap,
@ -1386,6 +1390,7 @@ pub const Inst = struct {
.is_non_null_ptr,
.is_non_err,
.is_non_err_ptr,
.ret_is_non_err,
.mod_rem,
.mul,
.mulwrap,
@ -1640,6 +1645,7 @@ pub const Inst = struct {
.is_non_null_ptr = .un_node,
.is_non_err = .un_node,
.is_non_err_ptr = .un_node,
.ret_is_non_err = .un_node,
.loop = .pl_node,
.repeat = .node,
.repeat_inline = .node,

View File

@ -179,6 +179,7 @@ const Writer = struct {
.is_non_null_ptr,
.is_non_err,
.is_non_err_ptr,
.ret_is_non_err,
.typeof,
.struct_init_empty,
.type_info,

View File

@ -2193,8 +2193,16 @@ pub const Value = extern union {
const payload_ty = ty.errorUnionPayload();
return eqlAdvanced(a_payload, payload_ty, b_payload, payload_ty, mod, opt_sema);
},
.eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
.opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
.eu_payload_ptr => {
const a_payload = a.castTag(.eu_payload_ptr).?.data;
const b_payload = b.castTag(.eu_payload_ptr).?.data;
return eqlAdvanced(a_payload.container_ptr, ty, b_payload.container_ptr, ty, mod, opt_sema);
},
.opt_payload_ptr => {
const a_payload = a.castTag(.opt_payload_ptr).?.data;
const b_payload = b.castTag(.opt_payload_ptr).?.data;
return eqlAdvanced(a_payload.container_ptr, ty, b_payload.container_ptr, ty, mod, opt_sema);
},
.function => {
const a_payload = a.castTag(.function).?.data;
const b_payload = b.castTag(.function).?.data;

View File

@ -148,3 +148,14 @@ test "simple else prong doesn't emit an error for unreachable else prong" {
};
try expect(a == 1);
}
test "errdefer used in function that doesn't return an error" {
const S = struct {
fn foo() u8 {
var a: u8 = 5;
errdefer a += 1;
return a;
}
};
try expect(S.foo() == 5);
}

View File

@ -566,3 +566,27 @@ test "value from struct @typeInfo default_value can be loaded at comptime" {
try expect(@ptrCast(*const u8, a).* == 1);
}
}
test "@typeInfo decls and usingnamespace" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
const A = struct {
const x = 5;
const y = 34;
comptime {}
};
const B = struct {
usingnamespace A;
const z = 56;
test {}
};
const decls = @typeInfo(B).Struct.decls;
try expect(decls.len == 3);
try expectEqualStrings(decls[0].name, "x");
try expectEqualStrings(decls[1].name, "y");
try expectEqualStrings(decls[2].name, "z");
}

View File

@ -0,0 +1,24 @@
pub fn sort(
comptime T: type,
items: []T,
context: anytype,
lessThan: *const fn (context: @TypeOf(context), lhs: T, rhs: T) u32,
) void {
_ = items;
_ = lessThan;
}
fn foo(_: void, _: u8, _: u8) u32 {
return 0;
}
pub export fn entry() void {
var items = [_]u8{ 3, 5, 7, 2, 6, 9, 4 };
sort(u8, &items, void, foo);
}
// error
// backend=llvm
// target=native
//
// :15:28: error: expected type '*const fn(comptime type, u8, u8) u32', found '*const fn(void, u8, u8) u32'
// :15:28: note: pointer type child 'fn(void, u8, u8) u32' cannot cast into pointer type child 'fn(comptime type, u8, u8) u32'
// :15:28: note: non-generic function cannot cast into a generic function

View File

@ -23,3 +23,4 @@ const T = struct {
// :5:5: error: union field missing name
// :8:5: error: tuple field has a name
// :15:5: error: tuple declarations cannot contain declarations
// :12:5: note: tuple field here