Zig: Implicit cast from zero sized array ptr to slice crashes compiler

Created on 21 Dec 2018  路  4Comments  路  Source: ziglang/zig

test "" {
    var b = "";
    const c: []const u8 = &b;
}
zig/src/codegen.cpp:2816: LLVMOpaqueValue* ir_render_cast(CodeGen*, IrExecutable*, IrInstructionCast*): Assertion `expr_val' failed.
bug

All 4 comments

note that @sizeOf(&"") == 0. pointers to 0-len arrays are one of the special 0-bit types.

@thejoshwolfe Yea, that makes sense. Still, this implicit cast should work and give us something like ([*]u8)(undefined)[0..0]

{
    var b = "";
    const c: []const u8 = &b;
}
{ // (IR)
Entry_0:
    #1  | bool        | 1 | false
    #2  | [0]u8       | 1 | ""
    #3  | void        | - | var b = "" // comptime = false
    #4  | type        | 1 | u8
    #5  | (unknown)   | 1 | []const u8
    #6  | bool        | 1 | false
    #7  | (unknown)   | 1 | &b
    #8  | void        | - | const c: #5 = #7 // comptime = false
    #9  | void        | 0 | {}
    #10 | void        | 2 | {}
    #11 | (unknown)   | - | @addImplicitReturnType({})
    #12 | noreturn    | - | return {}
}
fn () { // (analyzed)
Entry_0:
    #3  | void        | - | var b = "" // comptime = false
    #7  | *[0]u8      | 1 | &b
    #8  | []const u8  | 1 | cast #7 to []const u8
    #9  | void        | - | const c: []const u8 = #8 // comptime = false
    #13 | noreturn    | - | return anyerror!void({})
}

Me and @kristate been grinding at this for at bit, but did not come up with a solution.
The test case in this issue tries to emit an implicit cast from *[0]u8 -> []const u8 at runtime but failes because cast_instruction->value is 0 bits and therefor ir_llvm_value(g, cast_instruction->value) returns null.
https://github.com/ziglang/zig/blob/f00adb47f5c2b70496dac1c48f03236271b9bd57/src/codegen.cpp#L2946-L2952

The compiler should do this cast at comptime because *[0]u8 is 0 bits and therefor the value is always know, even if stored in var.

Where to fix this is unclear. We might need to add to check if the pointer in ir_resolve_ptr_of_array_to_slice is 0 bits and do the magic there. However, it is not clear exactly what this pointer should point to at comptime, since the pointer passed in is only runtime known, so it points to no ConstExprValue.

Another thing that makes this unclear is this test case:

test "" {
    var a = "";
    var b: *[0]u8 = a;
    var c: []const u8 = b;
}

This seems related at first, but actually crashes for a completely different reason. We hit zig_unreachable() in src/ir.cpp:193
(I can't link to ir.cpp, since the file is too big. Dammit Github)

191:    switch (const_val->data.x_ptr.special) {
192:        case ConstPtrSpecialInvalid:
193:            zig_unreachable();
194:        case ConstPtrSpecialRef:

This happens because ir_get_deref returns ir_const(ira, source_instruction, child_type) (src/ir.cpp:11825):

11824:            case OnePossibleValueYes:
11825:                return ir_const(ira, source_instruction, child_type);
11826:            case OnePossibleValueNo:

This is wrong behavior for pointers, as this will give you a pointer that is ConstPtrSpecialInvalid which should never happen.

Idk if these two things are related in some greater design flaw with 0 bit pointers, or if they can be fixed independently. I hope this little debug comment will help the next person who tries to solve this, as I'm not sure where to go next with this.

Was this page helpful?
0 / 5 - 0 ratings