authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-25 22:01:22-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-25 22:04:01-07:00
logf0ddc7f7a2e2fba2515dfb4f3ad06ea56646eba7
tree45f70c9e463de4113c1ac64ba689582736a260b9
parent618055db50c5354a17f9eefc2cee77ec2de96f6a

translate-c: update for new function pointer semantics

After #10656, function pointers are represented with e.g. `*const fn()void` rather than `fn()void`. This commit adds code to translate-c to emit different code depending on whether the output zig source code is intended to be compiled with stage1 or stage2. Ideally we will have stage1 and stage2 support the exact same Zig language, but for now they diverge because I would rather focus on finishing and shipping stage2 than implementing the features in stage1.

3 files changed, 46 insertions(+), 10 deletions(-)

src/Compilation.zig+4
......@@ -3305,7 +3305,10 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
33053305 var man = comp.obtainCObjectCacheManifest();
33063306 defer man.deinit();
33073307
3308 const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1;
3309
33083310 man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
3311 man.hash.add(use_stage1);
33093312 man.hash.addBytes(c_src);
33103313
33113314 // If the previous invocation resulted in clang errors, we will see a hit
......@@ -3369,6 +3372,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
33693372 new_argv.ptr + new_argv.len,
33703373 &clang_errors,
33713374 c_headers_dir_path_z,
3375 use_stage1,
33723376 ) catch |err| switch (err) {
33733377 error.OutOfMemory => return error.OutOfMemory,
33743378 error.ASTUnitFailure => {
src/main.zig+5-2
......@@ -2601,7 +2601,8 @@ fn buildOutputType(
26012601 return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena));
26022602 }
26032603 if (arg_mode == .translate_c) {
2604 return cmdTranslateC(comp, arena, have_enable_cache);
2604 const stage1_mode = use_stage1 orelse build_options.is_stage1;
2605 return cmdTranslateC(comp, arena, have_enable_cache, stage1_mode);
26052606 }
26062607
26072608 const hook: AfterUpdateHook = blk: {
......@@ -2997,7 +2998,7 @@ fn freePkgTree(gpa: Allocator, pkg: *Package, free_parent: bool) void {
29972998 }
29982999}
29993000
3000fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void {
3001fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool, stage1_mode: bool) !void {
30013002 if (!build_options.have_llvm)
30023003 fatal("cannot translate-c: compiler built without LLVM extensions", .{});
30033004
......@@ -3010,6 +3011,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void
30103011 defer if (enable_cache) man.deinit();
30113012
30123013 man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
3014 man.hash.add(stage1_mode);
30133015 man.hashCSource(c_source_file) catch |err| {
30143016 fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) });
30153017 };
......@@ -3061,6 +3063,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void
30613063 new_argv.ptr + new_argv.len,
30623064 &clang_errors,
30633065 c_headers_dir_path_z,
3066 stage1_mode,
30643067 ) catch |err| switch (err) {
30653068 error.OutOfMemory => return error.OutOfMemory,
30663069 error.ASTUnitFailure => fatal("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}),
src/translate_c.zig+37-8
......@@ -328,6 +328,16 @@ pub const Context = struct {
328328
329329 pattern_list: PatternList,
330330
331 /// This is used to emit different code depending on whether
332 /// the output zig source code is intended to be compiled with stage1 or stage2.
333 /// Ideally we will have stage1 and stage2 support the exact same Zig language,
334 /// but for now they diverge because I would rather focus on finishing and shipping
335 /// stage2 than implementing the features in stage1.
336 /// The list of differences are currently:
337 /// * function pointers in stage1 are e.g. `fn()void`
338 /// but in stage2 they are `*const fn()void`.
339 zig_is_stage1: bool,
340
331341 fn getMangle(c: *Context) u32 {
332342 c.mangle_count += 1;
333343 return c.mangle_count;
......@@ -356,6 +366,7 @@ pub fn translate(
356366 args_end: [*]?[*]const u8,
357367 errors: *[]ClangErrMsg,
358368 resources_path: [*:0]const u8,
369 zig_is_stage1: bool,
359370) !std.zig.Ast {
360371 const ast_unit = clang.LoadFromCommandLine(
361372 args_begin,
......@@ -383,6 +394,7 @@ pub fn translate(
383394 .global_scope = try arena_allocator.create(Scope.Root),
384395 .clang_context = ast_unit.getASTContext(),
385396 .pattern_list = try PatternList.init(gpa),
397 .zig_is_stage1 = zig_is_stage1,
386398 };
387399 context.global_scope.* = Scope.Root.init(&context);
388400 defer {
......@@ -3630,7 +3642,7 @@ fn transUnaryOperator(c: *Context, scope: *Scope, stmt: *const clang.UnaryOperat
36303642 else
36313643 return transCreatePreCrement(c, scope, stmt, .sub_assign, used),
36323644 .AddrOf => {
3633 if (cIsFunctionDeclRef(op_expr)) {
3645 if (c.zig_is_stage1 and cIsFunctionDeclRef(op_expr)) {
36343646 return transExpr(c, scope, op_expr, used);
36353647 }
36363648 return Tag.address_of.create(c.arena, try transExpr(c, scope, op_expr, used));
......@@ -4656,18 +4668,27 @@ fn transType(c: *Context, scope: *Scope, ty: *const clang.Type, source_loc: clan
46564668 },
46574669 .Pointer => {
46584670 const child_qt = ty.getPointeeType();
4659 if (qualTypeChildIsFnProto(child_qt)) {
4671 const is_fn_proto = qualTypeChildIsFnProto(child_qt);
4672 if (c.zig_is_stage1 and is_fn_proto) {
46604673 return Tag.optional_type.create(c.arena, try transQualType(c, scope, child_qt, source_loc));
46614674 }
4662 const is_const = child_qt.isConstQualified();
4675 const is_const = is_fn_proto or child_qt.isConstQualified();
46634676 const is_volatile = child_qt.isVolatileQualified();
46644677 const elem_type = try transQualType(c, scope, child_qt, source_loc);
4665 if (typeIsOpaque(c, child_qt.getTypePtr(), source_loc) or qualTypeWasDemotedToOpaque(c, child_qt)) {
4666 const ptr = try Tag.single_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type });
4678 const ptr_info = .{
4679 .is_const = is_const,
4680 .is_volatile = is_volatile,
4681 .elem_type = elem_type,
4682 };
4683 if (is_fn_proto or
4684 typeIsOpaque(c, child_qt.getTypePtr(), source_loc) or
4685 qualTypeWasDemotedToOpaque(c, child_qt))
4686 {
4687 const ptr = try Tag.single_pointer.create(c.arena, ptr_info);
46674688 return Tag.optional_type.create(c.arena, ptr);
46684689 }
46694690
4670 return Tag.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type });
4691 return Tag.c_pointer.create(c.arena, ptr_info);
46714692 },
46724693 .ConstantArray => {
46734694 const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty);
......@@ -6517,8 +6538,16 @@ fn getFnProto(c: *Context, ref: Node) ?*ast.Payload.Func {
65176538 return null;
65186539 if (getContainerTypeOf(c, init)) |ty_node| {
65196540 if (ty_node.castTag(.optional_type)) |prefix| {
6520 if (prefix.data.castTag(.func)) |fn_proto| {
6521 return fn_proto;
6541 if (c.zig_is_stage1) {
6542 if (prefix.data.castTag(.func)) |fn_proto| {
6543 return fn_proto;
6544 }
6545 } else {
6546 if (prefix.data.castTag(.single_pointer)) |sp| {
6547 if (sp.data.elem_type.castTag(.func)) |fn_proto| {
6548 return fn_proto;
6549 }
6550 }
65226551 }
65236552 }
65246553 }