mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 00:35:10 +00:00
Merge pull request #14257 from Vexu/compile-errors
compile error improvements and bug fixes
This commit is contained in:
commit
2a92b04b9d
@ -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,
|
||||
|
||||
@ -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;
|
||||
},
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
103
src/Sema.zig
103
src/Sema.zig
@ -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) {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user