mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
commit
9cca6728e5
@ -1164,6 +1164,10 @@ fn fnProtoExpr(
|
||||
const tree = astgen.tree;
|
||||
const token_tags = tree.tokens.items(.tag);
|
||||
|
||||
if (fn_proto.name_token) |some| {
|
||||
return astgen.failTok(some, "function type cannot have a name", .{});
|
||||
}
|
||||
|
||||
const is_extern = blk: {
|
||||
const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
|
||||
break :blk token_tags[maybe_extern_token] == .keyword_extern;
|
||||
|
||||
53
src/Sema.zig
53
src/Sema.zig
@ -2497,18 +2497,6 @@ fn zirEnumDecl(
|
||||
extra_index = try mod.scanNamespace(&enum_obj.namespace, extra_index, decls_len, new_decl);
|
||||
|
||||
const body = sema.code.extra[extra_index..][0..body_len];
|
||||
if (fields_len == 0) {
|
||||
assert(body.len == 0);
|
||||
if (tag_type_ref != .none) {
|
||||
const ty = try sema.resolveType(block, tag_ty_src, tag_type_ref);
|
||||
if (ty.zigTypeTag() != .Int and ty.zigTypeTag() != .ComptimeInt) {
|
||||
return sema.fail(block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(sema.mod)});
|
||||
}
|
||||
enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator);
|
||||
enum_obj.tag_ty_inferred = false;
|
||||
}
|
||||
return decl_val;
|
||||
}
|
||||
extra_index += body.len;
|
||||
|
||||
const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
|
||||
@ -2566,6 +2554,9 @@ fn zirEnumDecl(
|
||||
}
|
||||
enum_obj.tag_ty = try ty.copy(decl_arena_allocator);
|
||||
enum_obj.tag_ty_inferred = false;
|
||||
} else if (fields_len == 0) {
|
||||
enum_obj.tag_ty = try Type.Tag.int_unsigned.create(decl_arena_allocator, 0);
|
||||
enum_obj.tag_ty_inferred = true;
|
||||
} else {
|
||||
const bits = std.math.log2_int_ceil(usize, fields_len);
|
||||
enum_obj.tag_ty = try Type.Tag.int_unsigned.create(decl_arena_allocator, bits);
|
||||
@ -3788,6 +3779,7 @@ fn validateStructInit(
|
||||
if ((is_comptime or block.is_comptime) and
|
||||
(try sema.resolveDefinedValue(block, init_src, struct_ptr)) != null)
|
||||
{
|
||||
try sema.resolveStructLayout(block, init_src, struct_ty);
|
||||
// In this case the only thing we need to do is evaluate the implicit
|
||||
// store instructions for default field values, and report any missing fields.
|
||||
// Avoid the cost of the extra machinery for detecting a comptime struct init value.
|
||||
@ -3982,6 +3974,7 @@ fn validateStructInit(
|
||||
try sema.storePtr2(block, init_src, struct_ptr, init_src, struct_init, init_src, .store);
|
||||
return;
|
||||
}
|
||||
try sema.resolveStructLayout(block, init_src, struct_ty);
|
||||
|
||||
// Our task is to insert `store` instructions for all the default field values.
|
||||
for (found_fields) |field_ptr, i| {
|
||||
@ -9003,6 +8996,9 @@ fn zirSwitchCond(
|
||||
.ErrorSet,
|
||||
.Enum,
|
||||
=> {
|
||||
if (operand_ty.isSlice()) {
|
||||
return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(sema.mod)});
|
||||
}
|
||||
if ((try sema.typeHasOnePossibleValue(block, operand_src, operand_ty))) |opv| {
|
||||
return sema.addConstant(operand_ty, opv);
|
||||
}
|
||||
@ -15849,6 +15845,7 @@ fn finishStructInit(
|
||||
}
|
||||
|
||||
if (is_ref) {
|
||||
try sema.resolveStructLayout(block, dest_src, struct_ty);
|
||||
const target = sema.mod.getTarget();
|
||||
const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{
|
||||
.pointee_type = struct_ty,
|
||||
@ -21895,17 +21892,18 @@ fn unionFieldPtr(
|
||||
if (union_val.isUndef()) {
|
||||
return sema.failWithUseOfUndef(block, src);
|
||||
}
|
||||
const enum_field_index = union_obj.tag_ty.enumFieldIndex(field_name).?;
|
||||
const tag_and_val = union_val.castTag(.@"union").?.data;
|
||||
var field_tag_buf: Value.Payload.U32 = .{
|
||||
.base = .{ .tag = .enum_field_index },
|
||||
.data = field_index,
|
||||
.data = @intCast(u32, enum_field_index),
|
||||
};
|
||||
const field_tag = Value.initPayload(&field_tag_buf.base);
|
||||
const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod);
|
||||
if (!tag_matches) {
|
||||
const msg = msg: {
|
||||
const active_index = tag_and_val.tag.castTag(.enum_field_index).?.data;
|
||||
const active_field_name = union_obj.fields.keys()[active_index];
|
||||
const active_field_name = union_obj.tag_ty.enumFieldName(active_index);
|
||||
const msg = try sema.errMsg(block, src, "access of union field '{s}' while field '{s}' is active", .{ field_name, active_field_name });
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.addDeclaredHereNote(msg, union_ty);
|
||||
@ -21930,12 +21928,11 @@ fn unionFieldPtr(
|
||||
if (!initializing and union_obj.layout == .Auto and block.wantSafety() and
|
||||
union_ty.unionTagTypeSafety() != null and union_obj.fields.count() > 1)
|
||||
{
|
||||
const enum_ty = union_ty.unionTagTypeHypothetical();
|
||||
const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
|
||||
const wanted_tag = try sema.addConstant(enum_ty, wanted_tag_val);
|
||||
const wanted_tag = try sema.addConstant(union_obj.tag_ty, wanted_tag_val);
|
||||
// TODO would it be better if get_union_tag supported pointers to unions?
|
||||
const union_val = try block.addTyOp(.load, union_ty, union_ptr);
|
||||
const active_tag = try block.addTyOp(.get_union_tag, enum_ty, union_val);
|
||||
const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_val);
|
||||
const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag);
|
||||
try sema.addSafetyCheck(block, ok, .inactive_union_field);
|
||||
}
|
||||
@ -21966,9 +21963,10 @@ fn unionFieldVal(
|
||||
if (union_val.isUndef()) return sema.addConstUndef(field.ty);
|
||||
|
||||
const tag_and_val = union_val.castTag(.@"union").?.data;
|
||||
const enum_field_index = union_obj.tag_ty.enumFieldIndex(field_name).?;
|
||||
var field_tag_buf: Value.Payload.U32 = .{
|
||||
.base = .{ .tag = .enum_field_index },
|
||||
.data = field_index,
|
||||
.data = @intCast(u32, enum_field_index),
|
||||
};
|
||||
const field_tag = Value.initPayload(&field_tag_buf.base);
|
||||
const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod);
|
||||
@ -21979,7 +21977,7 @@ fn unionFieldVal(
|
||||
} else {
|
||||
const msg = msg: {
|
||||
const active_index = tag_and_val.tag.castTag(.enum_field_index).?.data;
|
||||
const active_field_name = union_obj.fields.keys()[active_index];
|
||||
const active_field_name = union_obj.tag_ty.enumFieldName(active_index);
|
||||
const msg = try sema.errMsg(block, src, "access of union field '{s}' while field '{s}' is active", .{ field_name, active_field_name });
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.addDeclaredHereNote(msg, union_ty);
|
||||
@ -22004,10 +22002,9 @@ fn unionFieldVal(
|
||||
if (union_obj.layout == .Auto and block.wantSafety() and
|
||||
union_ty.unionTagTypeSafety() != null and union_obj.fields.count() > 1)
|
||||
{
|
||||
const enum_ty = union_ty.unionTagTypeHypothetical();
|
||||
const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
|
||||
const wanted_tag = try sema.addConstant(enum_ty, wanted_tag_val);
|
||||
const active_tag = try block.addTyOp(.get_union_tag, enum_ty, union_byval);
|
||||
const wanted_tag = try sema.addConstant(union_obj.tag_ty, wanted_tag_val);
|
||||
const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_byval);
|
||||
const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag);
|
||||
try sema.addSafetyCheck(block, ok, .inactive_union_field);
|
||||
}
|
||||
@ -22254,8 +22251,7 @@ fn tupleField(
|
||||
|
||||
if (try sema.resolveMaybeUndefVal(block, tuple_src, tuple)) |tuple_val| {
|
||||
if (tuple_val.isUndef()) return sema.addConstUndef(field_ty);
|
||||
const field_values = tuple_val.castTag(.aggregate).?.data;
|
||||
return sema.addConstant(field_ty, field_values[field_index]);
|
||||
return sema.addConstant(field_ty, tuple_val.fieldValue(tuple_ty, field_index));
|
||||
}
|
||||
|
||||
try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_src);
|
||||
@ -28854,10 +28850,11 @@ pub fn typeHasOnePossibleValue(
|
||||
|
||||
.tuple, .anon_struct => {
|
||||
const tuple = ty.tupleFields();
|
||||
for (tuple.values) |val| {
|
||||
if (val.tag() == .unreachable_value) {
|
||||
return null; // non-comptime field
|
||||
}
|
||||
for (tuple.values) |val, i| {
|
||||
const is_comptime = val.tag() != .unreachable_value;
|
||||
if (is_comptime) continue;
|
||||
if ((try sema.typeHasOnePossibleValue(block, src, tuple.types[i])) != null) continue;
|
||||
return null;
|
||||
}
|
||||
return Value.initTag(.empty_struct_value);
|
||||
},
|
||||
|
||||
@ -1721,13 +1721,13 @@ const Writer = struct {
|
||||
const body = self.code.extra[extra_index..][0..body_len];
|
||||
extra_index += body.len;
|
||||
|
||||
const prev_parent_decl_node = self.parent_decl_node;
|
||||
if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off);
|
||||
try self.writeBracedDecl(stream, body);
|
||||
if (fields_len == 0) {
|
||||
assert(body.len == 0);
|
||||
try stream.writeAll("{}, {})");
|
||||
try stream.writeAll(", {})");
|
||||
self.parent_decl_node = prev_parent_decl_node;
|
||||
} else {
|
||||
const prev_parent_decl_node = self.parent_decl_node;
|
||||
if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off);
|
||||
try self.writeBracedDecl(stream, body);
|
||||
try stream.writeAll(", {\n");
|
||||
|
||||
self.indent += 2;
|
||||
|
||||
15
src/type.zig
15
src/type.zig
@ -4979,19 +4979,20 @@ pub const Type = extern union {
|
||||
const s = ty.castTag(.@"struct").?.data;
|
||||
assert(s.haveFieldTypes());
|
||||
for (s.fields.values()) |field| {
|
||||
if (field.ty.onePossibleValue() == null) {
|
||||
return null;
|
||||
}
|
||||
if (field.is_comptime) continue;
|
||||
if (field.ty.onePossibleValue() != null) continue;
|
||||
return null;
|
||||
}
|
||||
return Value.initTag(.empty_struct_value);
|
||||
},
|
||||
|
||||
.tuple, .anon_struct => {
|
||||
const tuple = ty.tupleFields();
|
||||
for (tuple.values) |val| {
|
||||
if (val.tag() == .unreachable_value) {
|
||||
return null; // non-comptime field
|
||||
}
|
||||
for (tuple.values) |val, i| {
|
||||
const is_comptime = val.tag() != .unreachable_value;
|
||||
if (is_comptime) continue;
|
||||
if (tuple.types[i].onePossibleValue() != null) continue;
|
||||
return null;
|
||||
}
|
||||
return Value.initTag(.empty_struct_value);
|
||||
},
|
||||
|
||||
@ -1170,3 +1170,8 @@ test "switch on an extern enum with negative value" {
|
||||
Foo.Bar => return,
|
||||
}
|
||||
}
|
||||
|
||||
test "Non-exhaustive enum with nonstandard int size behaves correctly" {
|
||||
const E = enum(u15) { _ };
|
||||
try expect(@sizeOf(E) == @sizeOf(u15));
|
||||
}
|
||||
|
||||
@ -564,3 +564,18 @@ test "nested packed struct field access test" {
|
||||
try std.testing.expect(arg.g.h == 6);
|
||||
try std.testing.expect(arg.g.i == 8);
|
||||
}
|
||||
|
||||
test "runtime init of unnamed packed struct type" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
|
||||
var z: u8 = 123;
|
||||
try (packed struct {
|
||||
x: u8,
|
||||
pub fn m(s: @This()) !void {
|
||||
try expect(s.x == 123);
|
||||
}
|
||||
}{ .x = z }).m();
|
||||
}
|
||||
|
||||
@ -301,3 +301,30 @@ test "tuple type with void field" {
|
||||
const x = T{{}};
|
||||
try expect(@TypeOf(x[0]) == void);
|
||||
}
|
||||
|
||||
test "zero sized struct in tuple handled correctly" {
|
||||
const State = struct {
|
||||
const Self = @This();
|
||||
data: @Type(.{
|
||||
.Struct = .{
|
||||
.is_tuple = true,
|
||||
.layout = .Auto,
|
||||
.decls = &.{},
|
||||
.fields = &.{.{
|
||||
.name = "0",
|
||||
.field_type = struct {},
|
||||
.default_value = null,
|
||||
.is_comptime = false,
|
||||
.alignment = 0,
|
||||
}},
|
||||
},
|
||||
}),
|
||||
|
||||
pub fn do(this: Self) usize {
|
||||
return @sizeOf(@TypeOf(this));
|
||||
}
|
||||
};
|
||||
|
||||
var s: State = undefined;
|
||||
try expect(s.do() == 0);
|
||||
}
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
const Enum = enum(u32) { a, b };
|
||||
const TaggedUnion = union(Enum) {
|
||||
b: []const u8,
|
||||
a: []const u8,
|
||||
};
|
||||
pub export fn entry() void {
|
||||
const result = TaggedUnion{ .b = "b" };
|
||||
_ = result.b;
|
||||
_ = result.a;
|
||||
}
|
||||
pub export fn entry1() void {
|
||||
const result = TaggedUnion{ .b = "b" };
|
||||
_ = &result.b;
|
||||
_ = &result.a;
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :9:15: error: access of union field 'a' while field 'b' is active
|
||||
// :2:21: note: union declared here
|
||||
// :14:16: error: access of union field 'a' while field 'b' is active
|
||||
@ -0,0 +1,10 @@
|
||||
pub export fn entry() void {
|
||||
_ = .{ .a = 0, .a = 1 };
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :2:21: error: duplicate field
|
||||
// :2:13: note: other field here
|
||||
7
test/cases/compile_errors/function_type_named.zig
Normal file
7
test/cases/compile_errors/function_type_named.zig
Normal file
@ -0,0 +1,7 @@
|
||||
const aFunc = fn someFunc(x: i32) void;
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :1:18: error: function type cannot have a name
|
||||
@ -0,0 +1,14 @@
|
||||
const MyStruct = struct { x: i32 };
|
||||
|
||||
fn hi(comptime T: type) usize {
|
||||
return @sizeOf(T);
|
||||
}
|
||||
|
||||
export const value = hi(MyStruct{ .x = 12 });
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :7:33: error: expected type 'type', found 'tmp.MyStruct'
|
||||
// :1:18: note: struct declared here
|
||||
13
test/cases/compile_errors/switch_on_slice.zig
Normal file
13
test/cases/compile_errors/switch_on_slice.zig
Normal file
@ -0,0 +1,13 @@
|
||||
pub export fn entry() void {
|
||||
var a: [:0]const u8 = "foo";
|
||||
switch (a) {
|
||||
"--version", "version" => unreachable,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :3:13: error: switch on type '[:0]const u8'
|
||||
Loading…
x
Reference in New Issue
Block a user