mirror of
https://github.com/ziglang/zig.git
synced 2024-11-26 23:22:44 +00:00
add tests for tuple declarations
This commit is contained in:
parent
4cea15f12b
commit
8eea73fb92
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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), "_")) {
|
||||
|
49
src/Sema.zig
49
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]
|
||||
|
@ -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 };
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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");
|
||||
}
|
||||
|
79
test/behavior/tuple_declarations.zig
Normal file
79
test/behavior/tuple_declarations.zig
Normal file
@ -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");
|
||||
}
|
@ -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
|
||||
|
80
test/cases/compile_errors/reify_struct.zig
Normal file
80
test/cases/compile_errors/reify_struct.zig
Normal file
@ -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
|
@ -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
|
25
test/cases/compile_errors/tuple_declarations.zig
Normal file
25
test/cases/compile_errors/tuple_declarations.zig
Normal file
@ -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
|
@ -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 '+'",
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user