Skip to content

Commit

Permalink
translate _Static_asserts
Browse files Browse the repository at this point in the history
  • Loading branch information
Vexu committed Dec 16, 2024
1 parent 24f7fa0 commit 2c88153
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 9 deletions.
54 changes: 48 additions & 6 deletions src/Translator.zig
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ pub fn translate(
context.decl_table.deinit(gpa);
context.alias_list.deinit();
context.global_names.deinit(gpa);
context.weak_global_names.deinit(gpa);
context.opaque_demotes.deinit(gpa);
context.unnamed_typedefs.deinit(gpa);
context.typedefs.deinit(gpa);
Expand Down Expand Up @@ -610,7 +611,9 @@ fn transDecl(c: *Context, scope: *Scope, decl: NodeIndex) !void {
=> {
try transVarDecl(c, decl);
},
.static_assert => try warn(c, &c.global_scope.base, c.tokenIndex(decl) orelse 0, "ignoring _Static_assert declaration", .{}),
.static_assert => {
try transStaticAssert(c, &c.global_scope.base, decl);
},
else => unreachable,
}
}
Expand Down Expand Up @@ -991,6 +994,40 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const Type.Enum, field_
}
}

fn transStaticAssert(c: *Context, scope: *Scope, static_assert_node: NodeIndex) Error!void {
const node_data = c.tree.nodes.items(.data)[@intFromEnum(static_assert_node)];

const condition = c.transExpr(node_data.bin.lhs, .used) catch |err| switch (err) {
error.UnsupportedTranslation, error.UnsupportedType => {
return try warn(c, &c.global_scope.base, c.tokenIndex(node_data.bin.lhs).?, "unable to translate _Static_assert condition", .{});
},
error.OutOfMemory => |e| return e,
};

// generate @compileError message that matches C compiler output
const diagnostic = if (node_data.bin.rhs != .none) str: {
// Aro guarantees this to be a string literal.
const str_val = c.tree.value_map.get(node_data.bin.rhs).?;
const str_ty = c.tree.nodes.items(.ty)[@intFromEnum(node_data.bin.rhs)];

const bytes = c.comp.interner.get(str_val.ref()).bytes;
var buf = std.ArrayList(u8).init(c.gpa);
defer buf.deinit();

try buf.appendSlice("\"static assertion failed \\");

try buf.ensureUnusedCapacity(bytes.len);
try aro.Value.printString(bytes, str_ty, c.comp, buf.writer());
_ = buf.pop(); // printString adds a terminating " so we need to remove it
try buf.appendSlice("\\\"\"");

break :str try ZigTag.string_literal.create(c.arena, try c.arena.dupe(u8, buf.items));
} else try ZigTag.string_literal.create(c.arena, "\"static assertion failed\"");

const assert_node = try ZigTag.static_assert.create(c.arena, .{ .lhs = condition, .rhs = diagnostic });
try scope.appendNode(assert_node);
}

fn getTypeStr(c: *Context, ty: Type) ![]const u8 {
var buf: std.ArrayListUnmanaged(u8) = .empty;
defer buf.deinit(c.gpa);
Expand Down Expand Up @@ -1316,16 +1353,21 @@ fn transFnType(
return ZigNode.initPayload(&payload.base);
}

fn transStmt(c: *Context, node: NodeIndex) TransError!ZigNode {
_ = c;
_ = node;
return error.UnsupportedTranslation;
fn transStmt(c: *Context, scope: *Scope, stmt: NodeIndex) TransError!ZigNode {
const node_tags = c.tree.nodes.items(.tag);
switch (node_tags[@intFromEnum(stmt)]) {
.static_assert => {
try transStaticAssert(c, scope, stmt);
return ZigTag.declaration.init();
},
else => return fail(c, error.UnsupportedTranslation, c.tokenIndex(stmt).?, "TODO implement translation of stmt {s}", .{@tagName(node_tags[@intFromEnum(stmt)])}),
}
}

fn transCompoundStmtInline(c: *Context, compound: NodeIndex, block: *Scope.Block) TransError!void {
const stmts = c.tree.childNodes(compound);
for (stmts) |stmt| {
const result = try transStmt(c, stmt);
const result = try transStmt(c, &block.base, stmt);
switch (result.tag()) {
.declaration, .empty_block => {},
else => try block.statements.append(result),
Expand Down
64 changes: 62 additions & 2 deletions src/ast.zig
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ pub const Node = extern union {
/// [1]type{val} ** count
array_filler,

/// comptime { if (!(lhs)) @compileError(rhs); }
static_assert,

pub const last_no_payload_tag = Tag.@"break";
pub const no_payload_count = @intFromEnum(last_no_payload_tag) + 1;

Expand Down Expand Up @@ -336,6 +339,7 @@ pub const Node = extern union {
.div_exact,
.offset_of,
.helpers_cast,
.static_assert,
=> Payload.BinOp,

.integer_literal,
Expand Down Expand Up @@ -753,7 +757,7 @@ pub const Payload = struct {
/// Converts the nodes into a Zig Ast.
/// Caller must free the source slice.
pub fn render(gpa: Allocator, nodes: []const Node) !std.zig.Ast {
var ctx = Context{
var ctx: Context = .{
.gpa = gpa,
.buf = std.ArrayList(u8).init(gpa),
};
Expand Down Expand Up @@ -803,7 +807,7 @@ pub fn render(gpa: Allocator, nodes: []const Node) !std.zig.Ast {
.start = @as(u32, @intCast(ctx.buf.items.len)),
});

return std.zig.Ast{
return .{
.source = try ctx.buf.toOwnedSliceSentinel(0),
.tokens = ctx.tokens.toOwnedSlice(),
.nodes = ctx.nodes.toOwnedSlice(),
Expand Down Expand Up @@ -2127,6 +2131,61 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
},
};
},
.static_assert => {
const payload = node.castTag(.static_assert).?.data;
const comptime_tok = try c.addToken(.keyword_comptime, "comptime");
const l_brace = try c.addToken(.l_brace, "{");

const if_tok = try c.addToken(.keyword_if, "if");
_ = try c.addToken(.l_paren, "(");
const cond = try c.addNode(.{
.tag = .bool_not,
.main_token = try c.addToken(.bang, "!"),
.data = .{
.lhs = try renderNodeGrouped(c, payload.lhs),
.rhs = undefined,
},
});
_ = try c.addToken(.r_paren, ")");

const compile_error_tok = try c.addToken(.builtin, "@compileError");
_ = try c.addToken(.l_paren, "(");
const err_msg = try renderNode(c, payload.rhs);
_ = try c.addToken(.r_paren, ")");
const compile_error = try c.addNode(.{
.tag = .builtin_call_two,
.main_token = compile_error_tok,
.data = .{ .lhs = err_msg, .rhs = 0 },
});

const if_node = try c.addNode(.{
.tag = .if_simple,
.main_token = if_tok,
.data = .{
.lhs = cond,
.rhs = compile_error,
},
});
_ = try c.addToken(.semicolon, ";");
_ = try c.addToken(.r_brace, "}");
const block_node = try c.addNode(.{
.tag = .block_two_semicolon,
.main_token = l_brace,
.data = .{
.lhs = if_node,
.rhs = 0,
},
});

return c.addNode(.{
.tag = .@"comptime",
.main_token = comptime_tok,
.data = .{
.lhs = block_node,
.rhs = undefined,
},
});
},
.@"anytype" => unreachable, // Handled in renderParams
}
}
Expand Down Expand Up @@ -2527,6 +2586,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
.assign,
.helpers_macro,
.import_c_builtin,
.static_assert,
=> {
// these should never appear in places where grouping might be needed.
unreachable;
Expand Down
6 changes: 5 additions & 1 deletion src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,12 @@ fn translate(d: *aro.Driver, args: []const []const u8) !void {
return;
}

// TODO this should probably pass an arraylist for the generated source.
var zig_tree = try Translator.translate(gpa, d.comp, c_tree);
defer zig_tree.deinit(gpa);
defer {
gpa.free(zig_tree.source);
zig_tree.deinit(gpa);
}

const formatted = try zig_tree.render(gpa);
defer gpa.free(formatted);
Expand Down
1 change: 1 addition & 0 deletions test/cases/translate/_Static_assert.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ _Static_assert(1 == 1, "");

// translate
// target=x86_64-linux
// expect=fail
//
// tmp.c:1:1: warning: ignoring _Static_assert declaration

0 comments on commit 2c88153

Please sign in to comment.