Merge pull request #11077 from mitchellh/array-init-ty

stage2: sentinel-terminated array initialization
This commit is contained in:
Andrew Kelley 2022-03-07 13:54:09 -05:00 committed by GitHub
commit f59cbd89e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 113 additions and 19 deletions

View File

@ -1259,10 +1259,12 @@ fn arrayInitExpr(
const types: struct {
array: Zir.Inst.Ref,
elem: Zir.Inst.Ref,
sentinel: Zir.Inst.Ref,
} = inst: {
if (array_init.ast.type_expr == 0) break :inst .{
.array = .none,
.elem = .none,
.sentinel = .none,
};
infer: {
@ -1282,6 +1284,7 @@ fn arrayInitExpr(
break :inst .{
.array = array_type_inst,
.elem = elem_type,
.sentinel = .none,
};
} else {
const sentinel = try comptimeExpr(gz, scope, .{ .ty = elem_type }, array_type.ast.sentinel);
@ -1297,6 +1300,7 @@ fn arrayInitExpr(
break :inst .{
.array = array_type_inst,
.elem = elem_type,
.sentinel = sentinel,
};
}
}
@ -1307,6 +1311,7 @@ fn arrayInitExpr(
break :inst .{
.array = array_type_inst,
.elem = elem_type,
.sentinel = .none,
};
};
@ -1319,25 +1324,25 @@ fn arrayInitExpr(
},
.ref => {
if (types.array != .none) {
return arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, .array_init_ref);
return arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, types.sentinel, true);
} else {
return arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon_ref);
}
},
.none => {
if (types.array != .none) {
return arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, .array_init);
return arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, types.sentinel, false);
} else {
return arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon);
}
},
.ty, .coerced_ty => |ty_inst| {
if (types.array != .none) {
const result = try arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, .array_init);
const result = try arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, types.sentinel, false);
return rvalue(gz, rl, result, node);
} else {
const elem_type = try gz.addUnNode(.elem_type, ty_inst, node);
return arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, elem_type, .array_init);
return arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, elem_type, types.sentinel, false);
}
},
.ptr => |ptr_inst| {
@ -1387,14 +1392,32 @@ fn arrayInitExprRlTy(
node: Ast.Node.Index,
elements: []const Ast.Node.Index,
elem_ty_inst: Zir.Inst.Ref,
tag: Zir.Inst.Tag,
sentinel: Zir.Inst.Ref,
ref: bool,
) InnerError!Zir.Inst.Ref {
const astgen = gz.astgen;
const info: struct {
len: usize,
tag: Zir.Inst.Tag,
} = blk: {
if (sentinel != .none) {
break :blk .{
.len = elements.len + 1,
.tag = if (ref) .array_init_sent_ref else .array_init_sent,
};
} else {
break :blk .{
.len = elements.len,
.tag = if (ref) .array_init_ref else .array_init,
};
}
};
const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
.operands_len = @intCast(u32, elements.len),
.operands_len = @intCast(u32, info.len),
});
var extra_index = try reserveExtra(astgen, elements.len);
var extra_index = try reserveExtra(astgen, info.len);
const elem_rl: ResultLoc = .{ .ty = elem_ty_inst };
for (elements) |elem_init| {
@ -1402,7 +1425,13 @@ fn arrayInitExprRlTy(
astgen.extra.items[extra_index] = @enumToInt(elem_ref);
extra_index += 1;
}
return try gz.addPlNodePayloadIndex(tag, node, payload_index);
if (sentinel != .none) {
astgen.extra.items[extra_index] = @enumToInt(sentinel);
extra_index += 1;
}
return try gz.addPlNodePayloadIndex(info.tag, node, payload_index);
}
fn arrayInitExprRlPtr(
@ -2217,8 +2246,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
.struct_init_anon_ref,
.array_init,
.array_init_anon,
.array_init_sent,
.array_init_ref,
.array_init_anon_ref,
.array_init_sent_ref,
.union_init,
.field_type,
.field_type_ref,

View File

@ -712,8 +712,10 @@ fn analyzeBodyInner(
.struct_init_ref => try sema.zirStructInit(block, inst, true),
.struct_init_anon => try sema.zirStructInitAnon(block, inst, false),
.struct_init_anon_ref => try sema.zirStructInitAnon(block, inst, true),
.array_init => try sema.zirArrayInit(block, inst, false),
.array_init_ref => try sema.zirArrayInit(block, inst, true),
.array_init => try sema.zirArrayInit(block, inst, false, false),
.array_init_sent => try sema.zirArrayInit(block, inst, false, true),
.array_init_ref => try sema.zirArrayInit(block, inst, true, false),
.array_init_sent_ref => try sema.zirArrayInit(block, inst, true, true),
.array_init_anon => try sema.zirArrayInitAnon(block, inst, false),
.array_init_anon_ref => try sema.zirArrayInitAnon(block, inst, true),
.union_init => try sema.zirUnionInit(block, inst),
@ -11782,6 +11784,7 @@ fn zirArrayInit(
block: *Block,
inst: Zir.Inst.Index,
is_ref: bool,
is_sent: bool,
) CompileError!Air.Inst.Ref {
const gpa = sema.gpa;
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
@ -11797,22 +11800,38 @@ fn zirArrayInit(
for (args) |arg, i| resolved_args[i] = sema.resolveInst(arg);
const elem_ty = sema.typeOf(resolved_args[0]);
const array_ty = blk: {
if (!is_sent) {
break :blk try Type.Tag.array.create(sema.arena, .{
.len = resolved_args.len,
.elem_type = elem_ty,
});
}
const array_ty = try Type.Tag.array.create(sema.arena, .{
.len = resolved_args.len,
.elem_type = elem_ty,
});
const sentinel_ref = resolved_args[resolved_args.len - 1];
const val = try sema.resolveConstValue(block, src, sentinel_ref);
break :blk try Type.Tag.array_sentinel.create(sema.arena, .{
.len = resolved_args.len - 1,
.sentinel = val,
.elem_type = elem_ty,
});
};
const opt_runtime_src: ?LazySrcLoc = for (resolved_args) |arg| {
const elems = if (!is_sent)
resolved_args
else
resolved_args[0 .. resolved_args.len - 1];
const opt_runtime_src: ?LazySrcLoc = for (elems) |arg| {
const arg_src = src; // TODO better source location
const comptime_known = try sema.isComptimeKnown(block, arg_src, arg);
if (!comptime_known) break arg_src;
} else null;
const runtime_src = opt_runtime_src orelse {
const elem_vals = try sema.arena.alloc(Value, resolved_args.len);
const elem_vals = try sema.arena.alloc(Value, elems.len);
for (resolved_args) |arg, i| {
for (elems) |arg, i| {
// We checked that all args are comptime above.
elem_vals[i] = (sema.resolveMaybeUndefVal(block, src, arg) catch unreachable).?;
}
@ -11839,7 +11858,7 @@ fn zirArrayInit(
});
const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty);
for (resolved_args) |arg, i| {
for (elems) |arg, i| {
const index = try sema.addIntUnsigned(Type.usize, i);
const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref);
_ = try block.addBinOp(.store, elem_ptr, arg);
@ -11847,7 +11866,7 @@ fn zirArrayInit(
return alloc;
}
return block.addAggregateInit(array_ty, resolved_args);
return block.addAggregateInit(array_ty, elems);
}
fn zirArrayInitAnon(

View File

@ -710,12 +710,20 @@ pub const Inst = struct {
/// Array initialization syntax.
/// Uses the `pl_node` field. Payload is `MultiOp`.
array_init,
/// Array initialization with sentinel.
/// Uses the `pl_node` field. Payload is `MultiOp`.
/// Final op in MultiOp is the sentinel.
array_init_sent,
/// Anonymous array initialization syntax.
/// Uses the `pl_node` field. Payload is `MultiOp`.
array_init_anon,
/// Array initialization syntax, make the result a pointer.
/// Uses the `pl_node` field. Payload is `MultiOp`.
array_init_ref,
/// Array initialization with sentinel.
/// Uses the `pl_node` field. Payload is `MultiOp`.
/// Final op in MultiOp is the sentinel.
array_init_sent_ref,
/// Anonymous array initialization syntax, make the result a pointer.
/// Uses the `pl_node` field. Payload is `MultiOp`.
array_init_anon_ref,
@ -1121,8 +1129,10 @@ pub const Inst = struct {
.struct_init_anon,
.struct_init_anon_ref,
.array_init,
.array_init_sent,
.array_init_anon,
.array_init_ref,
.array_init_sent_ref,
.array_init_anon_ref,
.union_init,
.field_type,
@ -1379,8 +1389,10 @@ pub const Inst = struct {
.struct_init_anon = .pl_node,
.struct_init_anon_ref = .pl_node,
.array_init = .pl_node,
.array_init_sent = .pl_node,
.array_init_anon = .pl_node,
.array_init_ref = .pl_node,
.array_init_sent_ref = .pl_node,
.array_init_anon_ref = .pl_node,
.union_init = .pl_node,
.type_info = .un_node,

View File

@ -269,6 +269,10 @@ const Writer = struct {
.array_init_anon_ref,
=> try self.writeArrayInit(stream, inst),
.array_init_sent,
.array_init_sent_ref,
=> try self.writeArrayInitSent(stream, inst),
.slice_start => try self.writeSliceStart(stream, inst),
.slice_end => try self.writeSliceEnd(stream, inst),
.slice_sentinel => try self.writeSliceSentinel(stream, inst),
@ -2022,6 +2026,26 @@ const Writer = struct {
try self.writeSrc(stream, inst_data.src());
}
fn writeArrayInitSent(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
const extra = self.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
const args = self.code.refSlice(extra.end, extra.data.operands_len);
const sent = args[args.len - 1];
const elems = args[0 .. args.len - 1];
try self.writeInstRef(stream, sent);
try stream.writeAll(", ");
try stream.writeAll(".{");
for (elems) |elem, i| {
if (i != 0) try stream.writeAll(", ");
try self.writeInstRef(stream, elem);
}
try stream.writeAll("}) ");
try self.writeSrc(stream, inst_data.src());
}
fn writeUnreachable(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
const inst_data = self.code.instructions.items(.data)[inst].@"unreachable";
const safety_str = if (inst_data.safety) "safe" else "unsafe";

View File

@ -270,6 +270,14 @@ test "assign null directly to C pointer and test null equality" {
comptime try expect((y1 orelse &othery) == y1);
}
test "array initialization types" {
const E = enum { A, B, C };
try expect(@TypeOf([_]u8{}) == [0]u8);
try expect(@TypeOf([_:0]u8{}) == [0:0]u8);
try expect(@TypeOf([_:.A]E{}) == [0:.A]E);
try expect(@TypeOf([_:0]u8{ 1, 2, 3 }) == [3:0]u8);
}
test "null terminated pointer" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO