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....@@ -8059,6 +8059,78 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
8059 return block.addTyOp(.not, operand_type, operand);8059 return block.addTyOp(.not, operand_type, operand);
8060}8060}
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
8062fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8134fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
8063 const tracy = trace(@src());8135 const tracy = trace(@src());
8064 defer tracy.end();8136 defer tracy.end();
...@@ -8069,6 +8141,11 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -8069,6 +8141,11 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
8069 const rhs = sema.resolveInst(extra.rhs);8141 const rhs = sema.resolveInst(extra.rhs);
8070 const lhs_ty = sema.typeOf(lhs);8142 const lhs_ty = sema.typeOf(lhs);
8071 const rhs_ty = sema.typeOf(rhs);8143 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
8072 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };8149 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
8073 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };8150 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...@@ -8165,6 +8242,72 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, inst: Air.Inst.R
8165 };8242 };
8166}8243}
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
8168fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8311fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
8169 const tracy = trace(@src());8312 const tracy = trace(@src());
8170 defer tracy.end();8313 defer tracy.end();
...@@ -8179,6 +8322,11 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -8179,6 +8322,11 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
81798322
8180 // In `**` rhs has to be comptime-known, but lhs can be runtime-known8323 // In `**` rhs has to be comptime-known, but lhs can be runtime-known
8181 const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize);8324 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
8182 const mulinfo = (try sema.getArrayCatInfo(block, lhs_src, lhs)) orelse8330 const mulinfo = (try sema.getArrayCatInfo(block, lhs_src, lhs)) orelse
8183 return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty});8331 return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty});
81848332
...@@ -14754,6 +14902,11 @@ fn tupleFieldVal(...@@ -14754,6 +14902,11 @@ fn tupleFieldVal(
14754 tuple_ty, field_name, @errorName(err),14902 tuple_ty, field_name, @errorName(err),
14755 });14903 });
14756 };14904 };
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 }
14757 return tupleFieldValByIndex(sema, block, src, tuple_byval, field_index, tuple_ty);14910 return tupleFieldValByIndex(sema, block, src, tuple_byval, field_index, tuple_ty);
14758}14911}
1475914912
src/type.zig+13-3
...@@ -4531,7 +4531,15 @@ pub const Type = extern union {...@@ -4531,7 +4531,15 @@ pub const Type = extern union {
4531 };4531 };
45324532
4533 pub fn isTuple(ty: Type) bool {4533 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 };
4535 }4543 }
45364544
4537 /// The sub-types are named after what fields they contain.4545 /// The sub-types are named after what fields they contain.
...@@ -4683,11 +4691,13 @@ pub const Type = extern union {...@@ -4683,11 +4691,13 @@ pub const Type = extern union {
46834691
4684 pub const Tuple = struct {4692 pub const Tuple = struct {
4685 base: Payload = .{ .tag = .tuple },4693 base: Payload = .{ .tag = .tuple },
4686 data: struct {4694 data: Data,
4695
4696 pub const Data = struct {
4687 types: []Type,4697 types: []Type,
4688 /// unreachable_value elements are used to indicate runtime-known.4698 /// unreachable_value elements are used to indicate runtime-known.
4689 values: []Value,4699 values: []Value,
4690 },4700 };
4691 };4701 };
46924702
4693 pub const Union = struct {4703 pub const Union = struct {
src/value.zig+21-7
...@@ -1829,7 +1829,7 @@ pub const Value = extern union {...@@ -1829,7 +1829,7 @@ pub const Value = extern union {
1829 assert(a_tag != .undef);1829 assert(a_tag != .undef);
1830 assert(b_tag != .undef);1830 assert(b_tag != .undef);
1831 if (a_tag == b_tag) switch (a_tag) {1831 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,
1833 .enum_literal => {1833 .enum_literal => {
1834 const a_name = a.castTag(.enum_literal).?.data;1834 const a_name = a.castTag(.enum_literal).?.data;
1835 const b_name = b.castTag(.enum_literal).?.data;1835 const b_name = b.castTag(.enum_literal).?.data;
...@@ -1892,10 +1892,18 @@ pub const Value = extern union {...@@ -1892,10 +1892,18 @@ pub const Value = extern union {
1892 return a_payload == b_payload;1892 return a_payload == b_payload;
1893 },1893 },
1894 .@"struct" => {1894 .@"struct" => {
1895 const fields = ty.structFields().values();
1896 const a_field_vals = a.castTag(.@"struct").?.data;1895 const a_field_vals = a.castTag(.@"struct").?.data;
1897 const b_field_vals = b.castTag(.@"struct").?.data;1896 const b_field_vals = b.castTag(.@"struct").?.data;
1898 assert(a_field_vals.len == b_field_vals.len);1897 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();
1899 assert(fields.len == a_field_vals.len);1907 assert(fields.len == a_field_vals.len);
1900 for (fields) |field, i| {1908 for (fields) |field, i| {
1901 if (!eql(a_field_vals[i], b_field_vals[i], field.ty)) return false;1909 if (!eql(a_field_vals[i], b_field_vals[i], field.ty)) return false;
...@@ -1967,11 +1975,10 @@ pub const Value = extern union {...@@ -1967,11 +1975,10 @@ pub const Value = extern union {
1967 return true;1975 return true;
1968 },1976 },
1969 .Struct => {1977 .Struct => {
1970 // must be a struct with no fields since we checked for if1978 // A tuple can be represented with .empty_struct_value,
1971 // both have the struct tag above.1979 // the_one_possible_value, .@"struct" in which case we could
1972 const fields = ty.structFields().values();1980 // end up here and the values are equal if the type has zero fields.
1973 assert(fields.len == 0);1981 return ty.structFieldCount() != 0;
1974 return true;
1975 },1982 },
1976 else => return order(a, b).compare(.eq),1983 else => return order(a, b).compare(.eq),
1977 }1984 }
...@@ -2024,6 +2031,13 @@ pub const Value = extern union {...@@ -2024,6 +2031,13 @@ pub const Value = extern union {
2024 }2031 }
2025 },2032 },
2026 .Struct => {2033 .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 }
2027 const fields = ty.structFields().values();2041 const fields = ty.structFields().values();
2028 if (fields.len == 0) return;2042 if (fields.len == 0) return;
2029 const field_values = val.castTag(.@"struct").?.data;2043 const field_values = val.castTag(.@"struct").?.data;
test/behavior/tuple.zig+9-9
...@@ -23,28 +23,30 @@ test "tuple concatenation" {...@@ -23,28 +23,30 @@ test "tuple concatenation" {
23}23}
2424
25test "tuple multiplication" {25test "tuple multiplication" {
26 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
27
28 const S = struct {26 const S = struct {
29 fn doTheTest() !void {27 fn doTheTest() !void {
30 {28 {
31 const t = .{} ** 4;29 const t = .{} ** 4;
32 try expectEqual(0, @typeInfo(@TypeOf(t)).Struct.fields.len);30 try expect(@typeInfo(@TypeOf(t)).Struct.fields.len == 0);
33 }31 }
34 {32 {
35 const t = .{'a'} ** 4;33 const t = .{'a'} ** 4;
36 try expectEqual(4, @typeInfo(@TypeOf(t)).Struct.fields.len);34 try expect(@typeInfo(@TypeOf(t)).Struct.fields.len == 4);
37 inline for (t) |x| try expectEqual('a', x);35 inline for (t) |x| try expect(x == 'a');
38 }36 }
39 {37 {
40 const t = .{ 1, 2, 3 } ** 4;38 const t = .{ 1, 2, 3 } ** 4;
41 try expectEqual(12, @typeInfo(@TypeOf(t)).Struct.fields.len);39 try expect(@typeInfo(@TypeOf(t)).Struct.fields.len == 12);
42 inline for (t) |x, i| try expectEqual(1 + i % 3, x);40 inline for (t) |x, i| try expect(x == 1 + i % 3);
43 }41 }
44 }42 }
45 };43 };
46 try S.doTheTest();44 try S.doTheTest();
47 comptime try S.doTheTest();45 comptime try S.doTheTest();
46}
47
48test "tuple concatenation" {
49 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
4850
49 const T = struct {51 const T = struct {
50 fn consume_tuple(tuple: anytype, len: usize) !void {52 fn consume_tuple(tuple: anytype, len: usize) !void {
...@@ -86,8 +88,6 @@ test "tuple multiplication" {...@@ -86,8 +88,6 @@ test "tuple multiplication" {
86}88}
8789
88test "pass tuple to comptime var parameter" {90test "pass tuple to comptime var parameter" {
89 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
90
91 const S = struct {91 const S = struct {
92 fn Foo(comptime args: anytype) !void {92 fn Foo(comptime args: anytype) !void {
93 try expect(args[0] == 1);93 try expect(args[0] == 1);