odin-lang / Odin

Odin Programming Language
https://odin-lang.org
BSD 3-Clause "New" or "Revised" License
6.55k stars 570 forks source link

Compiler crash (divide by zero) while trying to report integer overflow #266

Closed hasenj closed 5 years ago

hasenj commented 5 years ago

Look at the following procedure. It might be buggy in there somewhat (due to signed or unsigned numbers), but the compiler does not barf on it:

to_lower :: proc(c: byte) -> byte {
    if (c >= 'A' && c <='Z') {
        return c + 'A' - 'a';
    } else {
        return c;
    }
}

Now if we just take the line return c + 'A' - 'a' and put parenthesis here:

        return c + ('A' - 'a');

The compiler crashes with a division by zero error.

I tried hopping into the place where the error occurs in src/big_int.cpp

@@ -765,7 +765,10 @@ void bi__divWW(u64 u1, u64 u0, u64 y, u64 *q, u64 *r) {
        u64 un10 = u0 << s;
        u64 un1  = un10 >> 32ull;
        u64 un0  = un10 & M;
-       u64 q1   = un32 / vn1;
+       u64 q1   = 0;
+       if (vn1 != 0) {
+        q1 = un32 / vn1;
+    }
        u64 rhat = un32 - (q1*vn1);

        while ((q1 >= B) || ((q1*vn0) > (B*rhat+un1))) {
@@ -777,7 +780,10 @@ void bi__divWW(u64 u1, u64 u0, u64 y, u64 *q, u64 *r) {
        }

        u64 un21 = (un32*B) + un1 - (q1*y);
-       u64 q0 = un21 / vn1;
+       u64 q0 = 0;
+       if (vn1 != 0) {
+        q0 = un21 / vn1;
+    }
        rhat = un21 - (q0*vn1);

        while ((q0 >= B) || ((q0*vn0) > (B*rhat+un0))) {

And now the compiler error is:

src/big_int.cpp(1371): Assert Failure: `digit < 16` 
Illegal instruction: 4
hasenj commented 5 years ago

Here's a backtrace (using lldb)

(lldb) run build main.odin
Process 70268 launched: '/Users/hasenj/code/odin/odin' (x86_64)
src/big_int.cpp(1371): Assert Failure: `digit < 16` 
Process 70268 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
    frame #0: 0x000000010003dde9 odin`digit_to_char(digit=' ') at big_int.cpp:1371
1368    
1369    
1370    char digit_to_char(u8 digit) {
-> 1371     GB_ASSERT(digit < 16);
1372        if (digit <= 9) {
1373            return digit + '0';
1374        } else if (digit <= 15) {
Target 0: (odin) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
* frame #0: 0x000000010003dde9 odin`digit_to_char(digit=' ') at big_int.cpp:1371
    frame #1: 0x000000010003e42d odin`big_int_to_string(allocator=gbAllocator @ 0x00007ffeefbf44e0, x=0x00007ffeefbf50c0, base=10) at big_int.cpp:1410
    frame #2: 0x0000000100098265 odin`check_is_expressible(c=0x00007ffeefbf6820, o=0x00007ffeefbf50a8, type=0x00000001001cba40) at check_expr.cpp:1400
    frame #3: 0x0000000100094d80 odin`convert_to_typed(c=0x00007ffeefbf6820, operand=0x00007ffeefbf50a8, target_type=0x00000001001cba40) at check_expr.cpp:2433
    frame #4: 0x000000010009dde7 odin`check_binary_expr(c=0x00007ffeefbf6820, x=0x00007ffeefbf67d8, node=0x0000000101215930, use_lhs_as_type_hint=false) at check_expr.cpp:2174
    frame #5: 0x00000001000b9023 odin`check_expr_base_internal(c=0x00007ffeefbf6820, o=0x00007ffeefbf67d8, node=0x0000000101215930, type_hint=0x00000001001cba40) at check_expr.cpp:5922
    frame #6: 0x00000001000a12e0 odin`check_expr_base(c=0x00007ffeefbf6820, o=0x00007ffeefbf67d8, node=0x0000000101215930, type_hint=0x00000001001cba40) at check_expr.cpp:6152
    frame #7: 0x00000001000a8e7d odin`check_unpack_arguments(ctx=0x00007ffeefbfcab0, lhs=0x00000001007773d0, lhs_count=1, operands=0x00007ffeefbf7e10, rhs=0x0000000101215af8, allow_ok=true) at check_expr.cpp:3907
    frame #8: 0x00000001000db701 odin`check_stmt_internal(ctx=0x00007ffeefbfcab0, node=0x0000000101215a70, flags=32) at check_stmt.cpp:1232
    frame #9: 0x00000001000d8781 odin`check_stmt(ctx=0x00007ffeefbfcab0, node=0x0000000101215a70, flags=32) at check_stmt.cpp:364
    frame #10: 0x00000001000d7d49 odin`check_stmt_list(ctx=0x00007ffeefbfcab0, stmts=0x0000000101215bf8, flags=32) at check_stmt.cpp:42
    frame #11: 0x00000001000daf42 odin`check_stmt_internal(ctx=0x00007ffeefbfcab0, node=0x0000000101215bb0, flags=32) at check_stmt.cpp:1165
    frame #12: 0x00000001000d8781 odin`check_stmt(ctx=0x00007ffeefbfcab0, node=0x0000000101215bb0, flags=32) at check_stmt.cpp:364
    frame #13: 0x00000001000db0c1 odin`check_stmt_internal(ctx=0x00007ffeefbfcab0, node=0x00000001012160b0, flags=32) at check_stmt.cpp:1182
    frame #14: 0x00000001000d8781 odin`check_stmt(ctx=0x00007ffeefbfcab0, node=0x00000001012160b0, flags=32) at check_stmt.cpp:364
    frame #15: 0x00000001000d7d49 odin`check_stmt_list(ctx=0x00007ffeefbfcab0, stmts=0x0000000101216238, flags=32) at check_stmt.cpp:42
    frame #16: 0x00000001000d7899 odin`check_proc_body(ctx_=0x00007ffeefbfcd78, token=Token @ 0x00007ffeefbfcbe0, decl=0x0000000100778200, type=0x0000000101f90bc0, body=0x00000001012161f0) at check_decl.cpp:1027
    frame #17: 0x00000001000efaa5 odin`check_proc_info(c=0x00007ffeefbfdd90, pi=ProcInfo @ 0x00007ffeefbfce60) at checker.cpp:3219
    frame #18: 0x00000001000f0349 odin`check_parsed_files(c=0x00007ffeefbfdd90) at checker.cpp:3301
    frame #19: 0x000000010014144f odin`main(arg_count=3, arg_ptr=0x00007ffeefbfea68) at main.cpp:833
    frame #20: 0x00007fff73750015 libdyld.dylib`start + 1
    frame #21: 0x00007fff73750015 libdyld.dylib`start + 1
hasenj commented 5 years ago

Note: the logic is actually wrong, it should be:

return c + ('a' - 'A');

In which case the compiler does not crash.

But this bug report is about the compiler crashing on: ('A' - 'a')

In fact, this program exhibits the problem:

package main;

import "core:fmt";

main :: proc() {
    b : byte = 'A' - 'a';
    fmt.println(b);
}
hasenj commented 5 years ago

This line also triggers the crash:

b : byte = 510;

It seems to occur while the compiler is attempting to build an error message.

gingerBill commented 5 years ago

I am not able to replicate this on Windows. What operating system are you on?

lunaticLipid commented 5 years ago

@gingerBill It's the same thing as here: https://github.com/odin-lang/Odin/issues/248#issuecomment-417938740 You should be able to replicate it if you remove those ifdefs.

hasenj commented 5 years ago

macOS

Breush commented 5 years ago

@hasenj Fact is @gingerBill just merged something on master that could fix this issue. Can you try again?

hasenj commented 5 years ago

@Breush @gingerBill yes that fixes it!