authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-02-28 10:09:23+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-28 13:09:14-07:00
logdfeffcfbf825c29d89ccec281ab95dd2383317ac
tree64b86ad830e83fea4b1acf27e5aef17e265f66f4
parent3a65fa269f476ecb9dafd8be77ee9f9c0c4ba996

stage2: tuple mul/cat


4 files changed, 196 insertions(+), 19 deletions(-)

src/Sema.zig+153
......@@ -8059,6 +8059,78 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
80598059 return block.addTyOp(.not, operand_type, operand);
80608060}
80618061
8062fn analyzeTupleCat(
8063 sema: *Sema,
8064 block: *Block,
8065 src_node: i32,
8066 lhs: Air.Inst.Ref,
8067 rhs: Air.Inst.Ref,
8068) CompileError!Air.Inst.Ref {
8069 const lhs_ty = sema.typeOf(lhs);
8070 const rhs_ty = sema.typeOf(rhs);
8071 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node };
8072 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node };
8073
8074 const lhs_tuple = lhs_ty.tupleFields();
8075 const rhs_tuple = rhs_ty.tupleFields();
8076 const dest_fields = lhs_tuple.types.len + rhs_tuple.types.len;
8077
8078 if (dest_fields == 0) {
8079 return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value));
8080 }
8081
8082 const types = try sema.arena.alloc(Type, dest_fields);
8083 const values = try sema.arena.alloc(Value, dest_fields);
8084
8085 const opt_runtime_src = rs: {
8086 var runtime_src: ?LazySrcLoc = null;
8087 for (lhs_tuple.types) |ty, i| {
8088 types[i] = ty;
8089 values[i] = lhs_tuple.values[i];
8090 const operand_src = lhs_src; // TODO better source location
8091 if (values[i].tag() == .unreachable_value) {
8092 runtime_src = operand_src;
8093 }
8094 }
8095 const offset = lhs_tuple.types.len;
8096 for (rhs_tuple.types) |ty, i| {
8097 types[i + offset] = ty;
8098 values[i + offset] = rhs_tuple.values[i];
8099 const operand_src = rhs_src; // TODO better source location
8100 if (rhs_tuple.values[i].tag() == .unreachable_value) {
8101 runtime_src = operand_src;
8102 }
8103 }
8104 break :rs runtime_src;
8105 };
8106
8107 const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{
8108 .types = types,
8109 .values = values,
8110 });
8111
8112 const runtime_src = opt_runtime_src orelse {
8113 const tuple_val = try Value.Tag.@"struct".create(sema.arena, values);
8114 return sema.addConstant(tuple_ty, tuple_val);
8115 };
8116
8117 try sema.requireRuntimeBlock(block, runtime_src);
8118
8119 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_fields);
8120 for (lhs_tuple.types) |_, i| {
8121 const operand_src = lhs_src; // TODO better source location
8122 element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, lhs, @intCast(u32, i), lhs_ty);
8123 }
8124 const offset = lhs_tuple.types.len;
8125 for (rhs_tuple.types) |_, i| {
8126 const operand_src = rhs_src; // TODO better source location
8127 element_refs[i + offset] =
8128 try sema.tupleFieldValByIndex(block, operand_src, rhs, @intCast(u32, i), rhs_ty);
8129 }
8130
8131 return block.addAggregateInit(tuple_ty, element_refs);
8132}
8133
80628134fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
80638135 const tracy = trace(@src());
80648136 defer tracy.end();
......@@ -8069,6 +8141,11 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
80698141 const rhs = sema.resolveInst(extra.rhs);
80708142 const lhs_ty = sema.typeOf(lhs);
80718143 const rhs_ty = sema.typeOf(rhs);
8144
8145 if (lhs_ty.isTuple() and rhs_ty.isTuple()) {
8146 return sema.analyzeTupleCat(block, inst_data.src_node, lhs, rhs);
8147 }
8148
80728149 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
80738150 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
80748151
......@@ -8165,6 +8242,72 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, inst: Air.Inst.R
81658242 };
81668243}
81678244
8245fn analyzeTupleMul(
8246 sema: *Sema,
8247 block: *Block,
8248 src_node: i32,
8249 operand: Air.Inst.Ref,
8250 factor: u64,
8251) CompileError!Air.Inst.Ref {
8252 const operand_ty = sema.typeOf(operand);
8253 const operand_tuple = operand_ty.tupleFields();
8254 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node };
8255 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node };
8256
8257 const tuple_len = operand_tuple.types.len;
8258 const final_len_u64 = std.math.mul(u64, tuple_len, factor) catch
8259 return sema.fail(block, rhs_src, "operation results in overflow", .{});
8260
8261 if (final_len_u64 == 0) {
8262 return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value));
8263 }
8264
8265 const types = try sema.arena.alloc(Type, final_len_u64);
8266 const values = try sema.arena.alloc(Value, final_len_u64);
8267
8268 const opt_runtime_src = rs: {
8269 var runtime_src: ?LazySrcLoc = null;
8270 for (operand_tuple.types) |ty, i| {
8271 types[i] = ty;
8272 values[i] = operand_tuple.values[i];
8273 const operand_src = lhs_src; // TODO better source location
8274 if (values[i].tag() == .unreachable_value) {
8275 runtime_src = operand_src;
8276 }
8277 }
8278 var i: usize = 1;
8279 while (i < factor) : (i += 1) {
8280 mem.copy(Type, types[tuple_len * i ..], operand_tuple.types);
8281 mem.copy(Value, values[tuple_len * i ..], operand_tuple.values);
8282 }
8283 break :rs runtime_src;
8284 };
8285
8286 const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{
8287 .types = types,
8288 .values = values,
8289 });
8290
8291 const runtime_src = opt_runtime_src orelse {
8292 const tuple_val = try Value.Tag.@"struct".create(sema.arena, values);
8293 return sema.addConstant(tuple_ty, tuple_val);
8294 };
8295
8296 try sema.requireRuntimeBlock(block, runtime_src);
8297
8298 const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len_u64);
8299 for (operand_tuple.types) |_, i| {
8300 const operand_src = lhs_src; // TODO better source location
8301 element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, operand, @intCast(u32, i), operand_ty);
8302 }
8303 var i: usize = 1;
8304 while (i < factor) : (i += 1) {
8305 mem.copy(Air.Inst.Ref, element_refs[tuple_len * i ..], element_refs[0..tuple_len]);
8306 }
8307
8308 return block.addAggregateInit(tuple_ty, element_refs);
8309}
8310
81688311fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
81698312 const tracy = trace(@src());
81708313 defer tracy.end();
......@@ -8179,6 +8322,11 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
81798322
81808323 // In `**` rhs has to be comptime-known, but lhs can be runtime-known
81818324 const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize);
8325
8326 if (lhs_ty.isTuple()) {
8327 return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor);
8328 }
8329
81828330 const mulinfo = (try sema.getArrayCatInfo(block, lhs_src, lhs)) orelse
81838331 return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty});
81848332
......@@ -14754,6 +14902,11 @@ fn tupleFieldVal(
1475414902 tuple_ty, field_name, @errorName(err),
1475514903 });
1475614904 };
14905 if (field_index >= tuple_ty.structFieldCount()) {
14906 return sema.fail(block, field_name_src, "tuple {} has no such field '{s}'", .{
14907 tuple_ty, field_name,
14908 });
14909 }
1475714910 return tupleFieldValByIndex(sema, block, src, tuple_byval, field_index, tuple_ty);
1475814911}
1475914912
src/type.zig+13-3
......@@ -4531,7 +4531,15 @@ pub const Type = extern union {
45314531 };
45324532
45334533 pub fn isTuple(ty: Type) bool {
4534 return ty.tag() == .tuple;
4534 return ty.tag() == .tuple or ty.tag() == .empty_struct_literal;
4535 }
4536
4537 pub fn tupleFields(ty: Type) Payload.Tuple.Data {
4538 return switch (ty.tag()) {
4539 .tuple => ty.castTag(.tuple).?.data,
4540 .empty_struct_literal => .{ .types = &.{}, .values = &.{} },
4541 else => unreachable,
4542 };
45354543 }
45364544
45374545 /// The sub-types are named after what fields they contain.
......@@ -4683,11 +4691,13 @@ pub const Type = extern union {
46834691
46844692 pub const Tuple = struct {
46854693 base: Payload = .{ .tag = .tuple },
4686 data: struct {
4694 data: Data,
4695
4696 pub const Data = struct {
46874697 types: []Type,
46884698 /// unreachable_value elements are used to indicate runtime-known.
46894699 values: []Value,
4690 },
4700 };
46914701 };
46924702
46934703 pub const Union = struct {
src/value.zig+21-7
......@@ -1829,7 +1829,7 @@ pub const Value = extern union {
18291829 assert(a_tag != .undef);
18301830 assert(b_tag != .undef);
18311831 if (a_tag == b_tag) switch (a_tag) {
1832 .void_value, .null_value, .the_only_possible_value => return true,
1832 .void_value, .null_value, .the_only_possible_value, .empty_struct_value => return true,
18331833 .enum_literal => {
18341834 const a_name = a.castTag(.enum_literal).?.data;
18351835 const b_name = b.castTag(.enum_literal).?.data;
......@@ -1892,10 +1892,18 @@ pub const Value = extern union {
18921892 return a_payload == b_payload;
18931893 },
18941894 .@"struct" => {
1895 const fields = ty.structFields().values();
18961895 const a_field_vals = a.castTag(.@"struct").?.data;
18971896 const b_field_vals = b.castTag(.@"struct").?.data;
18981897 assert(a_field_vals.len == b_field_vals.len);
1898 if (ty.isTuple()) {
1899 const types = ty.tupleFields().types;
1900 assert(types.len == a_field_vals.len);
1901 for (types) |field_ty, i| {
1902 if (!eql(a_field_vals[i], b_field_vals[i], field_ty)) return false;
1903 }
1904 return true;
1905 }
1906 const fields = ty.structFields().values();
18991907 assert(fields.len == a_field_vals.len);
19001908 for (fields) |field, i| {
19011909 if (!eql(a_field_vals[i], b_field_vals[i], field.ty)) return false;
......@@ -1967,11 +1975,10 @@ pub const Value = extern union {
19671975 return true;
19681976 },
19691977 .Struct => {
1970 // must be a struct with no fields since we checked for if
1971 // both have the struct tag above.
1972 const fields = ty.structFields().values();
1973 assert(fields.len == 0);
1974 return true;
1978 // A tuple can be represented with .empty_struct_value,
1979 // the_one_possible_value, .@"struct" in which case we could
1980 // end up here and the values are equal if the type has zero fields.
1981 return ty.structFieldCount() != 0;
19751982 },
19761983 else => return order(a, b).compare(.eq),
19771984 }
......@@ -2024,6 +2031,13 @@ pub const Value = extern union {
20242031 }
20252032 },
20262033 .Struct => {
2034 if (ty.isTuple()) {
2035 const fields = ty.tupleFields();
2036 for (fields.values) |field_val, i| {
2037 field_val.hash(fields.types[i], hasher);
2038 }
2039 return;
2040 }
20272041 const fields = ty.structFields().values();
20282042 if (fields.len == 0) return;
20292043 const field_values = val.castTag(.@"struct").?.data;
test/behavior/tuple.zig+9-9
......@@ -23,28 +23,30 @@ test "tuple concatenation" {
2323}
2424
2525test "tuple multiplication" {
26 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
27
2826 const S = struct {
2927 fn doTheTest() !void {
3028 {
3129 const t = .{} ** 4;
32 try expectEqual(0, @typeInfo(@TypeOf(t)).Struct.fields.len);
30 try expect(@typeInfo(@TypeOf(t)).Struct.fields.len == 0);
3331 }
3432 {
3533 const t = .{'a'} ** 4;
36 try expectEqual(4, @typeInfo(@TypeOf(t)).Struct.fields.len);
37 inline for (t) |x| try expectEqual('a', x);
34 try expect(@typeInfo(@TypeOf(t)).Struct.fields.len == 4);
35 inline for (t) |x| try expect(x == 'a');
3836 }
3937 {
4038 const t = .{ 1, 2, 3 } ** 4;
41 try expectEqual(12, @typeInfo(@TypeOf(t)).Struct.fields.len);
42 inline for (t) |x, i| try expectEqual(1 + i % 3, x);
39 try expect(@typeInfo(@TypeOf(t)).Struct.fields.len == 12);
40 inline for (t) |x, i| try expect(x == 1 + i % 3);
4341 }
4442 }
4543 };
4644 try S.doTheTest();
4745 comptime try S.doTheTest();
46}
47
48test "tuple concatenation" {
49 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
4850
4951 const T = struct {
5052 fn consume_tuple(tuple: anytype, len: usize) !void {
......@@ -86,8 +88,6 @@ test "tuple multiplication" {
8688}
8789
8890test "pass tuple to comptime var parameter" {
89 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
90
9191 const S = struct {
9292 fn Foo(comptime args: anytype) !void {
9393 try expect(args[0] == 1);