Skip to content
This repository was archived by the owner on Nov 26, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

.fingerprint = 0x1c84f6455a54f043, // Changing this has security and trust implications.

.minimum_zig_version = "0.16.0-dev.577+c50aa2b95",
.minimum_zig_version = "0.16.0-dev.1334+06d08daba",

.dependencies = .{
.aro = .{
.url = "git+https://github.com/Vexu/arocc#6f7a5dd36545d35ffdbf7583a67596e3921769f8",
.hash = "aro-0.0.0-JSD1QmndJwCAV9Cyp5Qx6SsmSwjUCG_F3dqKgNRHpImr",
.url = "git+https://github.com/Vexu/arocc#d84be01a6a85ac91f51b3ad7647ca356db3f3c76",
.hash = "aro-0.0.0-JSD1QvVvNgCxkaLWsgk1JClDUgsxTcPrUo43odmNIGT2",
},
},

Expand Down
53 changes: 38 additions & 15 deletions src/MacroTranslator.zig
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ fn parseCPrimaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {

// for handling type macros (EVIL)
// TODO maybe detect and treat type macros as typedefs in parseCSpecifierQualifierList?
if (try mt.parseCTypeName(scope, true)) |type_name| {
if (try mt.parseCTypeName(scope)) |type_name| {
return type_name;
}

Expand Down Expand Up @@ -825,6 +825,18 @@ fn parseCMulExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
switch (mt.peek()) {
.asterisk => {
mt.i += 1;
switch (mt.peek()) {
.comma, .r_paren, .eof => {
// This is probably a pointer type
return ZigTag.c_pointer.create(mt.t.arena, .{
.is_const = false,
.is_volatile = false,
.is_allowzero = false,
.elem_type = node,
});
},
else => {},
}
const lhs = try mt.macroIntFromBool(node);
const rhs = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
node = try ZigTag.mul.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
Expand All @@ -848,7 +860,7 @@ fn parseCMulExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {

fn parseCCastExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
if (mt.eat(.l_paren)) {
if (try mt.parseCTypeName(scope, true)) |type_name| {
if (try mt.parseCTypeName(scope)) |type_name| {
while (true) {
const next_tok = mt.peek();
if (next_tok == .r_paren) {
Expand Down Expand Up @@ -882,24 +894,24 @@ fn parseCCastExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
}

// allow_fail is set when unsure if we are parsing a type-name
fn parseCTypeName(mt: *MacroTranslator, scope: *Scope, allow_fail: bool) ParseError!?ZigNode {
if (try mt.parseCSpecifierQualifierList(scope, allow_fail)) |node| {
fn parseCTypeName(mt: *MacroTranslator, scope: *Scope) ParseError!?ZigNode {
if (try mt.parseCSpecifierQualifierList(scope)) |node| {
return try mt.parseCAbstractDeclarator(node);
}
return null;
}

fn parseCSpecifierQualifierList(mt: *MacroTranslator, scope: *Scope, allow_fail: bool) ParseError!?ZigNode {
fn parseCSpecifierQualifierList(mt: *MacroTranslator, scope: *Scope) ParseError!?ZigNode {
const tok = mt.peek();
switch (tok) {
.macro_param, .macro_param_no_expand => {
const param = mt.macro.params[mt.tokens[mt.i].end];

// Assume that this is only a cast if the next token is ')'
// e.g. param)identifier
if (allow_fail and (mt.macro.tokens.len < mt.i + 3 or
if (mt.macro.tokens.len < mt.i + 3 or
mt.macro.tokens[mt.i + 1].id != .r_paren or
mt.macro.tokens[mt.i + 2].id != .identifier))
mt.macro.tokens[mt.i + 2].id != .identifier)
return null;

mt.i += 1;
Expand All @@ -912,10 +924,10 @@ fn parseCSpecifierQualifierList(mt: *MacroTranslator, scope: *Scope, allow_fail:

if (mt.t.global_scope.blank_macros.contains(slice)) {
mt.i += 1;
return try mt.parseCSpecifierQualifierList(scope, allow_fail);
return try mt.parseCSpecifierQualifierList(scope);
}

if (!allow_fail or mt.t.typedefs.contains(mangled_name)) {
if (mt.t.typedefs.contains(mangled_name)) {
mt.i += 1;
if (Translator.builtin_typedef_map.get(mangled_name)) |ty| {
return try ZigTag.type.create(mt.t.arena, ty);
Expand Down Expand Up @@ -952,6 +964,10 @@ fn parseCSpecifierQualifierList(mt: *MacroTranslator, scope: *Scope, allow_fail:
.keyword_enum, .keyword_struct, .keyword_union => {
const tag_name = mt.tokSlice();
mt.i += 1;
if (mt.peek() != .identifier) {
mt.i -= 1;
return null;
}

// struct Foo will be declared as struct_Foo by transRecordDecl
const identifier = mt.tokSlice();
Expand All @@ -963,10 +979,7 @@ fn parseCSpecifierQualifierList(mt: *MacroTranslator, scope: *Scope, allow_fail:
else => {},
}

if (allow_fail) return null;

try mt.fail("unable to translate C expr: unexpected token '{s}'", .{tok.symbol()});
return error.ParseError;
return null;
}

fn parseCNumericType(mt: *MacroTranslator) ParseError!ZigNode {
Expand Down Expand Up @@ -1126,13 +1139,23 @@ fn parseCPostfixExprInner(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNo
switch (mt.peek()) {
.period => {
mt.i += 1;
const tok = mt.tokens[mt.i];
if (tok.id == .macro_param or tok.id == .macro_param_no_expand) {
try mt.fail("unable to translate C expr: field access using macro parameter", .{});
return error.ParseError;
}
const field_name = mt.tokSlice();
try mt.expect(.identifier);

node = try ZigTag.field_access.create(arena, .{ .lhs = node, .field_name = field_name });
},
.arrow => {
mt.i += 1;
const tok = mt.tokens[mt.i];
if (tok.id == .macro_param or tok.id == .macro_param_no_expand) {
try mt.fail("unable to translate C expr: field access using macro parameter", .{});
return error.ParseError;
}
const field_name = mt.tokSlice();
try mt.expect(.identifier);

Expand Down Expand Up @@ -1286,7 +1309,7 @@ fn parseCUnaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
.keyword_sizeof => {
mt.i += 1;
const operand = if (mt.eat(.l_paren)) blk: {
const inner = (try mt.parseCTypeName(scope, false)).?;
const inner = (try mt.parseCTypeName(scope)) orelse try mt.parseCUnaryExpr(scope);
try mt.expect(.r_paren);
break :blk inner;
} else try mt.parseCUnaryExpr(scope);
Expand All @@ -1298,7 +1321,7 @@ fn parseCUnaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
// TODO this won't work if using <stdalign.h>'s
// #define alignof _Alignof
try mt.expect(.l_paren);
const operand = (try mt.parseCTypeName(scope, false)).?;
const operand = (try mt.parseCTypeName(scope)) orelse try mt.parseCUnaryExpr(scope);
try mt.expect(.r_paren);

return ZigTag.alignof.create(mt.t.arena, operand);
Expand Down
1 change: 1 addition & 0 deletions src/PatternList.zig
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ fn tokenizeMacro(allocator: mem.Allocator, source: []const u8, tok_list: *std.Ar
.buf = source,
.source = .unused,
.langopts = .{},
.splice_locs = &.{},
};
{
const name_tok = tokenizer.nextNoWS();
Expand Down
63 changes: 39 additions & 24 deletions src/Translator.zig
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,11 @@ pub fn getMangle(t: *Translator) u32 {

/// Convert an `aro.Source.Location` to a 'file:line:column' string.
pub fn locStr(t: *Translator, loc: aro.Source.Location) ![]const u8 {
const source = t.comp.getSource(loc.id);
const line_col = source.lineCol(loc);
const filename = source.path;
const expanded = loc.expand(t.comp);
const filename = expanded.path;

const line = source.physicalLine(loc);
const col = line_col.col;
const line = expanded.line_no;
const col = expanded.col;

return std.fmt.allocPrint(t.arena, "{s}:{d}:{d}", .{ filename, line, col });
}
Expand Down Expand Up @@ -139,7 +138,11 @@ pub fn failDeclExtra(
// location
// pub const name = @compileError(msg);
const fail_msg = try std.fmt.allocPrint(t.arena, format, args);
const fail_decl = try ZigTag.fail_decl.create(t.arena, .{ .actual = name, .mangled = fail_msg });
const fail_decl = try ZigTag.fail_decl.create(t.arena, .{
.actual = name,
.mangled = fail_msg,
.local = scope.id != .root,
});

const str = try t.locStr(loc);
const location_comment = try std.fmt.allocPrint(t.arena, "// {s}", .{str});
Expand Down Expand Up @@ -307,7 +310,7 @@ fn prepopulateGlobalNameTable(t: *Translator) !void {
}

for (t.pp.defines.keys(), t.pp.defines.values()) |name, macro| {
if (macro.is_builtin) continue;
if (macro.isBuiltin()) continue;
if (!t.isSelfDefinedMacro(name, macro)) {
try t.global_names.put(t.gpa, name, {});
}
Expand Down Expand Up @@ -537,6 +540,13 @@ fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!voi
break :init ZigTag.opaque_literal.init();
}

// Demote record to opaque if it contains an opaque field
if (t.typeWasDemotedToOpaque(field.qt)) {
try t.opaque_demotes.put(t.gpa, base.qt, {});
try t.warn(scope, field_loc, "{s} demoted to opaque type - has opaque field", .{container_kind_name});
break :init ZigTag.opaque_literal.init();
}

var field_name = field.name.lookup(t.comp);
if (field.name_tok == 0) {
field_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{unnamed_field_count});
Expand Down Expand Up @@ -1029,7 +1039,7 @@ fn transStaticAssert(t: *Translator, scope: *Scope, static_assert: Node.StaticAs
try scope.appendNode(assert_node);
}

fn transGlobalAsm(t: *Translator, scope: *Scope, global_asm: Node.SimpleAsm) Error!void {
fn transGlobalAsm(t: *Translator, scope: *Scope, global_asm: Node.GlobalAsm) Error!void {
const asm_string = t.tree.value_map.get(global_asm.asm_str).?;
const bytes = t.comp.interner.get(asm_string.ref()).bytes;

Expand Down Expand Up @@ -1087,6 +1097,17 @@ fn transType(t: *Translator, scope: *Scope, qt: QualType, source_loc: TokenIndex
.double => return ZigTag.type.create(t.arena, "f64"),
.long_double => return ZigTag.type.create(t.arena, "c_longdouble"),
.float128 => return ZigTag.type.create(t.arena, "f128"),
.bf16,
.float32,
.float64,
.float32x,
.float64x,
.float128x,
.dfloat32,
.dfloat64,
.dfloat128,
.dfloat64x,
=> return t.fail(error.UnsupportedType, source_loc, "TODO support atomic type: '{s}'", .{try t.getTypeStr(qt)}),
},
.pointer => |pointer_ty| {
const child_qt = pointer_ty.child;
Expand Down Expand Up @@ -1456,18 +1477,7 @@ fn typeIsOpaque(t: *Translator, qt: QualType) bool {
}

fn typeWasDemotedToOpaque(t: *Translator, qt: QualType) bool {
const base = qt.base(t.comp);
switch (base.type) {
.@"struct", .@"union" => |record_ty| {
if (t.opaque_demotes.contains(base.qt)) return true;
for (record_ty.fields) |field| {
if (t.typeWasDemotedToOpaque(field.qt)) return true;
}
return false;
},
.@"enum" => return t.opaque_demotes.contains(base.qt),
else => return false,
}
return t.opaque_demotes.contains(qt);
}

fn typeHasWrappingOverflow(t: *Translator, qt: QualType) bool {
Expand Down Expand Up @@ -1549,7 +1559,7 @@ fn transStmt(t: *Translator, scope: *Scope, stmt: Node.Index) TransError!ZigNode
.goto_stmt, .computed_goto_stmt, .labeled_stmt => {
return t.fail(error.UnsupportedTranslation, stmt.tok(t.tree), "TODO goto", .{});
},
.gnu_asm_simple => {
.asm_stmt => {
return t.fail(error.UnsupportedTranslation, stmt.tok(t.tree), "TODO asm inside function", .{});
},
else => return t.transExprCoercing(scope, stmt, .unused),
Expand Down Expand Up @@ -2210,7 +2220,7 @@ fn transExpr(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed)
.default_stmt,
.goto_stmt,
.computed_goto_stmt,
.gnu_asm_simple,
.asm_stmt,
.global_asm,
.typedef,
.struct_decl,
Expand Down Expand Up @@ -3044,6 +3054,10 @@ fn transMemberAccess(
.normal => member_access.base.qt(t.tree),
.ptr => member_access.base.qt(t.tree).childType(t.comp),
};
if (t.typeWasDemotedToOpaque(base_info)) {
return t.fail(error.UnsupportedTranslation, member_access.access_tok, "member access of demoted record", .{});
}

const record = base_info.getRecord(t.comp).?;
const field = record.fields[member_access.member_index];
const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{
Expand Down Expand Up @@ -3979,7 +3993,8 @@ fn createFlexibleMemberFn(

// return @ptrCast(&self.*.<field_name>);
const address_of = try ZigTag.address_of.create(t.arena, field_access);
const casted = try ZigTag.ptr_cast.create(t.arena, address_of);
const aligned = try ZigTag.align_cast.create(t.arena, address_of);
const casted = try ZigTag.ptr_cast.create(t.arena, aligned);
const return_stmt = try ZigTag.@"return".create(t.arena, casted);
const body = try ZigTag.block_single.create(t.arena, return_stmt);

Expand Down Expand Up @@ -4011,7 +4026,7 @@ fn transMacros(t: *Translator) !void {
defer pattern_list.deinit(t.gpa);

for (t.pp.defines.keys(), t.pp.defines.values()) |name, macro| {
if (macro.is_builtin) continue;
if (macro.isBuiltin()) continue;
if (t.global_scope.containsNow(name)) {
continue;
}
Expand Down
40 changes: 36 additions & 4 deletions src/ast.zig
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,8 @@ pub const Node = extern union {
.block => Payload.Block,
.c_pointer, .single_pointer => Payload.Pointer,
.array_type, .null_sentinel_array_type => Payload.Array,
.arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,
.arg_redecl, .alias => Payload.ArgRedecl,
.fail_decl => Payload.FailDecl,
.var_simple, .pub_var_simple, .wrapped_local, .mut_str => Payload.SimpleVarDecl,
.enum_constant => Payload.EnumConstant,
.array_filler => Payload.ArrayFiller,
Expand Down Expand Up @@ -708,6 +709,15 @@ pub const Payload = struct {
},
};

pub const FailDecl = struct {
base: Payload,
data: struct {
actual: []const u8,
mangled: []const u8,
local: bool,
},
};

pub const SimpleVarDecl = struct {
base: Payload,
data: struct {
Expand Down Expand Up @@ -1203,12 +1213,25 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
},
.fail_decl => {
const payload = node.castTag(.fail_decl).?.data;
// pub const name = @compileError(msg);
_ = try c.addToken(.keyword_pub, "pub");
// pub const name = (if (true))? @compileError(msg);
if (!payload.local) _ = try c.addToken(.keyword_pub, "pub");
const const_tok = try c.addToken(.keyword_const, "const");
_ = try c.addIdentifier(payload.actual);
_ = try c.addToken(.equal, "=");

var if_tok: TokenIndex = undefined;
var true_node: NodeIndex = undefined;
if (payload.local) {
if_tok = try c.addToken(.keyword_if, "if");
_ = try c.addToken(.l_paren, "(");
true_node = try c.addNode(.{
.tag = .identifier,
.main_token = try c.addToken(.identifier, "true"),
.data = undefined,
});
_ = try c.addToken(.r_paren, ")");
}

const compile_error_tok = try c.addToken(.builtin, "@compileError");
_ = try c.addToken(.l_paren, "(");
const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(payload.mangled)});
Expand All @@ -1233,7 +1256,16 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
.data = .{
.opt_node_and_opt_node = .{
.none, // Type expression
compile_error.toOptional(), // Init expression
if (payload.local) // Init expression
(try c.addNode(.{
.tag = .if_simple,
.main_token = if_tok,
.data = .{ .node_and_node = .{
true_node, compile_error,
} },
})).toOptional()
else
compile_error.toOptional(),
},
},
});
Expand Down
Loading