From 90ab4f07b9dbaa3c651e17a7d3e162e613e22179 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 27 Jul 2020 14:58:02 +0200 Subject: [PATCH] Corrected ptr->bool conversion. Avoid checking function body if function is invalid. Switch defer test cases. --- resources/testfragments/super_simple.c3 | 182 ++---------------- resources/tests/arithmetics.c3 | 38 ---- resources/tests/structs_ok.c3 | 0 src/compiler/llvm_codegen_expr.c | 2 +- src/compiler/sema_stmts.c | 3 +- .../errors/illegal_use_of_failable.c3 | 14 ++ test/test_suite/expressions/elvis.c3t | 45 +++++ .../statements/conditional_return.c3 | 12 ++ .../statements/defer_break_switch.c3t | 49 +++++ .../statements/defer_next_switch.c3t | 49 +++++ .../statements/for_with_extra_declarations.c3 | 17 ++ .../statements/labelled_continue_for.c3t | 35 ++++ test/test_suite/statements/return_stmt.c3 | 11 ++ test/test_suite/statements/return_switch.c3t | 36 ++++ .../statements/return_with_other_at_end.c3 | 7 + test/test_suite/types/enum_inference.c3 | 43 +++++ test/test_suite/types/recursive_typedef.c3 | 9 + test/test_suite/types/typedefs.c3 | 6 - 18 files changed, 346 insertions(+), 212 deletions(-) delete mode 100644 resources/tests/arithmetics.c3 delete mode 100644 resources/tests/structs_ok.c3 create mode 100644 test/test_suite/errors/illegal_use_of_failable.c3 create mode 100644 test/test_suite/expressions/elvis.c3t create mode 100644 test/test_suite/statements/conditional_return.c3 create mode 100644 test/test_suite/statements/defer_break_switch.c3t create mode 100644 test/test_suite/statements/defer_next_switch.c3t create mode 100644 test/test_suite/statements/for_with_extra_declarations.c3 create mode 100644 test/test_suite/statements/labelled_continue_for.c3t create mode 100644 test/test_suite/statements/return_stmt.c3 create mode 100644 test/test_suite/statements/return_switch.c3t create mode 100644 test/test_suite/statements/return_with_other_at_end.c3 create mode 100644 test/test_suite/types/enum_inference.c3 create mode 100644 test/test_suite/types/recursive_typedef.c3 diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index 0464e0564..2a0073f40 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -2,6 +2,7 @@ module bar; import baz; import gen; + typedef int as Bob; /* macro namespaceFor(ns; void(int i) $body) @@ -103,21 +104,6 @@ enum EnumTestOverflowAfter -enum Inf -{ - A, - B, - C = 10000 -} - -enum Inf2 : byte -{ - A, - B, - C = 129, -} - -typedef Inf as BooInf; struct TestStruct @@ -201,8 +187,8 @@ func void testUnion() SimpleUnion y = s = { 2 }; double yval = SimpleUnion{ f = 3.333 }.f; - printf("Union y: %d\n" as y.a); - printf("Union simple: %f\n" as yval); + printf("Union y: %d\n", y.a); + printf("Union simple: %f\n", yval); } func TestStruct2 structTest(int i) @@ -216,31 +202,6 @@ func TestStruct2 structTest(int i) return bar2; } -func void enumInferenceTest() -{ - Inf x = Inf.A; - x = BooInf.B; - x = A; - int x1 = 0; - bool y = x1 == x1; - Inf2 z = C; - if (z == Inf2.A) return; - if (z == 1) return; - z = 2; - switch (z) - { - case Inf2.A: - x1++; - return; - case B: - return; - case 111: - x1 += 1; - return; - default: - return; - } -} func void switchTest() { @@ -288,78 +249,8 @@ func int! borok() return 1; }*/ -func void testNoReturn() -{ - int i = 0; - i = -i; -} -func int testReturn() -{ - int i = 0; - return i; -} -func int testReturnWithOtherAtEnd() -{ - int i = 0; - return i; - if (i == 10) i++; - i = i + 2; -} - -func int testReturnWithConditional() -{ - int i = 0; - if (i > 0) - { - return 1; - } - else - { - return 2; - } -} -/* -func int testReturnWithCondThrow() throws Error -{ - int i = 0; - if (i > 0) - { - throw Error.NO_SUCH_FILE; - } - else - { - throw Error.NO_SUCH_FILE; - } -} -*/ -func int testReturnSwitch() -{ - int i = 0; - switch (i) - { - case 0: - case 3: - case 1: - return 2; - case 2: - return 3; - default: - return 4; - } -} - -/* -func int barok() throws Error as OtherError -{ - if (true) - { - throw Error.NO_SUCH_FILE; - } - return 100; -} -*/ struct SimpleStruct { @@ -373,11 +264,11 @@ struct SimpleStruct func void testSimpleStruct(int x) { SimpleStruct snoinit; - SimpleStruct sinit = { b = 1 as d = 3.0, z1 = 1 }; + SimpleStruct sinit = { b = 1, d = 3.0, z1 = 1 }; sinit.a = 1; sinit.b = 2; printf("Testing:\n"); - printf("a = %d, b = %d (2), c = %f, d = %f (3.0), z1 = %d (1), z2 = %d\n", sinit.a, sinit.b, sinit.c, sinit.d, cast(sinit.z1 as int) as cast(sinit.z2 as int)); + printf("a = %d, b = %d (2), c = %f, d = %f (3.0), z1 = %d (1), z2 = %d\n", sinit.a, sinit.b, sinit.c, sinit.d, cast(sinit.z1 as int), cast(sinit.z2 as int)); if (sinit.a != 1) printf("ERROR a\n"); if (sinit.b != 2) printf("ERROR b\n"); if (sinit.d != 3.0) printf("ERROR d\n"); @@ -388,7 +279,7 @@ func void testSimpleStruct(int x) snoinit.c = 2.0; snoinit.z1 = 1; snoinit.z2 = 2; - printf("b = %d (1) as d = %f (3.0) as z1 = %d (1)\n", snoinit.b, snoinit.d, snoinit.z1); + printf("b = %d (1), d = %f (3.0), z1 = %d (1)\n", snoinit.b, snoinit.d, snoinit.z1); if (snoinit.b != 1) printf("ERROR b\n"); if (snoinit.d != 3.0) printf("ERROR d\n"); if (snoinit.z1 != 1) printf("ERROR z1\n"); @@ -506,19 +397,6 @@ func int boba(int y, int j) //Test3 xc = { eo = 1, t.a = 1 }; // throw Error.NO_SUCH_FILE; - for (int i = 0; i < 10; i++) - { - - } - for (int i = 0, int foo = 0; i < 10; i++) - { - - } - - for (int i = 0, j = 1; i < 10; i++, j++) - { - - } Test2 bar; bar.b = 1; @@ -563,10 +441,7 @@ func int test(int x = 100) return y; } -func int* elvis(int *x, int *y) -{ - return x ?: y; -} + func int test3() { @@ -749,22 +624,7 @@ func int! testThrow1() { return TestErr1!; } -/* -func void syntaxErrors() -{ - int! i = 0; - while (i + 1) {} - if (i + 1) {} - for (int x = i;;) {} - for (int x = 0; x < i + 1;) {} - for (int x = 0; x < 10; x += i + 1) {} - switch (i + 1) - { - default: - i + 1; - } -} -*/ + func int frob(int i) { @@ -830,7 +690,7 @@ func void! testAllErrors() printf("Fex: %d\n", fex.x); error test33 = fex; FooErr fex2 = cast(test33 as FooErr); - printf("Fex2: %d\n" as fex.x); + printf("Fex2: %d\n", fex.x); TestErr1 eok = TestErr1{}; error e = eok; catch (err = x) @@ -1070,22 +930,7 @@ func void testArray() } } -func void testBreak1() -{ - for FOO: (int i = 0; i < 10; i++) - { - for (int j = 0; j < 10; j++) - { - for BAR: (int k = 0; k < 10; k++) - { - printf("%d %d %d\n", i, j, k); - continue FOO; - } - printf("ERROR\n"); - } - printf("ERROR\n"); - } -} + func void testDefer() { printf("1 == %d\n", testReturnDefer()); @@ -1372,8 +1217,13 @@ public func int! decode(char[] infile, byte[] out) */ func int main(int x) { + + int[3] feok2 = { 1, 8, 100}; + int[] feok = &feok2; + printf("Feok: %d\n", feok[0]); + printf("Feok: %d\n", feok[1]); + printf("Len: %d", feok.len); baz::runBaz(); - testBreak(); show(); testMacros(); testTypeof(); diff --git a/resources/tests/arithmetics.c3 b/resources/tests/arithmetics.c3 deleted file mode 100644 index 97cf11bdc..000000000 --- a/resources/tests/arithmetics.c3 +++ /dev/null @@ -1,38 +0,0 @@ -module arithmetics; - -func void testAdd(int a, int b) -{ - a = a + b; - a = a +% b; - a +%= b; - a += b; - a += 1; - a +%= 1; -} - -func void testSub(int a, int b) -{ - a = a - b; - a = a -% b; - a -%= b; - a -= b; - a -%= 1; - a -= 1; -} - -func void testMult(int a, int b) -{ - a = a * b; - a = a *% b; - a *%= b; - /*a *= b; - a *%= 1; - a *= 1;*/ -} - -func void testDiv(int a, int b) -{ - a = a / b; - a /= b; - a /= 1; -} \ No newline at end of file diff --git a/resources/tests/structs_ok.c3 b/resources/tests/structs_ok.c3 deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 4e39e862c..79512c571 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -294,7 +294,7 @@ LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMV case CAST_EUBOOL: return LLVMBuildICmp(context->builder, LLVMIntNE, value, gencontext_emit_no_error_union(context), "eubool"); case CAST_PTRBOOL: - return LLVMBuildICmp(context->builder, LLVMIntNE, value, LLVMConstPointerNull(llvm_type(from_type->canonical)), "ptrbool"); + return LLVMBuildIsNotNull(context->builder, value, "ptrbool"); case CAST_BOOLINT: return LLVMBuildTrunc(context->builder, value, llvm_type(to_type), "boolsi"); case CAST_FPBOOL: diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 62eac869f..ed2d80761 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -333,7 +333,7 @@ static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement) if (decl->var.unwrap && !decl->var.init_expr->failable) { SEMA_ERROR(decl->var.init_expr, "A failable expression was expected here."); - return false; + return decl_poison(decl); } } return true; @@ -1316,6 +1316,7 @@ bool sema_analyse_statement(Context *context, Ast *statement) bool sema_analyse_function_body(Context *context, Decl *func) { + if (!decl_ok(func)) return false; FunctionSignature *signature = &func->func.function_signature; context->active_function_for_analysis = func; context->rtype = signature->rtype->type; diff --git a/test/test_suite/errors/illegal_use_of_failable.c3 b/test/test_suite/errors/illegal_use_of_failable.c3 new file mode 100644 index 000000000..dc5d13277 --- /dev/null +++ b/test/test_suite/errors/illegal_use_of_failable.c3 @@ -0,0 +1,14 @@ +func void syntaxErrors() +{ + int! i = 0; + while (i + 1) {} // #error: 'int!' cannot be converted into 'bool' + if (i + 1) {} // #error: 'int!' cannot be converted into 'bool' + for (int x = i;;) {} // #error: 'int!' cannot be implicitly cast to 'int'. + for (int x = 0; x < i + 1;) {} // #error: 'bool!' cannot be implicitly cast to 'bool' + for (int x = 0; x < 10; x += i + 1) {} // #error: 'int!' cannot be implicitly cast to 'int' + switch (i + 1) // #error: 'int!' cannot be converted into 'int' + { + default: + i + 1; + } +} diff --git a/test/test_suite/expressions/elvis.c3t b/test/test_suite/expressions/elvis.c3t new file mode 100644 index 000000000..31d6e1720 --- /dev/null +++ b/test/test_suite/expressions/elvis.c3t @@ -0,0 +1,45 @@ +func int* elvis(int *x, int *y) +{ + return x ?: y; +} + +func int* elvis2(int *x, int *y) +{ + return x ?: (y ?: x); +} + +// #expect: elvis.ll + + store i32* %0, i32** %x + store i32* %1, i32** %y + %2 = load i32*, i32** %x + %ptrbool = icmp ne i32* %2, null + br i1 %ptrbool, label %cond.phi, label %cond.rhs +cond.rhs: + %3 = load i32*, i32** %y + br label %cond.phi +cond.phi: + %val = phi i32* [ %2, %entry ], [ %3, %cond.rhs ] + ret i32* %val + + store i32* %0, i32** %x + store i32* %1, i32** %y + %2 = load i32*, i32** %x + %ptrbool = icmp ne i32* %2, null + br i1 %ptrbool, label %cond.phi3, label %cond.rhs +cond.rhs: + %3 = load i32*, i32** %y + %ptrbool1 = icmp ne i32* %3, null + br i1 %ptrbool1, label %cond.phi, label %cond.rhs2 + +cond.rhs2: + %4 = load i32*, i32** %x + br label %cond.phi + +cond.phi: + %val = phi i32* [ %3, %cond.rhs ], [ %4, %cond.rhs2 ] + br label %cond.phi3 + +cond.phi3: + %val4 = phi i32* [ %2, %entry ], [ %val, %cond.phi ] + ret i32* %val4 diff --git a/test/test_suite/statements/conditional_return.c3 b/test/test_suite/statements/conditional_return.c3 new file mode 100644 index 000000000..5ca9aeeb6 --- /dev/null +++ b/test/test_suite/statements/conditional_return.c3 @@ -0,0 +1,12 @@ +func int testReturnWithConditional() +{ + int i = 0; + if (i > 0) + { + return 1; + } + else + { + return 2; + } +} \ No newline at end of file diff --git a/test/test_suite/statements/defer_break_switch.c3t b/test/test_suite/statements/defer_break_switch.c3t new file mode 100644 index 000000000..2eeec1f65 --- /dev/null +++ b/test/test_suite/statements/defer_break_switch.c3t @@ -0,0 +1,49 @@ + +func void test1() +{} + +func void test2() +{} + +func void test(int i) +{ + bool b = true; + switch (i) + { + case 1: + defer test2(); + if (b) break; + test1(); + case 2: + test1(); + } +} + +// #expect: defer_break_switch.ll + +switch.case: + %4 = load i1, i1* %b + br i1 %4, label %if.then, label %if.exit + +if.then: + call void @defer_break_switch.test2() + br label %exit + +exit: + br label %switch.exit + +if.exit: + call void @defer_break_switch.test1() + call void @defer_break_switch.test2() + br label %exit1 + +exit1: + br label %switch.exit + +switch.case2: + call void @defer_break_switch.test1() + br label %switch.exit + +switch.exit: + ret void +} diff --git a/test/test_suite/statements/defer_next_switch.c3t b/test/test_suite/statements/defer_next_switch.c3t new file mode 100644 index 000000000..d317c58b8 --- /dev/null +++ b/test/test_suite/statements/defer_next_switch.c3t @@ -0,0 +1,49 @@ + +func void test1() +{} + +func void test2() +{} + +func void test(int i) +{ + bool b = true; + switch (i) + { + case 1: + defer test2(); + if (b) next; + test1(); + case 2: + test1(); + } +} + +// #expect: defer_next_switch.ll + +switch.case: + %4 = load i1, i1* %b + br i1 %4, label %if.then, label %if.exit + +if.then: + call void @defer_next_switch.test2() + br label %exit + +exit: + br label %switch.case2 + +if.exit: + call void @defer_next_switch.test1() + call void @defer_next_switch.test2() + br label %exit1 + +exit1: + br label %switch.exit + +switch.case2: + call void @defer_next_switch.test1() + br label %switch.exit + +switch.exit: + ret void +} diff --git a/test/test_suite/statements/for_with_extra_declarations.c3 b/test/test_suite/statements/for_with_extra_declarations.c3 new file mode 100644 index 000000000..d1e2b493c --- /dev/null +++ b/test/test_suite/statements/for_with_extra_declarations.c3 @@ -0,0 +1,17 @@ +func void test() +{ + int j; + for (int i = 0; i < 10; i++) + { + + } + for (int i = 0, int foo = 0; i < 10; i++) + { + + } + + for (int i = 0, j = 1; i < 10; i++, j++) + { + + } +} \ No newline at end of file diff --git a/test/test_suite/statements/labelled_continue_for.c3t b/test/test_suite/statements/labelled_continue_for.c3t new file mode 100644 index 000000000..4b5e7b71b --- /dev/null +++ b/test/test_suite/statements/labelled_continue_for.c3t @@ -0,0 +1,35 @@ +func void errored() +{} + +func void test() {} + +func void testBreak() +{ + for FOO: (int i = 0; i < 10; i++) + { + for (int j = 0; j < 10; j++) + { + for BAR: (int k = 0; k < 10; k++) + { + test(); + continue FOO; + } + errored(); + } + errored(); + } +} + +// #expect: labelled_continue_for.ll + +for.cond: + + call void @labelled_continue_for.test() + br label %for.inc8 + +for.inc8: + %4 = load i32, i32* %i + %add9 = add nsw i32 %4, 1 + store i32 %add9, i32* %i + br label %for.cond + diff --git a/test/test_suite/statements/return_stmt.c3 b/test/test_suite/statements/return_stmt.c3 new file mode 100644 index 000000000..257d7516b --- /dev/null +++ b/test/test_suite/statements/return_stmt.c3 @@ -0,0 +1,11 @@ +func void testNoReturn() +{ + int i = 0; + i = -i; +} + +func int testReturn() +{ + int i = 0; + return i; +} \ No newline at end of file diff --git a/test/test_suite/statements/return_switch.c3t b/test/test_suite/statements/return_switch.c3t new file mode 100644 index 000000000..5e45ca1e3 --- /dev/null +++ b/test/test_suite/statements/return_switch.c3t @@ -0,0 +1,36 @@ +func int testReturnSwitch() +{ + int i = 0; + switch (i) + { + case 0: + case 3: + case 1: + return 2; + case 2: + return 3; + default: + return 4; + } +} + +// #expect: return_switch.ll + +switch.entry: + %2 = load i32, i32* %0 + switch i32 %2, label %switch.default [ + i32 0, label %switch.case + i32 3, label %switch.case + i32 1, label %switch.case + i32 2, label %switch.case1 + ] + +switch.case: + ret i32 2 + +switch.case1: + ret i32 3 + +switch.default: + ret i32 4 +} \ No newline at end of file diff --git a/test/test_suite/statements/return_with_other_at_end.c3 b/test/test_suite/statements/return_with_other_at_end.c3 new file mode 100644 index 000000000..d8655df4b --- /dev/null +++ b/test/test_suite/statements/return_with_other_at_end.c3 @@ -0,0 +1,7 @@ +func int testReturnWithOtherAtEnd() +{ + int i = 0; + return i; + if (i == 10) i++; + i = i + 2; +} diff --git a/test/test_suite/types/enum_inference.c3 b/test/test_suite/types/enum_inference.c3 new file mode 100644 index 000000000..70a3663f4 --- /dev/null +++ b/test/test_suite/types/enum_inference.c3 @@ -0,0 +1,43 @@ + +enum Inf +{ + A, + B, + C = 10000 +} + +enum Inf2 : byte +{ + A, + B, + C = 129, +} + +typedef Inf as BooInf; + + +func void enumInferenceTest() +{ + Inf x = Inf.A; + x = BooInf.B; + x = A; + int x1 = 0; + bool y = x1 == x1; + Inf2 z = C; + if (z == Inf2.A) return; + if (z == 1) return; + z = 2; + switch (z) + { + case Inf2.A: + x1++; + return; + case B: + return; + case 111: + x1 += 1; + return; + default: + return; + } +} \ No newline at end of file diff --git a/test/test_suite/types/recursive_typedef.c3 b/test/test_suite/types/recursive_typedef.c3 new file mode 100644 index 000000000..7470e470c --- /dev/null +++ b/test/test_suite/types/recursive_typedef.c3 @@ -0,0 +1,9 @@ +typedef Number1 as Number2; // #error: Recursive definition of 'Number2' +typedef Number2 as Number1; + +typedef Number as Number; // #error: Recursive definition of 'Number' + + +typedef Loop as Loop2; // #error: Recursive definition of 'Loop2' +typedef Loop2 as Loop3; +typedef Loop3 as Loop; diff --git a/test/test_suite/types/typedefs.c3 b/test/test_suite/types/typedefs.c3 index a22cd8bad..b3472f928 100644 --- a/test/test_suite/types/typedefs.c3 +++ b/test/test_suite/types/typedefs.c3 @@ -9,9 +9,3 @@ func void test1() int d = a; // #error: cast 'Arr' (int[4]) to 'int' } -typedef Number1 as Number2; // #error: Recursive definition of 'Number2' -typedef Number2 as Number1; - -typedef Number as Number; // #error: Recursive definition of 'Number' - -