From 9a54ff72df5cc8631c467f3478117eb85a952ced Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 19 Sep 2021 11:51:32 +0300 Subject: [PATCH 1/4] stage2: implement cImport --- src/AstGen.zig | 6 ++++ src/Compilation.zig | 2 +- src/Module.zig | 3 ++ src/Sema.zig | 88 +++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 92 insertions(+), 7 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index f1eabe4c0c..32b62fc3f5 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -7269,6 +7269,7 @@ fn builtinCall( return rvalue(gz, rl, result, node); }, .c_define => { + if (!gz.c_import) return gz.astgen.failNode(node, "C define valid only inside C import block", .{}); const name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[0]); const value = try comptimeExpr(gz, scope, .none, params[1]); const result = try gz.addExtendedPayload(.c_define, Zir.Inst.BinNode{ @@ -7641,6 +7642,8 @@ fn simpleCBuiltin( operand_node: Ast.Node.Index, tag: Zir.Inst.Extended, ) InnerError!Zir.Inst.Ref { + const name: []const u8 = if (tag == .c_undef) "C undef" else "C include"; + if (!gz.c_import) return gz.astgen.failNode(node, "{s} valid only inside C import block", .{name}); const operand = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, operand_node); _ = try gz.addExtendedPayload(tag, Zir.Inst.UnNode{ .node = gz.nodeIndexToRelative(node), @@ -7698,6 +7701,7 @@ fn cImport( var block_scope = gz.makeSubBlock(scope); block_scope.force_comptime = true; + block_scope.c_import = true; defer block_scope.instructions.deinit(gpa); const block_inst = try gz.addBlock(.c_import, node); @@ -9025,6 +9029,7 @@ const GenZir = struct { base: Scope = Scope{ .tag = base_tag }, force_comptime: bool, in_defer: bool, + c_import: bool = false, /// How decls created in this scope should be named. anon_name_strategy: Zir.Inst.NameStrategy = .anon, /// The containing decl AST node. @@ -9070,6 +9075,7 @@ const GenZir = struct { return .{ .force_comptime = gz.force_comptime, .in_defer = gz.in_defer, + .c_import = gz.c_import, .decl_node_index = gz.decl_node_index, .decl_line = gz.decl_line, .parent = scope, diff --git a/src/Compilation.zig b/src/Compilation.zig index c1dfe91dc2..55cc9d0867 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -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); - try comp.stage1_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); + if (build_options.is_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 }); diff --git a/src/Module.zig b/src/Module.zig index 500c34bcb0..6a183db21f 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1325,6 +1325,8 @@ pub const Scope = struct { /// when null, it is determined by build mode, changed by @setRuntimeSafety want_safety: ?bool = null, + c_import_buf: ?*std.ArrayList(u8) = null, + const Param = struct { /// `noreturn` means `anytype`. ty: Type, @@ -1377,6 +1379,7 @@ pub const Scope = struct { .runtime_loop = parent.runtime_loop, .runtime_index = parent.runtime_index, .want_safety = parent.want_safety, + .c_import_buf = parent.c_import_buf, }; } diff --git a/src/Sema.zig b/src/Sema.zig index ff32ce49ca..ac11cf4dcc 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2138,10 +2138,72 @@ fn zirCImport(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Com const tracy = trace(@src()); defer tracy.end(); - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); + const pl_node = sema.code.instructions.items(.data)[inst].pl_node; + const src = pl_node.src(); + const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index); + const body = sema.code.extra[extra.end..][0..extra.data.body_len]; - return sema.mod.fail(&parent_block.base, src, "TODO: implement Sema.zirCImport", .{}); + // we check this here to avoid undefined symbols + if (!@import("build_options").have_llvm) + return sema.mod.fail(&parent_block.base, src, "cannot do C import on Zig compiler not built with LLVM-extension", .{}); + + var c_import_buf = std.ArrayList(u8).init(sema.gpa); + defer c_import_buf.deinit(); + + var child_block: Scope.Block = .{ + .parent = parent_block, + .sema = sema, + .src_decl = parent_block.src_decl, + .instructions = .{}, + .inlining = parent_block.inlining, + .is_comptime = parent_block.is_comptime, + .c_import_buf = &c_import_buf, + }; + defer child_block.instructions.deinit(sema.gpa); + + _ = try sema.analyzeBody(&child_block, body); + + const c_import_res = sema.mod.comp.cImport(c_import_buf.items) catch |err| + return sema.mod.fail(&child_block.base, src, "C import failed: {s}", .{@errorName(err)}); + + if (c_import_res.errors.len != 0) { + const msg = try sema.mod.errMsg(&child_block.base, src, "C import failed", .{}); + errdefer msg.destroy(sema.gpa); + + if (!sema.mod.comp.bin_file.options.link_libc) + try sema.mod.errNote(&child_block.base, src, msg, "libc headers not available; compilation does not link against libc", .{}); + + for (c_import_res.errors) |_| { + // TODO integrate with LazySrcLoc + // try sema.mod.errNoteNonLazy(.{}, msg, "{s}", .{clang_err.msg_ptr[0..clang_err.msg_len]}); + // if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)", + // clang_err.line + 1, + // clang_err.column + 1, + } + @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( + sema.gpa, + sema.mod.comp.local_cache_directory, + null, + std.fs.path.basename(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 + }; + const std_pkg = sema.mod.main_pkg.table.get("std").?; + const builtin_pkg = sema.mod.main_pkg.table.get("builtin").?; + try c_import_pkg.add(sema.gpa, "builtin", builtin_pkg); + try c_import_pkg.add(sema.gpa, "std", std_pkg); + + const result = sema.mod.importPkg(c_import_pkg) 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); + return sema.addType(file_root_decl.ty); } fn zirSuspendBlock(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -8226,7 +8288,10 @@ fn zirCUndef( ) CompileError!Air.Inst.Ref { const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src: LazySrcLoc = .{ .node_offset = extra.node }; - return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirCUndef", .{}); + + const name = try sema.resolveConstString(block, src, extra.operand); + try block.c_import_buf.?.writer().print("#undefine {s}\n", .{name}); + return Air.Inst.Ref.void_value; } fn zirCInclude( @@ -8236,7 +8301,10 @@ fn zirCInclude( ) CompileError!Air.Inst.Ref { const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src: LazySrcLoc = .{ .node_offset = extra.node }; - return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirCInclude", .{}); + + const name = try sema.resolveConstString(block, src, extra.operand); + try block.c_import_buf.?.writer().print("#include <{s}>\n", .{name}); + return Air.Inst.Ref.void_value; } fn zirCDefine( @@ -8246,7 +8314,15 @@ fn zirCDefine( ) CompileError!Air.Inst.Ref { const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; const src: LazySrcLoc = .{ .node_offset = extra.node }; - return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirCDefine", .{}); + + const name = try sema.resolveConstString(block, src, extra.lhs); + if (sema.typeOf(extra.rhs).zigTypeTag() != .Void) { + const value = try sema.resolveConstString(block, src, extra.rhs); + try block.c_import_buf.?.writer().print("#define {s} {s}\n", .{ name, value }); + } else { + try block.c_import_buf.?.writer().print("#define {s}\n", .{name}); + } + return Air.Inst.Ref.void_value; } fn zirWasmMemorySize( From d64d5cfc0a72a9989ed58548dd806a663f847ef5 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 19 Sep 2021 19:38:17 +0300 Subject: [PATCH 2/4] stage2: implement typeInfo for more types --- src/Sema.zig | 168 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 154 insertions(+), 14 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index ac11cf4dcc..2d50462f91 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6518,19 +6518,80 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr const target = sema.mod.getTarget(); switch (ty.zigTypeTag()) { + .Type => return sema.addConstant( + type_info_ty, + try Value.Tag.@"union".create(sema.arena, .{ + .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Type)), + .val = Value.initTag(.unreachable_value), + }), + ), + .Void => return sema.addConstant( + type_info_ty, + try Value.Tag.@"union".create(sema.arena, .{ + .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Void)), + .val = Value.initTag(.unreachable_value), + }), + ), + .Bool => return sema.addConstant( + type_info_ty, + try Value.Tag.@"union".create(sema.arena, .{ + .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Bool)), + .val = Value.initTag(.unreachable_value), + }), + ), + .NoReturn => return sema.addConstant( + type_info_ty, + try Value.Tag.@"union".create(sema.arena, .{ + .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.NoReturn)), + .val = Value.initTag(.unreachable_value), + }), + ), + .ComptimeFloat => return sema.addConstant( + type_info_ty, + try Value.Tag.@"union".create(sema.arena, .{ + .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ComptimeFloat)), + .val = Value.initTag(.unreachable_value), + }), + ), + .ComptimeInt => return sema.addConstant( + type_info_ty, + try Value.Tag.@"union".create(sema.arena, .{ + .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ComptimeInt)), + .val = Value.initTag(.unreachable_value), + }), + ), + .Undefined => return sema.addConstant( + type_info_ty, + try Value.Tag.@"union".create(sema.arena, .{ + .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Undefined)), + .val = Value.initTag(.unreachable_value), + }), + ), + .Null => return sema.addConstant( + type_info_ty, + try Value.Tag.@"union".create(sema.arena, .{ + .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Null)), + .val = Value.initTag(.unreachable_value), + }), + ), + .EnumLiteral => return sema.addConstant( + type_info_ty, + try Value.Tag.@"union".create(sema.arena, .{ + .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.EnumLiteral)), + .val = Value.initTag(.unreachable_value), + }), + ), .Fn => { + const info = ty.fnInfo(); const field_values = try sema.arena.alloc(Value, 6); // calling_convention: CallingConvention, - field_values[0] = try Value.Tag.enum_field_index.create( - sema.arena, - @enumToInt(ty.fnCallingConvention()), - ); + field_values[0] = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.cc)); // alignment: comptime_int, field_values[1] = try Value.Tag.int_u64.create(sema.arena, ty.abiAlignment(target)); // is_generic: bool, - field_values[2] = Value.initTag(.bool_false); // TODO + field_values[2] = if (info.is_generic) Value.initTag(.bool_true) else Value.initTag(.bool_false); // is_var_args: bool, - field_values[3] = Value.initTag(.bool_false); // TODO + field_values[3] = if (info.is_var_args) Value.initTag(.bool_true) else Value.initTag(.bool_false); // return_type: ?type, field_values[4] = try Value.Tag.ty.create(sema.arena, ty.fnReturnType()); // args: []const FnArg, @@ -6539,10 +6600,7 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr return sema.addConstant( type_info_ty, try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create( - sema.arena, - @enumToInt(@typeInfo(std.builtin.TypeInfo).Union.tag_type.?.Fn), - ), + .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Fn)), .val = try Value.Tag.@"struct".create(sema.arena, field_values), }), ); @@ -6561,10 +6619,92 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr return sema.addConstant( type_info_ty, try Value.Tag.@"union".create(sema.arena, .{ - .tag = try Value.Tag.enum_field_index.create( - sema.arena, - @enumToInt(@typeInfo(std.builtin.TypeInfo).Union.tag_type.?.Int), - ), + .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Int)), + .val = try Value.Tag.@"struct".create(sema.arena, field_values), + }), + ); + }, + .Float => { + const field_values = try sema.arena.alloc(Value, 1); + // bits: comptime_int, + field_values[0] = try Value.Tag.int_u64.create(sema.arena, ty.bitSize(target)); + + return sema.addConstant( + type_info_ty, + try Value.Tag.@"union".create(sema.arena, .{ + .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Float)), + .val = try Value.Tag.@"struct".create(sema.arena, field_values), + }), + ); + }, + .Pointer => { + const info = ty.ptrInfo().data; + const field_values = try sema.arena.alloc(Value, 7); + // size: Size, + field_values[0] = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.size)); + // is_const: bool, + field_values[1] = if (!info.mutable) Value.initTag(.bool_true) else Value.initTag(.bool_false); + // is_volatile: bool, + field_values[2] = if (info.@"volatile") Value.initTag(.bool_true) else Value.initTag(.bool_false); + // alignment: comptime_int, + field_values[3] = try Value.Tag.int_u64.create(sema.arena, info.@"align"); + // child: type, + field_values[4] = try Value.Tag.ty.create(sema.arena, info.pointee_type); + // is_allowzero: bool, + field_values[5] = if (info.@"allowzero") Value.initTag(.bool_true) else Value.initTag(.bool_false); + // sentinel: anytype, + field_values[6] = if (info.sentinel) |some| try Value.Tag.opt_payload.create(sema.arena, some) else Value.initTag(.null_value); + + return sema.addConstant( + type_info_ty, + try Value.Tag.@"union".create(sema.arena, .{ + .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Pointer)), + .val = try Value.Tag.@"struct".create(sema.arena, field_values), + }), + ); + }, + .Array => { + const info = ty.arrayInfo(); + const field_values = try sema.arena.alloc(Value, 3); + // len: comptime_int, + field_values[0] = try Value.Tag.int_u64.create(sema.arena, info.len); + // child: type, + field_values[1] = try Value.Tag.ty.create(sema.arena, info.elem_type); + // sentinel: anytype, + field_values[2] = if (info.sentinel) |some| try Value.Tag.opt_payload.create(sema.arena, some) else Value.initTag(.null_value); + + return sema.addConstant( + type_info_ty, + try Value.Tag.@"union".create(sema.arena, .{ + .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Array)), + .val = try Value.Tag.@"struct".create(sema.arena, field_values), + }), + ); + }, + .Optional => { + const field_values = try sema.arena.alloc(Value, 1); + // child: type, + field_values[0] = try Value.Tag.ty.create(sema.arena, try ty.optionalChildAlloc(sema.arena)); + + return sema.addConstant( + type_info_ty, + try Value.Tag.@"union".create(sema.arena, .{ + .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Optional)), + .val = try Value.Tag.@"struct".create(sema.arena, field_values), + }), + ); + }, + .ErrorUnion => { + const field_values = try sema.arena.alloc(Value, 2); + // error_set: type, + field_values[0] = try Value.Tag.ty.create(sema.arena, ty.errorUnionSet()); + // payload: type, + field_values[1] = try Value.Tag.ty.create(sema.arena, ty.errorUnionPayload()); + + return sema.addConstant( + type_info_ty, + try Value.Tag.@"union".create(sema.arena, .{ + .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ErrorUnion)), .val = try Value.Tag.@"struct".create(sema.arena, field_values), }), ); From 55e7c099caa7cf8dc253dac21765bb994e06b741 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 20 Sep 2021 21:05:42 +0300 Subject: [PATCH 3/4] stage2: various fixes to cImport, sizeOf and types to get tests passing --- src/AstGen.zig | 5 +- src/Compilation.zig | 2 +- src/Sema.zig | 41 +++- src/print_zir.zig | 18 +- src/type.zig | 2 +- test/behavior.zig | 6 +- test/behavior/sizeof_and_typeof.zig | 214 ------------------- test/behavior/sizeof_and_typeof_stage1.zig | 218 ++++++++++++++++++++ test/behavior/translate_c_macros.zig | 22 -- test/behavior/translate_c_macros_stage1.zig | 26 +++ 10 files changed, 304 insertions(+), 250 deletions(-) create mode 100644 test/behavior/sizeof_and_typeof_stage1.zig create mode 100644 test/behavior/translate_c_macros_stage1.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index 32b62fc3f5..176203d37f 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -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( diff --git a/src/Compilation.zig b/src/Compilation.zig index 55cc9d0867..53e643acb8 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -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 }); diff --git a/src/Sema.zig b/src/Sema.zig index 2d50462f91..1bb071f4f5 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -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); } diff --git a/src/print_zir.zig b/src/print_zir.zig index 94fa0307bd..35b3da4479 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -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))"), diff --git a/src/type.zig b/src/type.zig index db193639a7..5d184ed2fc 100644 --- a/src/type.zig +++ b/src/type.zig @@ -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 }), diff --git a/test/behavior.zig b/test/behavior.zig index ebf3fff83f..ee3a789c39 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -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"); } } diff --git a/test/behavior/sizeof_and_typeof.zig b/test/behavior/sizeof_and_typeof.zig index 60951a4c5a..09bd305c68 100644 --- a/test/behavior/sizeof_and_typeof.zig +++ b/test/behavior/sizeof_and_typeof.zig @@ -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); -} diff --git a/test/behavior/sizeof_and_typeof_stage1.zig b/test/behavior/sizeof_and_typeof_stage1.zig new file mode 100644 index 0000000000..974e79015f --- /dev/null +++ b/test/behavior/sizeof_and_typeof_stage1.zig @@ -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); +} diff --git a/test/behavior/translate_c_macros.zig b/test/behavior/translate_c_macros.zig index 0daf4cec90..0baaf24283 100644 --- a/test/behavior/translate_c_macros.zig +++ b/test/behavior/translate_c_macros.zig @@ -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); diff --git a/test/behavior/translate_c_macros_stage1.zig b/test/behavior/translate_c_macros_stage1.zig new file mode 100644 index 0000000000..8de06ae8ea --- /dev/null +++ b/test/behavior/translate_c_macros_stage1.zig @@ -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); +} From 0c74ce1156d6a967cd089cd4657575a3e22bb782 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 20 Sep 2021 20:56:30 -0700 Subject: [PATCH 4/4] Sema: fix double-free of `@cImport` error message --- src/Sema.zig | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 1bb071f4f5..786db21b4a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -83,6 +83,7 @@ const Decl = Module.Decl; const LazySrcLoc = Module.LazySrcLoc; const RangeSet = @import("RangeSet.zig"); const target_util = @import("target.zig"); +const Package = @import("Package.zig"); pub const InstMap = std.AutoHashMapUnmanaged(Zir.Inst.Index, Air.Inst.Ref); @@ -2167,23 +2168,26 @@ fn zirCImport(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Com return sema.mod.fail(&child_block.base, src, "C import failed: {s}", .{@errorName(err)}); if (c_import_res.errors.len != 0) { - const msg = try sema.mod.errMsg(&child_block.base, src, "C import failed", .{}); - errdefer msg.destroy(sema.gpa); + const msg = msg: { + const msg = try sema.mod.errMsg(&child_block.base, src, "C import failed", .{}); + errdefer msg.destroy(sema.gpa); - if (!sema.mod.comp.bin_file.options.link_libc) - try sema.mod.errNote(&child_block.base, src, msg, "libc headers not available; compilation does not link against libc", .{}); + if (!sema.mod.comp.bin_file.options.link_libc) + try sema.mod.errNote(&child_block.base, src, msg, "libc headers not available; compilation does not link against libc", .{}); - for (c_import_res.errors) |_| { - // TODO integrate with LazySrcLoc - // try sema.mod.errNoteNonLazy(.{}, msg, "{s}", .{clang_err.msg_ptr[0..clang_err.msg_len]}); - // if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)", - // clang_err.line + 1, - // clang_err.column + 1, - } - @import("clang.zig").Stage2ErrorMsg.delete(c_import_res.errors.ptr, c_import_res.errors.len); + for (c_import_res.errors) |_| { + // TODO integrate with LazySrcLoc + // try sema.mod.errNoteNonLazy(.{}, msg, "{s}", .{clang_err.msg_ptr[0..clang_err.msg_len]}); + // if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)", + // clang_err.line + 1, + // clang_err.column + 1, + } + @import("clang.zig").Stage2ErrorMsg.delete(c_import_res.errors.ptr, c_import_res.errors.len); + break :msg msg; + }; return sema.mod.failWithOwnedErrorMsg(&child_block.base, msg); } - const c_import_pkg = @import("Package.zig").create( + const c_import_pkg = Package.create( sema.gpa, null, c_import_res.out_zig_path,