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.
This commit is contained in:
Andrew Kelley 2022-01-25 22:01:22 -07:00
parent 618055db50
commit f0ddc7f7a2
3 changed files with 46 additions and 10 deletions

View File

@ -3305,7 +3305,10 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
var man = comp.obtainCObjectCacheManifest();
defer man.deinit();
const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1;
man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
man.hash.add(use_stage1);
man.hash.addBytes(c_src);
// 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 {
new_argv.ptr + new_argv.len,
&clang_errors,
c_headers_dir_path_z,
use_stage1,
) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.ASTUnitFailure => {

View File

@ -2601,7 +2601,8 @@ fn buildOutputType(
return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena));
}
if (arg_mode == .translate_c) {
return cmdTranslateC(comp, arena, have_enable_cache);
const stage1_mode = use_stage1 orelse build_options.is_stage1;
return cmdTranslateC(comp, arena, have_enable_cache, stage1_mode);
}
const hook: AfterUpdateHook = blk: {
@ -2997,7 +2998,7 @@ fn freePkgTree(gpa: Allocator, pkg: *Package, free_parent: bool) void {
}
}
fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void {
fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool, stage1_mode: bool) !void {
if (!build_options.have_llvm)
fatal("cannot translate-c: compiler built without LLVM extensions", .{});
@ -3010,6 +3011,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void
defer if (enable_cache) man.deinit();
man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
man.hash.add(stage1_mode);
man.hashCSource(c_source_file) catch |err| {
fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) });
};
@ -3061,6 +3063,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void
new_argv.ptr + new_argv.len,
&clang_errors,
c_headers_dir_path_z,
stage1_mode,
) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
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", .{}),

View File

@ -328,6 +328,16 @@ pub const Context = struct {
pattern_list: PatternList,
/// This is used 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.
/// The list of differences are currently:
/// * function pointers in stage1 are e.g. `fn()void`
/// but in stage2 they are `*const fn()void`.
zig_is_stage1: bool,
fn getMangle(c: *Context) u32 {
c.mangle_count += 1;
return c.mangle_count;
@ -356,6 +366,7 @@ pub fn translate(
args_end: [*]?[*]const u8,
errors: *[]ClangErrMsg,
resources_path: [*:0]const u8,
zig_is_stage1: bool,
) !std.zig.Ast {
const ast_unit = clang.LoadFromCommandLine(
args_begin,
@ -383,6 +394,7 @@ pub fn translate(
.global_scope = try arena_allocator.create(Scope.Root),
.clang_context = ast_unit.getASTContext(),
.pattern_list = try PatternList.init(gpa),
.zig_is_stage1 = zig_is_stage1,
};
context.global_scope.* = Scope.Root.init(&context);
defer {
@ -3630,7 +3642,7 @@ fn transUnaryOperator(c: *Context, scope: *Scope, stmt: *const clang.UnaryOperat
else
return transCreatePreCrement(c, scope, stmt, .sub_assign, used),
.AddrOf => {
if (cIsFunctionDeclRef(op_expr)) {
if (c.zig_is_stage1 and cIsFunctionDeclRef(op_expr)) {
return transExpr(c, scope, op_expr, used);
}
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
},
.Pointer => {
const child_qt = ty.getPointeeType();
if (qualTypeChildIsFnProto(child_qt)) {
const is_fn_proto = qualTypeChildIsFnProto(child_qt);
if (c.zig_is_stage1 and is_fn_proto) {
return Tag.optional_type.create(c.arena, try transQualType(c, scope, child_qt, source_loc));
}
const is_const = child_qt.isConstQualified();
const is_const = is_fn_proto or child_qt.isConstQualified();
const is_volatile = child_qt.isVolatileQualified();
const elem_type = try transQualType(c, scope, child_qt, source_loc);
if (typeIsOpaque(c, child_qt.getTypePtr(), source_loc) or qualTypeWasDemotedToOpaque(c, child_qt)) {
const ptr = try Tag.single_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type });
const ptr_info = .{
.is_const = is_const,
.is_volatile = is_volatile,
.elem_type = elem_type,
};
if (is_fn_proto or
typeIsOpaque(c, child_qt.getTypePtr(), source_loc) or
qualTypeWasDemotedToOpaque(c, child_qt))
{
const ptr = try Tag.single_pointer.create(c.arena, ptr_info);
return Tag.optional_type.create(c.arena, ptr);
}
return Tag.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type });
return Tag.c_pointer.create(c.arena, ptr_info);
},
.ConstantArray => {
const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty);
@ -6517,8 +6538,16 @@ fn getFnProto(c: *Context, ref: Node) ?*ast.Payload.Func {
return null;
if (getContainerTypeOf(c, init)) |ty_node| {
if (ty_node.castTag(.optional_type)) |prefix| {
if (prefix.data.castTag(.func)) |fn_proto| {
return fn_proto;
if (c.zig_is_stage1) {
if (prefix.data.castTag(.func)) |fn_proto| {
return fn_proto;
}
} else {
if (prefix.data.castTag(.single_pointer)) |sp| {
if (sp.data.elem_type.castTag(.func)) |fn_proto| {
return fn_proto;
}
}
}
}
}