diff --git a/src/AstGen.zig b/src/AstGen.zig index 3398478266..943d0aad08 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -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; diff --git a/src/Sema.zig b/src/Sema.zig index 047d3f84c2..f884684d73 100644 --- a/src/Sema.zig +++ b/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); }, diff --git a/src/print_zir.zig b/src/print_zir.zig index 579a7970b7..f315d7f014 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -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; diff --git a/src/type.zig b/src/type.zig index cc6e5706ee..5ff9da8913 100644 --- a/src/type.zig +++ b/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); }, diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 9e96163cc0..517414780b 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -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)); +} diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index 9bc1c1f824..bd312e9cda 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -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(); +} diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index 971e52a0b5..2b715c3b23 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -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); +} diff --git a/test/cases/compile_errors/access_inactive_union_field_comptime.zig b/test/cases/compile_errors/access_inactive_union_field_comptime.zig new file mode 100644 index 0000000000..d990a85f9e --- /dev/null +++ b/test/cases/compile_errors/access_inactive_union_field_comptime.zig @@ -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 diff --git a/test/cases/compile_errors/duplicate_field_in_discarded_anon_init.zig b/test/cases/compile_errors/duplicate_field_in_discarded_anon_init.zig new file mode 100644 index 0000000000..6f850e6fe4 --- /dev/null +++ b/test/cases/compile_errors/duplicate_field_in_discarded_anon_init.zig @@ -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 diff --git a/test/cases/compile_errors/function_type_named.zig b/test/cases/compile_errors/function_type_named.zig new file mode 100644 index 0000000000..ad670fe88c --- /dev/null +++ b/test/cases/compile_errors/function_type_named.zig @@ -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 diff --git a/test/cases/compile_errors/struct_init_passed_to_type_param.zig b/test/cases/compile_errors/struct_init_passed_to_type_param.zig new file mode 100644 index 0000000000..b00c27986f --- /dev/null +++ b/test/cases/compile_errors/struct_init_passed_to_type_param.zig @@ -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 diff --git a/test/cases/compile_errors/switch_on_slice.zig b/test/cases/compile_errors/switch_on_slice.zig new file mode 100644 index 0000000000..b4644b132c --- /dev/null +++ b/test/cases/compile_errors/switch_on_slice.zig @@ -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'