commit 4a76523b92bcf0e9b48438cb22f49e67e0ab3fa1 (tree)
parent 372e9709ad2f4af24461f0fa601754a469091c2b
Author: Andrew Kelley <andrew@ziglang.org>
Date: Tue, 19 Oct 2021 22:39:46 -0400
Merge pull request #9984 from Snektron/field-elem-access
Field elem access
Diffstat:
12 files changed, 523 insertions(+), 311 deletions(-)
diff --git a/src/Air.zig b/src/Air.zig
@@ -367,6 +367,12 @@ pub const Inst = struct {
/// Given a slice value, return the pointer.
/// Uses the `ty_op` field.
slice_ptr,
+ /// Given a pointer to a slice, return a pointer to the length of the slice.
+ /// Uses the `ty_op` field.
+ ptr_slice_len_ptr,
+ /// Given a pointer to a slice, return a pointer to the pointer of the slice.
+ /// Uses the `ty_op` field.
+ ptr_slice_ptr_ptr,
/// Given an array value and element index, return the element value at that index.
/// Result type is the element type of the array operand.
/// Uses the `bin_op` field.
@@ -707,6 +713,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.wrap_errunion_payload,
.wrap_errunion_err,
.slice_ptr,
+ .ptr_slice_len_ptr,
+ .ptr_slice_ptr_ptr,
.struct_field_ptr_index_0,
.struct_field_ptr_index_1,
.struct_field_ptr_index_2,
diff --git a/src/AstGen.zig b/src/AstGen.zig
@@ -193,9 +193,6 @@ pub const ResultLoc = union(enum) {
/// The expression must generate a pointer rather than a value. For example, the left hand side
/// of an assignment uses this kind of result location.
ref,
- /// The callee will accept a ref, but it is not necessary, and the `ResultLoc`
- /// may be treated as `none` instead.
- none_or_ref,
/// The expression will be coerced into this type, but it will be evaluated as an rvalue.
ty: Zir.Inst.Ref,
/// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion,
@@ -231,7 +228,7 @@ pub const ResultLoc = union(enum) {
fn strategy(rl: ResultLoc, block_scope: *GenZir) Strategy {
switch (rl) {
// In this branch there will not be any store_to_block_ptr instructions.
- .discard, .none, .none_or_ref, .ty, .coerced_ty, .ref => return .{
+ .discard, .none, .ty, .coerced_ty, .ref => return .{
.tag = .break_operand,
.elide_store_to_block_ptr_instructions = false,
},
@@ -727,7 +724,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
.start = start,
});
switch (rl) {
- .ref, .none_or_ref => return result,
+ .ref => return result,
else => {
const dereffed = try gz.addUnNode(.load, result, node);
return rvalue(gz, rl, dereffed, node);
@@ -745,7 +742,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
.end = end,
});
switch (rl) {
- .ref, .none_or_ref => return result,
+ .ref => return result,
else => {
const dereffed = try gz.addUnNode(.load, result, node);
return rvalue(gz, rl, dereffed, node);
@@ -765,7 +762,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
.sentinel = sentinel,
});
switch (rl) {
- .ref, .none_or_ref => return result,
+ .ref => return result,
else => {
const dereffed = try gz.addUnNode(.load, result, node);
return rvalue(gz, rl, dereffed, node);
@@ -776,7 +773,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
.deref => {
const lhs = try expr(gz, scope, .none, node_datas[node].lhs);
switch (rl) {
- .ref, .none_or_ref => return lhs,
+ .ref => return lhs,
else => {
const result = try gz.addUnNode(.load, lhs, node);
return rvalue(gz, rl, result, node);
@@ -1273,7 +1270,7 @@ fn arrayInitExpr(
return arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon_ref);
}
},
- .none, .none_or_ref => {
+ .none => {
if (types.array != .none) {
return arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, .array_init);
} else {
@@ -1475,7 +1472,7 @@ fn structInitExpr(
return structInitExprRlNone(gz, scope, node, struct_init, .struct_init_anon_ref);
}
},
- .none, .none_or_ref => {
+ .none => {
if (struct_init.ast.type_expr != 0) {
const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init);
@@ -5133,7 +5130,7 @@ fn fieldAccess(
if (rl == .ref) {
return addFieldAccess(.field_ptr, gz, scope, .ref, node);
} else {
- const access = try addFieldAccess(.field_val, gz, scope, .none_or_ref, node);
+ const access = try addFieldAccess(.field_val, gz, scope, .none, node);
return rvalue(gz, rl, access, node);
}
}
@@ -5178,7 +5175,7 @@ fn arrayAccess(
),
else => return rvalue(gz, rl, try gz.addBin(
.elem_val,
- try expr(gz, scope, .none_or_ref, node_datas[node].lhs),
+ try expr(gz, scope, .none, node_datas[node].lhs),
try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs),
), node),
}
@@ -6664,7 +6661,7 @@ fn identifier(
);
switch (rl) {
- .ref, .none_or_ref => return ptr_inst,
+ .ref => return ptr_inst,
else => {
const loaded = try gz.addUnNode(.load, ptr_inst, ident);
return rvalue(gz, rl, loaded, ident);
@@ -6700,7 +6697,7 @@ fn identifier(
// Decl references happen by name rather than ZIR index so that when unrelated
// decls are modified, ZIR code containing references to them can be unmodified.
switch (rl) {
- .ref, .none_or_ref => return gz.addStrTok(.decl_ref, name_str_index, ident_token),
+ .ref => return gz.addStrTok(.decl_ref, name_str_index, ident_token),
else => {
const result = try gz.addStrTok(.decl_val, name_str_index, ident_token);
return rvalue(gz, rl, result, ident);
@@ -7105,7 +7102,7 @@ fn as(
) InnerError!Zir.Inst.Ref {
const dest_type = try typeExpr(gz, scope, lhs);
switch (rl) {
- .none, .none_or_ref, .discard, .ref, .ty, .coerced_ty => {
+ .none, .discard, .ref, .ty, .coerced_ty => {
const result = try reachableExpr(gz, scope, .{ .ty = dest_type }, rhs, node);
return rvalue(gz, rl, result, node);
},
@@ -7128,7 +7125,7 @@ fn unionInit(
const union_type = try typeExpr(gz, scope, params[0]);
const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
switch (rl) {
- .none, .none_or_ref, .discard, .ref, .ty, .coerced_ty, .inferred_ptr => {
+ .none, .discard, .ref, .ty, .coerced_ty, .inferred_ptr => {
_ = try gz.addPlNode(.field_type_ref, params[1], Zir.Inst.FieldTypeRef{
.container_type = union_type,
.field_name = field_name,
@@ -7192,7 +7189,7 @@ fn bitCast(
const astgen = gz.astgen;
const dest_type = try typeExpr(gz, scope, lhs);
switch (rl) {
- .none, .none_or_ref, .discard, .ty, .coerced_ty => {
+ .none, .discard, .ty, .coerced_ty => {
const operand = try expr(gz, scope, .none, rhs);
const result = try gz.addPlNode(.bitcast, node, Zir.Inst.Bin{
.lhs = dest_type,
@@ -8799,7 +8796,7 @@ fn rvalue(
) InnerError!Zir.Inst.Ref {
if (gz.endsWithNoReturn()) return result;
switch (rl) {
- .none, .none_or_ref, .coerced_ty => return result,
+ .none, .coerced_ty => return result,
.discard => {
// Emit a compile error for discarding error values.
_ = try gz.addUnNode(.ensure_result_non_error, result, src_node);
@@ -9561,9 +9558,7 @@ const GenZir = struct {
gz.rl_ty_inst = ty_inst;
gz.break_result_loc = parent_rl;
},
- .none_or_ref => {
- gz.break_result_loc = .ref;
- },
+
.discard, .none, .ptr, .ref => {
gz.break_result_loc = parent_rl;
},
diff --git a/src/Liveness.zig b/src/Liveness.zig
@@ -300,6 +300,8 @@ fn analyzeInst(
.wrap_errunion_err,
.slice_ptr,
.slice_len,
+ .ptr_slice_len_ptr,
+ .ptr_slice_ptr_ptr,
.struct_field_ptr_index_0,
.struct_field_ptr_index_1,
.struct_field_ptr_index_2,
diff --git a/src/Sema.zig b/src/Sema.zig
@@ -300,7 +300,7 @@ pub const Block = struct {
.ty = ty,
.payload = try block.sema.addExtra(Air.StructField{
.struct_operand = struct_ptr,
- .field_index = @intCast(u32, field_index),
+ .field_index = field_index,
}),
} },
});
@@ -315,6 +315,24 @@ pub const Block = struct {
});
}
+ pub fn addStructFieldVal(
+ block: *Block,
+ struct_val: Air.Inst.Ref,
+ field_index: u32,
+ field_ty: Type,
+ ) !Air.Inst.Ref {
+ return block.addInst(.{
+ .tag = .struct_field_val,
+ .data = .{ .ty_pl = .{
+ .ty = try block.sema.addType(field_ty),
+ .payload = try block.sema.addExtra(Air.StructField{
+ .struct_operand = struct_val,
+ .field_index = field_index,
+ }),
+ } },
+ });
+ }
+
pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
return Air.indexToRef(try block.addInstAsIndex(inst));
}
@@ -1968,44 +1986,38 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- const array = sema.resolveInst(inst_data.operand);
- const array_ty = sema.typeOf(array);
+ const object = sema.resolveInst(inst_data.operand);
+ const object_ty = sema.typeOf(object);
- if (array_ty.isSlice()) {
- return sema.analyzeSliceLen(block, src, array);
- }
+ const is_pointer_to = object_ty.isSinglePointer();
- if (array_ty.isSinglePointer()) {
- const elem_ty = array_ty.elemType();
- if (elem_ty.isSlice()) {
- const slice_inst = try sema.analyzeLoad(block, src, array, src);
- return sema.analyzeSliceLen(block, src, slice_inst);
- }
- if (!elem_ty.isIndexable()) {
- const msg = msg: {
- const msg = try sema.errMsg(
- block,
- src,
- "type '{}' does not support indexing",
- .{elem_ty},
- );
- errdefer msg.destroy(sema.gpa);
- try sema.errNote(
- block,
- src,
- msg,
- "for loop operand must be an array, slice, tuple, or vector",
- .{},
- );
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(msg);
- }
- const result_ptr = try sema.fieldPtr(block, src, array, "len", src);
- return sema.analyzeLoad(block, src, result_ptr, src);
+ const array_ty = if (is_pointer_to)
+ object_ty.childType()
+ else
+ object_ty;
+
+ if (!array_ty.isIndexable()) {
+ const msg = msg: {
+ const msg = try sema.errMsg(
+ block,
+ src,
+ "type '{}' does not support indexing",
+ .{array_ty},
+ );
+ errdefer msg.destroy(sema.gpa);
+ try sema.errNote(
+ block,
+ src,
+ msg,
+ "for loop operand must be an array, slice, tuple, or vector",
+ .{},
+ );
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(msg);
}
- return sema.fail(block, src, "TODO implement Sema.zirIndexablePtrLen", .{});
+ return sema.fieldVal(block, src, object, "len", src);
}
fn zirAllocExtended(
@@ -2304,7 +2316,7 @@ fn validateStructInit(
const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
const field_name = sema.code.nullTerminatedString(field_ptr_extra.field_name_start);
const field_index = struct_obj.fields.getIndex(field_name) orelse
- return sema.failWithBadFieldAccess(block, struct_obj, field_src, field_name);
+ return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name);
if (found_fields[field_index] != 0) {
const other_field_ptr = found_fields[field_index];
const other_field_ptr_data = sema.code.instructions.items(.data)[other_field_ptr].pl_node;
@@ -2366,7 +2378,32 @@ fn zirValidateArrayInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil
}
}
-fn failWithBadFieldAccess(
+fn failWithBadMemberAccess(
+ sema: *Sema,
+ block: *Block,
+ agg_ty: Type,
+ field_src: LazySrcLoc,
+ field_name: []const u8,
+) CompileError {
+ const kw_name = switch (agg_ty.zigTypeTag()) {
+ .Union => "union",
+ .Struct => "struct",
+ .Opaque => "opaque",
+ .Enum => "enum",
+ else => unreachable,
+ };
+ const msg = msg: {
+ const msg = try sema.errMsg(block, field_src, "{s} '{}' has no member named '{s}'", .{
+ kw_name, agg_ty, field_name,
+ });
+ errdefer msg.destroy(sema.gpa);
+ try sema.addDeclaredHereNote(msg, agg_ty);
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(msg);
+}
+
+fn failWithBadStructFieldAccess(
sema: *Sema,
block: *Block,
struct_obj: *Module.Struct,
@@ -5119,16 +5156,10 @@ fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
- const lhs_src: LazySrcLoc = src; // TODO
const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
const field_name = sema.code.nullTerminatedString(extra.field_name_start);
const object = sema.resolveInst(extra.lhs);
- if (sema.typeOf(object).isSinglePointer()) {
- const result_ptr = try sema.fieldPtr(block, src, object, field_name, field_name_src);
- return sema.analyzeLoad(block, src, result_ptr, lhs_src);
- } else {
- return sema.fieldVal(block, src, object, field_name, field_name_src);
- }
+ return sema.fieldVal(block, src, object, field_name, field_name_src);
}
fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -8822,7 +8853,7 @@ fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool)
const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
const field_name = sema.code.nullTerminatedString(field_type_extra.name_start);
const field_index = struct_obj.fields.getIndex(field_name) orelse
- return sema.failWithBadFieldAccess(block, struct_obj, field_src, field_name);
+ return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name);
if (found_fields[field_index] != 0) {
const other_field_type = found_fields[field_index];
const other_field_type_data = zir_datas[other_field_type].pl_node;
@@ -9031,7 +9062,7 @@ fn zirFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
.Struct => {
const struct_obj = resolved_ty.castTag(.@"struct").?.data;
const field = struct_obj.fields.get(field_name) orelse
- return sema.failWithBadFieldAccess(block, struct_obj, src, field_name);
+ return sema.failWithBadStructFieldAccess(block, struct_obj, src, field_name);
return sema.addType(field.ty);
},
.Union => {
@@ -9331,7 +9362,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const dest_info = dest_ty.intInfo(target);
if (src_info.bits == 0 or dest_info.bits == 0) {
- return sema.addConstant(dest_ty, Value.initTag(.zero));
+ return sema.addConstant(dest_ty, Value.zero);
}
if (!src_is_comptime_int) {
@@ -10688,12 +10719,22 @@ fn fieldVal(
const object_src = src; // TODO better source location
const object_ty = sema.typeOf(object);
- switch (object_ty.zigTypeTag()) {
+ // Zig allows dereferencing a single pointer during field lookup. Note that
+ // we don't actually need to generate the dereference some field lookups, like the
+ // length of arrays and other comptime operations.
+ const is_pointer_to = object_ty.isSinglePointer();
+
+ const inner_ty = if (is_pointer_to)
+ object_ty.childType()
+ else
+ object_ty;
+
+ switch (inner_ty.zigTypeTag()) {
.Array => {
if (mem.eql(u8, field_name, "len")) {
return sema.addConstant(
Type.initTag(.comptime_int),
- try Value.Tag.int_u64.create(arena, object_ty.arrayLen()),
+ try Value.Tag.int_u64.create(arena, inner_ty.arrayLen()),
);
} else {
return sema.fail(
@@ -10704,75 +10745,60 @@ fn fieldVal(
);
}
},
- .Pointer => switch (object_ty.ptrSize()) {
- .Slice => {
- if (mem.eql(u8, field_name, "ptr")) {
- const buf = try arena.create(Type.SlicePtrFieldTypeBuffer);
- const result_ty = object_ty.slicePtrFieldType(buf);
- if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| {
- if (val.isUndef()) return sema.addConstUndef(result_ty);
- return sema.addConstant(result_ty, val.slicePtr());
- }
- try sema.requireRuntimeBlock(block, src);
- return block.addTyOp(.slice_ptr, result_ty, object);
- } else if (mem.eql(u8, field_name, "len")) {
- const result_ty = Type.usize;
- if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| {
- if (val.isUndef()) return sema.addConstUndef(result_ty);
- return sema.addConstant(
- result_ty,
- try Value.Tag.int_u64.create(arena, val.sliceLen()),
- );
- }
- try sema.requireRuntimeBlock(block, src);
- return block.addTyOp(.slice_len, result_ty, object);
- } else {
- return sema.fail(
- block,
- field_name_src,
- "no member named '{s}' in '{}'",
- .{ field_name, object_ty },
- );
+ .Pointer => if (inner_ty.isSlice()) {
+ if (mem.eql(u8, field_name, "ptr")) {
+ const slice = if (is_pointer_to)
+ try sema.analyzeLoad(block, src, object, object_src)
+ else
+ object;
+
+ const buf = try arena.create(Type.SlicePtrFieldTypeBuffer);
+ const result_ty = inner_ty.slicePtrFieldType(buf);
+
+ if (try sema.resolveMaybeUndefVal(block, object_src, slice)) |val| {
+ if (val.isUndef()) return sema.addConstUndef(result_ty);
+ return sema.addConstant(result_ty, val.slicePtr());
}
- },
- .One => {
- const ptr_child = object_ty.elemType();
- switch (ptr_child.zigTypeTag()) {
- .Array => {
- if (mem.eql(u8, field_name, "len")) {
- return sema.addConstant(
- Type.initTag(.comptime_int),
- try Value.Tag.int_u64.create(arena, ptr_child.arrayLen()),
- );
- } else {
- return sema.fail(
- block,
- field_name_src,
- "no member named '{s}' in '{}'",
- .{ field_name, object_ty },
- );
- }
- },
- .Struct => {
- const struct_ptr_deref = try sema.analyzeLoad(block, src, object, object_src);
- return sema.unionFieldVal(block, src, struct_ptr_deref, field_name, field_name_src, ptr_child);
- },
- .Union => {
- const union_ptr_deref = try sema.analyzeLoad(block, src, object, object_src);
- return sema.unionFieldVal(block, src, union_ptr_deref, field_name, field_name_src, ptr_child);
- },
- else => {},
+ try sema.requireRuntimeBlock(block, src);
+ return block.addTyOp(.slice_ptr, result_ty, slice);
+ } else if (mem.eql(u8, field_name, "len")) {
+ const slice = if (is_pointer_to)
+ try sema.analyzeLoad(block, src, object, object_src)
+ else
+ object;
+
+ const result_ty = Type.usize;
+
+ if (try sema.resolveMaybeUndefVal(block, object_src, slice)) |val| {
+ if (val.isUndef()) return sema.addConstUndef(result_ty);
+ return sema.addConstant(
+ result_ty,
+ try Value.Tag.int_u64.create(arena, val.sliceLen()),
+ );
}
- },
- .Many, .C => {},
+ try sema.requireRuntimeBlock(block, src);
+ return block.addTyOp(.slice_len, result_ty, slice);
+ } else {
+ return sema.fail(
+ block,
+ field_name_src,
+ "no member named '{s}' in '{}'",
+ .{ field_name, object_ty },
+ );
+ }
},
.Type => {
- const val = (try sema.resolveDefinedValue(block, object_src, object)).?;
+ const dereffed_type = if (is_pointer_to)
+ try sema.analyzeLoad(block, src, object, object_src)
+ else
+ object;
+
+ const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?;
var to_type_buffer: Value.ToTypeBuffer = undefined;
const child_type = val.toType(&to_type_buffer);
+
switch (child_type.zigTypeTag()) {
.ErrorSet => {
- // TODO resolve inferred error sets
const name: []const u8 = if (child_type.castTag(.error_set)) |payload| blk: {
const error_set = payload.data;
// TODO this is O(N). I'm putting off solving this until we solve inferred
@@ -10842,8 +10868,20 @@ fn fieldVal(
else => return sema.fail(block, src, "type '{}' has no members", .{child_type}),
}
},
- .Struct => return sema.structFieldVal(block, src, object, field_name, field_name_src, object_ty),
- .Union => return sema.unionFieldVal(block, src, object, field_name, field_name_src, object_ty),
+ .Struct => if (is_pointer_to) {
+ // Avoid loading the entire struct by fetching a pointer and loading that
+ const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty);
+ return sema.analyzeLoad(block, src, field_ptr, object_src);
+ } else {
+ return sema.structFieldVal(block, src, object, field_name, field_name_src, inner_ty);
+ },
+ .Union => if (is_pointer_to) {
+ // Avoid loading the entire union by fetching a pointer and loading that
+ const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty);
+ return sema.analyzeLoad(block, src, field_ptr, object_src);
+ } else {
+ return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty);
+ },
else => {},
}
return sema.fail(block, src, "type '{}' does not support field access", .{object_ty});
@@ -10866,14 +10904,25 @@ fn fieldPtr(
.Pointer => object_ptr_ty.elemType(),
else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty}),
};
- switch (object_ty.zigTypeTag()) {
+
+ // Zig allows dereferencing a single pointer during field lookup. Note that
+ // we don't actually need to generate the dereference some field lookups, like the
+ // length of arrays and other comptime operations.
+ const is_pointer_to = object_ty.isSinglePointer();
+
+ const inner_ty = if (is_pointer_to)
+ object_ty.childType()
+ else
+ object_ty;
+
+ switch (inner_ty.zigTypeTag()) {
.Array => {
if (mem.eql(u8, field_name, "len")) {
var anon_decl = try block.startAnonDecl();
defer anon_decl.deinit();
return sema.analyzeDeclRef(try anon_decl.finish(
Type.initTag(.comptime_int),
- try Value.Tag.int_u64.create(anon_decl.arena(), object_ty.arrayLen()),
+ try Value.Tag.int_u64.create(anon_decl.arena(), inner_ty.arrayLen()),
));
} else {
return sema.fail(
@@ -10884,77 +10933,74 @@ fn fieldPtr(
);
}
},
- .Pointer => switch (object_ty.ptrSize()) {
- .Slice => {
- // Here for the ptr and len fields what we need to do is the situation
- // when a temporary has its address taken, e.g. `&a[c..d].len`.
- // This value may be known at compile-time or runtime. In the former
- // case, it should create an anonymous Decl and return a decl_ref to it.
- // In the latter case, it should add an `alloc` instruction, store
- // the runtime value to it, and then return the `alloc`.
- // In both cases the pointer should be const.
- if (mem.eql(u8, field_name, "ptr")) {
- return sema.fail(
- block,
- field_name_src,
- "TODO: implement reference to 'ptr' field of slice '{}'",
- .{object_ty},
- );
- } else if (mem.eql(u8, field_name, "len")) {
- return sema.fail(
- block,
- field_name_src,
- "TODO: implement reference to 'len' field of slice '{}'",
- .{object_ty},
- );
- } else {
- return sema.fail(
- block,
- field_name_src,
- "no member named '{s}' in '{}'",
- .{ field_name, object_ty },
- );
+ .Pointer => if (inner_ty.isSlice()) {
+ const inner_ptr = if (is_pointer_to)
+ try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
+ else
+ object_ptr;
+
+ if (mem.eql(u8, field_name, "ptr")) {
+ const buf = try sema.arena.create(Type.SlicePtrFieldTypeBuffer);
+ const slice_ptr_ty = inner_ty.slicePtrFieldType(buf);
+
+ if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
+ var anon_decl = try block.startAnonDecl();
+ defer anon_decl.deinit();
+
+ return sema.analyzeDeclRef(try anon_decl.finish(
+ try slice_ptr_ty.copy(anon_decl.arena()),
+ try val.slicePtr().copy(anon_decl.arena()),
+ ));
}
- },
- .One => {
- const ptr_child = object_ty.elemType();
- switch (ptr_child.zigTypeTag()) {
- .Array => {
- if (mem.eql(u8, field_name, "len")) {
- var anon_decl = try block.startAnonDecl();
- defer anon_decl.deinit();
- return sema.analyzeDeclRef(try anon_decl.finish(
- Type.initTag(.comptime_int),
- try Value.Tag.int_u64.create(anon_decl.arena(), ptr_child.arrayLen()),
- ));
- } else {
- return sema.fail(
- block,
- field_name_src,
- "no member named '{s}' in '{}'",
- .{ field_name, object_ty },
- );
- }
- },
- .Struct => {
- const struct_ptr_deref = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
- return sema.structFieldPtr(block, src, struct_ptr_deref, field_name, field_name_src, ptr_child);
- },
- .Union => {
- const union_ptr_deref = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
- return sema.unionFieldPtr(block, src, union_ptr_deref, field_name, field_name_src, ptr_child);
- },
- else => {},
+ try sema.requireRuntimeBlock(block, src);
+
+ const result_ty = try Type.ptr(sema.arena, .{
+ .pointee_type = slice_ptr_ty,
+ .mutable = object_ptr_ty.ptrIsMutable(),
+ .@"addrspace" = object_ptr_ty.ptrAddressSpace(),
+ });
+
+ return block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr);
+ } else if (mem.eql(u8, field_name, "len")) {
+ if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
+ var anon_decl = try block.startAnonDecl();
+ defer anon_decl.deinit();
+
+ return sema.analyzeDeclRef(try anon_decl.finish(
+ Type.usize,
+ try Value.Tag.int_u64.create(anon_decl.arena(), val.sliceLen()),
+ ));
}
- },
- .Many, .C => {},
+ try sema.requireRuntimeBlock(block, src);
+
+ const result_ty = try Type.ptr(sema.arena, .{
+ .pointee_type = Type.usize,
+ .mutable = object_ptr_ty.ptrIsMutable(),
+ .@"addrspace" = object_ptr_ty.ptrAddressSpace(),
+ });
+
+ return block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr);
+ } else {
+ return sema.fail(
+ block,
+ field_name_src,
+ "no member named '{s}' in '{}'",
+ .{ field_name, object_ty },
+ );
+ }
},
.Type => {
_ = try sema.resolveConstValue(block, object_ptr_src, object_ptr);
const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
- const val = (sema.resolveDefinedValue(block, src, result) catch unreachable).?;
+ const inner = if (is_pointer_to)
+ try sema.analyzeLoad(block, src, result, object_ptr_src)
+ else
+ result;
+
+ const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?;
var to_type_buffer: Value.ToTypeBuffer = undefined;
const child_type = val.toType(&to_type_buffer);
+
switch (child_type.zigTypeTag()) {
.ErrorSet => {
// TODO resolve inferred error sets
@@ -10980,22 +11026,24 @@ fn fieldPtr(
try Value.Tag.@"error".create(anon_decl.arena(), .{ .name = name }),
));
},
- .Struct, .Opaque, .Union => {
+ .Union => {
if (child_type.getNamespace()) |namespace| {
if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
return inst;
}
}
- // TODO add note: declared here
- const kw_name = switch (child_type.zigTypeTag()) {
- .Struct => "struct",
- .Opaque => "opaque",
- .Union => "union",
- else => unreachable,
- };
- return sema.fail(block, src, "{s} '{}' has no member named '{s}'", .{
- kw_name, child_type, field_name,
- });
+ if (child_type.unionTagType()) |enum_ty| {
+ if (enum_ty.enumFieldIndex(field_name)) |field_index| {
+ const field_index_u32 = @intCast(u32, field_index);
+ var anon_decl = try block.startAnonDecl();
+ defer anon_decl.deinit();
+ return sema.analyzeDeclRef(try anon_decl.finish(
+ try enum_ty.copy(anon_decl.arena()),
+ try Value.Tag.enum_field_index.create(anon_decl.arena(), field_index_u32),
+ ));
+ }
+ }
+ return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
},
.Enum => {
if (child_type.getNamespace()) |namespace| {
@@ -11004,23 +11052,7 @@ fn fieldPtr(
}
}
const field_index = child_type.enumFieldIndex(field_name) orelse {
- const msg = msg: {
- const msg = try sema.errMsg(
- block,
- src,
- "enum '{}' has no member named '{s}'",
- .{ child_type, field_name },
- );
- errdefer msg.destroy(sema.gpa);
- try sema.mod.errNoteNonLazy(
- child_type.declSrcLoc(),
- msg,
- "enum declared here",
- .{},
- );
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
};
const field_index_u32 = @intCast(u32, field_index);
var anon_decl = try block.startAnonDecl();
@@ -11030,14 +11062,34 @@ fn fieldPtr(
try Value.Tag.enum_field_index.create(anon_decl.arena(), field_index_u32),
));
},
+ .Struct, .Opaque => {
+ if (child_type.getNamespace()) |namespace| {
+ if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
+ return inst;
+ }
+ }
+ return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
+ },
else => return sema.fail(block, src, "type '{}' has no members", .{child_type}),
}
},
- .Struct => return sema.structFieldPtr(block, src, object_ptr, field_name, field_name_src, object_ty),
- .Union => return sema.unionFieldPtr(block, src, object_ptr, field_name, field_name_src, object_ty),
+ .Struct => {
+ const inner_ptr = if (is_pointer_to)
+ try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
+ else
+ object_ptr;
+ return sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty);
+ },
+ .Union => {
+ const inner_ptr = if (is_pointer_to)
+ try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
+ else
+ object_ptr;
+ return sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty);
+ },
else => {},
}
- return sema.fail(block, src, "type '{}' does not support field access", .{object_ty});
+ return sema.fail(block, src, "type '{}' does not support field access (fieldPtr, {}.{s})", .{ object_ty, object_ptr_ty, field_name });
}
fn fieldCallBind(
@@ -11209,7 +11261,7 @@ fn structFieldPtr(
const struct_obj = struct_ty.castTag(.@"struct").?.data;
const field_index_big = struct_obj.fields.getIndex(field_name) orelse
- return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name);
+ return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name);
const field_index = @intCast(u32, field_index_big);
const field = struct_obj.fields.values()[field_index];
const ptr_field_ty = try Type.ptr(arena, .{
@@ -11246,8 +11298,9 @@ fn structFieldVal(
const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty);
const struct_obj = struct_ty.castTag(.@"struct").?.data;
- const field_index = struct_obj.fields.getIndex(field_name) orelse
- return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name);
+ const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
+ return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name);
+ const field_index = @intCast(u32, field_index_usize);
const field = struct_obj.fields.values()[field_index];
if (try sema.resolveMaybeUndefVal(block, src, struct_byval)) |struct_val| {
@@ -11258,16 +11311,7 @@ fn structFieldVal(
}
try sema.requireRuntimeBlock(block, src);
- return block.addInst(.{
- .tag = .struct_field_val,
- .data = .{ .ty_pl = .{
- .ty = try sema.addType(field.ty),
- .payload = try sema.addExtra(Air.StructField{
- .struct_operand = struct_byval,
- .field_index = @intCast(u32, field_index),
- }),
- } },
- });
+ return block.addStructFieldVal(struct_byval, field_index, field.ty);
}
fn unionFieldPtr(
@@ -11326,8 +11370,9 @@ fn unionFieldVal(
const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty);
const union_obj = union_ty.cast(Type.Payload.Union).?.data;
- const field_index = union_obj.fields.getIndex(field_name) orelse
+ const field_index_big = union_obj.fields.getIndex(field_name) orelse
return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);
+ const field_index = @intCast(u32, field_index_big);
const field = union_obj.fields.values()[field_index];
@@ -11340,7 +11385,7 @@ fn unionFieldVal(
}
try sema.requireRuntimeBlock(block, src);
- return sema.fail(block, src, "TODO implement runtime union field access", .{});
+ return block.addStructFieldVal(union_byval, field_index, field.ty);
}
fn elemPtr(
@@ -13141,6 +13186,10 @@ pub fn resolveTypeLayout(
}
union_obj.status = .have_layout;
},
+ .Array => {
+ const elem_ty = ty.childType();
+ return sema.resolveTypeLayout(block, src, elem_ty);
+ },
else => {},
}
}
@@ -13693,10 +13742,9 @@ fn typeHasOnePossibleValue(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
- starting_type: Type,
+ ty: Type,
) CompileError!?Value {
- var ty = starting_type;
- while (true) switch (ty.tag()) {
+ switch (ty.tag()) {
.f16,
.f32,
.f64,
@@ -13790,7 +13838,7 @@ fn typeHasOnePossibleValue(
const enum_obj = resolved_ty.castTag(.enum_numbered).?.data;
if (enum_obj.fields.count() == 1) {
if (enum_obj.values.count() == 0) {
- return Value.initTag(.zero); // auto-numbered
+ return Value.zero; // auto-numbered
} else {
return enum_obj.values.keys()[0];
}
@@ -13803,7 +13851,7 @@ fn typeHasOnePossibleValue(
const enum_obj = resolved_ty.castTag(.enum_full).?.data;
if (enum_obj.fields.count() == 1) {
if (enum_obj.values.count() == 0) {
- return Value.initTag(.zero); // auto-numbered
+ return Value.zero; // auto-numbered
} else {
return enum_obj.values.keys()[0];
}
@@ -13815,12 +13863,19 @@ fn typeHasOnePossibleValue(
const resolved_ty = try sema.resolveTypeFields(block, src, ty);
const enum_simple = resolved_ty.castTag(.enum_simple).?.data;
if (enum_simple.fields.count() == 1) {
- return Value.initTag(.zero);
+ return Value.zero;
+ } else {
+ return null;
+ }
+ },
+ .enum_nonexhaustive => {
+ const tag_ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty;
+ if (!tag_ty.hasCodeGenBits()) {
+ return Value.zero;
} else {
return null;
}
},
- .enum_nonexhaustive => ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty,
.@"union" => {
return null; // TODO
},
@@ -13836,7 +13891,7 @@ fn typeHasOnePossibleValue(
.int_unsigned, .int_signed => {
if (ty.cast(Type.Payload.Bits).?.data == 0) {
- return Value.initTag(.zero);
+ return Value.zero;
} else {
return null;
}
@@ -13844,14 +13899,16 @@ fn typeHasOnePossibleValue(
.vector, .array, .array_u8 => {
if (ty.arrayLen() == 0)
return Value.initTag(.empty_array);
- ty = ty.elemType();
- continue;
+ if ((try sema.typeHasOnePossibleValue(block, src, ty.elemType())) != null) {
+ return Value.initTag(.the_only_possible_value);
+ }
+ return null;
},
.inferred_alloc_const => unreachable,
.inferred_alloc_mut => unreachable,
.generic_poison => return error.GenericPoison,
- };
+ }
}
fn getAstTree(sema: *Sema, block: *Block) CompileError!*const std.zig.Ast {
diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig
@@ -494,6 +494,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.slice_ptr => try self.airSlicePtr(inst),
.slice_len => try self.airSliceLen(inst),
+ .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst),
+ .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst),
+
.array_elem_val => try self.airArrayElemVal(inst),
.slice_elem_val => try self.airSliceElemVal(inst),
.ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),
@@ -1057,6 +1060,18 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
+fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {
+ const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_slice_len_ptr for {}", .{self.target.cpu.arch});
+ return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+}
+
+fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
+ const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_slice_ptr_ptr for {}", .{self.target.cpu.arch});
+ return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+}
+
fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
const is_volatile = false; // TODO
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
diff --git a/src/codegen.zig b/src/codegen.zig
@@ -842,6 +842,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.slice_ptr => try self.airSlicePtr(inst),
.slice_len => try self.airSliceLen(inst),
+ .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst),
+ .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst),
+
.array_elem_val => try self.airArrayElemVal(inst),
.slice_elem_val => try self.airSliceElemVal(inst),
.ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),
@@ -1498,6 +1501,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
+ fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {
+ const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
+ else => return self.fail("TODO implement ptr_slice_len_ptr for {}", .{self.target.cpu.arch}),
+ };
+ return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+ }
+
+ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
+ const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
+ else => return self.fail("TODO implement ptr_slice_ptr_ptr for {}", .{self.target.cpu.arch}),
+ };
+ return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+ }
+
fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
const is_volatile = false; // TODO
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
@@ -1075,6 +1075,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
.slice_ptr => try airSliceField(f, inst, ".ptr;\n"),
.slice_len => try airSliceField(f, inst, ".len;\n"),
+ .ptr_slice_len_ptr => try airPtrSliceFieldPtr(f, inst, ".len;\n"),
+ .ptr_slice_ptr_ptr => try airPtrSliceFieldPtr(f, inst, ".ptr;\n"),
+
.ptr_elem_val => try airPtrElemVal(f, inst, "["),
.ptr_ptr_elem_val => try airPtrElemVal(f, inst, "[0]["),
.ptr_elem_ptr => try airPtrElemPtr(f, inst),
@@ -1114,6 +1117,21 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !CValue
return local;
}
+fn airPtrSliceFieldPtr(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !CValue {
+ if (f.liveness.isUnused(inst))
+ return CValue.none;
+
+ const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+ const operand = try f.resolveInst(ty_op.operand);
+ const writer = f.object.writer();
+
+ _ = writer;
+ _ = operand;
+ _ = suffix;
+
+ return f.fail("TODO: C backend: airPtrSliceFieldPtr", .{});
+}
+
fn airPtrElemVal(f: *Function, inst: Air.Inst.Index, prefix: []const u8) !CValue {
const is_volatile = false; // TODO
if (!is_volatile and f.liveness.isUnused(inst))
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
@@ -1261,6 +1261,10 @@ pub const DeclGen = struct {
}
const field_ty = tv.ty.unionFieldType(tag_and_val.tag);
const payload = p: {
+ if (!field_ty.hasCodeGenBits()) {
+ const padding_len = @intCast(c_uint, layout.payload_size);
+ break :p self.context.intType(8).arrayType(padding_len).getUndef();
+ }
const field = try genTypedValue(self, .{ .ty = field_ty, .val = tag_and_val.val });
const field_size = field_ty.abiSize(target);
if (field_size == layout.payload_size) {
@@ -1709,6 +1713,10 @@ pub const FuncGen = struct {
.assembly => try self.airAssembly(inst),
.slice_ptr => try self.airSliceField(inst, 0),
.slice_len => try self.airSliceField(inst, 1),
+
+ .ptr_slice_ptr_ptr => try self.airPtrSliceFieldPtr(inst, 0),
+ .ptr_slice_len_ptr => try self.airPtrSliceFieldPtr(inst, 1),
+
.array_to_slice => try self.airArrayToSlice(inst),
.float_to_int => try self.airFloatToInt(inst),
.int_to_float => try self.airIntToFloat(inst),
@@ -2091,6 +2099,15 @@ pub const FuncGen = struct {
return self.builder.buildExtractValue(operand, index, "");
}
+ fn airPtrSliceFieldPtr(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
+ const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+ const slice_ptr = try self.resolveInst(ty_op.operand);
+
+ return self.builder.buildStructGEP(slice_ptr, index, "");
+ }
+
fn airSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const slice_ty = self.air.typeOf(bin_op.lhs);
@@ -2223,17 +2240,34 @@ pub const FuncGen = struct {
const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
const struct_ty = self.air.typeOf(struct_field.struct_operand);
const struct_llvm_val = try self.resolveInst(struct_field.struct_operand);
- const field_index = llvmFieldIndex(struct_ty, struct_field.field_index);
- if (isByRef(struct_ty)) {
- const field_ptr = self.builder.buildStructGEP(struct_llvm_val, field_index, "");
- const field_ty = struct_ty.structFieldType(struct_field.field_index);
- if (isByRef(field_ty)) {
- return field_ptr;
- } else {
- return self.builder.buildLoad(field_ptr, "");
- }
+ const field_index = struct_field.field_index;
+ const field_ty = struct_ty.structFieldType(field_index);
+ if (!field_ty.hasCodeGenBits()) {
+ return null;
+ }
+
+ assert(isByRef(struct_ty));
+
+ const field_ptr = switch (struct_ty.zigTypeTag()) {
+ .Struct => blk: {
+ const llvm_field_index = llvmFieldIndex(struct_ty, field_index);
+ break :blk self.builder.buildStructGEP(struct_llvm_val, llvm_field_index, "");
+ },
+ .Union => blk: {
+ const llvm_field_ty = try self.dg.llvmType(field_ty);
+ const target = self.dg.module.getTarget();
+ const layout = struct_ty.unionGetLayout(target);
+ const payload_index = @boolToInt(layout.tag_align >= layout.payload_align);
+ const union_field_ptr = self.builder.buildStructGEP(struct_llvm_val, payload_index, "");
+ break :blk self.builder.buildBitCast(union_field_ptr, llvm_field_ty.pointerType(0), "");
+ },
+ else => unreachable,
+ };
+
+ if (isByRef(field_ty)) {
+ return field_ptr;
} else {
- return self.builder.buildExtractValue(struct_llvm_val, field_index, "");
+ return self.builder.buildLoad(field_ptr, "");
}
}
diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig
@@ -866,6 +866,7 @@ pub const Context = struct {
.struct_field_ptr_index_1 => self.airStructFieldPtrIndex(inst, 1),
.struct_field_ptr_index_2 => self.airStructFieldPtrIndex(inst, 2),
.struct_field_ptr_index_3 => self.airStructFieldPtrIndex(inst, 3),
+ .struct_field_val => self.airStructFieldVal(inst),
.switch_br => self.airSwitchBr(inst),
.unreach => self.airUnreachable(inst),
.wrap_optional => self.airWrapOptional(inst),
@@ -1456,6 +1457,15 @@ pub const Context = struct {
return WValue{ .local = struct_ptr.multi_value.index + index };
}
+ fn airStructFieldVal(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
+ if (self.liveness.isUnused(inst)) return WValue.none;
+
+ const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+ const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
+ const struct_multivalue = self.resolveInst(extra.struct_operand).multi_value;
+ return WValue{ .local = struct_multivalue.index + extra.field_index };
+ }
+
fn airSwitchBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
// result type is always 'noreturn'
const blocktype = wasm.block_empty;
diff --git a/src/print_air.zig b/src/print_air.zig
@@ -183,6 +183,8 @@ const Writer = struct {
.wrap_errunion_err,
.slice_ptr,
.slice_len,
+ .ptr_slice_len_ptr,
+ .ptr_slice_ptr_ptr,
.struct_field_ptr_index_0,
.struct_field_ptr_index_1,
.struct_field_ptr_index_2,
diff --git a/src/type.zig b/src/type.zig
@@ -2676,7 +2676,7 @@ pub const Type = extern union {
.pointer => return self.castTag(.pointer).?.data.sentinel,
.array_sentinel => return self.castTag(.array_sentinel).?.data.sentinel,
- .array_u8_sentinel_0 => return Value.initTag(.zero),
+ .array_u8_sentinel_0 => return Value.zero,
else => unreachable,
};
@@ -3096,6 +3096,14 @@ pub const Type = extern union {
}
return Value.initTag(.empty_struct_value);
},
+ .enum_numbered => {
+ const enum_numbered = ty.castTag(.enum_numbered).?.data;
+ if (enum_numbered.fields.count() == 1) {
+ return enum_numbered.values.keys()[0];
+ } else {
+ return null;
+ }
+ },
.enum_full => {
const enum_full = ty.castTag(.enum_full).?.data;
if (enum_full.fields.count() == 1) {
@@ -3107,13 +3115,19 @@ pub const Type = extern union {
.enum_simple => {
const enum_simple = ty.castTag(.enum_simple).?.data;
if (enum_simple.fields.count() == 1) {
- return Value.initTag(.zero);
+ return Value.zero;
+ } else {
+ return null;
+ }
+ },
+ .enum_nonexhaustive => {
+ const tag_ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty;
+ if (!tag_ty.hasCodeGenBits()) {
+ return Value.zero;
} else {
return null;
}
},
- .enum_nonexhaustive => ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty,
- .enum_numbered => ty = ty.castTag(.enum_numbered).?.data.tag_ty,
.@"union" => {
return null; // TODO
},
@@ -3129,7 +3143,7 @@ pub const Type = extern union {
.int_unsigned, .int_signed => {
if (ty.cast(Payload.Bits).?.data == 0) {
- return Value.initTag(.zero);
+ return Value.zero;
} else {
return null;
}
@@ -3137,8 +3151,9 @@ pub const Type = extern union {
.vector, .array, .array_u8 => {
if (ty.arrayLen() == 0)
return Value.initTag(.empty_array);
- ty = ty.elemType();
- continue;
+ if (ty.elemType().onePossibleValue() != null)
+ return Value.initTag(.the_only_possible_value);
+ return null;
},
.inferred_alloc_const => unreachable,
@@ -3179,7 +3194,7 @@ pub const Type = extern union {
const info = self.intInfo(target);
if (info.signedness == .unsigned) {
- return Value.initTag(.zero);
+ return Value.zero;
}
if (info.bits <= 6) {
@@ -4013,7 +4028,7 @@ pub const Type = extern union {
) Allocator.Error!Type {
if (elem_type.eql(Type.u8)) {
if (sent) |some| {
- if (some.eql(Value.initTag(.zero), elem_type)) {
+ if (some.eql(Value.zero, elem_type)) {
return Tag.array_u8_sentinel_0.create(arena, len);
}
} else {
diff --git a/src/value.zig b/src/value.zig
@@ -86,6 +86,8 @@ pub const Value = extern union {
one,
void_value,
unreachable_value,
+ /// The only possible value for a particular type, which is stored externally.
+ the_only_possible_value,
null_value,
bool_true,
bool_false,
@@ -226,6 +228,7 @@ pub const Value = extern union {
.one,
.void_value,
.unreachable_value,
+ .the_only_possible_value,
.empty_struct_value,
.empty_array,
.null_value,
@@ -415,6 +418,7 @@ pub const Value = extern union {
.one,
.void_value,
.unreachable_value,
+ .the_only_possible_value,
.empty_array,
.null_value,
.bool_true,
@@ -664,6 +668,7 @@ pub const Value = extern union {
.one => return out_stream.writeAll("1"),
.void_value => return out_stream.writeAll("{}"),
.unreachable_value => return out_stream.writeAll("unreachable"),
+ .the_only_possible_value => return out_stream.writeAll("(the only possible value)"),
.bool_true => return out_stream.writeAll("true"),
.bool_false => return out_stream.writeAll("false"),
.ty => return val.castTag(.ty).?.data.format("", options, out_stream),
@@ -755,6 +760,7 @@ pub const Value = extern union {
const decl_val = try decl.value();
return decl_val.toAllocatedBytes(decl.ty, allocator);
},
+ .the_only_possible_value => return &[_]u8{},
else => unreachable,
}
}
@@ -847,53 +853,63 @@ pub const Value = extern union {
// TODO should `@intToEnum` do this `@intCast` for you?
return @intToEnum(E, @intCast(@typeInfo(E).Enum.tag_type, field_index));
},
+ .the_only_possible_value => {
+ const fields = std.meta.fields(E);
+ assert(fields.len == 1);
+ return @intToEnum(E, fields[0].value);
+ },
else => unreachable,
}
}
pub fn enumToInt(val: Value, ty: Type, buffer: *Payload.U64) Value {
- if (val.castTag(.enum_field_index)) |enum_field_payload| {
- const field_index = enum_field_payload.data;
- switch (ty.tag()) {
- .enum_full, .enum_nonexhaustive => {
- const enum_full = ty.cast(Type.Payload.EnumFull).?.data;
- if (enum_full.values.count() != 0) {
- return enum_full.values.keys()[field_index];
- } else {
- // Field index and integer values are the same.
- buffer.* = .{
- .base = .{ .tag = .int_u64 },
- .data = field_index,
- };
- return Value.initPayload(&buffer.base);
- }
- },
- .enum_numbered => {
- const enum_obj = ty.castTag(.enum_numbered).?.data;
- if (enum_obj.values.count() != 0) {
- return enum_obj.values.keys()[field_index];
- } else {
- // Field index and integer values are the same.
- buffer.* = .{
- .base = .{ .tag = .int_u64 },
- .data = field_index,
- };
- return Value.initPayload(&buffer.base);
- }
- },
- .enum_simple => {
+ const field_index = switch (val.tag()) {
+ .enum_field_index => val.castTag(.enum_field_index).?.data,
+ .the_only_possible_value => blk: {
+ assert(ty.enumFieldCount() == 1);
+ break :blk 0;
+ },
+ // Assume it is already an integer and return it directly.
+ else => return val,
+ };
+
+ switch (ty.tag()) {
+ .enum_full, .enum_nonexhaustive => {
+ const enum_full = ty.cast(Type.Payload.EnumFull).?.data;
+ if (enum_full.values.count() != 0) {
+ return enum_full.values.keys()[field_index];
+ } else {
// Field index and integer values are the same.
buffer.* = .{
.base = .{ .tag = .int_u64 },
.data = field_index,
};
return Value.initPayload(&buffer.base);
- },
- else => unreachable,
- }
+ }
+ },
+ .enum_numbered => {
+ const enum_obj = ty.castTag(.enum_numbered).?.data;
+ if (enum_obj.values.count() != 0) {
+ return enum_obj.values.keys()[field_index];
+ } else {
+ // Field index and integer values are the same.
+ buffer.* = .{
+ .base = .{ .tag = .int_u64 },
+ .data = field_index,
+ };
+ return Value.initPayload(&buffer.base);
+ }
+ },
+ .enum_simple => {
+ // Field index and integer values are the same.
+ buffer.* = .{
+ .base = .{ .tag = .int_u64 },
+ .data = field_index,
+ };
+ return Value.initPayload(&buffer.base);
+ },
+ else => unreachable,
}
- // Assume it is already an integer and return it directly.
- return val;
}
/// Asserts the value is an integer.
@@ -901,6 +917,7 @@ pub const Value = extern union {
switch (self.tag()) {
.zero,
.bool_false,
+ .the_only_possible_value, // i0, u0
=> return BigIntMutable.init(&space.limbs, 0).toConst(),
.one,
@@ -922,6 +939,7 @@ pub const Value = extern union {
switch (self.tag()) {
.zero,
.bool_false,
+ .the_only_possible_value, // i0, u0
=> return 0,
.one,
@@ -943,6 +961,7 @@ pub const Value = extern union {
switch (self.tag()) {
.zero,
.bool_false,
+ .the_only_possible_value, // i0, u0
=> return 0,
.one,
@@ -1124,6 +1143,11 @@ pub const Value = extern union {
@panic("TODO implement int_big_negative Value clz");
},
+ .the_only_possible_value => {
+ assert(ty_bits == 0);
+ return ty_bits;
+ },
+
else => unreachable,
}
}
@@ -1134,6 +1158,7 @@ pub const Value = extern union {
switch (self.tag()) {
.zero,
.bool_false,
+ .the_only_possible_value,
=> return 0,
.one,
@@ -1213,6 +1238,11 @@ pub const Value = extern union {
else => unreachable,
},
+ .the_only_possible_value => {
+ assert(ty.intInfo(target).bits == 0);
+ return true;
+ },
+
else => unreachable,
}
}
@@ -1251,7 +1281,7 @@ pub const Value = extern union {
/// Asserts the value is numeric
pub fn isZero(self: Value) bool {
return switch (self.tag()) {
- .zero => true,
+ .zero, .the_only_possible_value => true,
.one => false,
.int_u64 => self.castTag(.int_u64).?.data == 0,
@@ -1272,6 +1302,7 @@ pub const Value = extern union {
return switch (lhs.tag()) {
.zero,
.bool_false,
+ .the_only_possible_value,
=> .eq,
.one,
@@ -1354,7 +1385,7 @@ pub const Value = extern union {
assert(b_tag != .undef);
if (a_tag == b_tag) {
switch (a_tag) {
- .void_value, .null_value => return true,
+ .void_value, .null_value, .the_only_possible_value => return true,
.enum_literal => {
const a_name = a.castTag(.enum_literal).?.data;
const b_name = b.castTag(.enum_literal).?.data;
@@ -1706,6 +1737,9 @@ pub const Value = extern union {
.decl_ref => return val.castTag(.decl_ref).?.data.val.elemValueAdvanced(index, arena, buffer),
.decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.val.elemValueAdvanced(index, arena, buffer),
+ // The child type of arrays which have only one possible value need to have only one possible value itself.
+ .the_only_possible_value => return val,
+
else => unreachable,
}
}
@@ -1722,6 +1756,8 @@ pub const Value = extern union {
// TODO assert the tag is correct
return payload.val;
},
+ // Structs which have only one possible value need to consist of members which have only one possible value.
+ .the_only_possible_value => return val,
else => unreachable,
}
@@ -1820,6 +1856,7 @@ pub const Value = extern union {
pub fn intToFloat(val: Value, allocator: *Allocator, dest_ty: Type, target: Target) !Value {
switch (val.tag()) {
.undef, .zero, .one => return val,
+ .the_only_possible_value => return Value.initTag(.zero), // for i0, u0
.int_u64 => {
return intToFloatInner(val.castTag(.int_u64).?.data, allocator, dest_ty, target);
},