| /* |
| PERMUTE_ARGS: -O -inline |
| RUN_OUTPUT: |
| --- |
| getAndPrintS |
| --- |
| */ |
| |
| import core.stdc.stdio; |
| import core.exception : AssertError; |
| |
| /*****************************************/ |
| |
| // noreturn is inferred for functions that always throw |
| // The code must not strip the destructor when calling a noreturn function |
| |
| struct WithDtor |
| { |
| __gshared int destroyed; |
| |
| int num; |
| |
| int acceptNoreturn(int a, int b, int c) |
| { |
| puts("unreachable"); |
| return num + a + b + c; |
| } |
| |
| ~this() |
| { |
| destroyed += num; |
| } |
| } |
| |
| noreturn doesThrow() |
| { |
| WithDtor wd = WithDtor(1); |
| throw new Exception(""); |
| } |
| |
| noreturn callDoesThrow() |
| { |
| WithDtor wd = WithDtor(2); |
| doesThrow(); |
| } |
| |
| |
| void testDtors() |
| { |
| try |
| { |
| callDoesThrow(); |
| assert(0); |
| } catch (Exception e) {} |
| |
| assert(WithDtor.destroyed == 3); |
| } |
| |
| /*****************************************************************************/ |
| |
| /// Verifies that `func` throws a `Throwable` with `message` at `line` |
| void testAssertFailure(size_t expLine, string expMsg, void function() func, size_t callLine = __LINE__) |
| { |
| void enforce(bool check, string error) |
| { |
| if (!check) |
| throw new AssertError(error, __FILE__, callLine); |
| } |
| |
| bool caught; |
| try |
| { |
| func(); |
| } |
| catch (Throwable t) |
| { |
| // Save members because t might be overwritten by an Assertion failure below |
| string actFile = t.file; |
| size_t actLine = t.line; |
| string actMsg = t.msg; |
| caught = true; |
| |
| scope (failure) |
| { |
| printf("\nfile = \"%.*s\"\nline = %zu\nmsg = \"%.*s\"\n\n", |
| cast(int) actFile.length, actFile.ptr, |
| actLine, |
| cast(int) actMsg.length, actMsg.ptr |
| ); |
| fflush(stdout); |
| } |
| |
| enforce(actFile == __FILE__, "Wrong file"); |
| enforce(actLine == expLine, "Wrong line"); |
| enforce(actMsg == expMsg, "Wrong message"); |
| } |
| |
| enforce(caught, "No Throwable was thrown!"); |
| } |
| |
| void testAccess() |
| { |
| enum msg = "Accessed expression of type `noreturn`"; |
| |
| // FIXME: Another assertion failure in the backend trying to generate noreturn.sizeof = 0 byte assignment |
| version (FIXME) |
| testAssertFailure(__LINE__ + 3, msg, function noreturn() |
| { |
| noreturn a; |
| noreturn b = a; |
| }); |
| |
| if (false) // read does not assert! |
| testAssertFailure(__LINE__ + 3, msg, function noreturn() |
| { |
| noreturn a; |
| int b = a; |
| assert(false, "Unreachable!"); // Statement above not detected as noreturn |
| }); |
| |
| testAssertFailure(__LINE__ + 2, msg, function noreturn() |
| { |
| cast(noreturn) 1; |
| }); |
| |
| version (FIXME) |
| testAssertFailure(__LINE__ + 3, msg, function noreturn() |
| { |
| noreturn a; |
| noreturn b = cast(noreturn) 1; |
| }); |
| |
| if (false) // Read does not assert |
| testAssertFailure(__LINE__ + 3, msg, function noreturn() |
| { |
| noreturn a; |
| return a; |
| }); |
| |
| if (false) // Read does not assert |
| testAssertFailure(__LINE__ + 4, msg, function noreturn() |
| { |
| static void foo(noreturn) {} |
| noreturn a; |
| foo(a); |
| assert(false, "Unreachable!"); // Ditto |
| }); |
| } |
| |
| /*****************************************/ |
| |
| void testFuncCall() |
| { |
| enum msg = "Called abort()"; |
| enum line = __LINE__ + 1; |
| static noreturn abort() { assert(0, msg); } |
| |
| // Canaries to check for side effects |
| __gshared int countLeft, countRight; |
| |
| scope (failure) printf("countLeft = %d\ncountRight = %d\n", countLeft, countRight); |
| |
| |
| // D function arguments are evaluated left to right |
| testAssertFailure(line, msg, function() |
| { |
| static void acceptNoreturnD(int, int, int) { puts("unreachable"); } |
| |
| acceptNoreturnD(countLeft++, abort(), countRight++); |
| }); |
| |
| assert(countLeft == 1); |
| assert(countRight == 0); |
| |
| // // C function arguments are still evaluated left to right |
| // // Despite them being push in reverse order |
| testAssertFailure(line, msg, function() |
| { |
| static extern(C) void acceptNoreturnC(int, int, int) { puts("unreachable"); } |
| |
| acceptNoreturnC(countLeft++, abort(), countRight++); |
| |
| assert(false); |
| }); |
| |
| assert(countLeft == 2); |
| assert(countRight == 0); |
| |
| WithDtor.destroyed = 0; |
| |
| testAssertFailure(__LINE__ + 2, "Error", function() |
| { |
| static WithDtor getS() { assert(false, "Error"); } |
| |
| getS().acceptNoreturn(countLeft++, abort(), countRight++); |
| }); |
| |
| assert(countLeft == 2); // No changes |
| assert(countRight == 0); |
| assert(WithDtor.destroyed == 0); // No temporary to destruct |
| |
| testAssertFailure(line, msg, function() |
| { |
| static WithDtor getAndPrintS() { puts("getAndPrintS"); return WithDtor(1); } |
| |
| getAndPrintS().acceptNoreturn(countLeft++, abort(), countRight++); |
| }); |
| |
| assert(countLeft == 3); |
| assert(countRight == 0); |
| assert(WithDtor.destroyed == 1); |
| } |
| |
| int main() |
| { |
| testDtors(); |
| testAccess(); |
| testFuncCall(); |
| return 0; |
| } |