-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Closed
Labels
bugObserved behavior contradicts documented or intended behaviorObserved behavior contradicts documented or intended behaviormiscompilationThe compiler reports success but produces semantically incorrect code.The compiler reports success but produces semantically incorrect code.stage1The process of building from source via WebAssembly and the C backend.The process of building from source via WebAssembly and the C backend.
Milestone
Description
(Using nightly zig-linux-x86_64-0.5.0+b72f85819 from 2020-01-18.)
Behold, a maximally minimal (really!) zig runnable main.zig repro case, extracted from bigger project --- but first, skip down to the upshot / short description just below this 88-line snippet:
const std = @import("std");
pub fn main() !void {
var mem = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer mem.deinit();
const list = try listFromStr(&mem.allocator, "Hail Zig =)");
std.debug.warn("{s}", .{try listToStr(&mem.allocator, list)});
}
fn listFromStr(mem: *std.mem.Allocator, from: []const u8) !Expr {
var ret = Expr{ .FuncRef = @enumToInt(StdFunc.Nil) }; // all type defs at bottom of snippet
var i = from.len;
while (i > 0) {
i -= 1;
ret = Expr{
.Call = try enHeap(mem, ExprCall{
.Callee = Expr{ .FuncRef = @enumToInt(StdFunc.Cons) },
.Args = try std.mem.dupe(mem, Expr, &[_]Expr{ ret, Expr{ .NumInt = from[i] } }),
}),
};
}
return ret;
}
fn listToStr(mem: *std.mem.Allocator, expr: Expr) !?[]const u8 {
const maybenumlist = try maybeList(mem, expr);
return if (maybenumlist) |it| listToBytes(mem, it) else null;
}
fn maybeList(mem: *std.mem.Allocator, self: Expr) !?[]const Expr {
var list = try std.ArrayList(Expr).initCapacity(mem, 1024);
errdefer list.deinit();
var next = self;
while (true) {
switch (next) {
.FuncRef => |f| if (f == @enumToInt(StdFunc.Nil))
return list.toOwnedSlice(),
.Call => |c| if (c.Args.len == 2) switch (c.Callee) {
.FuncRef => |f| if (f == @enumToInt(StdFunc.Cons)) {
try list.append(c.Args[1]);
next = c.Args[0];
continue;
},
else => {},
},
else => {},
}
break;
}
list.deinit();
return null;
}
fn listToBytes(mem: *std.mem.Allocator, maybeNumList: []const Expr) !?[]const u8 {
var ok = false;
const bytes = try mem.alloc(u8, maybeNumList.len);
defer if (!ok) mem.free(bytes);
for (maybeNumList) |expr, i| switch (expr) {
.NumInt => |n| if (n < 0 or n > 255) return null else bytes[i] = @intCast(u8, n),
else => return null,
};
ok = true;
return bytes;
}
inline fn enHeap(mem: *std.mem.Allocator, it: var) !*@TypeOf(it) {
var ret = try mem.create(@TypeOf(it));
ret.* = it;
return ret;
}
const ExprCall = struct {
Callee: Expr,
Args: []Expr,
};
Expr = union(enum) {
NumInt: isize,
FuncRef: isize,
Call: *const ExprCall,
};
const StdFunc = enum(isize) {
Nil = 3,
Cons = 4,
};With the above, doing zig run main.zig:
Segmentation fault at address 0x1b
~/tmp/repros/segfault_2020jan18/main.zig:39:41: 0x22bfbe in maybeList (run)
.Call => |c| if (c.Args.len == 2) switch (c.Callee) {
^
Now to circumvent this (until fixed/explained =), from what I have found one has 2 options, either one by itself alone will already do the trick:
- either "manually inline" the already-auto-inlined
fn enHeapinto the callsite directly / by hand - or line 19: take out the expression assigned to
.Args =, put it between line 15 & 16 to be assigned to a new localconst tmpargs = ..., then use that local for the.Args =initializer
Both ways the program will work as expected, printing out the original input string from line 7. These "fixes" are mere re-arrangements / different placements without (AFAICT) any semantic differences. (So shouldn't have to dig around for such silly workarounds =)
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugObserved behavior contradicts documented or intended behaviorObserved behavior contradicts documented or intended behaviormiscompilationThe compiler reports success but produces semantically incorrect code.The compiler reports success but produces semantically incorrect code.stage1The process of building from source via WebAssembly and the C backend.The process of building from source via WebAssembly and the C backend.