Merge pull request #9984 from Snektron/field-elem-access

Field elem access
This commit is contained in:
Andrew Kelley 2021-10-19 22:39:46 -04:00 committed by GitHub
commit 4a76523b92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 524 additions and 312 deletions

View File

@ -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,

View File

@ -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;
},

View File

@ -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,

View File

@ -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();
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);
}
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);
}
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());
}
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()),
);
}
},
.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 => {},
}
},
.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 {

View File

@ -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;

View File

@ -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;

View File

@ -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))

View File

@ -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, "");
}
}

View File

@ -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;

View File

@ -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,

View File

@ -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 {

View File

@ -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);
},