stage2: various fixes to cImport, sizeOf and types to get tests passing

This commit is contained in:
Veikka Tuominen 2021-09-20 21:05:42 +03:00 committed by Andrew Kelley
parent d64d5cfc0a
commit 55e7c099ca
10 changed files with 304 additions and 250 deletions

View File

@ -7082,7 +7082,7 @@ fn builtinCall(
.bit_cast => return bitCast( gz, scope, rl, node, params[0], params[1]),
.TypeOf => return typeOf( gz, scope, rl, node, params),
.union_init => return unionInit(gz, scope, rl, node, params),
.c_import => return cImport( gz, scope, rl, node, params[0]),
.c_import => return cImport( gz, scope, node, params[0]),
.@"export" => {
const node_tags = tree.nodes.items(.tag);
@ -7692,7 +7692,6 @@ fn shiftOp(
fn cImport(
gz: *GenZir,
scope: *Scope,
rl: ResultLoc,
node: Ast.Node.Index,
body_node: Ast.Node.Index,
) InnerError!Zir.Inst.Ref {
@ -7712,7 +7711,7 @@ fn cImport(
try block_scope.setBlockBody(block_inst);
try gz.instructions.append(gpa, block_inst);
return rvalue(gz, rl, .void_value, node);
return indexToRef(block_inst);
}
fn overflowArithmetic(

View File

@ -2644,7 +2644,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
const dep_basename = std.fs.path.basename(out_dep_path);
try man.addDepFilePost(zig_cache_tmp_dir, dep_basename);
if (build_options.is_stage1) try comp.stage1_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename);
if (build_options.is_stage1 and comp.bin_file.options.use_stage1) try comp.stage1_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename);
const digest = man.final();
const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });

View File

@ -2183,11 +2183,10 @@ fn zirCImport(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Com
@import("clang.zig").Stage2ErrorMsg.delete(c_import_res.errors.ptr, c_import_res.errors.len);
return sema.mod.failWithOwnedErrorMsg(&child_block.base, msg);
}
const c_import_pkg = @import("Package.zig").createWithDir(
const c_import_pkg = @import("Package.zig").create(
sema.gpa,
sema.mod.comp.local_cache_directory,
null,
std.fs.path.basename(c_import_res.out_zig_path),
c_import_res.out_zig_path,
) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => unreachable, // we pass null for root_src_dir_path
@ -2200,6 +2199,9 @@ fn zirCImport(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Com
const result = sema.mod.importPkg(c_import_pkg) catch |err|
return sema.mod.fail(&child_block.base, src, "C import failed: {s}", .{@errorName(err)});
sema.mod.astGenFile(result.file) catch |err|
return sema.mod.fail(&child_block.base, src, "C import failed: {s}", .{@errorName(err)});
try sema.mod.semaFile(result.file);
const file_root_decl = result.file.root_decl.?;
try sema.mod.declareDeclDependency(sema.owner_decl, file_root_decl);
@ -6467,10 +6469,41 @@ fn runtimeBoolCmp(
fn zirSizeOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);
const target = sema.mod.getTarget();
const abi_size = operand_ty.abiSize(target);
const abi_size = switch (operand_ty.zigTypeTag()) {
.Fn => unreachable,
.NoReturn,
.Undefined,
.Null,
.BoundFn,
.Opaque,
=> return sema.mod.fail(&block.base, src, "no size available for type '{}'", .{operand_ty}),
.Type,
.EnumLiteral,
.ComptimeFloat,
.ComptimeInt,
.Void,
=> 0,
.Bool,
.Int,
.Float,
.Pointer,
.Array,
.Struct,
.Optional,
.ErrorUnion,
.ErrorSet,
.Enum,
.Union,
.Vector,
.Frame,
.AnyFrame,
=> operand_ty.abiSize(target),
};
return sema.addIntUnsigned(Type.initTag(.comptime_int), abi_size);
}

View File

@ -407,15 +407,27 @@ const Writer = struct {
.mul_with_saturation,
.shl_with_saturation,
=> try self.writeSaturatingArithmetic(stream, extended),
.struct_decl => try self.writeStructDecl(stream, extended),
.union_decl => try self.writeUnionDecl(stream, extended),
.enum_decl => try self.writeEnumDecl(stream, extended),
.c_undef, .c_include => {
const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data;
try self.writeInstRef(stream, inst_data.operand);
try stream.writeAll(") ");
},
.c_define => {
const inst_data = self.code.extraData(Zir.Inst.BinNode, extended.operand).data;
try self.writeInstRef(stream, inst_data.lhs);
try stream.writeAll(", ");
try self.writeInstRef(stream, inst_data.rhs);
try stream.writeByte(')');
},
.alloc,
.builtin_extern,
.c_undef,
.c_include,
.c_define,
.wasm_memory_size,
.wasm_memory_grow,
=> try stream.writeAll("TODO))"),

View File

@ -602,8 +602,8 @@ pub const Type = extern union {
}
return false;
},
.Float => return a.tag() == b.tag(),
.Opaque,
.Float,
.BoundFn,
.Frame,
=> std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),

View File

@ -11,6 +11,8 @@ test {
_ = @import("behavior/array.zig");
_ = @import("behavior/usingnamespace.zig");
_ = @import("behavior/atomics.zig");
_ = @import("behavior/sizeof_and_typeof.zig");
_ = @import("behavior/translate_c_macros.zig");
if (builtin.zig_is_stage2) {
// When all comptime_memory.zig tests pass, #9646 can be closed.
@ -128,7 +130,7 @@ test {
_ = @import("behavior/saturating_arithmetic.zig");
_ = @import("behavior/shuffle.zig");
_ = @import("behavior/select.zig");
_ = @import("behavior/sizeof_and_typeof.zig");
_ = @import("behavior/sizeof_and_typeof_stage1.zig");
_ = @import("behavior/slice.zig");
_ = @import("behavior/slice_sentinel_comptime.zig");
_ = @import("behavior/struct.zig");
@ -157,6 +159,6 @@ test {
_ = @import("behavior/while.zig");
_ = @import("behavior/widening.zig");
_ = @import("behavior/src.zig");
_ = @import("behavior/translate_c_macros.zig");
_ = @import("behavior/translate_c_macros_stage1.zig");
}
}

View File

@ -10,118 +10,6 @@ test "@sizeOf and @TypeOf" {
const x: u16 = 13;
const z: @TypeOf(x) = 19;
const A = struct {
a: u8,
b: u32,
c: u8,
d: u3,
e: u5,
f: u16,
g: u16,
h: u9,
i: u7,
};
const P = packed struct {
a: u8,
b: u32,
c: u8,
d: u3,
e: u5,
f: u16,
g: u16,
h: u9,
i: u7,
};
test "@offsetOf" {
// Packed structs have fixed memory layout
try expect(@offsetOf(P, "a") == 0);
try expect(@offsetOf(P, "b") == 1);
try expect(@offsetOf(P, "c") == 5);
try expect(@offsetOf(P, "d") == 6);
try expect(@offsetOf(P, "e") == 6);
try expect(@offsetOf(P, "f") == 7);
try expect(@offsetOf(P, "g") == 9);
try expect(@offsetOf(P, "h") == 11);
try expect(@offsetOf(P, "i") == 12);
// Normal struct fields can be moved/padded
var a: A = undefined;
try expect(@ptrToInt(&a.a) - @ptrToInt(&a) == @offsetOf(A, "a"));
try expect(@ptrToInt(&a.b) - @ptrToInt(&a) == @offsetOf(A, "b"));
try expect(@ptrToInt(&a.c) - @ptrToInt(&a) == @offsetOf(A, "c"));
try expect(@ptrToInt(&a.d) - @ptrToInt(&a) == @offsetOf(A, "d"));
try expect(@ptrToInt(&a.e) - @ptrToInt(&a) == @offsetOf(A, "e"));
try expect(@ptrToInt(&a.f) - @ptrToInt(&a) == @offsetOf(A, "f"));
try expect(@ptrToInt(&a.g) - @ptrToInt(&a) == @offsetOf(A, "g"));
try expect(@ptrToInt(&a.h) - @ptrToInt(&a) == @offsetOf(A, "h"));
try expect(@ptrToInt(&a.i) - @ptrToInt(&a) == @offsetOf(A, "i"));
}
test "@offsetOf packed struct, array length not power of 2 or multiple of native pointer width in bytes" {
const p3a_len = 3;
const P3 = packed struct {
a: [p3a_len]u8,
b: usize,
};
try std.testing.expectEqual(0, @offsetOf(P3, "a"));
try std.testing.expectEqual(p3a_len, @offsetOf(P3, "b"));
const p5a_len = 5;
const P5 = packed struct {
a: [p5a_len]u8,
b: usize,
};
try std.testing.expectEqual(0, @offsetOf(P5, "a"));
try std.testing.expectEqual(p5a_len, @offsetOf(P5, "b"));
const p6a_len = 6;
const P6 = packed struct {
a: [p6a_len]u8,
b: usize,
};
try std.testing.expectEqual(0, @offsetOf(P6, "a"));
try std.testing.expectEqual(p6a_len, @offsetOf(P6, "b"));
const p7a_len = 7;
const P7 = packed struct {
a: [p7a_len]u8,
b: usize,
};
try std.testing.expectEqual(0, @offsetOf(P7, "a"));
try std.testing.expectEqual(p7a_len, @offsetOf(P7, "b"));
const p9a_len = 9;
const P9 = packed struct {
a: [p9a_len]u8,
b: usize,
};
try std.testing.expectEqual(0, @offsetOf(P9, "a"));
try std.testing.expectEqual(p9a_len, @offsetOf(P9, "b"));
// 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 25 etc. are further cases
}
test "@bitOffsetOf" {
// Packed structs have fixed memory layout
try expect(@bitOffsetOf(P, "a") == 0);
try expect(@bitOffsetOf(P, "b") == 8);
try expect(@bitOffsetOf(P, "c") == 40);
try expect(@bitOffsetOf(P, "d") == 48);
try expect(@bitOffsetOf(P, "e") == 51);
try expect(@bitOffsetOf(P, "f") == 56);
try expect(@bitOffsetOf(P, "g") == 72);
try expect(@offsetOf(A, "a") * 8 == @bitOffsetOf(A, "a"));
try expect(@offsetOf(A, "b") * 8 == @bitOffsetOf(A, "b"));
try expect(@offsetOf(A, "c") * 8 == @bitOffsetOf(A, "c"));
try expect(@offsetOf(A, "d") * 8 == @bitOffsetOf(A, "d"));
try expect(@offsetOf(A, "e") * 8 == @bitOffsetOf(A, "e"));
try expect(@offsetOf(A, "f") * 8 == @bitOffsetOf(A, "f"));
try expect(@offsetOf(A, "g") * 8 == @bitOffsetOf(A, "g"));
}
test "@sizeOf on compile-time types" {
try expect(@sizeOf(comptime_int) == 0);
try expect(@sizeOf(comptime_float) == 0);
@ -129,34 +17,6 @@ test "@sizeOf on compile-time types" {
try expect(@sizeOf(@TypeOf(type)) == 0);
}
test "@sizeOf(T) == 0 doesn't force resolving struct size" {
const S = struct {
const Foo = struct {
y: if (@sizeOf(Foo) == 0) u64 else u32,
};
const Bar = struct {
x: i32,
y: if (0 == @sizeOf(Bar)) u64 else u32,
};
};
try expect(@sizeOf(S.Foo) == 4);
try expect(@sizeOf(S.Bar) == 8);
}
test "@TypeOf() has no runtime side effects" {
const S = struct {
fn foo(comptime T: type, ptr: *T) T {
ptr.* += 1;
return ptr.*;
}
};
var data: i32 = 0;
const T = @TypeOf(S.foo(i32, &data));
comptime try expect(T == i32);
try expect(data == 0);
}
test "@TypeOf() with multiple arguments" {
{
var var_1: u32 = undefined;
@ -180,19 +40,6 @@ test "@TypeOf() with multiple arguments" {
}
}
test "branching logic inside @TypeOf" {
const S = struct {
var data: i32 = 0;
fn foo() anyerror!i32 {
data += 1;
return undefined;
}
};
const T = @TypeOf(S.foo() catch undefined);
comptime try expect(T == i32);
try expect(S.data == 0);
}
fn fn1(alpha: bool) void {
const n: usize = 7;
_ = if (alpha) n else @sizeOf(usize);
@ -201,64 +48,3 @@ fn fn1(alpha: bool) void {
test "lazy @sizeOf result is checked for definedness" {
_ = fn1;
}
test "@bitSizeOf" {
try expect(@bitSizeOf(u2) == 2);
try expect(@bitSizeOf(u8) == @sizeOf(u8) * 8);
try expect(@bitSizeOf(struct {
a: u2,
}) == 8);
try expect(@bitSizeOf(packed struct {
a: u2,
}) == 2);
}
test "@sizeOf comparison against zero" {
const S0 = struct {
f: *@This(),
};
const U0 = union {
f: *@This(),
};
const S1 = struct {
fn H(comptime T: type) type {
return struct {
x: T,
};
}
f0: H(*@This()),
f1: H(**@This()),
f2: H(***@This()),
};
const U1 = union {
fn H(comptime T: type) type {
return struct {
x: T,
};
}
f0: H(*@This()),
f1: H(**@This()),
f2: H(***@This()),
};
const S = struct {
fn doTheTest(comptime T: type, comptime result: bool) !void {
try expectEqual(result, @sizeOf(T) > 0);
}
};
// Zero-sized type
try S.doTheTest(u0, false);
try S.doTheTest(*u0, false);
// Non byte-sized type
try S.doTheTest(u1, true);
try S.doTheTest(*u1, true);
// Regular type
try S.doTheTest(u8, true);
try S.doTheTest(*u8, true);
try S.doTheTest(f32, true);
try S.doTheTest(*f32, true);
// Container with ptr pointing to themselves
try S.doTheTest(S0, true);
try S.doTheTest(U0, true);
try S.doTheTest(S1, true);
try S.doTheTest(U1, true);
}

View File

@ -0,0 +1,218 @@
const std = @import("std");
const builtin = std.builtin;
const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual;
const A = struct {
a: u8,
b: u32,
c: u8,
d: u3,
e: u5,
f: u16,
g: u16,
h: u9,
i: u7,
};
const P = packed struct {
a: u8,
b: u32,
c: u8,
d: u3,
e: u5,
f: u16,
g: u16,
h: u9,
i: u7,
};
test "@offsetOf" {
// Packed structs have fixed memory layout
try expect(@offsetOf(P, "a") == 0);
try expect(@offsetOf(P, "b") == 1);
try expect(@offsetOf(P, "c") == 5);
try expect(@offsetOf(P, "d") == 6);
try expect(@offsetOf(P, "e") == 6);
try expect(@offsetOf(P, "f") == 7);
try expect(@offsetOf(P, "g") == 9);
try expect(@offsetOf(P, "h") == 11);
try expect(@offsetOf(P, "i") == 12);
// Normal struct fields can be moved/padded
var a: A = undefined;
try expect(@ptrToInt(&a.a) - @ptrToInt(&a) == @offsetOf(A, "a"));
try expect(@ptrToInt(&a.b) - @ptrToInt(&a) == @offsetOf(A, "b"));
try expect(@ptrToInt(&a.c) - @ptrToInt(&a) == @offsetOf(A, "c"));
try expect(@ptrToInt(&a.d) - @ptrToInt(&a) == @offsetOf(A, "d"));
try expect(@ptrToInt(&a.e) - @ptrToInt(&a) == @offsetOf(A, "e"));
try expect(@ptrToInt(&a.f) - @ptrToInt(&a) == @offsetOf(A, "f"));
try expect(@ptrToInt(&a.g) - @ptrToInt(&a) == @offsetOf(A, "g"));
try expect(@ptrToInt(&a.h) - @ptrToInt(&a) == @offsetOf(A, "h"));
try expect(@ptrToInt(&a.i) - @ptrToInt(&a) == @offsetOf(A, "i"));
}
test "@offsetOf packed struct, array length not power of 2 or multiple of native pointer width in bytes" {
const p3a_len = 3;
const P3 = packed struct {
a: [p3a_len]u8,
b: usize,
};
try std.testing.expectEqual(0, @offsetOf(P3, "a"));
try std.testing.expectEqual(p3a_len, @offsetOf(P3, "b"));
const p5a_len = 5;
const P5 = packed struct {
a: [p5a_len]u8,
b: usize,
};
try std.testing.expectEqual(0, @offsetOf(P5, "a"));
try std.testing.expectEqual(p5a_len, @offsetOf(P5, "b"));
const p6a_len = 6;
const P6 = packed struct {
a: [p6a_len]u8,
b: usize,
};
try std.testing.expectEqual(0, @offsetOf(P6, "a"));
try std.testing.expectEqual(p6a_len, @offsetOf(P6, "b"));
const p7a_len = 7;
const P7 = packed struct {
a: [p7a_len]u8,
b: usize,
};
try std.testing.expectEqual(0, @offsetOf(P7, "a"));
try std.testing.expectEqual(p7a_len, @offsetOf(P7, "b"));
const p9a_len = 9;
const P9 = packed struct {
a: [p9a_len]u8,
b: usize,
};
try std.testing.expectEqual(0, @offsetOf(P9, "a"));
try std.testing.expectEqual(p9a_len, @offsetOf(P9, "b"));
// 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 25 etc. are further cases
}
test "@bitOffsetOf" {
// Packed structs have fixed memory layout
try expect(@bitOffsetOf(P, "a") == 0);
try expect(@bitOffsetOf(P, "b") == 8);
try expect(@bitOffsetOf(P, "c") == 40);
try expect(@bitOffsetOf(P, "d") == 48);
try expect(@bitOffsetOf(P, "e") == 51);
try expect(@bitOffsetOf(P, "f") == 56);
try expect(@bitOffsetOf(P, "g") == 72);
try expect(@offsetOf(A, "a") * 8 == @bitOffsetOf(A, "a"));
try expect(@offsetOf(A, "b") * 8 == @bitOffsetOf(A, "b"));
try expect(@offsetOf(A, "c") * 8 == @bitOffsetOf(A, "c"));
try expect(@offsetOf(A, "d") * 8 == @bitOffsetOf(A, "d"));
try expect(@offsetOf(A, "e") * 8 == @bitOffsetOf(A, "e"));
try expect(@offsetOf(A, "f") * 8 == @bitOffsetOf(A, "f"));
try expect(@offsetOf(A, "g") * 8 == @bitOffsetOf(A, "g"));
}
test "@sizeOf(T) == 0 doesn't force resolving struct size" {
const S = struct {
const Foo = struct {
y: if (@sizeOf(Foo) == 0) u64 else u32,
};
const Bar = struct {
x: i32,
y: if (0 == @sizeOf(Bar)) u64 else u32,
};
};
try expect(@sizeOf(S.Foo) == 4);
try expect(@sizeOf(S.Bar) == 8);
}
test "@TypeOf() has no runtime side effects" {
const S = struct {
fn foo(comptime T: type, ptr: *T) T {
ptr.* += 1;
return ptr.*;
}
};
var data: i32 = 0;
const T = @TypeOf(S.foo(i32, &data));
comptime try expect(T == i32);
try expect(data == 0);
}
test "branching logic inside @TypeOf" {
const S = struct {
var data: i32 = 0;
fn foo() anyerror!i32 {
data += 1;
return undefined;
}
};
const T = @TypeOf(S.foo() catch undefined);
comptime try expect(T == i32);
try expect(S.data == 0);
}
test "@bitSizeOf" {
try expect(@bitSizeOf(u2) == 2);
try expect(@bitSizeOf(u8) == @sizeOf(u8) * 8);
try expect(@bitSizeOf(struct {
a: u2,
}) == 8);
try expect(@bitSizeOf(packed struct {
a: u2,
}) == 2);
}
test "@sizeOf comparison against zero" {
const S0 = struct {
f: *@This(),
};
const U0 = union {
f: *@This(),
};
const S1 = struct {
fn H(comptime T: type) type {
return struct {
x: T,
};
}
f0: H(*@This()),
f1: H(**@This()),
f2: H(***@This()),
};
const U1 = union {
fn H(comptime T: type) type {
return struct {
x: T,
};
}
f0: H(*@This()),
f1: H(**@This()),
f2: H(***@This()),
};
const S = struct {
fn doTheTest(comptime T: type, comptime result: bool) !void {
try expectEqual(result, @sizeOf(T) > 0);
}
};
// Zero-sized type
try S.doTheTest(u0, false);
try S.doTheTest(*u0, false);
// Non byte-sized type
try S.doTheTest(u1, true);
try S.doTheTest(*u1, true);
// Regular type
try S.doTheTest(u8, true);
try S.doTheTest(*u8, true);
try S.doTheTest(f32, true);
try S.doTheTest(*f32, true);
// Container with ptr pointing to themselves
try S.doTheTest(S0, true);
try S.doTheTest(U0, true);
try S.doTheTest(S1, true);
try S.doTheTest(U1, true);
}

View File

@ -3,28 +3,6 @@ const expectEqual = @import("std").testing.expectEqual;
const h = @cImport(@cInclude("behavior/translate_c_macros.h"));
test "initializer list expression" {
try expectEqual(h.Color{
.r = 200,
.g = 200,
.b = 200,
.a = 255,
}, h.LIGHTGRAY);
}
test "sizeof in macros" {
try expectEqual(@as(c_int, @sizeOf(u32)), h.MY_SIZEOF(u32));
try expectEqual(@as(c_int, @sizeOf(u32)), h.MY_SIZEOF2(u32));
}
test "reference to a struct type" {
try expectEqual(@sizeOf(h.struct_Foo), h.SIZE_OF_FOO);
}
test "cast negative integer to pointer" {
try expectEqual(@intToPtr(?*c_void, @bitCast(usize, @as(isize, -1))), h.MAP_FAILED);
}
test "casting to void with a macro" {
h.IGNORE_ME_1(42);
h.IGNORE_ME_2(42);

View File

@ -0,0 +1,26 @@
const expect = @import("std").testing.expect;
const expectEqual = @import("std").testing.expectEqual;
const h = @cImport(@cInclude("behavior/translate_c_macros.h"));
test "initializer list expression" {
try expectEqual(h.Color{
.r = 200,
.g = 200,
.b = 200,
.a = 255,
}, h.LIGHTGRAY);
}
test "sizeof in macros" {
try expectEqual(@as(c_int, @sizeOf(u32)), h.MY_SIZEOF(u32));
try expectEqual(@as(c_int, @sizeOf(u32)), h.MY_SIZEOF2(u32));
}
test "reference to a struct type" {
try expectEqual(@sizeOf(h.struct_Foo), h.SIZE_OF_FOO);
}
test "cast negative integer to pointer" {
try expectEqual(@intToPtr(?*c_void, @bitCast(usize, @as(isize, -1))), h.MAP_FAILED);
}