Commit 8eea73fb92
Changed files (14)
lib
std
src
test
lib/std/zig/parse.zig
@@ -884,7 +884,7 @@ const Parser = struct {
var align_expr: Node.Index = 0;
var type_expr: Node.Index = 0;
- if (p.eatToken(.colon) != null or tuple_like) |_| {
+ if (p.eatToken(.colon) != null or tuple_like) {
type_expr = try p.expectTypeExpr();
align_expr = try p.parseByteAlign();
}
lib/std/zig/render.zig
@@ -1189,6 +1189,16 @@ fn renderContainerField(
try renderToken(ais, tree, t, .space); // comptime
}
if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {
+ if (field.ast.align_expr != 0) {
+ try renderIdentifier(ais, tree, field.ast.main_token, .space, .eagerly_unquote); // name
+ const lparen_token = tree.firstToken(field.ast.align_expr) - 1;
+ const align_kw = lparen_token - 1;
+ const rparen_token = tree.lastToken(field.ast.align_expr) + 1;
+ try renderToken(ais, tree, align_kw, .none); // align
+ try renderToken(ais, tree, lparen_token, .none); // (
+ try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment
+ return renderToken(ais, tree, rparen_token, .space); // )
+ }
return renderIdentifierComma(ais, tree, field.ast.main_token, space, .eagerly_unquote); // name
}
if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {
@@ -1211,6 +1221,15 @@ fn renderContainerField(
}
if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {
try renderIdentifier(ais, tree, field.ast.main_token, .space, .eagerly_unquote); // name
+ if (field.ast.align_expr != 0) {
+ const lparen_token = tree.firstToken(field.ast.align_expr) - 1;
+ const align_kw = lparen_token - 1;
+ const rparen_token = tree.lastToken(field.ast.align_expr) + 1;
+ try renderToken(ais, tree, align_kw, .none); // align
+ try renderToken(ais, tree, lparen_token, .none); // (
+ try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment
+ try renderToken(ais, tree, rparen_token, .space); // )
+ }
try renderToken(ais, tree, field.ast.main_token + 1, .space); // =
return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
}
src/arch/x86_64/abi.zig
@@ -552,6 +552,7 @@ test "C_C_D" {
.layout = .Extern,
.status = .fully_resolved,
.known_non_opv = true,
+ .is_tuple = false,
};
var C_C_D = Type.Payload.Struct{ .data = &C_C_D_struct };
src/AstGen.zig
@@ -4854,8 +4854,9 @@ fn containerDecl(
},
);
}
- // Alignment expressions in enums are caught by the parser.
- assert(member.ast.align_expr == 0);
+ if (member.ast.align_expr != 0) {
+ return astgen.failNode(member.ast.align_expr, "enum fields cannot be aligned", .{});
+ }
const name_token = member.ast.main_token;
if (mem.eql(u8, tree.tokenSlice(name_token), "_")) {
src/Sema.zig
@@ -11834,6 +11834,12 @@ fn analyzeTupleCat(
if (dest_fields == 0) {
return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value));
}
+ if (lhs_len == 0) {
+ return rhs;
+ }
+ if (rhs_len == 0) {
+ return lhs;
+ }
const final_len = try sema.usizeCast(block, rhs_src, dest_fields);
const types = try sema.arena.alloc(Type, final_len);
@@ -11880,13 +11886,13 @@ fn analyzeTupleCat(
var i: u32 = 0;
while (i < lhs_len) : (i += 1) {
const operand_src = lhs_src; // TODO better source location
- element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, lhs, @intCast(u32, i), lhs_ty);
+ element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, lhs, i, lhs_ty);
}
i = 0;
while (i < rhs_len) : (i += 1) {
const operand_src = rhs_src; // TODO better source location
element_refs[i + lhs_len] =
- try sema.tupleFieldValByIndex(block, operand_src, rhs, @intCast(u32, i), rhs_ty);
+ try sema.tupleFieldValByIndex(block, operand_src, rhs, i, rhs_ty);
}
return block.addAggregateInit(tuple_ty, element_refs);
@@ -18502,8 +18508,12 @@ fn reifyStruct(
}
const abi_align = @intCast(u29, (try alignment_val.getUnsignedIntAdvanced(target, sema)).?);
- if (layout == .Packed and abi_align != 0) {
- return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{});
+ if (layout == .Packed) {
+ if (abi_align != 0) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{});
+ if (is_comptime_val.toBool()) return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{});
+ }
+ if (layout == .Extern and is_comptime_val.toBool()) {
+ return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{});
}
const field_name = try name_val.toAllocatedBytes(
@@ -18512,6 +18522,25 @@ fn reifyStruct(
mod,
);
+ if (is_tuple) {
+ const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch {
+ return sema.fail(
+ block,
+ src,
+ "tuple cannot have non-numeric field '{s}'",
+ .{field_name},
+ );
+ };
+
+ if (field_index >= fields_len) {
+ return sema.fail(
+ block,
+ src,
+ "tuple field {} exceeds tuple field count",
+ .{field_index},
+ );
+ }
+ }
const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
if (gop.found_existing) {
// TODO: better source location
@@ -18525,6 +18554,9 @@ fn reifyStruct(
opt_val;
break :blk try payload_val.copy(new_decl_arena_allocator);
} else Value.initTag(.unreachable_value);
+ if (is_comptime_val.toBool() and default_val.tag() == .unreachable_value) {
+ return sema.fail(block, src, "comptime field without default initialization value", .{});
+ }
var buffer: Value.ToTypeBuffer = undefined;
gop.value_ptr.* = .{
@@ -27094,7 +27126,7 @@ fn coerceTupleToStruct(
const inst_ty = sema.typeOf(inst);
var runtime_src: ?LazySrcLoc = null;
- const field_count = struct_ty.structFieldCount();
+ const field_count = inst_ty.structFieldCount();
var field_i: u32 = 0;
while (field_i < field_count) : (field_i += 1) {
const field_src = inst_src; // TODO better source location
@@ -27176,15 +27208,16 @@ fn coerceTupleToTuple(
inst: Air.Inst.Ref,
inst_src: LazySrcLoc,
) !Air.Inst.Ref {
- const field_count = tuple_ty.structFieldCount();
- const field_vals = try sema.arena.alloc(Value, field_count);
+ const dest_field_count = tuple_ty.structFieldCount();
+ const field_vals = try sema.arena.alloc(Value, dest_field_count);
const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
mem.set(Air.Inst.Ref, field_refs, .none);
const inst_ty = sema.typeOf(inst);
+ const inst_field_count = inst_ty.structFieldCount();
var runtime_src: ?LazySrcLoc = null;
var field_i: u32 = 0;
- while (field_i < field_count) : (field_i += 1) {
+ while (field_i < inst_field_count) : (field_i += 1) {
const field_src = inst_src; // TODO better source location
const field_name = if (inst_ty.castTag(.anon_struct)) |payload|
payload.data.names[field_i]
src/type.zig
@@ -804,7 +804,7 @@ pub const Type = extern union {
return a_struct_obj == b_struct_obj;
},
.tuple, .empty_struct_literal => {
- if (!b.isTuple()) return false;
+ if (!b.isSimpleTuple()) return false;
const a_tuple = a.tupleFields();
const b_tuple = b.tupleFields();
@@ -5572,7 +5572,11 @@ pub const Type = extern union {
pub fn structFieldCount(ty: Type) usize {
switch (ty.tag()) {
- .@"struct" => return ty.castTag(.@"struct").?.data.fields.count(),
+ .@"struct" => {
+ const struct_obj = ty.castTag(.@"struct").?.data;
+ assert(struct_obj.haveFieldTypes());
+ return struct_obj.fields.count();
+ },
.empty_struct, .empty_struct_literal => return 0,
.tuple => return ty.castTag(.tuple).?.data.types.len,
.anon_struct => return ty.castTag(.anon_struct).?.data.types.len,
test/behavior/tuple_declarations.zig
@@ -0,0 +1,79 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const testing = std.testing;
+const expect = testing.expect;
+const expectEqualStrings = testing.expectEqualStrings;
+
+test "tuple declaration type info" {
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
+
+ {
+ const T = struct { comptime u32 align(2) = 1, []const u8 };
+ const info = @typeInfo(T).Struct;
+
+ try expect(info.layout == .Auto);
+ try expect(info.backing_integer == null);
+ try expect(info.fields.len == 2);
+ try expect(info.decls.len == 0);
+ try expect(info.is_tuple);
+
+ try expectEqualStrings(info.fields[0].name, "0");
+ try expect(info.fields[0].field_type == u32);
+ try expect(@ptrCast(*const u32, @alignCast(@alignOf(u32), info.fields[0].default_value)).* == 1);
+ try expect(info.fields[0].is_comptime);
+ try expect(info.fields[0].alignment == 2);
+
+ try expectEqualStrings(info.fields[1].name, "1");
+ try expect(info.fields[1].field_type == []const u8);
+ try expect(info.fields[1].default_value == null);
+ try expect(!info.fields[1].is_comptime);
+ try expect(info.fields[1].alignment == @alignOf([]const u8));
+ }
+ {
+ const T = packed struct(u32) { u1, u30, u1 };
+ const info = @typeInfo(T).Struct;
+
+ try expect(std.mem.endsWith(u8, @typeName(T), "test.tuple declaration type info.T"));
+
+ try expect(info.layout == .Packed);
+ try expect(info.backing_integer == u32);
+ try expect(info.fields.len == 3);
+ try expect(info.decls.len == 0);
+ try expect(info.is_tuple);
+
+ try expectEqualStrings(info.fields[0].name, "0");
+ try expect(info.fields[0].field_type == u1);
+
+ try expectEqualStrings(info.fields[1].name, "1");
+ try expect(info.fields[1].field_type == u30);
+
+ try expectEqualStrings(info.fields[2].name, "2");
+ try expect(info.fields[2].field_type == u1);
+ }
+}
+
+test "Tuple declaration usage" {
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
+
+ const T = struct { u32, []const u8 };
+ var t: T = .{ 1, "foo" };
+ try expect(t[0] == 1);
+ try expectEqualStrings(t[1], "foo");
+
+ var mul = t ** 3;
+ try expect(@TypeOf(mul) != T);
+ try expect(mul.len == 6);
+ try expect(mul[2] == 1);
+ try expectEqualStrings(mul[3], "foo");
+
+ var t2: T = .{ 2, "bar" };
+ var cat = t ++ t2;
+ try expect(@TypeOf(cat) != T);
+ try expect(cat.len == 4);
+ try expect(cat[2] == 2);
+ try expectEqualStrings(cat[3], "bar");
+}
test/cases/compile_errors/alignment_of_enum_field_specified.zig
@@ -11,4 +11,4 @@ export fn entry1() void {
// backend=stage2
// target=native
//
-// :3:7: error: expected ',' after field
+// :3:13: error: enum fields cannot be aligned
test/cases/compile_errors/reify_struct.zig
@@ -0,0 +1,80 @@
+comptime {
+ @Type(.{ .Struct = .{
+ .layout = .Auto,
+ .fields = &.{.{
+ .name = "foo",
+ .field_type = u32,
+ .default_value = null,
+ .is_comptime = false,
+ .alignment = 4,
+ }},
+ .decls = &.{},
+ .is_tuple = true,
+ } });
+}
+comptime {
+ @Type(.{ .Struct = .{
+ .layout = .Auto,
+ .fields = &.{.{
+ .name = "3",
+ .field_type = u32,
+ .default_value = null,
+ .is_comptime = false,
+ .alignment = 4,
+ }},
+ .decls = &.{},
+ .is_tuple = true,
+ } });
+}
+comptime {
+ @Type(.{ .Struct = .{
+ .layout = .Auto,
+ .fields = &.{.{
+ .name = "0",
+ .field_type = u32,
+ .default_value = null,
+ .is_comptime = true,
+ .alignment = 4,
+ }},
+ .decls = &.{},
+ .is_tuple = true,
+ } });
+}
+comptime {
+ @Type(.{ .Struct = .{
+ .layout = .Extern,
+ .fields = &.{.{
+ .name = "0",
+ .field_type = u32,
+ .default_value = null,
+ .is_comptime = true,
+ .alignment = 4,
+ }},
+ .decls = &.{},
+ .is_tuple = true,
+ } });
+}
+comptime {
+ @Type(.{ .Struct = .{
+ .layout = .Packed,
+ .fields = &.{.{
+ .name = "0",
+ .field_type = u32,
+ .default_value = null,
+ .is_comptime = true,
+ .alignment = 4,
+ }},
+ .decls = &.{},
+ .is_tuple = true,
+ } });
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :2:5: error: tuple cannot have non-numeric field 'foo'
+// :16:5: error: tuple field 3 exceeds tuple field count
+// :30:5: error: comptime field without default initialization value
+// :44:5: error: extern struct fields cannot be marked comptime
+// :58:5: error: alignment in a packed struct field must be set to 0
test/cases/compile_errors/struct_field_missing_type.zig
@@ -1,13 +0,0 @@
-const Letter = struct {
- A,
-};
-export fn entry() void {
- var a = Letter { .A = {} };
- _ = a;
-}
-
-// error
-// backend=stage2
-// target=native
-//
-// :2:5: error: struct field missing type
test/cases/compile_errors/tuple_declarations.zig
@@ -0,0 +1,25 @@
+const E = enum {
+ *u32,
+};
+const U = union {
+ *u32,
+};
+const S = struct {
+ a: u32,
+ *u32,
+};
+const T = struct {
+ u32,
+ []const u8,
+
+ const a = 1;
+};
+
+// error
+// backend=stage2
+// target=native
+//
+// :2:5: error: enum field missing name
+// :5:5: error: union field missing name
+// :8:5: error: tuple field has a name
+// :15:5: error: tuple declarations cannot contain declarations
test/stage2/cbe.zig
@@ -670,7 +670,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = E1.a;
\\}
, &.{
- ":3:7: error: expected ',' after field",
+ ":3:13: error: enum fields cannot be aligned",
});
// Redundant non-exhaustive enum mark.
test/behavior.zig
@@ -200,6 +200,7 @@ test {
_ = @import("behavior/packed_struct_explicit_backing_int.zig");
_ = @import("behavior/empty_union.zig");
_ = @import("behavior/inline_switch.zig");
+ _ = @import("behavior/tuple_declarations.zig");
_ = @import("behavior/bugs/12723.zig");
_ = @import("behavior/bugs/12776.zig");
}
test/compile_errors.zig
@@ -213,7 +213,7 @@ pub fn addCases(ctx: *TestContext) !void {
case.backend = .stage2;
case.addSourceFile("b.zig",
- \\bad
+ \\+
);
case.addError(
@@ -221,7 +221,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = (@sizeOf(@import("b.zig")));
\\}
, &[_][]const u8{
- ":1:1: error: struct field missing type",
+ ":1:1: error: expected type expression, found '+'",
});
}