vexu / arocc Goto Github PK
View Code? Open in Web Editor NEWA C compiler written in Zig.
Home Page: http://aro.vexu.eu/
License: MIT License
A C compiler written in Zig.
Home Page: http://aro.vexu.eu/
License: MIT License
Lines 19 to 37 in 4befc7b
The Macro
enum stores the info about a #define
clause, and currently has three possible type:
simple
, for object macrosfunc
, for func macrosempty
, for object macros with no replacement listThe third possibility, empty
, is a residue of the old code and shall be unified with simple
.
On the other hand, if left with just two possibilities, Macro
could be further simplified as of #33 (comment):
empty special case could also be removed in favor of simple with 0 tokens. And while we're at it Macro could be replaced entirely with Func by adding a is_func flag that would fit in the padding.
Currently arocc doesn't issues appropriate warnings in many cases like:
etc. which are activated by -pedantic flag in gcc and clang
_Static_assert(1 == 0, "They are not equal!");
Output:
/Users/ehaas/source/c/test.c:1:1: error: static assertion failed "\xaa\xaa\xaa\xaa\xaa\x00\x00 \x00\x00\x00\x00\x00\x00\x00 \x05\x00\xaa"
_Static_assert(1 == 0, "They are not equal!");
^
1 error generated.
static_assert: 'void'
condition:
equal_expr: 'int' (value: 0)
lhs:
int_literal: 'int'
value: 1
rhs:
int_literal: 'int'
value: 0
diagnostic:
string_literal_expr: '[20]char' lvalue
data: "They are not equal!"
Following does not issue a warning:
void foo(void) {
const int *y = 0;
int *z;
z = y;
}
In Type.eql
, for pointer types, should we be unconditionally passing true
for check_qualifiers
when recursing into the elemType?
This test case should pass:
#define foo __has_attribute(used)
foo
#define EXPECTED_TOKENS 1
char *foo = "\UA0110000";
The offending line appears to be:
const c = std.fmt.parseInt(u21, slice[i.* + 1 ..][0..count], 16) catch unreachable; // Validated by tokenizer
So perhaps the tokenizer is too lenient?
void foo(void) {
int d __attribute__((aligned(4)));
d(4);
}
Should print error: cannot call non function type 'int'
but it crashes
/Users/ehaas/source/arocc/src/Type.zig:1354:58: 0x10a047751 in Type.printPrologue (arocc)
else => try w.writeAll(Builder.fromType(ty).str().?),
^
/Users/ehaas/source/arocc/src/Type.zig:1296:29: 0x10a04660a in Type.print (arocc)
_ = try ty.printPrologue(w);
^
/Users/ehaas/source/arocc/src/Parser.zig:212:17: 0x10a0ec3e2 in Parser.typeStr (arocc)
try ty.print(p.strings.writer());
^
/Users/ehaas/source/arocc/src/Parser.zig:4769:59: 0x10a18ecec in Parser.callExpr (arocc)
try p.errStr(.not_callable, l_paren, try p.typeStr(lhs.ty));
This only manifests on Linux, I wasn't able to reproduce it on MacOS. Zig version 0.9.0-dev.1561+5ebdc8c46
#define foo(X) X
foo(1,
root@006bbb66b04e:/arocc# ./zig-out/bin/arocc -E test/cases/leak.c
test/cases/leak.c:2:1: error: unterminated function macro argument list
foo(1,
^
1 error generated.
foo ( 1 ,
error(gpa): memory address 0x7fd5a77e0380 leaked:
/zig-linux-x86_64-0.9.0-dev.1561+5ebdc8c46/lib/std/array_list.zig:340:69: 0x349579 in std.array_list.ArrayListAligned([]const Tree.Token,null).ensureTotalCapacityPrecise (arocc)
const new_memory = try self.allocator.reallocAtLeast(self.allocatedSlice(), new_capacity);
^
/zig-linux-x86_64-0.9.0-dev.1561+5ebdc8c46/lib/std/array_list.zig:325:55: 0x27babe in std.array_list.ArrayListAligned([]const Tree.Token,null).ensureTotalCapacity (arocc)
return self.ensureTotalCapacityPrecise(better_capacity);
^
/zig-linux-x86_64-0.9.0-dev.1561+5ebdc8c46/lib/std/array_list.zig:364:41: 0x376b78 in std.array_list.ArrayListAligned([]const Tree.Token,null).addOne (arocc)
try self.ensureTotalCapacity(newlen);
^
/zig-linux-x86_64-0.9.0-dev.1561+5ebdc8c46/lib/std/array_list.zig:175:49: 0x3491c6 in std.array_list.ArrayListAligned([]const Tree.Token,null).append (arocc)
const new_item_ptr = try self.addOne();
^
error(gpa): memory address 0x7fd5a77e2500 leaked:
/zig-linux-x86_64-0.9.0-dev.1561+5ebdc8c46/lib/std/array_list.zig:340:69: 0x321489 in std.array_list.ArrayListAligned(Tree.Token,null).ensureTotalCapacityPrecise (arocc)
const new_memory = try self.allocator.reallocAtLeast(self.allocatedSlice(), new_capacity);
^
/zig-linux-x86_64-0.9.0-dev.1561+5ebdc8c46/lib/std/array_list.zig:325:55: 0x27bbde in std.array_list.ArrayListAligned(Tree.Token,null).ensureTotalCapacity (arocc)
return self.ensureTotalCapacityPrecise(better_capacity);
^
/zig-linux-x86_64-0.9.0-dev.1561+5ebdc8c46/lib/std/array_list.zig:364:41: 0x321328 in std.array_list.ArrayListAligned(Tree.Token,null).addOne (arocc)
try self.ensureTotalCapacity(newlen);
^
/zig-linux-x86_64-0.9.0-dev.1561+5ebdc8c46/lib/std/array_list.zig:175:49: 0x2fa4c6 in std.array_list.ArrayListAligned(Tree.Token,null).append (arocc)
const new_item_ptr = try self.addOne();
^
Should also check for .eof
in Preprocessor.pragma
(clang errors with error: division by zero in preprocessor expression
for this snippet)
#if 1/q
#endif
thread 18069770 panic: reached unreachable code
/Users/ehaas/source/zig/build/lib/zig/std/debug.zig:226:14: 0x104f172f8 in std.debug.assert (arocc)
if (!ok) unreachable; // assertion failure
^
/Users/ehaas/source/arocc/src/Parser.zig:3631:15: 0x104fe0607 in Parser.Result.saveValue (arocc)
assert(!p.in_macro);
^
/Users/ehaas/source/arocc/src/Parser.zig:3625:24: 0x1050419ae in Parser.Result.shouldEval (arocc)
try a.saveValue(p);
^
/Users/ehaas/source/arocc/src/Parser.zig:3362:32: 0x105009235 in Parser.Result.adjustTypes (arocc)
return a.shouldEval(b, p);
^
/Users/ehaas/source/arocc/src/Parser.zig:4238:32: 0x105078204 in Parser.mulExpr (arocc)
if (try lhs.adjustTypes(percent.?, &rhs, p, if (tag == .mod_expr) .integer else .arithmetic)) {
^
/Users/ehaas/source/arocc/src/Parser.zig:4196:28: 0x105077734 in Parser.addExpr (arocc)
var lhs = try p.mulExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4164:28: 0x105076cc4 in Parser.shiftExpr (arocc)
var lhs = try p.addExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4135:30: 0x105076374 in Parser.compExpr (arocc)
var lhs = try p.shiftExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4110:29: 0x1050758f4 in Parser.eqExpr (arocc)
var lhs = try p.compExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4090:27: 0x1050739d4 in Parser.andExpr (arocc)
var lhs = try p.eqExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4070:28: 0x10506c7c4 in Parser.xorExpr (arocc)
var lhs = try p.andExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4050:28: 0x10505ff34 in Parser.orExpr (arocc)
var lhs = try p.xorExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4028:27: 0x105040744 in Parser.landExpr (arocc)
var lhs = try p.orExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4006:29: 0x1050085b4 in Parser.lorExpr (arocc)
var lhs = try p.landExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:3968:29: 0x104fd6dd4 in Parser.condExpr (arocc)
var cond = try p.lorExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:3199:27: 0x104f7dc4e in Parser.macroExpr (arocc)
const res = p.condExpr() catch |e| switch (e) {
^
/Users/ehaas/source/arocc/src/Preprocessor.zig:465:28: 0x104f4e192 in Preprocessor.expr (arocc)
return parser.macroExpr();
^
/Users/ehaas/source/arocc/src/Preprocessor.zig:181:40: 0x104f428b9 in Preprocessor.preprocess (arocc)
if (try pp.expr(&tokenizer)) {
^
/Users/ehaas/source/arocc/src/main.zig:237:22: 0x104f27818 in processSource (arocc)
try pp.preprocess(source);
^
/Users/ehaas/source/arocc/src/main.zig:223:22: 0x104f1c151 in handleArgs (arocc)
processSource(comp, source, builtin, user_macros) catch |e| switch (e) {
^
/Users/ehaas/source/arocc/src/main.zig:41:15: 0x104f192dd in main (arocc)
handleArgs(&comp, args) catch |err| switch (err) {
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:524:29: 0x104f96b0c in std.start.callMain (arocc)
return root.main();
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:469:12: 0x104f1d257 in std.start.callMainWithArgs (arocc)
return @call(.{ .modifier = .always_inline }, callMain, .{});
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:434:12: 0x104f1d195 in std.start.main (arocc)
return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
^
???:?:?: 0x7fff204ecf3c in ??? (???)
???:?:?: 0x1 in ??? (???)
[1] 71375 abort ~/source/arocc/zig-out/bin/arocc test.c
int foo,
#pragma bar bar
int baz;
#pragma qux
thread 14693625 panic: index out of bounds
/Users/ehaas/source/arocc/src/Parser.zig:109:18: 0x101592c32 in Parser.eatToken (arocc)
if (p.tok_ids[p.tok_i] == id) {
^
/Users/ehaas/source/arocc/src/Parser.zig:396:22: 0x101592cae in Parser.pragma (arocc)
while (p.eatToken(.keyword_pragma) != null) {
^
/Users/ehaas/source/arocc/src/Parser.zig:454:25: 0x1015802ae in Parser.parse (arocc)
if (try p.pragma()) continue;
^
/Users/ehaas/source/arocc/src/main.zig:257:32: 0x101562b49 in processSource (arocc)
var tree = try Parser.parse(&pp);
^
/Users/ehaas/source/arocc/src/main.zig:223:22: 0x101557161 in handleArgs (arocc)
processSource(comp, source, builtin, user_macros) catch |e| switch (e) {
^
/Users/ehaas/source/arocc/src/main.zig:41:15: 0x1015542ed in main (arocc)
handleArgs(&comp, args) catch |err| switch (err) {
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:524:29: 0x1015d290c in std.start.callMain (arocc)
return root.main();
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:469:12: 0x101558267 in std.start.callMainWithArgs (arocc)
return @call(.{ .modifier = .always_inline }, callMain, .{});
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:434:12: 0x1015581a5 in std.start.main (arocc)
return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
^
???:?:?: 0x7fff204ecf3c in ??? (???)
???:?:?: 0x1 in ??? (???)
[1] 63839 abort ./zig-out/bin/arocc test/cases/crash.c
This is correctly handled:
void foo(void) {
const int x;
x = 10;
}
But this is not:
void foo(void) {
const int x = 5;
x = 10;
}
Example derived from https://github.com/Hirrolot/metalang99 which is a good source of preprocessor edge cases and stress tests.
#define REC_EMPTY
#define REC_DEFER(op) op REC_EMPTY
#define REC_0_HOOK() REC_0
#define REC_1 REC_DEFER(REC_0_HOOK)()
REC_1
With gcc and clang -E
option, this expands to: REC_0_HOOK ()
With aro, it currently expands to: REC_0
#define FOO(x) #x
FOO(int main "hello")
Clang preprocessor output:
"int main \"hello\""
Aro preprocessor output:
"intmain\"hello\""
❯ arocc test.c -o test
thread 12150 panic: integer cast truncated bits
/home/varlad/arocc/src/Parser.zig:226:19: 0x2b86bc in Parser.addList (arocc)
const start = @intCast(u32, p.data.items.len);
^
/home/varlad/arocc/src/Parser.zig:3275:71: 0x2a317c in Parser.condExpr (arocc)
.data = .{ .if3 = .{ .cond = cond.node, .body = (try p.addList(&.{ then_expr.node, else_expr.node })).start } },
^
/home/varlad/arocc/src/Parser.zig:2491:27: 0x28a5c0 in Parser.macroExpr (arocc)
const res = p.condExpr() catch |e| switch (e) {
^
/home/varlad/arocc/src/Preprocessor.zig:415:28: 0x26ef1f in Preprocessor.expr (arocc)
return parser.macroExpr();
^
/home/varlad/arocc/src/Preprocessor.zig:121:40: 0x267f80 in Preprocessor.preprocess (arocc)
if (try pp.expr(&tokenizer)) {
^
/home/varlad/arocc/src/Preprocessor.zig:1098:22: 0x26fecb in Preprocessor.include (arocc)
try pp.preprocess(new_source);
^
/home/varlad/arocc/src/Preprocessor.zig:205:55: 0x268a8a in Preprocessor.preprocess (arocc)
.keyword_include => try pp.include(&tokenizer),
^
/home/varlad/arocc/src/Preprocessor.zig:1098:22: 0x26fecb in Preprocessor.include (arocc)
try pp.preprocess(new_source);
^
/home/varlad/arocc/src/Preprocessor.zig:205:55: 0x268a8a in Preprocessor.preprocess (arocc)
.keyword_include => try pp.include(&tokenizer),
^
/home/varlad/arocc/src/Preprocessor.zig:1098:22: 0x26fecb in Preprocessor.include (arocc)
try pp.preprocess(new_source);
^
/home/varlad/arocc/src/Preprocessor.zig:205:55: 0x268a8a in Preprocessor.preprocess (arocc)
.keyword_include => try pp.include(&tokenizer),
^
/home/varlad/arocc/src/main.zig:209:22: 0x25f5ac in processSource (arocc)
try pp.preprocess(source);
^
/home/varlad/arocc/src/main.zig:196:22: 0x25884e in handleArgs (arocc)
processSource(comp, source, builtin, user_macros) catch |e| switch (e) {
^
/home/varlad/arocc/src/main.zig:33:15: 0x24f8c2 in main (arocc)
handleArgs(&comp, args) catch |err| switch (err) {
^
/home/varlad/zig-master/lib/std/start.zig:507:29: 0x2481cc in std.start.callMain (arocc)
return root.main();
^
/home/varlad/zig-master/lib/std/start.zig:452:12: 0x22c1ce in std.start.callMainWithArgs (arocc)
return @call(.{ .modifier = .always_inline }, callMain, .{});
^
/home/varlad/zig-master/lib/std/start.zig:366:17: 0x22b1f6 in std.start.posixCallMainAndExit (arocc)
std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp }));
^
/home/varlad/zig-master/lib/std/start.zig:279:5: 0x22b002 in std.start._start (arocc)
@call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
^
fish: Job 1, 'arocc test.c -o test' terminated by signal SIGABRT (Abort)
where test.c reads
#include "stdio.h"
int main()
{
printf("Well, heck");
return 0;
}
Built with Zig v0.9.0-dev.861+311797f68 via zig build
Currently they are elided
Currently panics:
void foo(void)
{
(void)(0/0);
}
int x;
int j[] = x;
➜ ~/source/arocc/zig-out/bin/arocc test.c
thread 18204944 panic: access of inactive union field
/Users/ehaas/source/arocc/src/Parser.zig:1210:64: 0x10671f5cb in Parser.initDeclarator (arocc)
init_d.d.ty.data.array.len = init_list_expr.ty.data.array.len;
^
/Users/ehaas/source/arocc/src/Parser.zig:585:39: 0x1066ed429 in Parser.decl (arocc)
var init_d = (try p.initDeclarator(&decl_spec)) orelse {
^
/Users/ehaas/source/arocc/src/Parser.zig:462:19: 0x1066d922a in Parser.parse (arocc)
if (p.decl() catch |er| switch (er) {
^
/Users/ehaas/source/arocc/src/main.zig:257:32: 0x1066bbbb9 in processSource (arocc)
var tree = try Parser.parse(&pp);
^
/Users/ehaas/source/arocc/src/main.zig:223:22: 0x1066b0151 in handleArgs (arocc)
processSource(comp, source, builtin, user_macros) catch |e| switch (e) {
^
/Users/ehaas/source/arocc/src/main.zig:41:15: 0x1066ad2dd in main (arocc)
handleArgs(&comp, args) catch |err| switch (err) {
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:524:29: 0x10672ab0c in std.start.callMain (arocc)
return root.main();
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:469:12: 0x1066b1257 in std.start.callMainWithArgs (arocc)
return @call(.{ .modifier = .always_inline }, callMain, .{});
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:434:12: 0x1066b1195 in std.start.main (arocc)
return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
^
???:?:?: 0x7fff204ecf3c in ??? (???)
???:?:?: 0x1 in ??? (???)
[1] 79148 abort ~/source/arocc/zig-out/bin/arocc test.c
enum Foo A = 13, B = A + 4;
truncated trace:
thread 18245272 panic: reached unreachable code
/Users/ehaas/source/arocc/src/Type.zig:452:21: 0x109ce1329 in Type.integerPromotion (arocc)
else => unreachable, // not an integer type
^
/Users/ehaas/source/arocc/src/Parser.zig:3583:49: 0x109cf053b in Parser.Result.usualArithmeticConversion (arocc)
const a_promoted = a.ty.integerPromotion(p.pp.comp);
^
/Users/ehaas/source/arocc/src/Parser.zig:3364:44: 0x109cb8366 in Parser.Result.adjustTypes (arocc)
try a.usualArithmeticConversion(b, p);
^
/Users/ehaas/source/arocc/src/Parser.zig:4208:32: 0x109d26ef0 in Parser.addExpr (arocc)
if (try lhs.adjustTypes(minus.?, &rhs, p, if (plus != null) .add else .sub)) {
^
/Users/ehaas/source/arocc/src/Parser.zig:4167:28: 0x109d261b4 in Parser.shiftExpr (arocc)
var lhs = try p.addExpr();
A future improvement will be to convert the source location mechanism into using contiguous arrays instead of linked lists now that they are being iterated upon a lot more.
Originally posted by @Vexu in #33 (comment)
Source.Location
is a linked list that works like this:
The following C code crashes the preprocessor:
#if c--
#endif
clang reports error: token is not a valid binary operator in a preprocessor subexpression
, pointing to the --
Partial trace:
/Users/ehaas/source/arocc/src/Tree.zig:463:30: 0x108972a7c in Tree.isLvalExtra (arocc)
switch (nodes.items(.tag)[@enumToInt(node)]) {
^
/Users/ehaas/source/arocc/src/Tree.zig:458:23: 0x10891086f in Tree.isLval (arocc)
return isLvalExtra(nodes, extra, value_map, node, &is_const);
^
/Users/ehaas/source/arocc/src/Parser.zig:4660:29: 0x108a01572 in Parser.suffixExpr (arocc)
if (!Tree.isLval(p.nodes.slice(), p.data.items, p.value_map, operand.node) or operand.ty.isConst()) {
^
/Users/ehaas/source/arocc/src/Parser.zig:4579:48: 0x1089fad86 in Parser.unExpr (arocc)
const suffix = try p.suffixExpr(lhs);
^
/Users/ehaas/source/arocc/src/Parser.zig:4327:20: 0x1089f99e1 in Parser.castExpr (arocc)
return p.unExpr();
Hello, I'm opening this issue mostly to clear up my understanding of these issues. Consider
#define f(a) a
#define z z[0]
f(f(z))
with gcc -E
this expands to z[0]
. With #29 , this expands to z[0][0][0]
.
From the current C standard draft, 6.10.3.4 (Rescanning and further replacement)
If the name of the macro being replaced is found during this scan of the replacement list (not
including the rest of the source file’s preprocessing tokens), it is not replaced. Furthermore, if any
nested replacements encounter the name of the macro being replaced, it is not replaced. These
nonreplaced macro name preprocessing tokens are no longer available for further replacement even
if they are later (re)examined in contexts in which that macro name preprocessing token would
otherwise have been replaced.
I guess the last sentence is what makes the difference in this case.
I would like to try to fix this myself.
This will currently parse with no errors or warnings:
void foo(void) {
#pragma GCC diagnostic error "-Wint-conversion"
int *x = 5;
#pragma GCC diagnostic ignored "-Wint-conversion"
}
#define bar(a) ## i
thread 14036537 panic: integer overflow
/Users/ehaas/source/arocc/src/Preprocessor.zig:1253:63: 0x1013ad8e6 in Preprocessor.defineFn (arocc)
if (pp.token_buf.items[pp.token_buf.items.len - 1].id == .macro_param) {
^
/Users/ehaas/source/arocc/src/Preprocessor.zig:1143:53: 0x10137d24b in Preprocessor.define (arocc)
if (first.id == .l_paren) return pp.defineFn(tokenizer, macro_name, first);
^
/Users/ehaas/source/arocc/src/Preprocessor.zig:264:53: 0x101371576 in Preprocessor.preprocess (arocc)
.keyword_define => try pp.define(&tokenizer),
^
/Users/ehaas/source/arocc/src/main.zig:237:22: 0x1013557a8 in processSource (arocc)
try pp.preprocess(source);
^
/Users/ehaas/source/arocc/src/main.zig:223:22: 0x10134a161 in handleArgs (arocc)
processSource(comp, source, builtin, user_macros) catch |e| switch (e) {
^
/Users/ehaas/source/arocc/src/main.zig:41:15: 0x1013472ed in main (arocc)
handleArgs(&comp, args) catch |err| switch (err) {
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:524:29: 0x1013c4d7c in std.start.callMain (arocc)
return root.main();
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:469:12: 0x10134b267 in std.start.callMainWithArgs (arocc)
return @call(.{ .modifier = .always_inline }, callMain, .{});
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:434:12: 0x10134b1a5 in std.start.main (arocc)
return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
^
???:?:?: 0x7fff204ecf3c in ??? (???)
???:?:?: 0x1 in ??? (???)
[1] 19263 abort ./zig-out/bin/arocc test/cases/tokenpaste.c
Note: this may be irrelevant if the plan is to move alignment out of the type itself and into an attributed type
Currently a u29
is used to store the alignment but we could get away with a u5
since the alignment must be a power of 2. 0 could be reserved for "no alignment specified", so we'd actually want to store log_2(alignment) + 1
. In this way a user-provided alignment of up to 2**30 could be supported.
Line 216 in 6d0baf3
(This is invalid syntax but it crashes the parser):
foo(X)
➜ ~ ./source/arocc/zig-out/bin/arocc test.c
thread 930248 panic: attempt to use null value
/Users/ehaas/source/arocc/src/Parser.zig:662:70: 0x10b39fb9f in Parser.decl (arocc)
.data = .{ .decl = .{ .name = init_d.d.name, .node = body.? } },
^
/Users/ehaas/source/arocc/src/Parser.zig:436:19: 0x10b38bc56 in Parser.parse (arocc)
if (p.decl() catch |er| switch (er) {
^
/Users/ehaas/source/arocc/src/main.zig:249:32: 0x10b36ec45 in processSource (arocc)
var tree = try Parser.parse(&pp);
^
/Users/ehaas/source/arocc/src/main.zig:216:22: 0x10b363e3e in handleArgs (arocc)
processSource(comp, source, builtin, user_macros) catch |e| switch (e) {
^
/Users/ehaas/source/arocc/src/main.zig:34:15: 0x10b361554 in main (arocc)
handleArgs(&comp, args) catch |err| switch (err) {
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:524:29: 0x10b3d7d3c in std.start.callMain (arocc)
return root.main();
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:469:12: 0x10b3650e7 in std.start.callMainWithArgs (arocc)
return @call(.{ .modifier = .always_inline }, callMain, .{});
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:434:12: 0x10b365025 in std.start.main (arocc)
return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
^
???:?:?: 0x7fff204ecf3c in ??? (???)
???:?:?: 0x1 in ??? (???)
[1] 75758 abort ./source/arocc/zig-out/bin/arocc test.c
Found using fuzz testing
void foo(void) {
1,;
}
Compile with --verbose-ast
:
thread 14001920 panic: reached unreachable code
/Users/ehaas/source/zig/build/lib/zig/std/debug.zig:226:14: 0x10a4dc2f8 in std.debug.assert (arocc)
if (!ok) unreachable; // assertion failure
^
/Users/ehaas/source/arocc/src/Tree.zig:557:21: 0x10a52235b in Tree.dumpNode (arocc)
std.debug.assert(node != .none);
^
/Users/ehaas/source/arocc/src/Tree.zig:905:30: 0x10a52a0c9 in Tree.dumpNode (arocc)
try tree.dumpNode(data.bin.rhs, level + delta, w);
^
/Users/ehaas/source/arocc/src/Tree.zig:688:57: 0x10a528297 in Tree.dumpNode (arocc)
if (data.bin.lhs != .none) try tree.dumpNode(data.bin.lhs, level + delta, w);
^
/Users/ehaas/source/arocc/src/Tree.zig:628:30: 0x10a527bf9 in Tree.dumpNode (arocc)
try tree.dumpNode(data.decl.node, level + delta, w);
^
/Users/ehaas/source/arocc/src/Tree.zig:541:26: 0x10a50ae64 in Tree.dump (arocc)
try tree.dumpNode(i, 0, writer);
^
/Users/ehaas/source/arocc/src/main.zig:262:18: 0x10a4ecc2d in processSource (arocc)
tree.dump(buf_writer.writer()) catch {};
^
/Users/ehaas/source/arocc/src/main.zig:223:22: 0x10a4e1161 in handleArgs (arocc)
processSource(comp, source, builtin, user_macros) catch |e| switch (e) {
^
/Users/ehaas/source/arocc/src/main.zig:41:15: 0x10a4de2ed in main (arocc)
handleArgs(&comp, args) catch |err| switch (err) {
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:524:29: 0x10a55bd7c in std.start.callMain (arocc)
return root.main();
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:469:12: 0x10a4e2267 in std.start.callMainWithArgs (arocc)
return @call(.{ .modifier = .always_inline }, callMain, .{});
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:434:12: 0x10a4e21a5 in std.start.main (arocc)
return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
I believe this is the opposite of #25
void foo(void) {
char *bar = 0;
const char *baz = bar;
}
/Users/ehaas/source/c/test.c:3:22: warning: initializing 'const char *' from incompatible type 'char *' discards qualifiers
const char *baz = bar;
^
1 warning generated.
fn_def: 'fn () void'
name: foo
body:
compound_stmt: 'void'
var: '*char'
name: bar
init:
null_to_pointer: '*char'
int_literal: 'int'
value: 0
var: '*const char'
name: baz
init:
lval_to_rval: '*char'
decl_ref_expr: '*char' lvalue
name: bar
implicit_return: 'void'
To reproduce compile any file with -Wfoo -Wno-unknown-warning-option
in that order. Switching the order results in expected behavior.
➜ ~ ~/source/arocc/zig-out/bin/arocc -Wfoo -Wno-unknown-warning-option test.c
thread 1633389 panic: reached unreachable code
/Users/ehaas/source/arocc/src/Diagnostics.zig:372:21: 0x103804142 in Diagnostics.renderExtra (arocc)
.off => unreachable,
^
/Users/ehaas/source/arocc/src/Diagnostics.zig:360:16: 0x1037ce427 in Diagnostics.render (arocc)
renderExtra(comp, &m);
^
/Users/ehaas/source/arocc/src/main.zig:259:22: 0x1037ddd7e in processSource (arocc)
comp.renderErrors();
^
/Users/ehaas/source/arocc/src/main.zig:216:22: 0x1037d2e3e in handleArgs (arocc)
processSource(comp, source, builtin, user_macros) catch |e| switch (e) {
^
/Users/ehaas/source/arocc/src/main.zig:34:15: 0x1037d0554 in main (arocc)
handleArgs(&comp, args) catch |err| switch (err) {
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:524:29: 0x103846dcc in std.start.callMain (arocc)
return root.main();
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:469:12: 0x1037d40e7 in std.start.callMainWithArgs (arocc)
return @call(.{ .modifier = .always_inline }, callMain, .{});
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:434:12: 0x1037d4025 in std.start.main (arocc)
return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
^
???:?:?: 0x7fff204ecf3c in ??? (???)
???:?:?: 0x3 in ??? (???)
[1] 39972 abort ~/source/arocc/zig-out/bin/arocc -Wfoo -Wno-unknown-warning-option test.c
The identifier __func__
is implicitly declared by the translator as if, immediately following the opening brace of each function definition, the declaration
static const char __func__[] = "function-name";
appeared, where function-name
is the name of the lexically-enclosing function
As an extension, at file scope, __func__
evaluates to the empty string. It should generate a warning warning: predefined identifier is only valid inside function
controlled by -Wpredefined-identifier-outside-function
(default on)
__FUNCTION__
is another name for __func__
__PRETTY_FUNCTION__
is yet another name for __func__
, except that at file scope, it evaluates to the string "top level"
These identifiers are variables, not preprocessor macros, and may not be used to initialize char arrays or be concatenated with string literals.
The Zig parser test uses a clever technique to check for correct memory handling during error conditions. Parse once using unlimited memory, measure how much was actually used, then parse again N times using FailingAllocator
, once for each size up to the actual amount of memory used. Each of those should fail (if not, memory usage is unexpectedly non-deterministic) and should free the same number of bytes as were allocated.
Currently the diagnostics system is a bit messy and verbose, some things that could improve it are:
tagKind
and tagOption
int foo = 6u + &2;
thread 15096939 panic: access of inactive union field
/Users/ehaas/source/arocc/src/Parser.zig:3690:69: 0x10fc42695 in Parser.Result.add (arocc)
8 => overflow = @addWithOverflow(u64, v.*, b.val.unsigned, v),
^
/Users/ehaas/source/arocc/src/Parser.zig:4188:28: 0x10fc4157e in Parser.addExpr (arocc)
try lhs.add(plus.?, rhs, p);
^
/Users/ehaas/source/arocc/src/Parser.zig:4145:28: 0x10fc40794 in Parser.shiftExpr (arocc)
var lhs = try p.addExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4116:30: 0x10fc3ffb4 in Parser.compExpr (arocc)
var lhs = try p.shiftExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4091:29: 0x10fc3f534 in Parser.eqExpr (arocc)
var lhs = try p.compExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4071:27: 0x10fc3d614 in Parser.andExpr (arocc)
var lhs = try p.eqExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4051:28: 0x10fc36404 in Parser.xorExpr (arocc)
var lhs = try p.andExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4031:28: 0x10fc29b74 in Parser.orExpr (arocc)
var lhs = try p.xorExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4009:27: 0x10fc0a3c4 in Parser.landExpr (arocc)
var lhs = try p.orExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:3987:29: 0x10fbd2424 in Parser.lorExpr (arocc)
var lhs = try p.landExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:3949:29: 0x10fba0b94 in Parser.condExpr (arocc)
var cond = try p.lorExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:3804:29: 0x10fbf02a4 in Parser.assignExpr (arocc)
var lhs = try p.condExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:2126:35: 0x10fbaebc7 in Parser.initializer (arocc)
var res = try p.assignExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:1197:47: 0x10fb54447 in Parser.initDeclarator (arocc)
var init_list_expr = try p.initializer(init_d.d.ty);
^
/Users/ehaas/source/arocc/src/Parser.zig:576:39: 0x10fb215b9 in Parser.decl (arocc)
var init_d = (try p.initDeclarator(&decl_spec)) orelse {
^
/Users/ehaas/source/arocc/src/Parser.zig:461:19: 0x10fb0d3ba in Parser.parse (arocc)
if (p.decl() catch |er| switch (er) {
^
/Users/ehaas/source/arocc/src/main.zig:257:32: 0x10faefc69 in processSource (arocc)
var tree = try Parser.parse(&pp);
^
/Users/ehaas/source/arocc/src/main.zig:223:22: 0x10fae4211 in handleArgs (arocc)
processSource(comp, source, builtin, user_macros) catch |e| switch (e) {
^
/Users/ehaas/source/arocc/src/main.zig:41:15: 0x10fae139d in main (arocc)
handleArgs(&comp, args) catch |err| switch (err) {
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:524:29: 0x10fb605fc in std.start.callMain (arocc)
return root.main();
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:469:12: 0x10fae5317 in std.start.callMainWithArgs (arocc)
return @call(.{ .modifier = .always_inline }, callMain, .{});
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:434:12: 0x10fae5255 in std.start.main (arocc)
return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
^
???:?:?: 0x7fff204ecf3c in ??? (???)
???:?:?: 0x1 in ??? (???)
[1] 90940 abort ./zig-out/bin/arocc test/cases/crash.c
decl()
allows any leading attribute since we don't know if we're parsing a function or variable declaration. Once we know what we've parsed we should retroactively validate those attributes for the relevant context.
Currently all attributes are allowed to have an arbitrary number of arguments of any type
Specifically p.tok_i
can end-up out of bounds in this conditional
if (p.tok_ids[p.tok_i] == id)
I've been able to workaround this issue locally by changing the condition of the while loop in Parser.zig to:
while ((p.tok_i < p.tok_ids.len) and (p.eatToken(.eof) == null))
I've been using a small amount of macro-trickery (in an attempt) to use arocc for build-time reflection on C-style enums, whilst still supporting C++ style enums for the actual C++ side of things. So the code that is being parsed is a little idiosyncratic:
#pragma once
#include "Reflection/Enum.meta.h"
DECLARE_ENUM(LogChannel, uint16_t)
{
Default = 0,
Rendering = 1,
Vulkan = 2,
VulkanInternal = 3,
Import = 4,
JobSystem = 5,
Containers = 6,
Engine = 7,
};
Where Enum.meta.h
is defined as:
#pragma once
#ifdef __cplusplus
#include "cstdint_wrapper.hpp"
#define DECLARE_ENUM(EnumName, EnumType) \
enum class EnumName : EnumType
#else
#define DECLARE_ENUM(EnumName, EnumType) \
enum EnumName
#endif
I've done a little bit of debugging to try and track down the cause - but I don't want to spend too much time being side-tracked by this problem. So I will leave it for now - so thought I would at least make the issue. I might come back to it later but I need to dedicate some time to properly understanding how arocc is structured in order to debug this issue.
Using nodes is wasteful since we usually don't need to explicitly store the tag or type (it's known from the attribute name + arg position)
Attribute arguments can be of the following types:
TokenIndex
)[]const u8
?)aligned
requires a power of 2)copy
attribute https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#index-copy-type-attribute) - in this case we'll probably need a NodeIndex
. We may also need to special-case the parser so it doesn't try to evaluate the expression.The following code triggers an assertion failure:
int foo = 0xFFFFFFFFFF + 1u;
thread 15090414 panic: reached unreachable code
/Users/ehaas/source/zig/build/lib/zig/std/debug.zig:226:14: 0x10705a2f8 in std.debug.assert (arocc)
if (!ok) unreachable; // assertion failure
^
/Users/ehaas/source/arocc/src/Parser.zig:3590:19: 0x107186309 in Parser.Result.usualArithmeticConversion (arocc)
assert(b_unsigned and (@enumToInt(b_promoted.specifier) >= @enumToInt(a_promoted.specifier)));
^
/Users/ehaas/source/arocc/src/Parser.zig:3345:44: 0x10714e016 in Parser.Result.adjustTypes (arocc)
try a.usualArithmeticConversion(b, p);
^
/Users/ehaas/source/arocc/src/Parser.zig:4186:32: 0x1071bc2f0 in Parser.addExpr (arocc)
if (try lhs.adjustTypes(minus.?, &rhs, p, if (plus != null) .add else .sub)) {
^
/Users/ehaas/source/arocc/src/Parser.zig:4145:28: 0x1071bb794 in Parser.shiftExpr (arocc)
var lhs = try p.addExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4116:30: 0x1071bafb4 in Parser.compExpr (arocc)
var lhs = try p.shiftExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4091:29: 0x1071ba534 in Parser.eqExpr (arocc)
var lhs = try p.compExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4071:27: 0x1071b8614 in Parser.andExpr (arocc)
var lhs = try p.eqExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4051:28: 0x1071b1404 in Parser.xorExpr (arocc)
var lhs = try p.andExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4031:28: 0x1071a4b74 in Parser.orExpr (arocc)
var lhs = try p.xorExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:4009:27: 0x1071853c4 in Parser.landExpr (arocc)
var lhs = try p.orExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:3987:29: 0x10714d424 in Parser.lorExpr (arocc)
var lhs = try p.landExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:3949:29: 0x10711bb94 in Parser.condExpr (arocc)
var cond = try p.lorExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:3804:29: 0x10716b2a4 in Parser.assignExpr (arocc)
var lhs = try p.condExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:2126:35: 0x107129bc7 in Parser.initializer (arocc)
var res = try p.assignExpr();
^
/Users/ehaas/source/arocc/src/Parser.zig:1197:47: 0x1070cf447 in Parser.initDeclarator (arocc)
var init_list_expr = try p.initializer(init_d.d.ty);
^
/Users/ehaas/source/arocc/src/Parser.zig:576:39: 0x10709c5b9 in Parser.decl (arocc)
var init_d = (try p.initDeclarator(&decl_spec)) orelse {
^
/Users/ehaas/source/arocc/src/Parser.zig:461:19: 0x1070883ba in Parser.parse (arocc)
if (p.decl() catch |er| switch (er) {
^
/Users/ehaas/source/arocc/src/main.zig:257:32: 0x10706ac69 in processSource (arocc)
var tree = try Parser.parse(&pp);
^
/Users/ehaas/source/arocc/src/main.zig:223:22: 0x10705f211 in handleArgs (arocc)
processSource(comp, source, builtin, user_macros) catch |e| switch (e) {
^
/Users/ehaas/source/arocc/src/main.zig:41:15: 0x10705c39d in main (arocc)
handleArgs(&comp, args) catch |err| switch (err) {
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:524:29: 0x1070db5fc in std.start.callMain (arocc)
return root.main();
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:469:12: 0x107060317 in std.start.callMainWithArgs (arocc)
return @call(.{ .modifier = .always_inline }, callMain, .{});
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:434:12: 0x107060255 in std.start.main (arocc)
return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
^
???:?:?: 0x7fff204ecf3c in ??? (???)
???:?:?: 0x1 in ??? (???)
[1] 89704 abort ./zig-out/bin/arocc test/cases/crash.c
When feeding in a source file containing an enum, it seems to be treated as an int var?
With the following input:
int IntTest;
enum EnumTest
{
Begin,
End,
};
struct StructTest{
int MemberTest;
};
You get the following output:
var: 'int'
name: IntTest
var: 'int'
name: EnumTest
var: 'int'
name: StructTest
int arr[1];
arr[arr] = 2;
thread 15122780 panic: access of inactive union field
/Users/ehaas/source/arocc/src/Type.zig:759:43: 0x1039f2148 in Type.eql (arocc)
if (a.data.array.len != b.data.array.len) return false;
^
/Users/ehaas/source/arocc/src/Parser.zig:1232:35: 0x103996a53 in Parser.initDeclarator (arocc)
.decl => |s| if (!s.ty.eql(init_d.d.ty, true)) {
^
/Users/ehaas/source/arocc/src/Parser.zig:576:39: 0x1039635b9 in Parser.decl (arocc)
var init_d = (try p.initDeclarator(&decl_spec)) orelse {
^
/Users/ehaas/source/arocc/src/Parser.zig:461:19: 0x10394f3ba in Parser.parse (arocc)
if (p.decl() catch |er| switch (er) {
^
/Users/ehaas/source/arocc/src/main.zig:257:32: 0x103931c69 in processSource (arocc)
var tree = try Parser.parse(&pp);
^
/Users/ehaas/source/arocc/src/main.zig:223:22: 0x103926211 in handleArgs (arocc)
processSource(comp, source, builtin, user_macros) catch |e| switch (e) {
^
/Users/ehaas/source/arocc/src/main.zig:41:15: 0x10392339d in main (arocc)
handleArgs(&comp, args) catch |err| switch (err) {
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:524:29: 0x1039a25fc in std.start.callMain (arocc)
return root.main();
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:469:12: 0x103927317 in std.start.callMainWithArgs (arocc)
return @call(.{ .modifier = .always_inline }, callMain, .{});
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:434:12: 0x103927255 in std.start.main (arocc)
return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
^
???:?:?: 0x7fff204ecf3c in ??? (???)
???:?:?: 0x1 in ??? (???)
[1] 92724 abort ./zig-out/bin/arocc test/cases/crash.c
#define CREATE_CALL F
#define F(x) (x + 2)
CREATE_CALL(2)
#define EXPECTED_TOKENS (2 + 2)
This test case fails as arocc preprocessor generates F ( 2 )
Following code produces a segfault:
struct A b = {};
lldb backtrace:
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
* frame #0: 0x00007fff2051404d libsystem_platform.dylib`_platform_memmove$VARIANT$Haswell + 77
frame #1: 0x0000000100117b0c arocc`Parser.convertInitList(p=0x00007ffeefbfcd40, il=InitList @ 0x00007ffeefbfb6a8, init_ty=Type @ 0x00007ffeefbfbfd0) at Parser.zig:2556:45
frame #2: 0x00000001000cf32d arocc`Parser.initializer(p=0x00007ffeefbfcd40, init_ty=Type @ 0x00007ffeefbfbfd0) at Parser.zig:2150:38
frame #3: 0x0000000100074a78 arocc`Parser.initDeclarator(p=0x00007ffeefbfcd40, decl_spec=0x00007ffeefbfc8c8) at Parser.zig:1206:47
frame #4: 0x000000010004349a arocc`Parser.decl(p=0x00007ffeefbfcd40) at Parser.zig:585:39
frame #5: 0x000000010002f22b arocc`Parser.parse(pp=0x00007ffeefbfd5b8) at Parser.zig:462:19
int foo(void) {
return 1U < 2U;
}
thread 10956225 panic: access of inactive union field
/Users/ehaas/source/arocc/src/Parser.zig:2674:68: 0x10d0b9670 in Parser.Result.compare (arocc)
.signed => |val| return std.math.compare(val, op, b.val.signed),
^
/Users/ehaas/source/arocc/src/Parser.zig:3115:47: 0x10d125d6e in Parser.compExpr (arocc)
.less_than_expr => lhs.compare(.lt, rhs),
^
/Users/ehaas/source/arocc/src/Parser.zig:3077:29: 0x10d123f54 in Parser.eqExpr (arocc)
var lhs = try p.compExpr();
...
#define a##
a
We already check this for function-like macros
thread 18232445 panic: integer overflow
/Users/ehaas/source/zig/build/lib/zig/std/array_list.zig:405:51: 0x10e992850 in std.array_list.ArrayListAligned(Tree.Token,null).pop (arocc)
const val = self.items[self.items.len - 1];
^
/Users/ehaas/source/arocc/src/Preprocessor.zig:553:32: 0x10e963a45 in Preprocessor.expandObjMacro (arocc)
const lhs = buf.pop();
^
/Users/ehaas/source/arocc/src/Preprocessor.zig:976:54: 0x10e909b82 in Preprocessor.expandMacroExhaustive (arocc)
const res = try pp.expandObjMacro(macro);
^
/Users/ehaas/source/arocc/src/Preprocessor.zig:1011:33: 0x10e8d9c7c in Preprocessor.expandMacro (arocc)
try pp.expandMacroExhaustive(tokenizer, &buf, 0, 1, true);
^
/Users/ehaas/source/arocc/src/Preprocessor.zig:308:35: 0x10e8cf4ef in Preprocessor.preprocess (arocc)
try pp.expandMacro(&tokenizer, tok);
^
/Users/ehaas/source/arocc/src/main.zig:237:22: 0x10e8b4818 in processSource (arocc)
try pp.preprocess(source);
^
/Users/ehaas/source/arocc/src/main.zig:223:22: 0x10e8a9151 in handleArgs (arocc)
processSource(comp, source, builtin, user_macros) catch |e| switch (e) {
^
/Users/ehaas/source/arocc/src/main.zig:41:15: 0x10e8a62dd in main (arocc)
handleArgs(&comp, args) catch |err| switch (err) {
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:524:29: 0x10e923c4c in std.start.callMain (arocc)
return root.main();
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:469:12: 0x10e8aa257 in std.start.callMainWithArgs (arocc)
return @call(.{ .modifier = .always_inline }, callMain, .{});
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:434:12: 0x10e8aa195 in std.start.main (arocc)
return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
^
???:?:?: 0x7fff204ecf3c in ??? (???)
???:?:?: 0x1 in ??? (???)
[1] 82004 abort ~/source/arocc/zig-out/bin/arocc test.c
Smallest example I could come up with:
#define f(a) a
#define g f
#define h g( f
g(1) h 5)
thread 18378322 panic: index out of bounds
/Users/ehaas/source/arocc/src/Preprocessor.zig:802:25: 0x10467dd1b in Preprocessor.nextBufToken (arocc)
return buf.items[start_idx.*];
^
/Users/ehaas/source/arocc/src/Preprocessor.zig:845:35: 0x10464b76d in Preprocessor.collectMacroFuncArguments (arocc)
var tok = try nextBufToken(pp, tokenizer, buf, start_idx, end_idx, extend_buf);
^
/Users/ehaas/source/arocc/src/Preprocessor.zig:919:67: 0x1045f3902 in Preprocessor.expandMacroExhaustive (arocc)
const args = (try pp.collectMacroFuncArguments(tokenizer, buf, ¯o_scan_idx, &moving_end_idx, extend_buf, macro.is_builtin)) orelse {
^
/Users/ehaas/source/arocc/src/Preprocessor.zig:1011:33: 0x1045c4c7c in Preprocessor.expandMacro (arocc)
try pp.expandMacroExhaustive(tokenizer, &buf, 0, 1, true);
^
/Users/ehaas/source/arocc/src/Preprocessor.zig:308:35: 0x1045ba4ef in Preprocessor.preprocess (arocc)
try pp.expandMacro(&tokenizer, tok);
^
/Users/ehaas/source/arocc/src/main.zig:237:22: 0x10459f818 in processSource (arocc)
try pp.preprocess(source);
^
/Users/ehaas/source/arocc/src/main.zig:223:22: 0x104594151 in handleArgs (arocc)
processSource(comp, source, builtin, user_macros) catch |e| switch (e) {
^
/Users/ehaas/source/arocc/src/main.zig:41:15: 0x1045912dd in main (arocc)
handleArgs(&comp, args) catch |err| switch (err) {
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:524:29: 0x10460ec4c in std.start.callMain (arocc)
return root.main();
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:469:12: 0x104595257 in std.start.callMainWithArgs (arocc)
return @call(.{ .modifier = .always_inline }, callMain, .{});
^
/Users/ehaas/source/zig/build/lib/zig/std/start.zig:434:12: 0x104595195 in std.start.main (arocc)
return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
^
???:?:?: 0x7fff204ecf3c in ??? (???)
???:?:?: 0x1 in ??? (???)
[1] 94782 abort ~/source/arocc/zig-out/bin/arocc test.c
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.