mirror of
https://github.com/ziglang/zig.git
synced 2026-01-21 06:45:24 +00:00
Sema: implement struct init is_ref=true
Takes advantage of the pattern already established with array_init_anon. Also upgrades array_init (non-anon) to the pattern. Implements comptime struct value equality and pointer value hashing.
This commit is contained in:
parent
db55f469c1
commit
82bd0ac572
150
src/Sema.zig
150
src/Sema.zig
@ -10433,7 +10433,13 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
|
||||
}
|
||||
}
|
||||
|
||||
fn structInitEmpty(sema: *Sema, block: *Block, obj_ty: Type, dest_src: LazySrcLoc, init_src: LazySrcLoc) CompileError!Air.Inst.Ref {
|
||||
fn structInitEmpty(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
obj_ty: Type,
|
||||
dest_src: LazySrcLoc,
|
||||
init_src: LazySrcLoc,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const gpa = sema.gpa;
|
||||
// This logic must be synchronized with that in `zirStructInit`.
|
||||
const struct_ty = try sema.resolveTypeFields(block, dest_src, obj_ty);
|
||||
@ -10477,7 +10483,12 @@ fn zirUnionInitPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
|
||||
return sema.fail(block, src, "TODO: Sema.zirUnionInitPtr", .{});
|
||||
}
|
||||
|
||||
fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
|
||||
fn zirStructInit(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
inst: Zir.Inst.Index,
|
||||
is_ref: bool,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const gpa = sema.gpa;
|
||||
const zir_datas = sema.code.instructions.items(.data);
|
||||
const inst_data = zir_datas[inst].pl_node;
|
||||
@ -10612,10 +10623,6 @@ fn finishStructInit(
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
}
|
||||
|
||||
if (is_ref) {
|
||||
return sema.fail(block, src, "TODO: Sema.zirStructInit is_ref=true", .{});
|
||||
}
|
||||
|
||||
const is_comptime = for (field_inits) |field_init| {
|
||||
if (!(try sema.isComptimeKnown(block, src, field_init))) {
|
||||
break false;
|
||||
@ -10627,10 +10634,24 @@ fn finishStructInit(
|
||||
for (field_inits) |field_init, i| {
|
||||
values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?;
|
||||
}
|
||||
return sema.addConstant(struct_ty, try Value.Tag.@"struct".create(sema.arena, values));
|
||||
const struct_val = try Value.Tag.@"struct".create(sema.arena, values);
|
||||
return sema.addConstantMaybeRef(block, src, struct_ty, struct_val, is_ref);
|
||||
}
|
||||
|
||||
return sema.fail(block, src, "TODO: Sema.zirStructInit for runtime-known struct values", .{});
|
||||
if (is_ref) {
|
||||
const alloc = try block.addTy(.alloc, struct_ty);
|
||||
for (field_inits) |field_init, i_usize| {
|
||||
const i = @intCast(u32, i_usize);
|
||||
const field_src = src;
|
||||
const field_ptr = try sema.structFieldPtrByIndex(block, src, alloc, i, struct_obj, field_src);
|
||||
try sema.storePtr(block, src, field_ptr, field_init);
|
||||
}
|
||||
|
||||
return alloc;
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addVectorInit(struct_ty, field_inits);
|
||||
}
|
||||
|
||||
fn zirStructInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
|
||||
@ -10674,51 +10695,43 @@ fn zirArrayInit(
|
||||
} else null;
|
||||
|
||||
const runtime_src = opt_runtime_src orelse {
|
||||
var anon_decl = try block.startAnonDecl(src);
|
||||
defer anon_decl.deinit();
|
||||
const elem_vals = try sema.arena.alloc(Value, resolved_args.len);
|
||||
|
||||
const elem_vals = try anon_decl.arena().alloc(Value, resolved_args.len);
|
||||
for (resolved_args) |arg, i| {
|
||||
// We checked that all args are comptime above.
|
||||
const arg_val = (sema.resolveMaybeUndefVal(block, src, arg) catch unreachable).?;
|
||||
elem_vals[i] = try arg_val.copy(anon_decl.arena());
|
||||
elem_vals[i] = (sema.resolveMaybeUndefVal(block, src, arg) catch unreachable).?;
|
||||
}
|
||||
|
||||
const val = try Value.Tag.array.create(anon_decl.arena(), elem_vals);
|
||||
const decl = try anon_decl.finish(try array_ty.copy(anon_decl.arena()), val);
|
||||
if (is_ref) {
|
||||
return sema.analyzeDeclRef(decl);
|
||||
} else {
|
||||
return sema.analyzeDeclVal(block, .unneeded, decl);
|
||||
}
|
||||
const array_val = try Value.Tag.array.create(sema.arena, elem_vals);
|
||||
return sema.addConstantMaybeRef(block, src, array_ty, array_val, is_ref);
|
||||
};
|
||||
|
||||
try sema.requireRuntimeBlock(block, runtime_src);
|
||||
try sema.resolveTypeLayout(block, src, elem_ty);
|
||||
|
||||
const alloc_ty = try Type.ptr(sema.arena, .{
|
||||
.pointee_type = array_ty,
|
||||
.@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
|
||||
});
|
||||
const alloc = try block.addTy(.alloc, alloc_ty);
|
||||
|
||||
const elem_ptr_ty = try Type.ptr(sema.arena, .{
|
||||
.mutable = true,
|
||||
.@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
|
||||
.pointee_type = elem_ty,
|
||||
});
|
||||
const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty);
|
||||
|
||||
for (resolved_args) |arg, i| {
|
||||
const index = try sema.addIntUnsigned(Type.u64, i);
|
||||
const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref);
|
||||
_ = try block.addBinOp(.store, elem_ptr, arg);
|
||||
}
|
||||
if (is_ref) {
|
||||
const alloc_ty = try Type.ptr(sema.arena, .{
|
||||
.pointee_type = array_ty,
|
||||
.@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
|
||||
});
|
||||
const alloc = try block.addTy(.alloc, alloc_ty);
|
||||
|
||||
const elem_ptr_ty = try Type.ptr(sema.arena, .{
|
||||
.mutable = true,
|
||||
.@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
|
||||
.pointee_type = elem_ty,
|
||||
});
|
||||
const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty);
|
||||
|
||||
for (resolved_args) |arg, i| {
|
||||
const index = try sema.addIntUnsigned(Type.u64, i);
|
||||
const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref);
|
||||
_ = try block.addBinOp(.store, elem_ptr, arg);
|
||||
}
|
||||
return alloc;
|
||||
} else {
|
||||
return sema.analyzeLoad(block, .unneeded, alloc, .unneeded);
|
||||
}
|
||||
|
||||
return block.addVectorInit(array_ty, resolved_args);
|
||||
}
|
||||
|
||||
fn zirArrayInitAnon(
|
||||
@ -10758,17 +10771,11 @@ fn zirArrayInitAnon(
|
||||
|
||||
const runtime_src = opt_runtime_src orelse {
|
||||
const tuple_val = try Value.Tag.@"struct".create(sema.arena, values);
|
||||
if (!is_ref) return sema.addConstant(tuple_ty, tuple_val);
|
||||
|
||||
var anon_decl = try block.startAnonDecl(src);
|
||||
defer anon_decl.deinit();
|
||||
const decl = try anon_decl.finish(
|
||||
try tuple_ty.copy(anon_decl.arena()),
|
||||
try tuple_val.copy(anon_decl.arena()),
|
||||
);
|
||||
return sema.analyzeDeclRef(decl);
|
||||
return sema.addConstantMaybeRef(block, src, tuple_ty, tuple_val, is_ref);
|
||||
};
|
||||
|
||||
try sema.requireRuntimeBlock(block, runtime_src);
|
||||
|
||||
if (is_ref) {
|
||||
const alloc = try block.addTy(.alloc, tuple_ty);
|
||||
for (operands) |operand, i_usize| {
|
||||
@ -10790,10 +10797,28 @@ fn zirArrayInitAnon(
|
||||
element_refs[i] = sema.resolveInst(operand);
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, runtime_src);
|
||||
return block.addVectorInit(tuple_ty, element_refs);
|
||||
}
|
||||
|
||||
fn addConstantMaybeRef(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
src: LazySrcLoc,
|
||||
ty: Type,
|
||||
val: Value,
|
||||
is_ref: bool,
|
||||
) !Air.Inst.Ref {
|
||||
if (!is_ref) return sema.addConstant(ty, val);
|
||||
|
||||
var anon_decl = try block.startAnonDecl(src);
|
||||
defer anon_decl.deinit();
|
||||
const decl = try anon_decl.finish(
|
||||
try ty.copy(anon_decl.arena()),
|
||||
try val.copy(anon_decl.arena()),
|
||||
);
|
||||
return sema.analyzeDeclRef(decl);
|
||||
}
|
||||
|
||||
fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
@ -13444,18 +13469,30 @@ fn structFieldPtr(
|
||||
field_name_src: LazySrcLoc,
|
||||
unresolved_struct_ty: Type,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const arena = sema.arena;
|
||||
assert(unresolved_struct_ty.zigTypeTag() == .Struct);
|
||||
|
||||
const struct_ptr_ty = sema.typeOf(struct_ptr);
|
||||
const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty);
|
||||
const struct_obj = struct_ty.castTag(.@"struct").?.data;
|
||||
|
||||
const field_index_big = 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_big);
|
||||
|
||||
return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, struct_obj, field_name_src);
|
||||
}
|
||||
|
||||
fn structFieldPtrByIndex(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
src: LazySrcLoc,
|
||||
struct_ptr: Air.Inst.Ref,
|
||||
field_index: u32,
|
||||
struct_obj: *Module.Struct,
|
||||
field_src: LazySrcLoc,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const field = struct_obj.fields.values()[field_index];
|
||||
|
||||
const struct_ptr_ty = sema.typeOf(struct_ptr);
|
||||
var ptr_ty_data: Type.Payload.Pointer.Data = .{
|
||||
.pointee_type = field.ty,
|
||||
.mutable = struct_ptr_ty.ptrIsMutable(),
|
||||
@ -13470,7 +13507,7 @@ fn structFieldPtr(
|
||||
var offset: u64 = 0;
|
||||
var running_bits: u16 = 0;
|
||||
for (struct_obj.fields.values()) |f, i| {
|
||||
if (!(try sema.typeHasRuntimeBits(block, field_name_src, f.ty))) continue;
|
||||
if (!(try sema.typeHasRuntimeBits(block, field_src, f.ty))) continue;
|
||||
|
||||
const field_align = f.packedAlignment();
|
||||
if (field_align == 0) {
|
||||
@ -13509,12 +13546,12 @@ fn structFieldPtr(
|
||||
const int_ty: Type = .{ .ptr_otherwise = &int_payload.base };
|
||||
ptr_ty_data.host_size = @intCast(u16, int_ty.abiSize(target));
|
||||
}
|
||||
const ptr_field_ty = try Type.ptr(arena, ptr_ty_data);
|
||||
const ptr_field_ty = try Type.ptr(sema.arena, ptr_ty_data);
|
||||
|
||||
if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
|
||||
return sema.addConstant(
|
||||
ptr_field_ty,
|
||||
try Value.Tag.field_ptr.create(arena, .{
|
||||
try Value.Tag.field_ptr.create(sema.arena, .{
|
||||
.container_ptr = struct_ptr_val,
|
||||
.field_index = field_index,
|
||||
}),
|
||||
@ -13546,6 +13583,9 @@ fn structFieldVal(
|
||||
|
||||
if (try sema.resolveMaybeUndefVal(block, src, struct_byval)) |struct_val| {
|
||||
if (struct_val.isUndef()) return sema.addConstUndef(field.ty);
|
||||
if ((try sema.typeHasOnePossibleValue(block, src, field.ty))) |opv| {
|
||||
return sema.addConstant(field.ty, opv);
|
||||
}
|
||||
|
||||
const field_values = struct_val.castTag(.@"struct").?.data;
|
||||
return sema.addConstant(field.ty, field_values[field_index]);
|
||||
|
||||
230
src/value.zig
230
src/value.zig
@ -1530,60 +1530,69 @@ pub const Value = extern union {
|
||||
const b_tag = b.tag();
|
||||
assert(a_tag != .undef);
|
||||
assert(b_tag != .undef);
|
||||
if (a_tag == b_tag) {
|
||||
switch (a_tag) {
|
||||
.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;
|
||||
return std.mem.eql(u8, a_name, b_name);
|
||||
},
|
||||
.enum_field_index => {
|
||||
const a_field_index = a.castTag(.enum_field_index).?.data;
|
||||
const b_field_index = b.castTag(.enum_field_index).?.data;
|
||||
return a_field_index == b_field_index;
|
||||
},
|
||||
.opt_payload => {
|
||||
const a_payload = a.castTag(.opt_payload).?.data;
|
||||
const b_payload = b.castTag(.opt_payload).?.data;
|
||||
var buffer: Type.Payload.ElemType = undefined;
|
||||
return eql(a_payload, b_payload, ty.optionalChild(&buffer));
|
||||
},
|
||||
.slice => {
|
||||
const a_payload = a.castTag(.slice).?.data;
|
||||
const b_payload = b.castTag(.slice).?.data;
|
||||
if (!eql(a_payload.len, b_payload.len, Type.usize)) return false;
|
||||
if (a_tag == b_tag) switch (a_tag) {
|
||||
.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;
|
||||
return std.mem.eql(u8, a_name, b_name);
|
||||
},
|
||||
.enum_field_index => {
|
||||
const a_field_index = a.castTag(.enum_field_index).?.data;
|
||||
const b_field_index = b.castTag(.enum_field_index).?.data;
|
||||
return a_field_index == b_field_index;
|
||||
},
|
||||
.opt_payload => {
|
||||
const a_payload = a.castTag(.opt_payload).?.data;
|
||||
const b_payload = b.castTag(.opt_payload).?.data;
|
||||
var buffer: Type.Payload.ElemType = undefined;
|
||||
return eql(a_payload, b_payload, ty.optionalChild(&buffer));
|
||||
},
|
||||
.slice => {
|
||||
const a_payload = a.castTag(.slice).?.data;
|
||||
const b_payload = b.castTag(.slice).?.data;
|
||||
if (!eql(a_payload.len, b_payload.len, Type.usize)) return false;
|
||||
|
||||
var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined;
|
||||
const ptr_ty = ty.slicePtrFieldType(&ptr_buf);
|
||||
var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined;
|
||||
const ptr_ty = ty.slicePtrFieldType(&ptr_buf);
|
||||
|
||||
return eql(a_payload.ptr, b_payload.ptr, ptr_ty);
|
||||
},
|
||||
.elem_ptr => @panic("TODO: Implement more pointer eql cases"),
|
||||
.field_ptr => @panic("TODO: Implement more pointer eql cases"),
|
||||
.eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
|
||||
.opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
|
||||
.array => {
|
||||
const a_array = a.castTag(.array).?.data;
|
||||
const b_array = b.castTag(.array).?.data;
|
||||
return eql(a_payload.ptr, b_payload.ptr, ptr_ty);
|
||||
},
|
||||
.elem_ptr => @panic("TODO: Implement more pointer eql cases"),
|
||||
.field_ptr => @panic("TODO: Implement more pointer eql cases"),
|
||||
.eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
|
||||
.opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
|
||||
.array => {
|
||||
const a_array = a.castTag(.array).?.data;
|
||||
const b_array = b.castTag(.array).?.data;
|
||||
|
||||
if (a_array.len != b_array.len) return false;
|
||||
if (a_array.len != b_array.len) return false;
|
||||
|
||||
const elem_ty = ty.childType();
|
||||
for (a_array) |a_elem, i| {
|
||||
const b_elem = b_array[i];
|
||||
const elem_ty = ty.childType();
|
||||
for (a_array) |a_elem, i| {
|
||||
const b_elem = b_array[i];
|
||||
|
||||
if (!eql(a_elem, b_elem, elem_ty)) return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
.function => {
|
||||
const a_payload = a.castTag(.function).?.data;
|
||||
const b_payload = b.castTag(.function).?.data;
|
||||
return a_payload == b_payload;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
if (!eql(a_elem, b_elem, elem_ty)) return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
.function => {
|
||||
const a_payload = a.castTag(.function).?.data;
|
||||
const b_payload = b.castTag(.function).?.data;
|
||||
return a_payload == b_payload;
|
||||
},
|
||||
.@"struct" => {
|
||||
const fields = ty.structFields().values();
|
||||
const a_field_vals = a.castTag(.@"struct").?.data;
|
||||
const b_field_vals = b.castTag(.@"struct").?.data;
|
||||
assert(a_field_vals.len == b_field_vals.len);
|
||||
assert(fields.len == a_field_vals.len);
|
||||
for (fields) |field, i| {
|
||||
if (!eql(a_field_vals[i], b_field_vals[i], field.ty)) return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
else => {},
|
||||
} else if (a_tag == .null_value or b_tag == .null_value) {
|
||||
return false;
|
||||
}
|
||||
@ -1628,6 +1637,13 @@ pub const Value = extern union {
|
||||
}
|
||||
return true;
|
||||
},
|
||||
.Struct => {
|
||||
// must be a struct with no fields since we checked for if
|
||||
// both have the struct tag above.
|
||||
const fields = ty.structFields().values();
|
||||
assert(fields.len == 0);
|
||||
return true;
|
||||
},
|
||||
else => return order(a, b).compare(.eq),
|
||||
}
|
||||
}
|
||||
@ -1651,31 +1667,13 @@ pub const Value = extern union {
|
||||
var buf: ToTypeBuffer = undefined;
|
||||
return val.toType(&buf).hashWithHasher(hasher);
|
||||
},
|
||||
.Bool => {
|
||||
std.hash.autoHash(hasher, val.toBool());
|
||||
},
|
||||
.Int, .ComptimeInt => {
|
||||
var space: BigIntSpace = undefined;
|
||||
const big = val.toBigInt(&space);
|
||||
std.hash.autoHash(hasher, big.positive);
|
||||
for (big.limbs) |limb| {
|
||||
std.hash.autoHash(hasher, limb);
|
||||
}
|
||||
},
|
||||
.Float, .ComptimeFloat => {
|
||||
// TODO double check the lang spec. should we to bitwise hashing here,
|
||||
// or a hash that normalizes the float value?
|
||||
const float = val.toFloat(f128);
|
||||
std.hash.autoHash(hasher, @bitCast(u128, float));
|
||||
},
|
||||
.Pointer => switch (val.tag()) {
|
||||
.decl_ref_mut,
|
||||
.extern_fn,
|
||||
.decl_ref,
|
||||
.function,
|
||||
.variable,
|
||||
=> std.hash.autoHash(hasher, val.pointerDecl().?),
|
||||
|
||||
.Bool, .Int, .ComptimeInt, .Pointer => switch (val.tag()) {
|
||||
.slice => {
|
||||
const slice = val.castTag(.slice).?.data;
|
||||
var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined;
|
||||
@ -1684,22 +1682,7 @@ pub const Value = extern union {
|
||||
hash(slice.len, Type.usize, hasher);
|
||||
},
|
||||
|
||||
// For these, hash them as hash of a pointer to the decl,
|
||||
// combined with a hash of the byte offset from the decl.
|
||||
.elem_ptr => @panic("TODO: Implement more pointer hashing cases"),
|
||||
.field_ptr => @panic("TODO: Implement more pointer hashing cases"),
|
||||
.eu_payload_ptr => @panic("TODO: Implement more pointer hashing cases"),
|
||||
.opt_payload_ptr => @panic("TODO: Implement more pointer hashing cases"),
|
||||
|
||||
.zero,
|
||||
.one,
|
||||
.int_u64,
|
||||
.int_i64,
|
||||
.int_big_positive,
|
||||
.int_big_negative,
|
||||
=> @panic("TODO: Implement pointer hashing for int pointers"),
|
||||
|
||||
else => unreachable,
|
||||
else => return hashPtr(val, hasher),
|
||||
},
|
||||
.Array, .Vector => {
|
||||
const len = ty.arrayLen();
|
||||
@ -1739,14 +1722,7 @@ pub const Value = extern union {
|
||||
.Enum => {
|
||||
var enum_space: Payload.U64 = undefined;
|
||||
const int_val = val.enumToInt(ty, &enum_space);
|
||||
|
||||
var space: BigIntSpace = undefined;
|
||||
const big = int_val.toBigInt(&space);
|
||||
|
||||
std.hash.autoHash(hasher, big.positive);
|
||||
for (big.limbs) |limb| {
|
||||
std.hash.autoHash(hasher, limb);
|
||||
}
|
||||
hashInt(int_val, hasher);
|
||||
},
|
||||
.Union => {
|
||||
const union_obj = val.cast(Payload.Union).?.data;
|
||||
@ -1757,8 +1733,12 @@ pub const Value = extern union {
|
||||
union_obj.val.hash(active_field_ty, hasher);
|
||||
},
|
||||
.Fn => {
|
||||
const func = val.castTag(.function).?.data;
|
||||
return std.hash.autoHash(hasher, func.owner_decl);
|
||||
const func: *Module.Fn = val.castTag(.function).?.data;
|
||||
// Note that his hashes the *Fn rather than the *Decl. This is
|
||||
// to differentiate function bodies from function pointers.
|
||||
// This is currently redundant since we already hash the zig type tag
|
||||
// at the top of this function.
|
||||
std.hash.autoHash(hasher, func);
|
||||
},
|
||||
.Frame => {
|
||||
@panic("TODO implement hashing frame values");
|
||||
@ -1824,6 +1804,65 @@ pub const Value = extern union {
|
||||
};
|
||||
}
|
||||
|
||||
fn hashInt(int_val: Value, hasher: *std.hash.Wyhash) void {
|
||||
var buffer: BigIntSpace = undefined;
|
||||
const big = int_val.toBigInt(&buffer);
|
||||
std.hash.autoHash(hasher, big.positive);
|
||||
for (big.limbs) |limb| {
|
||||
std.hash.autoHash(hasher, limb);
|
||||
}
|
||||
}
|
||||
|
||||
fn hashPtr(ptr_val: Value, hasher: *std.hash.Wyhash) void {
|
||||
switch (ptr_val.tag()) {
|
||||
.decl_ref,
|
||||
.decl_ref_mut,
|
||||
.extern_fn,
|
||||
.function,
|
||||
.variable,
|
||||
=> {
|
||||
const decl: *Module.Decl = ptr_val.pointerDecl().?;
|
||||
std.hash.autoHash(hasher, decl);
|
||||
},
|
||||
|
||||
.elem_ptr => {
|
||||
const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
|
||||
hashPtr(elem_ptr.array_ptr, hasher);
|
||||
std.hash.autoHash(hasher, Value.Tag.elem_ptr);
|
||||
std.hash.autoHash(hasher, elem_ptr.index);
|
||||
},
|
||||
.field_ptr => {
|
||||
const field_ptr = ptr_val.castTag(.field_ptr).?.data;
|
||||
std.hash.autoHash(hasher, Value.Tag.field_ptr);
|
||||
hashPtr(field_ptr.container_ptr, hasher);
|
||||
std.hash.autoHash(hasher, field_ptr.field_index);
|
||||
},
|
||||
.eu_payload_ptr => {
|
||||
const err_union_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;
|
||||
std.hash.autoHash(hasher, Value.Tag.eu_payload_ptr);
|
||||
hashPtr(err_union_ptr, hasher);
|
||||
},
|
||||
.opt_payload_ptr => {
|
||||
const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
|
||||
std.hash.autoHash(hasher, Value.Tag.opt_payload_ptr);
|
||||
hashPtr(opt_ptr, hasher);
|
||||
},
|
||||
|
||||
.zero,
|
||||
.one,
|
||||
.int_u64,
|
||||
.int_i64,
|
||||
.int_big_positive,
|
||||
.int_big_negative,
|
||||
.bool_false,
|
||||
.bool_true,
|
||||
.the_only_possible_value,
|
||||
=> return hashInt(ptr_val, hasher),
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn markReferencedDeclsAlive(val: Value) void {
|
||||
switch (val.tag()) {
|
||||
.decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.markAlive(),
|
||||
@ -1876,7 +1915,8 @@ pub const Value = extern union {
|
||||
pub fn slicePtr(val: Value) Value {
|
||||
return switch (val.tag()) {
|
||||
.slice => val.castTag(.slice).?.data.ptr,
|
||||
.decl_ref, .decl_ref_mut => val,
|
||||
// TODO this should require being a slice tag, and not allow decl_ref, field_ptr, etc.
|
||||
.decl_ref, .decl_ref_mut, .field_ptr, .elem_ptr => val,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
@ -98,7 +98,6 @@ test {
|
||||
_ = @import("behavior/bugs/3007.zig");
|
||||
_ = @import("behavior/bugs/9584.zig");
|
||||
_ = @import("behavior/cast_llvm.zig");
|
||||
_ = @import("behavior/enum_llvm.zig");
|
||||
_ = @import("behavior/error_llvm.zig");
|
||||
_ = @import("behavior/eval.zig");
|
||||
_ = @import("behavior/floatop.zig");
|
||||
@ -163,9 +162,7 @@ test {
|
||||
_ = @import("behavior/muladd.zig");
|
||||
_ = @import("behavior/null_stage1.zig");
|
||||
_ = @import("behavior/optional_stage1.zig");
|
||||
_ = @import("behavior/pointers_stage1.zig");
|
||||
_ = @import("behavior/popcount_stage1.zig");
|
||||
_ = @import("behavior/ptrcast_stage1.zig");
|
||||
_ = @import("behavior/reflection.zig");
|
||||
_ = @import("behavior/saturating_arithmetic_stage1.zig");
|
||||
_ = @import("behavior/select.zig");
|
||||
|
||||
@ -972,3 +972,116 @@ fn test3_2(f: Test3Foo) !void {
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
test "@tagName" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||
|
||||
try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three"));
|
||||
comptime try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three"));
|
||||
}
|
||||
|
||||
fn testEnumTagNameBare(n: anytype) []const u8 {
|
||||
return @tagName(n);
|
||||
}
|
||||
|
||||
const BareNumber = enum { One, Two, Three };
|
||||
|
||||
test "@tagName non-exhaustive enum" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||
|
||||
try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B"));
|
||||
comptime try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B"));
|
||||
}
|
||||
const NonExhaustive = enum(u8) { A, B, _ };
|
||||
|
||||
test "@tagName is null-terminated" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
fn doTheTest(n: BareNumber) !void {
|
||||
try expect(@tagName(n)[3] == 0);
|
||||
}
|
||||
};
|
||||
try S.doTheTest(.Two);
|
||||
try comptime S.doTheTest(.Two);
|
||||
}
|
||||
|
||||
test "tag name with assigned enum values" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||
|
||||
const LocalFoo = enum(u8) {
|
||||
A = 1,
|
||||
B = 0,
|
||||
};
|
||||
var b = LocalFoo.B;
|
||||
try expect(mem.eql(u8, @tagName(b), "B"));
|
||||
}
|
||||
|
||||
test "@tagName on enum literals" {
|
||||
try expect(mem.eql(u8, @tagName(.FooBar), "FooBar"));
|
||||
comptime try expect(mem.eql(u8, @tagName(.FooBar), "FooBar"));
|
||||
}
|
||||
|
||||
test "enum literal casting to optional" {
|
||||
var bar: ?Bar = undefined;
|
||||
bar = .B;
|
||||
|
||||
try expect(bar.? == Bar.B);
|
||||
}
|
||||
|
||||
const A = enum(u3) { One, Two, Three, Four, One2, Two2, Three2, Four2 };
|
||||
const B = enum(u3) { One3, Two3, Three3, Four3, One23, Two23, Three23, Four23 };
|
||||
const C = enum(u2) { One4, Two4, Three4, Four4 };
|
||||
|
||||
const BitFieldOfEnums = packed struct {
|
||||
a: A,
|
||||
b: B,
|
||||
c: C,
|
||||
};
|
||||
|
||||
const bit_field_1 = BitFieldOfEnums{
|
||||
.a = A.Two,
|
||||
.b = B.Three3,
|
||||
.c = C.Four4,
|
||||
};
|
||||
|
||||
test "bit field access with enum fields" {
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||
|
||||
var data = bit_field_1;
|
||||
try expect(getA(&data) == A.Two);
|
||||
try expect(getB(&data) == B.Three3);
|
||||
try expect(getC(&data) == C.Four4);
|
||||
comptime try expect(@sizeOf(BitFieldOfEnums) == 1);
|
||||
|
||||
data.b = B.Four3;
|
||||
try expect(data.b == B.Four3);
|
||||
|
||||
data.a = A.Three;
|
||||
try expect(data.a == A.Three);
|
||||
try expect(data.b == B.Four3);
|
||||
}
|
||||
|
||||
fn getA(data: *const BitFieldOfEnums) A {
|
||||
return data.a;
|
||||
}
|
||||
|
||||
fn getB(data: *const BitFieldOfEnums) B {
|
||||
return data.b;
|
||||
}
|
||||
|
||||
fn getC(data: *const BitFieldOfEnums) C {
|
||||
return data.c;
|
||||
}
|
||||
|
||||
test "enum literal in array literal" {
|
||||
const Items = enum { one, two };
|
||||
const array = [_]Items{ .one, .two };
|
||||
|
||||
try expect(array[0] == .one);
|
||||
try expect(array[1] == .two);
|
||||
}
|
||||
|
||||
@ -1,105 +0,0 @@
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
const mem = std.mem;
|
||||
const Tag = std.meta.Tag;
|
||||
|
||||
test "@tagName" {
|
||||
try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three"));
|
||||
comptime try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three"));
|
||||
}
|
||||
|
||||
fn testEnumTagNameBare(n: anytype) []const u8 {
|
||||
return @tagName(n);
|
||||
}
|
||||
|
||||
const BareNumber = enum { One, Two, Three };
|
||||
|
||||
test "@tagName non-exhaustive enum" {
|
||||
try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B"));
|
||||
comptime try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B"));
|
||||
}
|
||||
const NonExhaustive = enum(u8) { A, B, _ };
|
||||
|
||||
test "@tagName is null-terminated" {
|
||||
const S = struct {
|
||||
fn doTheTest(n: BareNumber) !void {
|
||||
try expect(@tagName(n)[3] == 0);
|
||||
}
|
||||
};
|
||||
try S.doTheTest(.Two);
|
||||
try comptime S.doTheTest(.Two);
|
||||
}
|
||||
|
||||
test "tag name with assigned enum values" {
|
||||
const LocalFoo = enum(u8) {
|
||||
A = 1,
|
||||
B = 0,
|
||||
};
|
||||
var b = LocalFoo.B;
|
||||
try expect(mem.eql(u8, @tagName(b), "B"));
|
||||
}
|
||||
|
||||
test "@tagName on enum literals" {
|
||||
try expect(mem.eql(u8, @tagName(.FooBar), "FooBar"));
|
||||
comptime try expect(mem.eql(u8, @tagName(.FooBar), "FooBar"));
|
||||
}
|
||||
|
||||
const Bar = enum { A, B, C, D };
|
||||
|
||||
test "enum literal casting to optional" {
|
||||
var bar: ?Bar = undefined;
|
||||
bar = .B;
|
||||
|
||||
try expect(bar.? == Bar.B);
|
||||
}
|
||||
|
||||
const A = enum(u3) { One, Two, Three, Four, One2, Two2, Three2, Four2 };
|
||||
const B = enum(u3) { One3, Two3, Three3, Four3, One23, Two23, Three23, Four23 };
|
||||
const C = enum(u2) { One4, Two4, Three4, Four4 };
|
||||
|
||||
const BitFieldOfEnums = packed struct {
|
||||
a: A,
|
||||
b: B,
|
||||
c: C,
|
||||
};
|
||||
|
||||
const bit_field_1 = BitFieldOfEnums{
|
||||
.a = A.Two,
|
||||
.b = B.Three3,
|
||||
.c = C.Four4,
|
||||
};
|
||||
|
||||
test "bit field access with enum fields" {
|
||||
var data = bit_field_1;
|
||||
try expect(getA(&data) == A.Two);
|
||||
try expect(getB(&data) == B.Three3);
|
||||
try expect(getC(&data) == C.Four4);
|
||||
comptime try expect(@sizeOf(BitFieldOfEnums) == 1);
|
||||
|
||||
data.b = B.Four3;
|
||||
try expect(data.b == B.Four3);
|
||||
|
||||
data.a = A.Three;
|
||||
try expect(data.a == A.Three);
|
||||
try expect(data.b == B.Four3);
|
||||
}
|
||||
|
||||
fn getA(data: *const BitFieldOfEnums) A {
|
||||
return data.a;
|
||||
}
|
||||
|
||||
fn getB(data: *const BitFieldOfEnums) B {
|
||||
return data.b;
|
||||
}
|
||||
|
||||
fn getC(data: *const BitFieldOfEnums) C {
|
||||
return data.c;
|
||||
}
|
||||
|
||||
test "enum literal in array literal" {
|
||||
const Items = enum { one, two };
|
||||
const array = [_]Items{ .one, .two };
|
||||
|
||||
try expect(array[0] == .one);
|
||||
try expect(array[1] == .two);
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
const builtin = @import("builtin");
|
||||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const expect = testing.expect;
|
||||
@ -97,3 +98,284 @@ test "C pointer comparison and arithmetic" {
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "dereference pointer again" {
|
||||
try testDerefPtrOneVal();
|
||||
comptime try testDerefPtrOneVal();
|
||||
}
|
||||
|
||||
const Foo1 = struct {
|
||||
x: void,
|
||||
};
|
||||
|
||||
fn testDerefPtrOneVal() !void {
|
||||
// Foo1 satisfies the OnePossibleValueYes criteria
|
||||
const x = &Foo1{ .x = {} };
|
||||
const y = x.*;
|
||||
try expect(@TypeOf(y.x) == void);
|
||||
}
|
||||
|
||||
test "peer type resolution with C pointers" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
var ptr_one: *u8 = undefined;
|
||||
var ptr_many: [*]u8 = undefined;
|
||||
var ptr_c: [*c]u8 = undefined;
|
||||
var t = true;
|
||||
var x1 = if (t) ptr_one else ptr_c;
|
||||
var x2 = if (t) ptr_many else ptr_c;
|
||||
var x3 = if (t) ptr_c else ptr_one;
|
||||
var x4 = if (t) ptr_c else ptr_many;
|
||||
try expect(@TypeOf(x1) == [*c]u8);
|
||||
try expect(@TypeOf(x2) == [*c]u8);
|
||||
try expect(@TypeOf(x3) == [*c]u8);
|
||||
try expect(@TypeOf(x4) == [*c]u8);
|
||||
}
|
||||
|
||||
test "implicit casting between C pointer and optional non-C pointer" {
|
||||
var slice: []const u8 = "aoeu";
|
||||
const opt_many_ptr: ?[*]const u8 = slice.ptr;
|
||||
var ptr_opt_many_ptr = &opt_many_ptr;
|
||||
var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr;
|
||||
try expect(c_ptr.*.* == 'a');
|
||||
ptr_opt_many_ptr = c_ptr;
|
||||
try expect(ptr_opt_many_ptr.*.?[1] == 'o');
|
||||
}
|
||||
|
||||
test "implicit cast error unions with non-optional to optional pointer" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
try expectError(error.Fail, foo());
|
||||
}
|
||||
fn foo() anyerror!?*u8 {
|
||||
return bar() orelse error.Fail;
|
||||
}
|
||||
fn bar() ?*u8 {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "compare equality of optional and non-optional pointer" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
const a = @intToPtr(*const usize, 0x12345678);
|
||||
const b = @intToPtr(?*usize, 0x12345678);
|
||||
try expect(a == b);
|
||||
try expect(b == a);
|
||||
}
|
||||
|
||||
test "allowzero pointer and slice" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||
|
||||
var ptr = @intToPtr([*]allowzero i32, 0);
|
||||
var opt_ptr: ?[*]allowzero i32 = ptr;
|
||||
try expect(opt_ptr != null);
|
||||
try expect(@ptrToInt(ptr) == 0);
|
||||
var runtime_zero: usize = 0;
|
||||
var slice = ptr[runtime_zero..10];
|
||||
comptime try expect(@TypeOf(slice) == []allowzero i32);
|
||||
try expect(@ptrToInt(&slice[5]) == 20);
|
||||
|
||||
comptime try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero);
|
||||
comptime try expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero);
|
||||
}
|
||||
|
||||
test "assign null directly to C pointer and test null equality" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
var x: [*c]i32 = null;
|
||||
try expect(x == null);
|
||||
try expect(null == x);
|
||||
try expect(!(x != null));
|
||||
try expect(!(null != x));
|
||||
if (x) |same_x| {
|
||||
_ = same_x;
|
||||
@panic("fail");
|
||||
}
|
||||
var otherx: i32 = undefined;
|
||||
try expect((x orelse &otherx) == &otherx);
|
||||
|
||||
const y: [*c]i32 = null;
|
||||
comptime try expect(y == null);
|
||||
comptime try expect(null == y);
|
||||
comptime try expect(!(y != null));
|
||||
comptime try expect(!(null != y));
|
||||
if (y) |same_y| {
|
||||
_ = same_y;
|
||||
@panic("fail");
|
||||
}
|
||||
const othery: i32 = undefined;
|
||||
comptime try expect((y orelse &othery) == &othery);
|
||||
|
||||
var n: i32 = 1234;
|
||||
var x1: [*c]i32 = &n;
|
||||
try expect(!(x1 == null));
|
||||
try expect(!(null == x1));
|
||||
try expect(x1 != null);
|
||||
try expect(null != x1);
|
||||
try expect(x1.?.* == 1234);
|
||||
if (x1) |same_x1| {
|
||||
try expect(same_x1.* == 1234);
|
||||
} else {
|
||||
@panic("fail");
|
||||
}
|
||||
try expect((x1 orelse &otherx) == x1);
|
||||
|
||||
const nc: i32 = 1234;
|
||||
const y1: [*c]const i32 = &nc;
|
||||
comptime try expect(!(y1 == null));
|
||||
comptime try expect(!(null == y1));
|
||||
comptime try expect(y1 != null);
|
||||
comptime try expect(null != y1);
|
||||
comptime try expect(y1.?.* == 1234);
|
||||
if (y1) |same_y1| {
|
||||
try expect(same_y1.* == 1234);
|
||||
} else {
|
||||
@compileError("fail");
|
||||
}
|
||||
comptime try expect((y1 orelse &othery) == y1);
|
||||
}
|
||||
|
||||
test "null terminated pointer" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var array_with_zero = [_:0]u8{ 'h', 'e', 'l', 'l', 'o' };
|
||||
var zero_ptr: [*:0]const u8 = @ptrCast([*:0]const u8, &array_with_zero);
|
||||
var no_zero_ptr: [*]const u8 = zero_ptr;
|
||||
var zero_ptr_again = @ptrCast([*:0]const u8, no_zero_ptr);
|
||||
try expect(std.mem.eql(u8, std.mem.sliceTo(zero_ptr_again, 0), "hello"));
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "allow any sentinel" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var array = [_:std.math.minInt(i32)]i32{ 1, 2, 3, 4 };
|
||||
var ptr: [*:std.math.minInt(i32)]i32 = &array;
|
||||
try expect(ptr[4] == std.math.minInt(i32));
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "pointer sentinel with enums" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
const Number = enum {
|
||||
one,
|
||||
two,
|
||||
sentinel,
|
||||
};
|
||||
|
||||
fn doTheTest() !void {
|
||||
var ptr: [*:.sentinel]const Number = &[_:.sentinel]Number{ .one, .two, .two, .one };
|
||||
try expect(ptr[4] == .sentinel); // TODO this should be comptime try expect, see #3731
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "pointer sentinel with optional element" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var ptr: [*:null]const ?i32 = &[_:null]?i32{ 1, 2, 3, 4 };
|
||||
try expect(ptr[4] == null); // TODO this should be comptime try expect, see #3731
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "pointer sentinel with +inf" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
const inf = std.math.inf_f32;
|
||||
var ptr: [*:inf]const f32 = &[_:inf]f32{ 1.1, 2.2, 3.3, 4.4 };
|
||||
try expect(ptr[4] == inf); // TODO this should be comptime try expect, see #3731
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "pointer to array at fixed address" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
const array = @intToPtr(*volatile [1]u32, 0x10);
|
||||
// Silly check just to reference `array`
|
||||
try expect(@ptrToInt(&array[0]) == 0x10);
|
||||
}
|
||||
|
||||
test "pointer arithmetic affects the alignment" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
{
|
||||
var ptr: [*]align(8) u32 = undefined;
|
||||
var x: usize = 1;
|
||||
|
||||
try expect(@typeInfo(@TypeOf(ptr)).Pointer.alignment == 8);
|
||||
const ptr1 = ptr + 1; // 1 * 4 = 4 -> lcd(4,8) = 4
|
||||
try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 4);
|
||||
const ptr2 = ptr + 4; // 4 * 4 = 16 -> lcd(16,8) = 8
|
||||
try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 8);
|
||||
const ptr3 = ptr + 0; // no-op
|
||||
try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8);
|
||||
const ptr4 = ptr + x; // runtime-known addend
|
||||
try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4);
|
||||
}
|
||||
{
|
||||
var ptr: [*]align(8) [3]u8 = undefined;
|
||||
var x: usize = 1;
|
||||
|
||||
const ptr1 = ptr + 17; // 3 * 17 = 51
|
||||
try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 1);
|
||||
const ptr2 = ptr + x; // runtime-known addend
|
||||
try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 1);
|
||||
const ptr3 = ptr + 8; // 3 * 8 = 24 -> lcd(8,24) = 8
|
||||
try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8);
|
||||
const ptr4 = ptr + 4; // 3 * 4 = 12 -> lcd(8,12) = 4
|
||||
try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4);
|
||||
}
|
||||
}
|
||||
|
||||
test "@ptrToInt on null optional at comptime" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
{
|
||||
const pointer = @intToPtr(?*u8, 0x000);
|
||||
const x = @ptrToInt(pointer);
|
||||
_ = x;
|
||||
comptime try expect(0 == @ptrToInt(pointer));
|
||||
}
|
||||
{
|
||||
const pointer = @intToPtr(?*u8, 0xf00);
|
||||
comptime try expect(0xf00 == @ptrToInt(pointer));
|
||||
}
|
||||
}
|
||||
|
||||
test "indexing array with sentinel returns correct type" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
var s: [:0]const u8 = "abc";
|
||||
try testing.expectEqualSlices(u8, "*const u8", @typeName(@TypeOf(&s[0])));
|
||||
}
|
||||
|
||||
@ -1,256 +0,0 @@
|
||||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const expect = testing.expect;
|
||||
const expectError = testing.expectError;
|
||||
|
||||
const Foo1 = struct {
|
||||
x: void,
|
||||
};
|
||||
|
||||
test "dereference pointer again" {
|
||||
try testDerefPtrOneVal();
|
||||
comptime try testDerefPtrOneVal();
|
||||
}
|
||||
|
||||
fn testDerefPtrOneVal() !void {
|
||||
// Foo1 satisfies the OnePossibleValueYes criteria
|
||||
const x = &Foo1{ .x = {} };
|
||||
const y = x.*;
|
||||
try expect(@TypeOf(y.x) == void);
|
||||
}
|
||||
|
||||
test "peer type resolution with C pointers" {
|
||||
var ptr_one: *u8 = undefined;
|
||||
var ptr_many: [*]u8 = undefined;
|
||||
var ptr_c: [*c]u8 = undefined;
|
||||
var t = true;
|
||||
var x1 = if (t) ptr_one else ptr_c;
|
||||
var x2 = if (t) ptr_many else ptr_c;
|
||||
var x3 = if (t) ptr_c else ptr_one;
|
||||
var x4 = if (t) ptr_c else ptr_many;
|
||||
try expect(@TypeOf(x1) == [*c]u8);
|
||||
try expect(@TypeOf(x2) == [*c]u8);
|
||||
try expect(@TypeOf(x3) == [*c]u8);
|
||||
try expect(@TypeOf(x4) == [*c]u8);
|
||||
}
|
||||
|
||||
test "implicit casting between C pointer and optional non-C pointer" {
|
||||
var slice: []const u8 = "aoeu";
|
||||
const opt_many_ptr: ?[*]const u8 = slice.ptr;
|
||||
var ptr_opt_many_ptr = &opt_many_ptr;
|
||||
var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr;
|
||||
try expect(c_ptr.*.* == 'a');
|
||||
ptr_opt_many_ptr = c_ptr;
|
||||
try expect(ptr_opt_many_ptr.*.?[1] == 'o');
|
||||
}
|
||||
|
||||
test "implicit cast error unions with non-optional to optional pointer" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
try expectError(error.Fail, foo());
|
||||
}
|
||||
fn foo() anyerror!?*u8 {
|
||||
return bar() orelse error.Fail;
|
||||
}
|
||||
fn bar() ?*u8 {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "compare equality of optional and non-optional pointer" {
|
||||
const a = @intToPtr(*const usize, 0x12345678);
|
||||
const b = @intToPtr(?*usize, 0x12345678);
|
||||
try expect(a == b);
|
||||
try expect(b == a);
|
||||
}
|
||||
|
||||
test "allowzero pointer and slice" {
|
||||
var ptr = @intToPtr([*]allowzero i32, 0);
|
||||
var opt_ptr: ?[*]allowzero i32 = ptr;
|
||||
try expect(opt_ptr != null);
|
||||
try expect(@ptrToInt(ptr) == 0);
|
||||
var runtime_zero: usize = 0;
|
||||
var slice = ptr[runtime_zero..10];
|
||||
comptime try expect(@TypeOf(slice) == []allowzero i32);
|
||||
try expect(@ptrToInt(&slice[5]) == 20);
|
||||
|
||||
comptime try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero);
|
||||
comptime try expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero);
|
||||
}
|
||||
|
||||
test "assign null directly to C pointer and test null equality" {
|
||||
var x: [*c]i32 = null;
|
||||
try expect(x == null);
|
||||
try expect(null == x);
|
||||
try expect(!(x != null));
|
||||
try expect(!(null != x));
|
||||
if (x) |same_x| {
|
||||
_ = same_x;
|
||||
@panic("fail");
|
||||
}
|
||||
var otherx: i32 = undefined;
|
||||
try expect((x orelse &otherx) == &otherx);
|
||||
|
||||
const y: [*c]i32 = null;
|
||||
comptime try expect(y == null);
|
||||
comptime try expect(null == y);
|
||||
comptime try expect(!(y != null));
|
||||
comptime try expect(!(null != y));
|
||||
if (y) |same_y| {
|
||||
_ = same_y;
|
||||
@panic("fail");
|
||||
}
|
||||
const othery: i32 = undefined;
|
||||
comptime try expect((y orelse &othery) == &othery);
|
||||
|
||||
var n: i32 = 1234;
|
||||
var x1: [*c]i32 = &n;
|
||||
try expect(!(x1 == null));
|
||||
try expect(!(null == x1));
|
||||
try expect(x1 != null);
|
||||
try expect(null != x1);
|
||||
try expect(x1.?.* == 1234);
|
||||
if (x1) |same_x1| {
|
||||
try expect(same_x1.* == 1234);
|
||||
} else {
|
||||
@panic("fail");
|
||||
}
|
||||
try expect((x1 orelse &otherx) == x1);
|
||||
|
||||
const nc: i32 = 1234;
|
||||
const y1: [*c]const i32 = &nc;
|
||||
comptime try expect(!(y1 == null));
|
||||
comptime try expect(!(null == y1));
|
||||
comptime try expect(y1 != null);
|
||||
comptime try expect(null != y1);
|
||||
comptime try expect(y1.?.* == 1234);
|
||||
if (y1) |same_y1| {
|
||||
try expect(same_y1.* == 1234);
|
||||
} else {
|
||||
@compileError("fail");
|
||||
}
|
||||
comptime try expect((y1 orelse &othery) == y1);
|
||||
}
|
||||
|
||||
test "null terminated pointer" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var array_with_zero = [_:0]u8{ 'h', 'e', 'l', 'l', 'o' };
|
||||
var zero_ptr: [*:0]const u8 = @ptrCast([*:0]const u8, &array_with_zero);
|
||||
var no_zero_ptr: [*]const u8 = zero_ptr;
|
||||
var zero_ptr_again = @ptrCast([*:0]const u8, no_zero_ptr);
|
||||
try expect(std.mem.eql(u8, std.mem.sliceTo(zero_ptr_again, 0), "hello"));
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "allow any sentinel" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var array = [_:std.math.minInt(i32)]i32{ 1, 2, 3, 4 };
|
||||
var ptr: [*:std.math.minInt(i32)]i32 = &array;
|
||||
try expect(ptr[4] == std.math.minInt(i32));
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "pointer sentinel with enums" {
|
||||
const S = struct {
|
||||
const Number = enum {
|
||||
one,
|
||||
two,
|
||||
sentinel,
|
||||
};
|
||||
|
||||
fn doTheTest() !void {
|
||||
var ptr: [*:.sentinel]const Number = &[_:.sentinel]Number{ .one, .two, .two, .one };
|
||||
try expect(ptr[4] == .sentinel); // TODO this should be comptime try expect, see #3731
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "pointer sentinel with optional element" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var ptr: [*:null]const ?i32 = &[_:null]?i32{ 1, 2, 3, 4 };
|
||||
try expect(ptr[4] == null); // TODO this should be comptime try expect, see #3731
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "pointer sentinel with +inf" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
const inf = std.math.inf_f32;
|
||||
var ptr: [*:inf]const f32 = &[_:inf]f32{ 1.1, 2.2, 3.3, 4.4 };
|
||||
try expect(ptr[4] == inf); // TODO this should be comptime try expect, see #3731
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "pointer to array at fixed address" {
|
||||
const array = @intToPtr(*volatile [1]u32, 0x10);
|
||||
// Silly check just to reference `array`
|
||||
try expect(@ptrToInt(&array[0]) == 0x10);
|
||||
}
|
||||
|
||||
test "pointer arithmetic affects the alignment" {
|
||||
{
|
||||
var ptr: [*]align(8) u32 = undefined;
|
||||
var x: usize = 1;
|
||||
|
||||
try expect(@typeInfo(@TypeOf(ptr)).Pointer.alignment == 8);
|
||||
const ptr1 = ptr + 1; // 1 * 4 = 4 -> lcd(4,8) = 4
|
||||
try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 4);
|
||||
const ptr2 = ptr + 4; // 4 * 4 = 16 -> lcd(16,8) = 8
|
||||
try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 8);
|
||||
const ptr3 = ptr + 0; // no-op
|
||||
try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8);
|
||||
const ptr4 = ptr + x; // runtime-known addend
|
||||
try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4);
|
||||
}
|
||||
{
|
||||
var ptr: [*]align(8) [3]u8 = undefined;
|
||||
var x: usize = 1;
|
||||
|
||||
const ptr1 = ptr + 17; // 3 * 17 = 51
|
||||
try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 1);
|
||||
const ptr2 = ptr + x; // runtime-known addend
|
||||
try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 1);
|
||||
const ptr3 = ptr + 8; // 3 * 8 = 24 -> lcd(8,24) = 8
|
||||
try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8);
|
||||
const ptr4 = ptr + 4; // 3 * 4 = 12 -> lcd(8,12) = 4
|
||||
try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4);
|
||||
}
|
||||
}
|
||||
|
||||
test "@ptrToInt on null optional at comptime" {
|
||||
{
|
||||
const pointer = @intToPtr(?*u8, 0x000);
|
||||
const x = @ptrToInt(pointer);
|
||||
_ = x;
|
||||
comptime try expect(0 == @ptrToInt(pointer));
|
||||
}
|
||||
{
|
||||
const pointer = @intToPtr(?*u8, 0xf00);
|
||||
comptime try expect(0xf00 == @ptrToInt(pointer));
|
||||
}
|
||||
}
|
||||
|
||||
test "indexing array with sentinel returns correct type" {
|
||||
var s: [:0]const u8 = "abc";
|
||||
try testing.expectEqualSlices(u8, "*const u8", @typeName(@TypeOf(&s[0])));
|
||||
}
|
||||
@ -2,3 +2,80 @@ const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const expect = std.testing.expect;
|
||||
const native_endian = builtin.target.cpu.arch.endian();
|
||||
|
||||
test "reinterpret bytes as integer with nonzero offset" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
try testReinterpretBytesAsInteger();
|
||||
comptime try testReinterpretBytesAsInteger();
|
||||
}
|
||||
|
||||
fn testReinterpretBytesAsInteger() !void {
|
||||
const bytes = "\x12\x34\x56\x78\xab";
|
||||
const expected = switch (native_endian) {
|
||||
.Little => 0xab785634,
|
||||
.Big => 0x345678ab,
|
||||
};
|
||||
try expect(@ptrCast(*align(1) const u32, bytes[1..5]).* == expected);
|
||||
}
|
||||
|
||||
test "reinterpret bytes of an array into an extern struct" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
try testReinterpretBytesAsExternStruct();
|
||||
comptime try testReinterpretBytesAsExternStruct();
|
||||
}
|
||||
|
||||
fn testReinterpretBytesAsExternStruct() !void {
|
||||
var bytes align(2) = [_]u8{ 1, 2, 3, 4, 5, 6 };
|
||||
|
||||
const S = extern struct {
|
||||
a: u8,
|
||||
b: u16,
|
||||
c: u8,
|
||||
};
|
||||
|
||||
var ptr = @ptrCast(*const S, &bytes);
|
||||
var val = ptr.c;
|
||||
try expect(val == 5);
|
||||
}
|
||||
|
||||
test "reinterpret struct field at comptime" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
const numNative = comptime Bytes.init(0x12345678);
|
||||
if (native_endian != .Little) {
|
||||
try expect(std.mem.eql(u8, &[_]u8{ 0x12, 0x34, 0x56, 0x78 }, &numNative.bytes));
|
||||
} else {
|
||||
try expect(std.mem.eql(u8, &[_]u8{ 0x78, 0x56, 0x34, 0x12 }, &numNative.bytes));
|
||||
}
|
||||
}
|
||||
|
||||
const Bytes = struct {
|
||||
bytes: [4]u8,
|
||||
|
||||
pub fn init(v: u32) Bytes {
|
||||
var res: Bytes = undefined;
|
||||
@ptrCast(*align(1) u32, &res.bytes).* = v;
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
test "comptime ptrcast keeps larger alignment" {
|
||||
comptime {
|
||||
const a: u32 = 1234;
|
||||
const p = @ptrCast([*]const u8, &a);
|
||||
try expect(@TypeOf(p) == [*]align(@alignOf(u32)) const u8);
|
||||
}
|
||||
}
|
||||
|
||||
test "implicit optional pointer to optional anyopaque pointer" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
var buf: [4]u8 = "aoeu".*;
|
||||
var x: ?[*]u8 = &buf;
|
||||
var y: ?*anyopaque = x;
|
||||
var z = @ptrCast(*[4]u8, y);
|
||||
try expect(std.mem.eql(u8, z, "aoeu"));
|
||||
}
|
||||
|
||||
@ -1,73 +0,0 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const expect = std.testing.expect;
|
||||
const native_endian = builtin.target.cpu.arch.endian();
|
||||
|
||||
test "reinterpret bytes as integer with nonzero offset" {
|
||||
try testReinterpretBytesAsInteger();
|
||||
comptime try testReinterpretBytesAsInteger();
|
||||
}
|
||||
|
||||
fn testReinterpretBytesAsInteger() !void {
|
||||
const bytes = "\x12\x34\x56\x78\xab";
|
||||
const expected = switch (native_endian) {
|
||||
.Little => 0xab785634,
|
||||
.Big => 0x345678ab,
|
||||
};
|
||||
try expect(@ptrCast(*align(1) const u32, bytes[1..5]).* == expected);
|
||||
}
|
||||
|
||||
test "reinterpret bytes of an array into an extern struct" {
|
||||
try testReinterpretBytesAsExternStruct();
|
||||
comptime try testReinterpretBytesAsExternStruct();
|
||||
}
|
||||
|
||||
fn testReinterpretBytesAsExternStruct() !void {
|
||||
var bytes align(2) = [_]u8{ 1, 2, 3, 4, 5, 6 };
|
||||
|
||||
const S = extern struct {
|
||||
a: u8,
|
||||
b: u16,
|
||||
c: u8,
|
||||
};
|
||||
|
||||
var ptr = @ptrCast(*const S, &bytes);
|
||||
var val = ptr.c;
|
||||
try expect(val == 5);
|
||||
}
|
||||
|
||||
test "reinterpret struct field at comptime" {
|
||||
const numNative = comptime Bytes.init(0x12345678);
|
||||
if (native_endian != .Little) {
|
||||
try expect(std.mem.eql(u8, &[_]u8{ 0x12, 0x34, 0x56, 0x78 }, &numNative.bytes));
|
||||
} else {
|
||||
try expect(std.mem.eql(u8, &[_]u8{ 0x78, 0x56, 0x34, 0x12 }, &numNative.bytes));
|
||||
}
|
||||
}
|
||||
|
||||
const Bytes = struct {
|
||||
bytes: [4]u8,
|
||||
|
||||
pub fn init(v: u32) Bytes {
|
||||
var res: Bytes = undefined;
|
||||
@ptrCast(*align(1) u32, &res.bytes).* = v;
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
test "comptime ptrcast keeps larger alignment" {
|
||||
comptime {
|
||||
const a: u32 = 1234;
|
||||
const p = @ptrCast([*]const u8, &a);
|
||||
try expect(@TypeOf(p) == [*]align(@alignOf(u32)) const u8);
|
||||
}
|
||||
}
|
||||
|
||||
test "implicit optional pointer to optional anyopaque pointer" {
|
||||
var buf: [4]u8 = "aoeu".*;
|
||||
var x: ?[*]u8 = &buf;
|
||||
var y: ?*anyopaque = x;
|
||||
var z = @ptrCast(*[4]u8, y);
|
||||
try expect(std.mem.eql(u8, z, "aoeu"));
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user