From dc86c21210bac92c3dec937b4965cf79c589bc84 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 22 Jun 2020 11:37:42 +0200 Subject: [PATCH] New failable based error handling. Labelled break/continue/next. --- resources/c3.l | 2 - resources/c3ir.md | 11 + resources/examples/binarydigits.c3 | 4 +- resources/examples/madlibs.c3 | 7 +- resources/examples/toml_parser_c2.c3 | 12 +- resources/grammar.y | 16 +- resources/testfragments/super_simple.c3 | 854 +++++++++++------ resources/testfragments/test_break.c3 | 30 + resources/testfragments/testscrap.c3 | 1 + src/compiler/ast.c | 180 ++-- src/compiler/compiler.c | 1 + src/compiler/compiler_internal.h | 464 +++++----- src/compiler/context.c | 11 +- src/compiler/enums.h | 120 +-- src/compiler/lexer.c | 101 +- src/compiler/llvm_codegen.c | 72 +- src/compiler/llvm_codegen_debug_info.c | 8 +- src/compiler/llvm_codegen_expr.c | 560 +++++------ src/compiler/llvm_codegen_function.c | 44 +- src/compiler/llvm_codegen_internal.h | 47 +- src/compiler/llvm_codegen_stmt.c | 659 ++++++++----- src/compiler/llvm_codegen_type.c | 116 +-- src/compiler/number.c | 6 - src/compiler/parse_expr.c | 141 ++- src/compiler/parse_stmt.c | 519 +++++++---- src/compiler/parser.c | 310 ++++--- src/compiler/parser_internal.h | 11 +- src/compiler/sema_casts.c | 129 +-- src/compiler/sema_decls.c | 121 +-- src/compiler/sema_expr.c | 590 +++++++----- src/compiler/sema_internal.h | 17 + src/compiler/sema_name_resolution.c | 52 +- src/compiler/sema_passes.c | 4 - src/compiler/sema_stmts.c | 1133 +++++++++++++---------- src/compiler/sema_types.c | 43 +- src/compiler/tokens.c | 34 +- src/compiler/types.c | 90 +- src/main.c | 1 + src/utils/lib.h | 6 +- src/utils/stringutils.c | 8 + 40 files changed, 3727 insertions(+), 2808 deletions(-) create mode 100644 resources/c3ir.md create mode 100644 resources/testfragments/test_break.c3 diff --git a/resources/c3.l b/resources/c3.l index 0d3d0bdea..4f5e43800 100644 --- a/resources/c3.l +++ b/resources/c3.l @@ -58,8 +58,6 @@ void comment(void); "void" { count(); return(VOID); } "volatile" { count(); return(VOLATILE); } "while" { count(); return(WHILE); } -"throw" { count(); return(THROW); } -"throws" { count(); return(THROWS); } "func" { count(); return(FUNC); } "nil" { count(); return(NIL); } "next" { count(); return(NEXT); } diff --git a/resources/c3ir.md b/resources/c3ir.md new file mode 100644 index 000000000..3fe0d5e80 --- /dev/null +++ b/resources/c3ir.md @@ -0,0 +1,11 @@ + +``` +block +{ + z = add(x, y) + z = sub(x, y) + z = mult(x, y) + neg(z, x) + goto + +``` \ No newline at end of file diff --git a/resources/examples/binarydigits.c3 b/resources/examples/binarydigits.c3 index 42e3803bf..d4c150b27 100644 --- a/resources/examples/binarydigits.c3 +++ b/resources/examples/binarydigits.c3 @@ -8,10 +8,10 @@ func int main() } } -func string@ bin(int x) +func string bin(int x) { int bits = (x == 0) ? 1 : log10(cast(x, double)) / log10(2); - string@ ret = string.make_repeat('0', bits); + string ret = str.make_repeat('0', bits); for (int i = 0; i < bits; i++) { ret[bits - i - 1] = x & 1 ? '1' : '0'; diff --git a/resources/examples/madlibs.c3 b/resources/examples/madlibs.c3 index 9e1e272d0..f075a96f3 100644 --- a/resources/examples/madlibs.c3 +++ b/resources/examples/madlibs.c3 @@ -21,12 +21,7 @@ func void main() { string s = match.string; printf("Enter a value for '%s': ", s.slice(1, s.size - 2)); - string! word = strin.readln().strip(); - catch (word) - { - // Exit on error. - return; - } + string word = strin.readln().strip() else return; story = story.replace(s, word); } diff --git a/resources/examples/toml_parser_c2.c3 b/resources/examples/toml_parser_c2.c3 index 801ee0ff5..0f16a53cd 100644 --- a/resources/examples/toml_parser_c2.c3 +++ b/resources/examples/toml_parser_c2.c3 @@ -110,7 +110,7 @@ struct Parser /** * @ensure const(input) */ -func Parser.parse(Parser* p, char* input, char* diagMsg, Blocks* blocks) throws +func void! Parser.parse(Parser* p, char* input, char* diagMsg, Blocks* blocks) { p.tokenizer.init(input); p.tok.init(); @@ -125,15 +125,9 @@ func Parser.parse(Parser* p, char* input, char* diagMsg, Blocks* blocks) throws try p.consumeToken(); try p.parseTopLevel(); - return true; - - catch (error e) - { - return false; - } } -func void Parser.parseTopLevel(Parser* p) throws ParseError +func void! Parser.parseTopLevel(Parser* p) { // key = value // | [[array]] @@ -150,7 +144,7 @@ func void Parser.parseTopLevel(Parser* p) throws ParseError p.parseTableArray(); default: sprintf(p.errorMsg, "syntax error %s", p.tok.loc.str()); - throw ParseError.SYNTAX_ERROR; + return SyntaxError!; } } } diff --git a/resources/grammar.y b/resources/grammar.y index 19d81cf8f..5d51d8292 100644 --- a/resources/grammar.y +++ b/resources/grammar.y @@ -16,6 +16,7 @@ void yyerror(char *s); %token ADD_MOD SUB_MOD MULT_MOD ADD_MOD_ASSIGN SUB_MOD_ASSIGN %token MULT_MOD_ASSIGN NEG_MOD %token XOR_ASSIGN OR_ASSIGN VAR NIL ELVIS HASH_IDENT NEXT +%token NOFAIL_ASSIGN %token TYPEDEF MODULE IMPORT %token CHAR SHORT INT LONG FLOAT DOUBLE CONST VOLATILE VOID @@ -402,14 +403,25 @@ expression_statement ; +if_expr + : failable_type IDENT '=' initializer + | failable_type IDENT NOFAIL_ASSIGN expression + | expression + ; + +if_cond_expr + : if_expr + | if_cond_expr ',' if_expr + ; + control_expression : decl_expr_list | decl_expr_list ';' decl_expr_list ; selection_statement - : IF '(' control_expression ')' statement - | IF '(' control_expression ')' compound_statement ELSE statement + : IF '(' if_cond_expr ')' statement + | IF '(' if_cond_expr ')' compound_statement ELSE statement | SWITCH '(' control_expression ')' compound_statement ; diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index 24f22400d..1cb0a1e29 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -1,5 +1,6 @@ module bar; + typedef int as Bob; struct Test @@ -65,17 +66,6 @@ enum EnumTestOverflowAfter -errset Error -{ - BLURB, - NO_SUCH_FILE, -} - -errset OtherError -{ - FOO_BAR -} - enum Inf { A, @@ -142,6 +132,37 @@ struct ExtraSimple } int g; } + + +func void testIf() +{ + + int a = 0; + int b = 1; + if (a, b) + { + printf("Yes!\n"); + } + if (int x = 0, int y = 2) + { + printf("Yes: %d/%d\n", x, y); + } + if (int x = 2, int y = 0) + { + printf("No: %d/%d\n", x, y); + } + int! z = 67; + /* + try (int j = z, int k = z + 1) + { + printf("Check: %d %d\n", j, k); + }*/ + try (z) + { + printf("Show :%d", z); + } +} + func void testSimple() { ExtraSimple a = { c.j = 3.3 }; @@ -190,7 +211,6 @@ func TestStruct2 structTest(int i) func void enumInferenceTest() { - OtherError e = OtherError.FOO_BAR; Inf x = Inf.A; x = BooInf.B; x = A; @@ -215,17 +235,51 @@ func void enumInferenceTest() } } +func void switchTest() +{ +/* + switch FOO: (cast(1, int)) + { + case 1: + printf("1\n"); + next FOO:3; + case 2: + printf("2\n"); + next 4; + case 3: + printf("3\n"); + next 2; + case 4: + printf("4\n"); + }*/ +} + func int jumptest() { - if (1) goto LABELX; - return 1; - LABELX: + + do LABELX: + { + if (0) break LABELX; + printf("Hello\n"); + }; + do LABELX: + { + if (1) break LABELX; + return 1; + } while(1); + do LABELX: + { + if (1) continue LABELX; + return 1; + } while(0); + return 2; } -func int borok() throws +/* +func int! borok() { return 1; -} +}*/ func void testNoReturn() { @@ -590,21 +644,6 @@ func void zzzz() x += 3; } -func int jumpback(int x) -{ - { - defer x += 1; - { - defer x += 2; - LABELX: - x += 3; - } - } - - if (x < 100) goto LABELX; - return x + 1; -} - func void test_expr_block(int x) { int a = ({ @@ -634,18 +673,17 @@ func int xxxx(int x) { + do LABELD: { x += 10; defer printf("XXX"); - if (x < 100) goto LABELD; + if (x < 100) break LABELD; defer printf("EODfe"); - } + }; { defer printf("Defer says hello!\n"); - LABELD: x--; } - if (x > 0) goto LABELD; return 1; } @@ -686,72 +724,157 @@ struct WithArray int[4] x; } -errset Err +error TestErr1; +error TestErr2; + +func void testErr1() { - TEST_ERR1 + } -errset Err2 +struct Feok { - TEST_ERR2 + long a; + long b; + long c; } -func int testThrow1() throws Err +error FooErr { - throw Err.TEST_ERR1; + int x; + int y; } -func int testThrow2() throws Err, Err2 +func int! testThrow1() { - throw Err.TEST_ERR1; + return TestErr1!; } - -func int testThrow3() throws +/* +func void syntaxErrors() { - throw Err.TEST_ERR1; + 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 testThrow4(int x) throws +func int frob(int i) { - throw x > 4 ? Err.TEST_ERR1 : Err2.TEST_ERR2; -} - -func int testThrow5(int x) throws -{ - throw x > 4 ? Err.TEST_ERR1 : Err.TEST_ERR1; -} - -func int testThrow6(int x) throws Err, Err2 -{ - if (x > 4) throw Err.TEST_ERR1; - throw Err2.TEST_ERR2; + return i * i; } -func int testThrow(int x) throws Err +func void show() +{ + int! num = 3; + num = frob(num + 1); + printf("Num: %d\n", num); +} +func void testAllErrors() +{ +/* + printf("Try test all\n"); + int! i = 7; + int*! iptr = &i; + int**! iptrptr = &iptr; + catch (err = iptrptr) + { + printf("Unexpected error!\n"); + } + catch (int i = **iptrptr) + { + printf("Unexpected error!\n"); + } + bool! hej = (i + 1); + try (hej) + { + printf("Was clean!\n"); + } + i = TestErr1!; + hej = (i + 1); + catch (hej) printf("Not clean!\n"); + i = 2938; + try(i) printf("Fine? %d\n", i); + catch(i) printf("Not fine\n"); + try (int j = i) + { + printf("Gotcha %d\n", j); + } + int hupp = 3; + iptr = &hupp; + iptr = TestErr1!; + catch (err = *iptr) + { + printf("Expected error!\n"); + } + + i = 2; + Feok z; + z.a = 1; + Feok! x = z; + int! j; + j = i; + j = TestErr1!; + FooErr fex = FooErr { 1, 3 }; + fex.x = 2; + printf("Fex: %d\n", fex.x); + error test33 = fex; + FooErr fex2 = cast(test33, FooErr); + printf("Fex2: %d\n", fex.x); + TestErr1 eok = TestErr1{}; + error e = eok; + catch (err = x) + { + printf("Error!\n"); + } + catch (err = j) + { + case FooErr: + printf("FooErr\n"); + case TestErr1: + printf("TestErr1\n"); + default: + printf("Default error!\n"); + } + printf("No error!\n"); + int! flow = 1; + catch (flow) return; + int xy = flow;*/ +} +/* + + +func int! testThrow4(int x) +{ + return x > 4 ? TestErr1! : TestErr2!; +} + + +func int! testThrow6(int x) +{ + if (x > 4) return TestErr1!; + return TestErr2!; +} + + +func int! testThrow(int x) { throwsDone.times++; - if (x < 0) throw Err.TEST_ERR1; + if (x < 0) return TestErr1!; return x * x; } +*/ - - -func int oekt() throws -{ - int z = try testThrow(-1) else goto NEXT; - printf("Skipped.\n"); - NEXT: - int x = try testThrow(-3); - catch (error e) - { - printf("An error %p\n", cast(e, usize)); - throw e; - } - return x; -} - -func int! oekt2() throws +/* +func int! oekt() { int z = testThrow(-1) else goto NEXT; printf("Skipped.\n"); @@ -759,89 +882,35 @@ func int! oekt2() throws int! x = testThrow(-3); catch (err = x) { - printf("An error %p\n", usize(e)); + printf("An error %p\n", cast(err, usize)); return err!; } return x; } - -func int! oekt3() +*/ +/* +func int! testThrowAny(int x) { - int z = testThrow(-1) else goto NEXT; - printf("Skipped.\n"); - NEXT: - int! x = testThrow(-3); - - if (e = catch(x)) printf("An error %p\n", usize(e)); - - return x; -} - -func int! oekt3() -{ - int z = testThrow(-1) else goto NEXT; - printf("Skipped.\n"); - NEXT: - int! x = testThrow(-3); - - if (e = x!) printf("An error %p\n", usize(e)); - - return x; -} - -errset Xan -{ - EE, - FF, - DD, -} - -func int testThrowAny(int x) throws -{ - if (x < 0) throw Err.TEST_ERR1; + if (x < 0) return TestErr1!; return x * x; } - +*/ +/* func void testErrorBug() { printf("Test error multi\n"); - { - printf("Will call\n"); - try oekt(); - } - catch (Err e) - { - printf("Expected particular error.\n"); - } - catch (error e) - { - error foo = Err.TEST_ERR1; - printf("Not expected %p != %p\n", cast(e, usize), cast(foo, usize)); - printf("Unexpected any error error.\n"); - } - printf("End\n"); -} - -func void testErrorBug2() -{ - printf("Test error multi\n"); - error e; - { - printf("Will call\n"); - e = catch(oekt()); - } - switch (e) + printf("Will call\n"); + catch(oekt()) { case TestErr1: printf("Expected particular error.\n"); default: error foo = Err.TEST_ERR1; - printf("Not expected %p != %p\n", usize(e), usize(foo)); + printf("Not expected %p != %p\n", cast(e, usize), cast(foo, usize)); printf("Unexpected any error error.\n"); } printf("End\n"); } - func void testErrorMulti() { defer printf("End defer\n"); @@ -853,7 +922,7 @@ func void testErrorMulti() z = oekt(); printf("Got here\n"); } - switch (e = z!) + switch (e = z) { case TestErr1: printf("Expected particular error.\n"); @@ -864,44 +933,21 @@ func void testErrorMulti() } printf("End\n"); } +*/ -func void testErrorMulti() -{ - defer printf("End defer\n"); - printf("Test error multi\n"); - { - defer printf("Defer\n"); - printf("Will call\n"); - int z = try oekt(); - printf("Got here\n"); - } - catch (Err e) - { - defer printf("Left error\n"); - printf("Expected particular error.\n"); - } - catch (error e) - { - defer printf("Left any error\n"); - error foo = Err.TEST_ERR1; - printf("Not expected %p != %p\n", cast(e, usize), cast(foo, usize)); - printf("Unexpected any error error.\n"); - } - printf("End\n"); -} - -func void throwAOrB(int i) throws Err, Err2 +/* +func void! throwAOrB(int i) { printf("AB\n"); defer printf("A-Err\n"); - if (i == 1) throw Err.TEST_ERR1; + if (i == 1) return TestErr1!; printf("B\n"); defer printf("B-Err\n"); - if (i == 2) throw Err2.TEST_ERR2; + if (i == 2) return TestErr2!; printf("None\n"); } - +*/ struct ThrowsDone { long x; @@ -911,61 +957,7 @@ struct ThrowsDone } ThrowsDone throwsDone; -func void testErrors() -{ - - printf("Throws: %d\n", throwsDone.times); - - int x = try testThrow(20) else 1; - printf("Value was %d, expected 400.\n", x); - - printf("Throws: %d\n", throwsDone.times); - - x = try testThrow(-1) else 20; - printf("Value was %d, expected 20.\n", x); - - printf("Throws: %d\n", throwsDone.times); - - printf("Begin\n"); - int y = try testThrow(-1); - - printf("Value was %d, expected 9.\n", y); - - printf("Didn't expect this one.\n"); - catch (error e) - { - printf("Expected this catch.\n"); - } - y = try testThrow(-1); - - catch (Err e) - { - printf("Particular error.\n"); - } - testErrorMulti(); - - try throwAOrB(1); - catch (Err e) - { - printf("A1\n"); - } - catch (Err2 e) - { - printf("A2\n"); - } - try throwAOrB(2); - catch (Err e) - { - printf("B1\n"); - } - catch (Err2 e) - { - printf("B2\n"); - } - printf("Throws: %d\n", throwsDone.times); - printf("End of errors\n"); -} - +/* func void testErrors() { @@ -989,9 +981,12 @@ func void testErrors() printf("Value was %d, expected 9.\n", y); printf("Didn't expect this one.\n"); } - else catch(y) + else { - printf("Expected this catch.\n"); + catch(y) + { + printf("Expected this catch.\n"); + } } y = testThrow(-1); @@ -999,7 +994,7 @@ func void testErrors() { printf("Particular error.\n"); } - testErrorMulti(); + // testErrorMulti(); catch (throwAOrB(1)) { @@ -1018,7 +1013,7 @@ func void testErrors() printf("Throws: %d\n", throwsDone.times); printf("End of errors\n"); } - +*/ macro void arrayCallMacro($x) { printf("Array call: %d, %d\n", $x[0], $x[1]); @@ -1080,6 +1075,22 @@ func void testArray() } } +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++) + { + printf("%d %d %d\n", i, j, k); + continue FOO; + } + printf("ERROR\n"); + } + printf("ERROR\n"); + } +} func void testDefer() { printf("1 == %d\n", testReturnDefer()); @@ -1092,12 +1103,7 @@ func void testDefer() printf("4"); } int i = 0; - goto JUMP; - defer printf("ERROR"); - int r = 0; -JUMP: defer printf("-JUMPDEFER-"); - if (i++ < 2) goto JUMP; switch (int boo = 2) { case 0: @@ -1222,8 +1228,6 @@ func void testTypeValues() printf("Struct size: %d = 12\n", TestStructInt.sizeof); printf("Struct size: %d = 16\n", TestStructSize.sizeof); printf("Union size: %d = 5\n", TestUnionSize.sizeof); - printf("Error size: %d = 4\n", Err.sizeof); - printf("Testunion.a sizeof: %d = 2\n", TestUnionSize.a.sizeof); } func int testTypeofHelp() @@ -1252,6 +1256,7 @@ func void testMacros() printf("i: %d\n", i); } +/* const char[] LUT_ENC = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', @@ -1262,9 +1267,10 @@ const char[] LUT_ENC = { 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', }; +*/ +//const byte ERR = 0xFF; -const byte ERR = 0xFF; - +/* const byte[256] LUT_DEC = { // '+', ',', '-', '.', '/', '0', '1', '2' 62, ERR, ERR, ERR, 63, 52, 53, 54, @@ -1287,65 +1293,63 @@ const byte[256] LUT_DEC = { // 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' 44, 45, 46, 47, 48, 49, 50, 51, }; - - +*/ +/* const char PAD = '='; const char FIRST = '+'; const char LAST = 'z'; +*/ +public error InvalidCharacterError; -public error Base64Error -{ - INVALID_CHARACTER -} - -public func void encode(byte[] in, string *out) +/* +public func void encode(byte[] infile, char[] *out) { int j = 0; - for (int i = 0; i < in.len; i++); + for (int i = 0; i < i.len; i++); { switch (i % 3) { case 0: - out[j++] = LUT_ENC[(in[i] >> 2) & 0x3F]; + out[j++] = LUT_ENC[(infile[i] >> 2) & 0x3F]; case 1: - out[j++] = LUT_ENC[(in[i-1] & 0x3) << 4 + ((in[i] >> 4) & 0xF)]; + out[j++] = LUT_ENC[(infile[i-1] & 0x3) << 4 + ((infile[i] >> 4) & 0xF)]; case 2: - out[j++] = LUT_ENC[(in[i-1] & 0xF) << 2 + ((in[i] >> 6) & 0x3)]; - out[j++] = LUT_ENC[in[i] & 0x3F]; + out[j++] = LUT_ENC[(infile[i-1] & 0xF) << 2 + ((infile[i] >> 6) & 0x3)]; + out[j++] = LUT_ENC[infile[i] & 0x3F]; } i++; } // move back - int last = in.len - 1; + int last = infile.len - 1; // check the last and add padding switch (last % 3) { case 0: - out[j++] = LUT_ENC[(in[last] & 0x3) << 4]; + out[j++] = LUT_ENC[(infile[last] & 0x3) << 4]; out[j++] = PAD; out[j++] = PAD; case 1: - out[j++] = LUT_ENC[(in[last] & 0xF) << 2]; + out[j++] = LUT_ENC[(infile[last] & 0xF) << 2]; out[j++] = PAD; } } -public func int decode(char[] in, byte[] out) throws Base64Error +public func int! decode(char[] infile, byte[] out) { int j = 0; for (int i = 0; i < len; i++) { - char value = in[i]; + char value = infile[i]; if (value == PAD) return j; - if (value < FIRST || in[i] > LAST) throw INVALID_CHARACTER; - byte c = LUT_DEC[in[i] - FIRST); - if (c == ERR) throw INVALID_CHARACTER; + if (value < FIRST || infile[i] > LAST) return InvalidCharacterError!; + byte c = LUT_DEC[infile[i] - FIRST]; + if (c == ERR) return InvalidCharacterError!; switch (i % 4) { @@ -1354,13 +1358,13 @@ public func int decode(char[] in, byte[] out) throws Base64Error case 1: out[j++] += (c >> 4) & 0x3; // if not last char with padding - if (i < (len - 3) || in[len - 2] != PAD) + if (i < (len - 3) || infile[len - 2] != PAD) { out[j] = (c & 0xF) << 4; } case 2: out[j++] += (c >> 2) & 0xF; - if (i < (len -2) || in[len -1] != PAD) + if (i < (len -2) || infile[len -1] != PAD) { out[j] = (c & 0x3) << 6; } @@ -1370,14 +1374,17 @@ public func int decode(char[] in, byte[] out) throws Base64Error } return j; } - +*/ func int main(int x) { + testBreak(); + show(); testMacros(); testTypeof(); testSimple(); - testErrorBug(); - testErrors(); + testAllErrors(); + //testErrorBug(); + //testErrors(); testDefault(y = 99); testPointers(2, 3); testDefer(); @@ -1392,6 +1399,7 @@ func int main(int x) testTypeValues(); int efd = 9; uint fefoek = 1; + testIf(); printf("Helo: %d\n", efd + cast(fefoek, int)); //long fefoek = -fefoek; int okfe = 1; @@ -1435,7 +1443,8 @@ func int main(int x) printf(":okko"); } printf("\n"); - while (int ok = 0; int j = ok++, ok < 10) + int ok = 0; + while (int j = ok++, ok < 10) { printf("foo"); } @@ -1468,35 +1477,40 @@ func int main(int x) long deee = (eok ? a : b) + (eok ? b : c); int i = 0; - JUMP: - i = i + 1; - //@hello(); - printf("Hello worldABC" "D" "E\u2701\n"); - float f = 3.0; - float* pf = &f; - switch (i) + do JUMP: { - case 0: - printf("c0\n"); - case 1: - printf("c1\n"); - case 2: - printf("c2\n"); - case 3: - printf("c3\n"); - default: - printf("default\n"); - } - if (*pf > i) goto JUMP; - goto EX; - YEF: - return 4; - EX: - printf("EX\n"); - goto YEF; + i = i + 1; + //@hello(); + printf("Hello worldABC" "D" "E\u2701\n"); + float f = 3.0; + float* pf = &f; + switch (i) + { + case 0: + printf("c0\n"); + case 1: + printf("c1\n"); + case 2: + printf("c2\n"); + case 3: + printf("c3\n"); + default: + printf("default\n"); + } + if (*pf > i) continue JUMP; + break JUMP; + } while (1); + return 1; } +func void teeeest() +{ + long z = 3; + (1 ? (z < 1 ? 2 : 1) : 3) == 3; + (1 ?: (z ?: 7)) == 4; + (1 ?: (3 ?: 7)) == 4; +} func void test2(int* x, int y, int z) { *(&(&x)[0]); @@ -1522,6 +1536,8 @@ func void test2(int* x, int y, int z) g <<= z; g >>= 2; g >>= z; + 2 << g; + 3 >> 4; int fz = 100; y && z; y || z; @@ -1530,7 +1546,6 @@ func void test2(int* x, int y, int z) ~z; !z; -z; - int i = 3; uint ui = 2; int j = 129; @@ -1552,3 +1567,254 @@ func void test2(int* x, int y, int z) j1 = *x; } + +func void testBreak() +{ + defer printf("ALL TESTS DONE\n"); + int i = 0; + int j = 0; + while (j < 2) + { + defer printf("Defer while %d\n", j); + break; + defer printf("Exit\n"); + } + do + { + defer printf("Defer do %d\n", j); + break; + defer printf("Exit\n"); + } while (j < 2); + + for (j = 0; j < 2; j++) + { + defer printf("Defer for %d\n", j); + break; + defer printf("Exit\n"); + } + + while (j < 2) + { + defer printf("Defer while %d\n", j); + j++; + continue; + defer printf("Exit\n"); + printf("ERROR\n"); + } + do + { + defer printf("Defer do %d\n", j); + j++; + continue; + defer printf("Exit\n"); + printf("ERROR\n"); + } while (j < 2); + + for (j = 0; j < 2; j++) + { + defer printf("Defer for %d\n", j); + continue; + defer printf("Exit\n"); + printf("ERROR\n"); + } + + if FOO: (i == 0) + { + printf("Enter if\n"); + i++; + defer("EXIT_FOO\n"); + while BAZ: (1) + { + defer("EXIT_BAZ\n"); + if BAR: (i > 0) break FOO; + } + printf("WRONG\n"); + } + printf("Test done\n"); + + while (i < 10) + { + i++; + if (i % 2 == 0) continue; + printf("%d\n", i); + if (i > 6) break; + } + + i = 0; + j = 0; + while FOO: (i < 10) + { + i++; + j = 0; + while (j < 3) + { + j++; + printf("%d, %d\n", i, j); + if ((i + j) % 3 == 0) continue FOO; + if (i + j > 8) break FOO; + } + } + i = 0; + do + { + i++; + if (i % 2 == 0) continue; + printf("%d\n", i); + if (i > 6) break; + } while (i < 10); + + i = 0; + do FOO: + { + i++; + j = 0; + while (j < 3) + { + j++; + printf("%d, %d\n", i, j); + if ((i + j) % 3 == 0) continue FOO; + if (i + j > 8) break FOO; + } + } while (i < 10); + + i = 0; + switch (i) + { + case 0: + printf("case0\n"); + break; + printf("ERROR\n"); + case 1: + printf("ERROR\n"); + } + + printf("TestFor\n"); + for (i = 0; i < 15; i++) + { + for (j = 0; j < 3; j++) + { + printf("%d, %d\n", i, j); + if ((i + j) % 3 == 0) continue; + if (i + j > 8) break; + } + } + + printf("TestFor2\n"); + for FOO: (i = 0; i < 12; i++) + { + for (j = 0; j < 3; j++) + { + printf("%d, %d\n", i, j); + if ((i + j) % 3 == 0) continue FOO; + if (i + j > 8) break FOO; + } + } + printf("Test1\n"); + i = 0; + switch FOO: (i) + { + case 0: + defer printf("Test1DeferOuter\n"); + while (1) + { + printf("case0\n"); + defer printf("Test1DeferInner\n"); + break FOO; + } + printf("ERROR\n"); + case 1: + printf("ERROR\n"); + } + printf("Test2\n"); + switch FOO: (i) + { + case 0: + printf("case0\n"); + defer printf("Test2DeferNext\n"); + next; + case 1: + defer printf("Test2DeferCase\n"); + printf("case1\n"); + } + printf("Test3\n"); + switch FOO: (i) + { + case 0: + defer printf("Test3DeferNext\n"); + printf("case0\n"); + next 2; + case 1: + defer printf("Test3DeferErr\n"); + printf("case1\n"); + case 2: + defer printf("Test3DeferCase\n"); + printf("case2\n"); + } + printf("Test4\n"); + switch FOO: (i) + { + case 0: + printf("case0\n"); + defer printf("OuterDefer\n"); + switch (i) + { + case 0: + printf("Inner\n"); + defer printf("InnerDefer\n"); + next FOO:2; + case 2: + printf("ERROR\n"); + } + next 1; + case 1: + printf("case1\n"); + case 2: + defer printf("Case2Defer\n"); + printf("case2\n"); + } + printf("Test5\n"); + switch FOO: (i) + { + case 0: + defer printf("DEFER\n"); + printf("case0\n"); + switch (i) + { + case 0: + printf("Inner\n"); + defer printf("DEFERINNER\n"); + int k = 1; + next FOO: k; + case 2: + printf("ERROR\n"); + } + next 1; + case 1: + printf("case1\n"); + case 2: + printf("case2\n"); + } + + i = 0; + switch FOO: (i) + { + case 0: + printf("0: %d\n", i); + i++; + next i; + case 1: + i += 2; + defer printf("DEFER1\n"); + printf("1: %d\n", i); + next i; + case 2: + printf("2: END\n", i); + case 3: + i--; + printf("3: %d\n", i); + next i; + default: + printf("WAAAIT\n"); + } + +} \ No newline at end of file diff --git a/resources/testfragments/test_break.c3 b/resources/testfragments/test_break.c3 new file mode 100644 index 000000000..8e663aed7 --- /dev/null +++ b/resources/testfragments/test_break.c3 @@ -0,0 +1,30 @@ +module foo; + +extern func void printf(char *hello, ...); + +error MyError; +error YourError; +func void main() +{ + int! i = 1; + catch (i) + { + printf("Caught\n"); + return; + } + i = MyError!; + catch FOO: (i) + { + case YourError: + printf("YourError\n"); + case MyError: + printf("MyError\n"); + next FOO : YourError; + default: + printf("Any error\n"); + } + do + { + printf("Test\n"); + } +} \ No newline at end of file diff --git a/resources/testfragments/testscrap.c3 b/resources/testfragments/testscrap.c3 index 5933d68ba..7341853a6 100644 --- a/resources/testfragments/testscrap.c3 +++ b/resources/testfragments/testscrap.c3 @@ -782,6 +782,7 @@ int i = 0; return 1; } + func void test2(int* x, int y, int z) { *(&(&x)[0]); diff --git a/src/compiler/ast.c b/src/compiler/ast.c index ec3865c89..11b12abd3 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -68,8 +68,8 @@ Decl *decl_new_with_type(Token name, DeclKind decl_type, Visibility visibility) case DECL_STRUCT: kind = TYPE_STRUCT; break; - case DECL_ERROR: - kind = TYPE_ERROR; + case DECL_ERR: + kind = TYPE_ERRTYPE; break; case DECL_ENUM: kind = TYPE_ENUM; @@ -80,7 +80,6 @@ Decl *decl_new_with_type(Token name, DeclKind decl_type, Visibility visibility) case DECL_POISONED: case DECL_VAR: case DECL_ENUM_CONSTANT: - case DECL_ERROR_CONSTANT: case DECL_ARRAY_VALUE: case DECL_IMPORT: case DECL_MACRO: @@ -90,6 +89,7 @@ Decl *decl_new_with_type(Token name, DeclKind decl_type, Visibility visibility) case DECL_CT_ELIF: case DECL_ATTRIBUTE: case DECL_MEMBER: + case DECL_LABEL: UNREACHABLE } Type *type = type_new(kind, name.string); @@ -111,6 +111,12 @@ const char *decl_var_to_string(VarDeclKind kind) return "local"; case VARDECL_PARAM: return "param"; + case VARDECL_ALIAS: + return "alias"; + case VARDECL_LOCAL_CT: + return "$local"; + case VARDECL_LOCAL_CT_TYPE: + return "$Local"; } UNREACHABLE } @@ -148,7 +154,7 @@ Decl *struct_find_name(Decl *decl, const char* name) Expr *expr_new(ExprKind kind, SourceRange start) { - Expr *expr = malloc_arena(sizeof(Expr)); + Expr *expr = CALLOCS(Expr); expr->expr_kind = kind; expr->span = start; expr->type = NULL; @@ -222,7 +228,7 @@ UnaryOp unary_op[TOKEN_LAST + 1] = { [TOKEN_STAR] = UNARYOP_DEREF, [TOKEN_AMP] = UNARYOP_ADDR, [TOKEN_BIT_NOT] = UNARYOP_BITNEG, - [TOKEN_NOT] = UNARYOP_NOT, + [TOKEN_BANG] = UNARYOP_NOT, [TOKEN_MINUS] = UNARYOP_NEG, [TOKEN_MINUS_MOD] = UNARYOP_NEGMOD, [TOKEN_PLUSPLUS] = UNARYOP_INC, @@ -331,8 +337,8 @@ void fprint_type_recursive(FILE *file, Type *type, int indent) case TYPE_ENUM: DUMPF("(enum %s)", type->name); return; - case TYPE_ERROR: - DUMPF("(error %s)", type->name); + case TYPE_ERRTYPE: + DUMPF("(errtype %s)", type->name); return; case TYPE_MEMBER: DUMPF("(member %s", type->name); @@ -381,8 +387,8 @@ void fprint_type_recursive(FILE *file, Type *type, int indent) case TYPE_STRING: DUMP("(string)"); return; - case TYPE_ERROR_UNION: - DUMP("(error-union)"); + case TYPE_ERR_UNION: + DUMP("(any-error)"); return; } } @@ -491,6 +497,20 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent) if (!expr) return; switch (expr->expr_kind) { + case EXPR_FAIL_CHECK: + DUMP("(fail-check"); + DUMPEXPC(expr); + DUMPEXPR(expr->fail_check_expr); + DUMPEND(); + case EXPR_DECL_LIST: + DUMP("(decllist"); + DUMPASTS(expr->dexpr_list_expr); + DUMPEND(); + case EXPR_FAILABLE: + DUMP("(failable"); + DUMPEXPC(expr); + DUMPEXPR(expr->failable_expr); + DUMPEND(); case EXPR_IDENTIFIER: if (expr->identifier_expr.is_macro) { @@ -538,6 +558,16 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent) DUMPEXPC(expr); DUMPEXPR(expr->post_expr.expr); DUMPEND(); + case EXPR_CATCH: + DUMP("(catch"); + DUMPEXPC(expr); + DUMPEXPR(expr->trycatch_expr); + DUMPEND(); + case EXPR_TRY: + DUMP("(try"); + DUMPEXPC(expr); + DUMPEXPR(expr->trycatch_expr); + DUMPEND(); case EXPR_TYPE_ACCESS: DUMPF("(typeaccess .%s", expr->type_access.name.string); DUMPEXPC(expr); @@ -615,29 +645,26 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent) DUMPEXPR(expr->subscript_expr.expr); DUMPEXPC(expr->subscript_expr.index); DUMPEND(); - case EXPR_TRY: - switch (expr->try_expr.type) + case EXPR_GUARD: + DUMP("(guard"); + DUMPEXPR(expr->guard_expr); + DUMPEND(); + case EXPR_ELSE: + if (expr->else_expr.is_jump) { - case TRY_EXPR_ELSE_JUMP: - DUMP("(try-else-jump"); - DUMPEXPC(expr); - DUMPEXPR(expr->try_expr.expr); - DUMPAST(expr->try_expr.else_stmt); - DUMPEND(); - case TRY_EXPR_ELSE_EXPR: - DUMP("(try-else"); - DUMPEXPC(expr); - DUMPEXPR(expr->try_expr.expr); - DUMPEXPR(expr->try_expr.else_expr); - DUMPEND(); - case TRY_STMT: - DUMP("(try-stmt"); - DUMPAST(expr->try_expr.stmt); - DUMPEND(); - case TRY_EXPR: - DUMP("(try-expr"); - DUMPEXPR(expr->try_expr.expr); - DUMPEND(); + DUMP("(else-jump"); + DUMPEXPC(expr); + DUMPEXPR(expr->else_expr.expr); + DUMPAST(expr->else_expr.else_stmt); + DUMPEND(); + } + else + { + DUMP("(else-expr"); + DUMPEXPC(expr); + DUMPEXPR(expr->else_expr.expr); + DUMPEXPR(expr->else_expr.else_expr); + DUMPEND(); } UNREACHABLE case EXPR_CAST: @@ -702,16 +729,6 @@ void fprint_func_signature(FILE *file, FunctionSignature *signature, int indent) DUMP("(params"); DUMPDECLS(signature->params); DUMPE(); - if (signature->throw_any) - { - DUMP("(throws any)"); - } - else - { - DUMP("(throws"); - VECEACH(signature->throws, i) DUMPTI(signature->throws[i]); - DUMPE(); - } indent--; } while (false); DUMPEND(); @@ -731,10 +748,20 @@ void fprint_decl_recursive(FILE *file, Decl *decl, int indent) case VARDECL_GLOBAL: case VARDECL_LOCAL: case VARDECL_PARAM: + case VARDECL_LOCAL_CT: DUMPEXPR(decl->var.init_expr); break; + case VARDECL_LOCAL_CT_TYPE: + DUMPTI(decl->var.type_info); + break; + case VARDECL_ALIAS: + DUMPDECL(decl->var.alias); + break; } DUMPEND(); + case DECL_LABEL: + DUMPF("(label %s", decl->name); + DUMPEND(); case DECL_MACRO: DUMPF("(macro %s", decl->name); DUMPTI(decl->macro_decl.rtype); @@ -771,9 +798,9 @@ void fprint_decl_recursive(FILE *file, Decl *decl, int indent) DUMPTI(decl->enums.type_info); DUMPDECLS(decl->enums.values); DUMPEND(); - case DECL_ERROR: + case DECL_ERR: DUMPF("(error %s", decl->name); - DUMPDECLS(decl->error.error_constants); + DUMPDECLS(decl->strukt.members); DUMPEND(); case DECL_ENUM_CONSTANT: if (!decl->enum_constant.expr) @@ -784,9 +811,6 @@ void fprint_decl_recursive(FILE *file, Decl *decl, int indent) DUMPF("(enum-constant %s", decl->name); DUMPEXPR(decl->enum_constant.expr); DUMPEND(); - case DECL_ERROR_CONSTANT: - DUMPF("(error-constant %s)", decl->name); - return; case DECL_GENERIC: DUMPF("(generic %s\n", decl->name); indent++; @@ -907,6 +931,11 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent) if (!ast) return; switch (ast->ast_kind) { + case AST_TRY_STMT: + DUMP("(try"); + DUMPEXPR(ast->try_stmt.decl_expr); + DUMPAST(ast->try_stmt.body); + DUMPEND(); case AST_COMPOUND_STMT: if (!ast->compound_stmt.stmts) { @@ -916,9 +945,9 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent) DUMP("(compound\n"); fprint_asts_recursive(file, ast->compound_stmt.stmts, indent + 1); DUMPEND(); - case AST_DECL_EXPR_LIST: - DUMP("(declexprlist"); - DUMPASTS(ast->decl_expr_stmt); + case AST_DEFINE_STMT: + DUMP("(define"); + DUMPDECL(ast->define_stmt); DUMPEND(); case AST_DECLARE_STMT: DUMP("(declare"); @@ -930,7 +959,7 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent) DUMPEND(); case AST_WHILE_STMT: DUMP("(while"); - DUMPAST(ast->while_stmt.cond); + DUMPEXPR(ast->while_stmt.cond); DUMPAST(ast->while_stmt.body); DUMPEND(); case AST_SCOPED_STMT: @@ -977,7 +1006,7 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent) return; case AST_FOR_STMT: DUMP("(for"); - DUMPAST(ast->for_stmt.init); + DUMPEXPR(ast->for_stmt.init); DUMPEXPR(ast->for_stmt.cond); if (ast->for_stmt.incr) { @@ -991,39 +1020,31 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent) DUMPEND(); case AST_IF_STMT: DUMP("(if"); - DUMPAST(ast->if_stmt.cond); + DUMPEXPR(ast->if_stmt.cond); DUMPAST(ast->if_stmt.then_body); DUMPAST(ast->if_stmt.else_body); DUMPEND(); case AST_SWITCH_STMT: DUMP("(switch"); - DUMPAST(ast->switch_stmt.cond); + DUMPEXPR(ast->switch_stmt.cond); DUMPASTS(ast->switch_stmt.cases); DUMPEND(); case AST_CASE_STMT: DUMP("(case"); - DUMPEXPR(ast->case_stmt.expr); + if (ast->case_stmt.is_type) + { + DUMPTI(ast->case_stmt.type_info); + } + else + { + DUMPEXPR(ast->case_stmt.expr); + } + DUMPAST(ast->case_stmt.body); DUMPEND(); case AST_DEFER_STMT: DUMP("(defer"); DUMPAST(ast->defer_stmt.body); DUMPEND(); - case AST_GENERIC_CASE_STMT: - DUMP("(generic-case"); - indent++; - DUMP("(match"); - VECEACH(ast->generic_case_stmt.types, i) - { - DUMPTI(ast->generic_case_stmt.types[i]); - } - DUMPE(); - indent--; - DUMPAST(ast->generic_case_stmt.body); - DUMPEND(); - case AST_GENERIC_DEFAULT_STMT: - DUMP("(generic-default"); - DUMPAST(ast->generic_default_stmt); - DUMPEND(); case AST_POISONED: DUMP("(ast-poisoned)"); return; @@ -1033,7 +1054,7 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent) DUMPEND(); case AST_CATCH_STMT: DUMP("(catch"); - DUMPDECL(ast->catch_stmt.error_param); + DUMPEXPR(ast->catch_stmt.catchable); DUMPAST(ast->catch_stmt.body); DUMPEND(); case AST_CT_IF_STMT: @@ -1057,26 +1078,9 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent) DUMPEXPR(ast->ct_switch_stmt.cond); DUMPASTS(ast->ct_switch_stmt.body); DUMPEND(); - case AST_CT_CASE_STMT: - DUMP("(ct-case"); - TODO - case AST_CT_DEFAULT_STMT: - DUMP("(ct-default"); - DUMPAST(ast->ct_default_stmt); - DUMPEND(); - case AST_GOTO_STMT: - DUMPF("(goto %s)", ast->goto_stmt.label_name); - return; - case AST_LABEL: - DUMPF("(label %s)", ast->label_stmt.name); - return; case AST_NOP_STMT: DUMP("(nop)"); return; - case AST_THROW_STMT: - DUMP("(throw"); - DUMPEXPR(ast->throw_stmt.throw_value); - DUMPEND(); case AST_VOLATILE_STMT: DUMP("(volatile"); DUMPAST(ast->volatile_stmt); diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index b90c9d4f5..ae1e3ac48 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -101,6 +101,7 @@ void compiler_compile(BuildTarget *target) Context *context = contexts[i]; llvm_codegen(context); } + print_arena_status(); exit(EXIT_SUCCESS); } diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 2d3b769b0..1b7e6ef92 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1,4 +1,7 @@ #pragma once +#pragma clang diagnostic push +#pragma ide diagnostic ignored "bugprone-reserved-identifier" + // Copyright (c) 2019 Christoffer Lerno. All rights reserved. // Use of this source code is governed by the GNU LGPLv3.0 license // a copy of which can be found in the LICENSE file. @@ -16,6 +19,7 @@ typedef uint32_t SourceLoc; #define INVALID_RANGE ((SourceRange){ .loc = UINT32_MAX }) #define EMPTY_TOKEN ((Token) { .string = NULL }) #define MAX_LOCALS 0xFFFF +#define MAX_FLAGS 0xFFFF #define MAX_SCOPE_DEPTH 0xFF #define MAX_PATH 1024 #define MAX_DEFERS 0xFFFF @@ -56,7 +60,6 @@ typedef struct int len; } string; Decl *enum_constant; - Decl *error_constant; }; // Valid type kinds: // bool, ints, floats, string @@ -232,12 +235,6 @@ typedef struct }; } Attr; -typedef struct -{ - Decl **error_constants; - void *start_value; -} ErrorDecl; - typedef struct { Path *path; @@ -255,12 +252,18 @@ typedef struct typedef struct _VarDecl { - unsigned id : 16; VarDeclKind kind : 3; bool constant : 1; + bool failable : 1; + bool unwrap : 1; TypeInfo *type_info; - Expr *init_expr; + union + { + Expr *init_expr; + Decl *alias; + }; void *backend_debug_ref; + void *failable_ref; } VarDecl; @@ -292,25 +295,15 @@ typedef struct TypeInfo *type_info; } EnumDecl; -typedef enum -{ - ERROR_RETURN_NONE = 0, - ERROR_RETURN_ONE = 1, - ERROR_RETURN_MANY = 2, - ERROR_RETURN_ANY = 3, -} ErrorReturn; - typedef struct _FunctionSignature { CallConvention convention : 4; bool variadic : 1; bool has_default : 1; - bool throw_any : 1; bool return_param : 1; - ErrorReturn error_return : 4; + bool failable : 1; TypeInfo *rtype; Decl** params; - TypeInfo** throws; const char *mangled_signature; } FunctionSignature; @@ -362,6 +355,7 @@ typedef struct typedef struct { + bool failable : 1; Decl **parameters; TypeInfo *rtype; // May be null! struct _Ast *body; @@ -375,6 +369,17 @@ typedef struct Path *path; // For redefinition } GenericDecl; +typedef struct +{ + Ast *defer; + void *break_target; + void *continue_target; + unsigned scope_id; + bool next_target : 1; + Ast *parent; + +} LabelDecl; + typedef struct { unsigned index : 32; @@ -422,7 +427,6 @@ typedef struct _Decl }; union { - ErrorDecl error; StructDecl strukt; EnumDecl enums; }; @@ -430,6 +434,7 @@ typedef struct _Decl ErrorConstantDecl error_constant; ImportDecl import; VarDecl var; + LabelDecl label; EnumConstantDecl enum_constant; FuncDecl func; AttrDecl attr; @@ -440,34 +445,23 @@ typedef struct _Decl CtIfDecl ct_elif_decl; Decl** ct_else_decl; Expr *incr_array_decl; - TypeInfo *throws; MemberDecl member_decl; }; } Decl; -typedef enum -{ - TRY_EXPR_ELSE_EXPR, - TRY_EXPR_ELSE_JUMP, - TRY_EXPR, - TRY_STMT, -} TryType; + typedef struct { - TryType type; - union - { - Expr *expr; - Ast *stmt; - }; + bool is_jump : 1; + Expr *expr; union { Expr *else_expr; Ast *else_stmt; }; void *jump_target; -} ExprTry; +} ExprElse; typedef struct { @@ -497,6 +491,8 @@ typedef struct Expr *left; Expr *right; BinaryOp operator; + bool left_maybe : 1; + bool right_maybe : 1; } ExprBinary; typedef struct @@ -505,6 +501,7 @@ typedef struct UnaryOp operator; } ExprUnary; + typedef struct { Expr* expr; @@ -512,33 +509,7 @@ typedef struct } ExprPostUnary; -typedef enum -{ - CATCH_TRY_ELSE, - CATCH_REGULAR, - CATCH_RETURN_ANY, - CATCH_RETURN_MANY, - CATCH_RETURN_ONE -} CatchKind; -typedef struct -{ - CatchKind kind; - union - { - Expr *try_else; - Ast *catch; - Decl *error; - }; - Ast *defer; -} CatchInfo; - -typedef struct -{ - bool is_completely_handled; - Ast *defer; - CatchInfo *catches; -} ThrowInfo; typedef struct { @@ -546,7 +517,6 @@ typedef struct bool is_pointer_call : 1; Expr *function; Expr **arguments; - ThrowInfo *throw_info; } ExprCall; typedef struct @@ -636,10 +606,17 @@ typedef struct Expr* value; } ExprDesignatedInit; +typedef struct +{ + const char *name; + SourceRange span; +} Label; + struct _Expr { ExprKind expr_kind : 8; ResolveStatus resolve_status : 3; + bool failable : 1; SourceRange span; Type *type; union { @@ -651,9 +628,12 @@ struct _Expr ExprRange range_expr; ExprStructValue struct_value_expr; ExprTypeAccess type_access; - ExprTry try_expr; + Expr *guard_expr; + Expr *trycatch_expr; + ExprElse else_expr; ExprBinary binary_expr; ExprTernary ternary_expr; + Expr *fail_check_expr; ExprUnary unary_expr; ExprPostUnary post_expr; ExprCall call_expr; @@ -667,14 +647,11 @@ struct _Expr ExprScope expr_scope; ExprFuncBlock expr_block; ExprMacroBlock macro_block; + Expr* failable_expr; + Ast** dexpr_list_expr; }; }; -typedef struct -{ - -} AstAttribute; - typedef struct { @@ -682,15 +659,6 @@ typedef struct DeferList defer_list; } AstCompoundStmt; -typedef struct -{ - const char *name; - uint16_t last_goto; - bool is_used : 1; - Ast *defer; - struct _Ast *in_defer; - void *backend_value; -} AstLabelStmt; typedef struct { @@ -700,79 +668,104 @@ typedef struct typedef struct { - Ast *decl; - Ast *cond; + Decl *label; + Expr *cond; Ast *body; + void *break_block; + void *continue_block; } AstWhileStmt; typedef struct { - Expr *expr; - Ast *body; DeferList expr_defer; DeferList body_defer; + union + { + struct + { + void *break_block; + void *continue_block; + }; + struct + { + Decl *label; + Expr *expr; + Ast *body; + }; + }; } AstDoStmt; typedef struct { - Ast *decl; - Ast *cond; + Decl *label; + Expr *cond; Ast *then_body; Ast *else_body; + void *break_block; } AstIfStmt; typedef struct { - Expr *expr; // NULL == DEFAULT + bool is_type; + union + { + TypeInfo *type_info; + Expr *expr; + }; Ast *body; - void *backend_value; + void *backend_block; } AstCaseStmt; typedef struct { - Ast *decl; - Ast *cond; + Decl *label; + Expr *cond; Ast **cases; + void *retry_block; + void *exit_block; + void *retry_var; + Ast *defer; } AstSwitchStmt; typedef struct { - Ast *init; + Decl *label; + Expr *init; Expr *cond; Expr *incr; Ast *body; + void *continue_block; + void *exit_block; } AstForStmt; - - typedef struct { - const char *label_name; - Ast *label; - DeferList defer; - union - { - struct _Ast *in_defer; - }; -} AstGotoStmt; - -typedef struct _AstDeferStmt -{ - bool emit_boolean : 1; Ast *body; // Compound statement Ast *prev_defer; - void *bool_var; } AstDeferStmt; -typedef struct _AstCatchStmt +typedef struct { - Decl *error_param; - struct _Ast *body; + bool is_switch : 1; + bool has_err_var : 1; + Decl *label; + union + { + Expr *catchable; + Decl *err_var; + }; + union + { + Ast *body; + Ast **cases; + }; void *block; + Ast *defer; } AstCatchStmt; + typedef struct _AstCtIfStmt { Expr *expr; @@ -781,12 +774,6 @@ typedef struct _AstCtIfStmt } AstCtIfStmt; -typedef struct _AstGenericCaseStmt -{ - TypeInfo **types; - struct _Ast *body; -} AstGenericCaseStmt; - typedef struct { Ast *stmt; @@ -799,11 +786,6 @@ typedef struct Ast **body; } AstCtSwitchStmt; -typedef struct -{ - TypeInfo **types; - Ast *body; -} AstCtCaseStmt; typedef struct { @@ -815,38 +797,67 @@ typedef struct typedef struct { + bool is_label; + union + { + Label label; + Ast *ast; + }; DeferList defers; } AstContinueBreakStmt; typedef struct { - Ast *prev; DeferList defers; + union + { + struct + { + Label label; + bool is_type; + union + { + Expr *target; + TypeInfo *type_info; + }; + }; + struct + { + Ast *case_switch_stmt; + Expr *switch_expr; + }; + }; } AstNextStmt; -typedef struct -{ - Expr *throw_value; - Ast *defer; -} AstThrowStmt; typedef struct { - Token name; + Expr *expr; + Token alias; Token constraints; - Token token; } AsmOperand; +typedef struct +{ + Expr *decl_expr; + Ast *body; +} AstTryStmt; + +typedef struct +{ + AsmOperand *inputs; + AsmOperand *outputs; + Token *clobbers; + Token *labels; +} AsmParams; + typedef struct { bool is_volatile : 1; bool is_inline : 1; bool is_goto : 1; - Expr *asm_template; - AsmOperand** output_operands; - AsmOperand** input_operands; - AsmOperand** clobbers; - Token* labels; + AsmParams *params; + Token *instructions; } AstAsmStmt; typedef struct _Ast @@ -855,42 +866,35 @@ typedef struct _Ast AstKind ast_kind : 8; union { - AstAttribute attribute; - AstAsmStmt asm_stmt; - AstCompoundStmt compound_stmt; - Decl *declare_stmt; - Expr *expr_stmt; - AstThrowStmt throw_stmt; - Ast *volatile_stmt; - AstLabelStmt label_stmt; - AstReturnStmt return_stmt; - AstWhileStmt while_stmt; - AstDoStmt do_stmt; - AstIfStmt if_stmt; - AstDeferStmt defer_stmt; - AstSwitchStmt switch_stmt; - AstCaseStmt case_stmt; - AstCtSwitchStmt ct_switch_stmt; - AstCtCaseStmt ct_case_stmt; - AstContinueBreakStmt continue_stmt; - AstContinueBreakStmt break_stmt; - Ast* ct_default_stmt; - AstNextStmt next_stmt; - AstCatchStmt catch_stmt; - AstGotoStmt goto_stmt; - AstForStmt for_stmt; - AstCtIfStmt ct_if_stmt; - AstCtIfStmt ct_elif_stmt; - Ast *ct_else_stmt; - AstCtForStmt ct_for_stmt; - AstGenericCaseStmt generic_case_stmt; - Ast *generic_default_stmt; - Ast** decl_expr_stmt; - AstScopedStmt scoped_stmt; + AstAsmStmt asm_stmt; // 24 + AstCompoundStmt compound_stmt; // 16 + Decl *declare_stmt; // 8 + Expr *expr_stmt; // 8 + AstTryStmt try_stmt; + Decl *define_stmt; // 8 + Ast *volatile_stmt; // 8 + AstReturnStmt return_stmt; // 16 + AstWhileStmt while_stmt; // 24 + AstDoStmt do_stmt; // 32 + AstIfStmt if_stmt; // 32 + AstDeferStmt defer_stmt; // 32 + AstSwitchStmt switch_stmt; // 24 + AstCaseStmt case_stmt; // 32 + AstCtSwitchStmt ct_switch_stmt; // 16 + AstContinueBreakStmt contbreak_stmt; // 8 + AstNextStmt next_stmt; // 16 + AstCatchStmt catch_stmt; // 32 + AstForStmt for_stmt; // 32 + AstCtIfStmt ct_if_stmt; // 24 + AstCtIfStmt ct_elif_stmt; // 24 + Ast *ct_else_stmt; // 8 + AstCtForStmt ct_for_stmt; // 64 + AstScopedStmt scoped_stmt; // 16 }; } Ast; + typedef struct _Module { Path *name; @@ -909,14 +913,17 @@ typedef struct _Module } Module; + typedef struct _DynamicScope { + unsigned scope_id; ScopeFlags flags; ScopeFlags flags_created; - unsigned throws; Decl **local_decl_start; DeferList defers; + Ast *current_defer; ExitType exit; + const char* label; } DynamicScope; @@ -928,37 +935,21 @@ typedef struct uint16_t source_file; File *current_file; SourceLoc last_in_range; - Token tok; - Token next_tok; } Lexer; -typedef enum -{ - THROW_TYPE_CALL_ANY, - THROW_TYPE_CALL_THROW_MANY, - THROW_TYPE_CALL_THROW_ONE, -} ThrowType; - -typedef struct -{ - SourceRange span; - ThrowType kind : 4; - ThrowInfo *throw_info; - TypeInfo **throws; -} Throw; typedef struct _Context { + unsigned numbering; + unsigned current_block; BuildTarget *target; Path *module_name; Token* module_parameters; File* file; Decl** imports; - Decl *specified_imports; Module *module; STable local_symbols; Decl **enums; - Decl **error_types; Decl **types; Decl **functions; Decl **methods; @@ -966,31 +957,30 @@ typedef struct _Context Decl **ct_ifs; Ast **defers; Decl *active_function_for_analysis; - Decl *active_type_for_analysis; - Decl **last_local; - Ast **labels; - Ast **gotos; Token *comments; Token *lead_comment; Token *trailing_comment; Token *next_lead_comment; + unsigned scope_id; + Ast *break_target; + Ast *break_defer; + Ast *continue_target; + Ast *continue_defer; + Ast *next_target; + Ast *next_switch; + Ast *next_defer; DynamicScope *current_scope; struct { Type *expected_block_type; Ast **returns; + bool expr_failable_return; // Reusable returns cache. Ast **returns_cache; }; Decl *evaluating_macro; - // Error handling - struct - { - Ast **throw; - Throw *error_calls; - int try_nesting; - }; Type *rtype; + bool failable_return; int in_volatile_section; struct { @@ -1000,7 +990,8 @@ typedef struct _Context int macro_counter; int macro_nesting; }; - Decl *locals[MAX_LOCALS]; + Decl* locals[MAX_LOCALS]; + Decl **last_local; DynamicScope scopes[MAX_SCOPE_DEPTH]; char path_scratch[MAX_PATH]; struct { @@ -1056,7 +1047,7 @@ extern Type *type_byte, *type_ushort, *type_uint, *type_ulong, *type_usize; extern Type *type_compint, *type_compfloat; extern Type *type_c_short, *type_c_int, *type_c_long, *type_c_longlong; extern Type *type_c_ushort, *type_c_uint, *type_c_ulong, *type_c_ulonglong; -extern Type *type_typeid, *type_error_union, *type_error_base; +extern Type *type_typeid, *type_error; extern const char *attribute_list[NUMBER_OF_ATTRIBUTES]; @@ -1072,13 +1063,13 @@ static inline bool ast_poison(Ast *ast) { ast->ast_kind = AST_POISONED; return f static inline Ast *new_ast(AstKind kind, SourceRange range) { - Ast *ast = malloc_arena(sizeof(Ast)); - memset(ast, 0, sizeof(Ast)); + Ast *ast = CALLOCS(Ast); ast->span = range; ast->ast_kind = kind; return ast; } + static inline Ast *extend_ast(Ast *ast, Token token) { ast->span.end_loc = token.span.end_loc; @@ -1140,14 +1131,15 @@ bool cast_implicit(Expr *expr, Type *to_type); bool cast(Expr *expr, Type *to_type, CastType cast_type); bool cast_binary_arithmetic(Expr *left, Expr *right, const char *action); CastKind cast_to_bool_kind(Type *type); -bool cast_to_runtime(Expr *expr); -void cast_to_smallest_runtime(Expr *expr); + +bool cast_implicitly_to_runtime(Expr *expr); void llvm_codegen(Context *context); void llvm_codegen_setup(); -bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr); +bool sema_expr_analyse_assign_right_side(Context *context, Expr *expr, Type *left_type, Expr *right, ExprFailableStatus lhs_is_failable); +bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr, bool may_be_failable); bool sema_analyse_expr(Context *context, Type *to, Expr *expr); bool sema_analyse_decl(Context *context, Decl *decl); @@ -1171,10 +1163,21 @@ Decl *decl_new_with_type(Token name, DeclKind decl_type, Visibility visibility); Decl *decl_new_var(Token name, TypeInfo *type, VarDeclKind kind, Visibility visibility); void decl_set_external_name(Decl *decl); const char *decl_var_to_string(VarDeclKind kind); +static inline Decl *decl_raw(Decl *decl) +{ + if (decl->decl_kind != DECL_VAR || decl->var.kind != VARDECL_ALIAS) return decl; + decl = decl->var.alias; + assert(decl->decl_kind != DECL_VAR || decl->var.kind != VARDECL_ALIAS); + return decl; +} -static inline bool decl_ok(Decl *decl) { return decl->decl_kind != DECL_POISONED; } +static inline bool decl_ok(Decl *decl) { return !decl || decl->decl_kind != DECL_POISONED; } static inline bool decl_poison(Decl *decl) { decl->decl_kind = DECL_POISONED; decl->resolve_status = RESOLVE_DONE; return false; } -static inline bool decl_is_struct_type(Decl *decl) { return decl->decl_kind == DECL_UNION || decl->decl_kind == DECL_STRUCT; } +static inline bool decl_is_struct_type(Decl *decl) +{ + DeclKind kind = decl->decl_kind; + return kind == DECL_UNION | kind == DECL_STRUCT | kind == DECL_ERR; +} static inline DeclKind decl_from_token(TokenType type) { if (type == TOKEN_STRUCT) return DECL_STRUCT; @@ -1216,6 +1219,8 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent); #pragma mark --- Lexer functions +Token lexer_scan_asm(Lexer *lexer); +Token lexer_scan_asm_constraint(Lexer *lexer); Token lexer_scan_token(Lexer *lexer); Token lexer_scan_ident_test(Lexer *lexer, const char *scan); void lexer_init_for_test(Lexer *lexer, const char *text, size_t len); @@ -1232,14 +1237,17 @@ Path *path_find_parent_path(Path *path); const char *resolve_status_to_string(ResolveStatus status); -#define SEMA_TOKEN_ERROR(_tok, ...) sema_error_range(_tok.span, __VA_ARGS__) -#define SEMA_ERROR(_node, ...) sema_error_range(_node->span, __VA_ARGS__) -#define SEMA_PREV(_node, ...) sema_prev_at_range(_node->span, __VA_ARGS__) +#define SEMA_TOKEN_ERROR(_tok, ...) sema_error_range((_tok).span, __VA_ARGS__) +#define SEMA_ERROR(_node, ...) sema_error_range((_node)->span, __VA_ARGS__) +#define SEMA_PREV(_node, ...) sema_prev_at_range((_node)->span, __VA_ARGS__) void sema_analysis_pass_process_imports(Context *context); void sema_analysis_pass_conditional_compilation(Context *context); void sema_analysis_pass_decls(Context *context); bool sema_add_local(Context *context, Decl *decl); +bool sema_unwrap_var(Context *context, Decl *decl); +bool sema_rewrap_var(Context *context, Decl *decl); + bool sema_analyse_statement(Context *context, Ast *statement); Decl *sema_resolve_symbol(Context *context, const char *symbol, Path *path, Decl **ambiguous_other_decl); bool sema_resolve_type_info(Context *context, TypeInfo *type_info); @@ -1277,19 +1285,10 @@ void *target_data_layout(); void *target_machine(); void *target_target(); -bool throw_completely_caught(TypeInfo *throw, CatchInfo *catches); -static inline Throw throw_new_union(SourceRange range, ThrowType type, ThrowInfo *info) -{ - return (Throw) { .kind = THROW_TYPE_CALL_ANY, .span = range, .throw_info = info }; -} -static inline Throw throw_new(SourceRange range, ThrowType type, ThrowInfo *info, TypeInfo **throws) -{ - return (Throw) { .kind = type, .span = range, .throw_info = info, .throws = throws }; -} - #define TOKEN_MAX_LENGTH 0xFFFF #define TOK2VARSTR(_token) _token.span.length, _token.start bool token_is_type(TokenType type); +bool token_is_any_type(TokenType type); bool token_is_symbol(TokenType type); const char *token_type_to_string(TokenType type); static inline Token wrap(const char *string) @@ -1308,11 +1307,10 @@ Type *type_unsigned_int_by_bitsize(unsigned bytesize); bool type_is_subtype(Type *type, Type *possible_subtype); Type *type_find_common_ancestor(Type *left, Type *right); const char *type_to_error_string(Type *type); -size_t type_size(Type *canonical); -unsigned int type_abi_alignment(Type *canonical); +size_t type_size(Type *type); +unsigned int type_abi_alignment(Type *type); void type_append_signature_name(Type *type, char *dst, size_t *offset); Type *type_find_max_type(Type *type, Type *other); - static inline bool type_is_builtin(TypeKind kind) { return kind >= TYPE_VOID && kind <= TYPE_FXX; } static inline bool type_kind_is_signed(TypeKind kind) { return kind >= TYPE_I8 && kind <= TYPE_I64; } static inline bool type_kind_is_unsigned(TypeKind kind) { return kind >= TYPE_U8 && kind <= TYPE_U64; } @@ -1327,7 +1325,6 @@ static inline Type *type_reduced(Type *type) { Type *canonical = type->canonical; if (canonical->type_kind == TYPE_ENUM) return canonical->decl->enums.type_info->type->canonical; - if (canonical->type_kind == TYPE_ERROR) return type_error_base; return canonical; } @@ -1382,6 +1379,24 @@ static inline bool type_info_poison(TypeInfo *type) return false; } +static inline bool type_is_ct(Type *type) +{ + while (1) + { + switch (type->type_kind) + { + case TYPE_FXX: + case TYPE_IXX: + return true; + case TYPE_TYPEDEF: + type = type->canonical; + break; + default: + return false; + } + } +} + static inline bool type_is_float(Type *type) { assert(type == type->canonical); @@ -1390,8 +1405,7 @@ static inline bool type_is_float(Type *type) static inline TypeInfo *type_info_new(TypeInfoKind kind, SourceRange range) { - TypeInfo *type_info = malloc_arena(sizeof(TypeInfo)); - memset(type_info, 0, sizeof(TypeInfo)); + TypeInfo *type_info = CALLOCS(TypeInfo); type_info->kind = kind; type_info->span = range; type_info->resolve_status = RESOLVE_NOT_DONE; @@ -1400,8 +1414,7 @@ static inline TypeInfo *type_info_new(TypeInfoKind kind, SourceRange range) static inline TypeInfo *type_info_new_base(Type *type, SourceRange range) { - TypeInfo *type_info = malloc_arena(sizeof(TypeInfo)); - memset(type_info, 0, sizeof(TypeInfo)); + TypeInfo *type_info = CALLOCS(TypeInfo); type_info->kind = TYPE_INFO_IDENTIFIER; type_info->resolve_status = RESOLVE_DONE; type_info->type = type; @@ -1429,7 +1442,7 @@ static inline bool type_convert_will_trunc(Type *destination, Type *source) static inline bool type_is_numeric(Type *type) { - assert(type == type->canonical); + if (type->type_kind == TYPE_TYPEDEF) type = type->canonical; return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_FXX; } @@ -1463,4 +1476,5 @@ static inline void advance_and_verify(Context *context, TokenType token_type) { assert(context->tok.type == token_type); advance(context); -} \ No newline at end of file +} +#pragma clang diagnostic pop \ No newline at end of file diff --git a/src/compiler/context.c b/src/compiler/context.c index a9000806f..46ce69273 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -113,6 +113,7 @@ void context_register_global_decl(Context *context, Decl *decl) case DECL_STRUCT: case DECL_UNION: case DECL_TYPEDEF: + case DECL_ERR: vec_add(context->types, decl); decl_set_external_name(decl); break; @@ -120,18 +121,14 @@ void context_register_global_decl(Context *context, Decl *decl) vec_add(context->enums, decl); decl_set_external_name(decl); break; - case DECL_ERROR: - vec_add(context->error_types, decl); - decl_set_external_name(decl); - break; case DECL_ENUM_CONSTANT: - case DECL_ERROR_CONSTANT: case DECL_ARRAY_VALUE: case DECL_IMPORT: case DECL_CT_ELSE: case DECL_CT_ELIF: case DECL_ATTRIBUTE: case DECL_MEMBER: + case DECL_LABEL: UNREACHABLE break; case DECL_CT_IF: @@ -208,10 +205,6 @@ void context_print_ast(Context *context, FILE *file) { fprint_decl(file, context->types[i]); } - VECEACH(context->error_types, i) - { - fprint_decl(file, context->error_types[i]); - } VECEACH(context->functions, i) { fprint_decl(file, context->functions[i]); diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 7e21a49b3..670046ffd 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -4,24 +4,6 @@ // Only include this from compiler_common.h -typedef enum -{ - ASSIGNOP_ERROR, - ASSIGNOP_ASSIGN, - ASSIGNOP_MULT_ASSIGN, - ASSIGNOP_ADD_ASSIGN, - ASSIGNOP_SUB_ASSIGN, - ASSIGNOP_DIV_ASSIGN, - ASSIGNOP_MOD_ASSIGN, - ASSIGNOP_AND_ASSIGN, - ASSIGNOP_OR_ASSIGN, - ASSIGNOP_BIT_AND_ASSIGN, - ASSIGNOP_BIT_OR_ASSIGN, - ASSIGNOP_BIT_XOR_ASSIGN, - ASSIGNOP_SHR_ASSIGN, - ASSIGNOP_SHL_ASSIGN, -} AssignOp; - typedef enum { BINARYOP_ERROR, @@ -74,29 +56,23 @@ typedef enum AST_CATCH_STMT, AST_COMPOUND_STMT, AST_CONTINUE_STMT, + AST_DEFINE_STMT, AST_CT_IF_STMT, AST_CT_ELIF_STMT, AST_CT_ELSE_STMT, AST_CT_FOR_STMT, AST_CT_SWITCH_STMT, - AST_CT_DEFAULT_STMT, - AST_CT_CASE_STMT, AST_DECLARE_STMT, AST_DEFAULT_STMT, AST_DEFER_STMT, AST_DO_STMT, AST_EXPR_STMT, + AST_TRY_STMT, AST_FOR_STMT, - AST_GENERIC_CASE_STMT, - AST_GENERIC_DEFAULT_STMT, - AST_GOTO_STMT, AST_IF_STMT, - AST_LABEL, AST_NOP_STMT, AST_RETURN_STMT, - AST_DECL_EXPR_LIST, AST_SWITCH_STMT, - AST_THROW_STMT, AST_NEXT_STMT, AST_VOLATILE_STMT, AST_WHILE_STMT, @@ -113,9 +89,9 @@ typedef enum typedef enum { CAST_ERROR, - CAST_ERREU, - CAST_EUERR, CAST_EUBOOL, + CAST_EUER, + CAST_EREU, CAST_XIERR, CAST_PTRPTR, CAST_PTRXI, @@ -140,34 +116,9 @@ typedef enum CAST_ENUMSI, CAST_APTSA, CAST_SAPTR, - /* - CAST_NONE, - CAST_INLINE, - CAST_FAILED, - CAST_SUBTYPE, - CAST_VARRPTR, - CAST_STRPTR, - CAST_ARRPTR, - CAST_INTPTR, - CAST_PTRINT, - CAST_PTRBOOL, - CAST_FPFP, - CAST_FPUI, - CAST_FPSI, - CAST_FPBOOL, - CAST_UIFP, - CAST_UIUI, - CAST_UISI, - CAST_SIFP, - CAST_SISI, - CAST_SIUI, - CAST_INTBOOL, - CAST_BOOLFP, - CAST_BOOLINT, - CAST_RVALUE,*/ } CastKind; -typedef enum _CastType +typedef enum { CAST_TYPE_EXPLICIT, CAST_TYPE_IMPLICIT, @@ -181,14 +132,14 @@ typedef enum DECL_POISONED = 0, DECL_FUNC, DECL_VAR, + DECL_LABEL, DECL_ENUM_CONSTANT, DECL_TYPEDEF, DECL_STRUCT, DECL_MEMBER, DECL_UNION, DECL_ENUM, - DECL_ERROR, - DECL_ERROR_CONSTANT, + DECL_ERR, DECL_ARRAY_VALUE, DECL_IMPORT, DECL_MACRO, @@ -203,18 +154,19 @@ typedef enum typedef enum { EXIT_NONE = 0, - EXIT_BREAK, - EXIT_NEXT, - EXIT_GOTO, - EXIT_CONTINUE, - EXIT_THROW, - EXIT_RETURN, + EXIT_BREAK = 1 << 1, + EXIT_NEXT = 1 << 2, + EXIT_CONTINUE = 1 << 5, + EXIT_RETURN = 1 << 6, } ExitType; typedef enum { EXPR_POISONED, + EXPR_GUARD, EXPR_TRY, + EXPR_CATCH, + EXPR_ELSE, EXPR_CONST, EXPR_BINARY, EXPR_TERNARY, @@ -237,16 +189,24 @@ typedef enum EXPR_RANGE, EXPR_DESIGNATED_INITIALIZER, EXPR_COMPOUND_LITERAL, + EXPR_FAILABLE, + EXPR_FAIL_CHECK, + EXPR_DECL_LIST } ExprKind; - +typedef enum +{ + FAILABLE_NO, + FAILABLE_YES, + FAILABLE_UNWRAPPED +} ExprFailableStatus; typedef enum { PREC_NONE, PREC_ASSIGNMENT, // =, *=, /=, %=, +=, etc - PREC_TRY, // try + PREC_TRY_ELSE, // try and else PREC_TERNARY, // ?: PREC_RANGE, // ... PREC_LOGICAL, // && || @@ -262,12 +222,10 @@ typedef enum typedef enum { SCOPE_NONE = 0, - SCOPE_BREAK = 1 << 0, - SCOPE_CONTINUE = 1 << 1, - SCOPE_NEXT = 1 << 2, - SCOPE_DEFER = 1 << 3, - SCOPE_EXPR_BLOCK = 1 << 4, - SCOPE_MACRO = 1 << 5 + SCOPE_DEFER = 1 << 4, + SCOPE_EXPR_BLOCK = 1 << 5, + SCOPE_MACRO = 1 << 6, + SCOPE_COND = 1 << 7, } ScopeFlags; typedef enum @@ -314,7 +272,7 @@ typedef enum TOKEN_LPAREN, // ( TOKEN_MINUS, // - TOKEN_MOD, // % - TOKEN_NOT, // ! + TOKEN_BANG, // ! TOKEN_OR, // | TOKEN_PLUS, // + TOKEN_QUESTION, // ? @@ -350,6 +308,7 @@ typedef enum TOKEN_SCOPE, // :: TOKEN_SHR, // >> TOKEN_SHL, // << + TOKEN_BANGBANG, // !! // Three or more TOKEN_ELLIPSIS, // ... @@ -390,11 +349,14 @@ typedef enum // Literals. - TOKEN_IDENT, // Any normal ident. TOKEN_CONST_IDENT, // Any purely upper case ident, TOKEN_TYPE_IDENT, // Any ident on the format FooBar or __FooBar + // Asm + TOKEN_ASM_STRING, + TOKEN_ASM_CONSTRAINT, + // We want to parse $foo separately. // Otherwise we allow things like "# foo" which would be pretty bad. TOKEN_CT_IDENT, // $foobar @@ -419,18 +381,18 @@ typedef enum TOKEN_CATCH, TOKEN_CONST, TOKEN_CONTINUE, + TOKEN_DEFINE, TOKEN_DEFAULT, TOKEN_DEFER, TOKEN_DO, TOKEN_ELSE, TOKEN_ENUM, - TOKEN_ERROR_TYPE, + TOKEN_ERR, TOKEN_EXTERN, TOKEN_FALSE, TOKEN_FOR, TOKEN_FUNC, TOKEN_GENERIC, - TOKEN_GOTO, TOKEN_IF, TOKEN_IMPORT, TOKEN_IN, @@ -443,8 +405,6 @@ typedef enum TOKEN_RETURN, TOKEN_STRUCT, TOKEN_SWITCH, - TOKEN_THROW, - TOKEN_THROWS, TOKEN_TRUE, TOKEN_TRY, TOKEN_TYPEDEF, @@ -454,7 +414,6 @@ typedef enum TOKEN_VOLATILE, TOKEN_WHILE, TOKEN_TYPEOF, - TOKEN_ERRSET, TOKEN_CT_CASE, // $case TOKEN_CT_DEFAULT, // $default @@ -498,11 +457,11 @@ typedef enum TYPE_FXX, TYPE_POINTER, TYPE_ENUM, - TYPE_ERROR, TYPE_FUNC, TYPE_STRUCT, TYPE_UNION, - TYPE_ERROR_UNION, + TYPE_ERRTYPE, + TYPE_ERR_UNION, TYPE_TYPEDEF, TYPE_STRING, TYPE_ARRAY, @@ -547,6 +506,9 @@ typedef enum VARDECL_GLOBAL = 1, VARDECL_LOCAL = 2, VARDECL_PARAM = 3, + VARDECL_LOCAL_CT = 4, + VARDECL_LOCAL_CT_TYPE = 5, + VARDECL_ALIAS = 6, } VarDeclKind; typedef enum diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index b323003ab..598330b4c 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -161,7 +161,7 @@ static inline Token parse_nested_comment(Lexer *lexer) } next(lexer); } - if (reached_end(lexer)) + if (nesting > 0) { return error_token(lexer, "Missing '+/' to end the nested comment."); } @@ -470,6 +470,102 @@ static inline Token scan_string(Lexer *lexer) #pragma mark --- Lexer public functions +Token lexer_scan_asm_constraint(Lexer *lexer) +{ + // Skip the whitespace. + skip_whitespace(lexer); + + // Point start to the first non-whitespace character. + lexer->lexing_start = lexer->current; + + if (reached_end(lexer)) + { + return make_token(lexer, TOKEN_EOF, "\n"); + } + + // Move past '+=&' + char c; + while (1) + { + c = next(lexer); + if (c == '+' || c == '=' || c == '&') continue; + break; + } + + while (1) + { + if (is_letter(c) || is_digit(c)) + { + c = next(lexer); + continue; + } + if (c != ' ') + { + return error_token(lexer, "Invalid asm constraint"); + } + break; + } + + return make_token(lexer, TOKEN_ASM_CONSTRAINT, strcopy(lexer->lexing_start, lexer->current - lexer->lexing_start + 1)); +} + +Token lexer_scan_asm(Lexer *lexer) +{ + // Skip the whitespace. + skip_whitespace(lexer); + + // Point start to the first non-whitespace character. + lexer->lexing_start = lexer->current; + + if (reached_end(lexer)) + { + return make_token(lexer, TOKEN_EOF, "\n"); + } + + int bracket = 0; + const char* last_non_whitespace = lexer->lexing_start; + while (1) + { + char c = next(lexer); + switch (c) + { + case '\n': + break; + case ';': + while (!reached_end(lexer) && next(lexer) != '\n'); + break; + case '\t': + case '\r': + case '\f': + case ' ': + continue; + case '{': + bracket++; + last_non_whitespace = lexer->current - 1; + continue; + case '}': + if (--bracket >= 0) + { + // Matched bracket. + last_non_whitespace = lexer->current - 1; + continue; + } + // Non matched right bracket. + // If this is the first non whitespace this is an end of asm. + if (lexer->lexing_start == lexer->current - 1) + { + return make_token(lexer, TOKEN_RBRACE, "}"); + } + // Otherwise we need to return the previous as a token. + break; + default: + last_non_whitespace = lexer->current - 1; + continue; + } + return make_token(lexer, TOKEN_ASM_STRING, strcopy(lexer->lexing_start, last_non_whitespace - lexer->lexing_start + 1)); + } +} + Token lexer_scan_token(Lexer *lexer) { // Now skip the whitespace. @@ -520,7 +616,8 @@ Token lexer_scan_token(Lexer *lexer) case ':': return match(lexer, ':') ? make_token(lexer, TOKEN_SCOPE, "::") : make_token(lexer, TOKEN_COLON, ":"); case '!': - return match(lexer, '=') ? make_token(lexer, TOKEN_NOT_EQUAL, "!=") : make_token(lexer, TOKEN_NOT, "!"); + if (match(lexer, '!')) return make_token(lexer, TOKEN_BANGBANG, "!!"); + return match(lexer, '=') ? make_token(lexer, TOKEN_NOT_EQUAL, "!=") : make_token(lexer, TOKEN_BANG, "!"); case '/': if (match(lexer, '/')) return parse_line_comment(lexer); if (match(lexer, '*')) return parse_multiline_comment(lexer); diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 4ac73777b..40006f1d0 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -52,10 +52,9 @@ LLVMValueRef gencontext_emit_memclear_size_align(GenContext *context, LLVMValueR LLVMValueRef gencontext_emit_memclear(GenContext *context, LLVMValueRef ref, Type *type) { - Type *canonical = type->canonical; // TODO avoid bitcast on those that do not need them. - return gencontext_emit_memclear_size_align(context, ref, type_size(canonical), - type_abi_alignment(canonical), true); + return gencontext_emit_memclear_size_align(context, ref, type_size(type), + type_abi_alignment(type), true); } @@ -74,7 +73,7 @@ static void gencontext_emit_global_variable_definition(GenContext *context, Decl } else { - LLVMSetInitializer(decl->ref, LLVMConstInt(llvm_type(type_bool), 0, false)); + LLVMSetInitializer(decl->ref, LLVMConstNull(llvm_type(decl->type))); } // If read only: LLVMSetGlobalConstant(decl->var.backend_ref, 1); @@ -116,8 +115,7 @@ static void gencontext_verify_ir(GenContext *context) { char *error = NULL; assert(context->module); - LLVMVerifyModule(context->module, LLVMPrintMessageAction, &error); - if (error) + if (LLVMVerifyModule(context->module, LLVMPrintMessageAction, &error)) { if (*error) { @@ -137,7 +135,7 @@ void gencontext_emit_object_file(GenContext *context) // Generate .o or .obj file char *filename = strformat("%.*s.o", (int)strlen(context->ast_context->file->name) - 3, context->ast_context->file->name); - if (LLVMTargetMachineEmitToFile(target_machine(), context->module, filename, LLVMObjectFile, &err) != 0) + if (LLVMTargetMachineEmitToFile(target_machine(), context->module, filename, LLVMObjectFile, &err)) { error_exit("Could not emit object file: %s", err); } @@ -147,7 +145,7 @@ void gencontext_print_llvm_ir(GenContext *context) { char *err = NULL; char *filename = strformat("%.*s.ll", (int)strlen(context->ast_context->file->name) - 3, context->ast_context->file->name); - if (LLVMPrintModuleToFile(context->module, filename, &err) != 0) + if (LLVMPrintModuleToFile(context->module, filename, &err)) { error_exit("Could not emit ir to file: %s", err); } @@ -239,26 +237,25 @@ void llvm_codegen_setup() intrinsics_setup = true; } -void gencontext_emit_struct_decl(GenContext *context, Decl *decl) +void gencontext_emit_introspection_type(GenContext *context, Decl *decl) { llvm_type(decl->type); - LLVMValueRef global_name = LLVMAddGlobal(context->module, llvm_type(type_bool), decl->name); - LLVMSetLinkage(global_name, LLVMInternalLinkage); + LLVMValueRef global_name = LLVMAddGlobal(context->module, llvm_type(type_byte), decl->name); LLVMSetGlobalConstant(global_name, 1); - LLVMSetInitializer(global_name, LLVMConstInt(llvm_type(type_bool), 1, false)); + LLVMSetInitializer(global_name, LLVMConstInt(llvm_type(type_byte), 1, false)); + decl->type->backend_typeid = LLVMBuildPtrToInt(context->builder, global_name, llvm_type(type_typeid), ""); - decl->type->backend_typeid = global_name; switch (decl->visibility) { case VISIBLE_MODULE: - LLVMSetVisibility(global_name, LLVMProtectedVisibility); - break; case VISIBLE_PUBLIC: + LLVMSetLinkage(global_name, LLVMLinkOnceODRLinkage); LLVMSetVisibility(global_name, LLVMDefaultVisibility); break; case VISIBLE_EXTERN: case VISIBLE_LOCAL: LLVMSetVisibility(global_name, LLVMHiddenVisibility); + LLVMSetLinkage(global_name, LLVMLinkerPrivateLinkage); break; } } @@ -275,35 +272,6 @@ static inline uint32_t upper_power_of_two(uint32_t v) return v; } -void gencontext_emit_error_decl(GenContext *context, Decl *decl) -{ - unsigned slots = vec_size(decl->error.error_constants) + 1; - LLVMTypeRef reserved_type = LLVMArrayType(llvm_type(type_char), slots); - char *buffer = strcat_arena(decl->external_name, "_DOMAIN"); - LLVMValueRef global_name = LLVMAddGlobal(context->module, reserved_type, buffer); - LLVMSetLinkage(global_name, LLVMExternalLinkage); - LLVMSetGlobalConstant(global_name, 1); - LLVMSetInitializer(global_name, LLVMConstAllOnes(reserved_type)); - decl->error.start_value = LLVMBuildPtrToInt(context->builder, global_name, llvm_type(type_usize), ""); - uint32_t min_align = upper_power_of_two(slots); - uint32_t pointer_align = type_abi_alignment(type_voidptr); - LLVMSetAlignment(global_name, pointer_align > min_align ? pointer_align : min_align); - LLVMSetVisibility(global_name, LLVMDefaultVisibility); - switch (decl->visibility) - { - case VISIBLE_MODULE: - LLVMSetVisibility(global_name, LLVMProtectedVisibility); - break; - case VISIBLE_PUBLIC: - LLVMSetVisibility(global_name, LLVMDefaultVisibility); - break; - case VISIBLE_EXTERN: - case VISIBLE_LOCAL: - LLVMSetVisibility(global_name, LLVMHiddenVisibility); - break; - } -} - static void gencontext_emit_decl(GenContext *context, Decl *decl) { @@ -324,17 +292,12 @@ static void gencontext_emit_decl(GenContext *context, Decl *decl) break;; case DECL_STRUCT: case DECL_UNION: - gencontext_emit_struct_decl(context, decl); + case DECL_ERR: + gencontext_emit_introspection_type(context, decl); break; case DECL_ENUM: // TODO break; - case DECL_ERROR: - UNREACHABLE; - break;; - case DECL_ERROR_CONSTANT: - //TODO - break;; case DECL_ARRAY_VALUE: case DECL_IMPORT: case DECL_MACRO: @@ -344,6 +307,7 @@ static void gencontext_emit_decl(GenContext *context, Decl *decl) case DECL_CT_ELIF: case DECL_ATTRIBUTE: case DECL_MEMBER: + case DECL_LABEL: UNREACHABLE } } @@ -371,10 +335,6 @@ void llvm_codegen(Context *context) { gencontext_emit_decl(&gen_context, context->types[i]); } - VECEACH(context->error_types, i) - { - gencontext_emit_error_decl(&gen_context, context->error_types[i]); - } VECEACH(context->vars, i) { gencontext_emit_global_variable_definition(&gen_context, context->vars[i]); @@ -429,6 +389,8 @@ void llvm_codegen(Context *context) // Serialize the LLVM IR, if requested if (build_options.emit_llvm) gencontext_print_llvm_ir(&gen_context); + gencontext_verify_ir(&gen_context); + if (build_options.emit_bitcode) gencontext_emit_object_file(&gen_context); gencontext_end_module(&gen_context); diff --git a/src/compiler/llvm_codegen_debug_info.c b/src/compiler/llvm_codegen_debug_info.c index 0aafe7645..a3aba8dc2 100644 --- a/src/compiler/llvm_codegen_debug_info.c +++ b/src/compiler/llvm_codegen_debug_info.c @@ -18,10 +18,10 @@ static inline LLVMMetadataRef gencontext_create_debug_type_from_decl(GenContext case DECL_CT_ELSE: case DECL_CT_ELIF: case DECL_VAR: - case DECL_ERROR_CONSTANT: case DECL_ARRAY_VALUE: case DECL_IMPORT: case DECL_MEMBER: + case DECL_LABEL: UNREACHABLE; case DECL_FUNC: { @@ -45,7 +45,7 @@ static inline LLVMMetadataRef gencontext_create_debug_type_from_decl(GenContext TODO case DECL_ENUM: TODO - case DECL_ERROR: + case DECL_ERR: TODO } UNREACHABLE @@ -134,7 +134,7 @@ LLVMMetadataRef gencontext_get_debug_type(GenContext *context, Type *type) return type->backend_debug_type = LLVMDIBuilderCreatePointerType(context->debug.builder, type->pointer->backend_debug_type, type_size(type->canonical->pointer), 0, 0, type->name, strlen(type->name)); case TYPE_ENUM: TODO - case TYPE_ERROR: + case TYPE_ERRTYPE: TODO case TYPE_FUNC: // TODO @@ -168,7 +168,7 @@ LLVMMetadataRef gencontext_get_debug_type(GenContext *context, Type *type) TODO case TYPE_SUBARRAY: TODO - case TYPE_ERROR_UNION: + case TYPE_ERR_UNION: TODO } UNREACHABLE diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 8dbec4c2d..cad1e7f64 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -130,11 +130,17 @@ static LLVMValueRef gencontext_emit_member_addr(GenContext *context, LLVMValueRe value = gencontext_emit_member_addr(context, value, parent, current_parent); } - if (current_parent->type->canonical->type_kind == TYPE_UNION) + switch (current_parent->type->canonical->type_kind) { - return LLVMBuildBitCast(context->builder, value, LLVMPointerType(llvm_type(member->type), 0), member->name); + case TYPE_UNION: + return LLVMBuildBitCast(context->builder, value, LLVMPointerType(llvm_type(member->type), 0), member->name); + case TYPE_ERRTYPE: + return LLVMBuildStructGEP2(context->builder, llvm_type(current_parent->type), value, member->member_decl.index + 1, member->name); + case TYPE_STRUCT: + return LLVMBuildStructGEP2(context->builder, llvm_type(current_parent->type), value, member->member_decl.index, member->name); + default: + UNREACHABLE } - return LLVMBuildStructGEP2(context->builder, llvm_type(current_parent->type), value, member->member_decl.index, member->name); } @@ -168,15 +174,15 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr) { case EXPR_RANGE: TODO - case EXPR_EXPR_BLOCK: - TODO case EXPR_DESIGNATED_INITIALIZER: // Should only appear when generating designated initializers. UNREACHABLE case EXPR_MACRO_BLOCK: TODO + case EXPR_FAIL_CHECK: + UNREACHABLE case EXPR_IDENTIFIER: - return expr->identifier_expr.decl->ref; + return decl_ref(expr->identifier_expr.decl); case EXPR_UNARY: assert(expr->unary_expr.operator == UNARYOP_DEREF); return gencontext_emit_expr(context, expr->unary_expr.expr); @@ -193,7 +199,7 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr) case EXPR_CONST: case EXPR_TYPEID: case EXPR_POISONED: - case EXPR_TRY: + case EXPR_GUARD: case EXPR_BINARY: case EXPR_TERNARY: case EXPR_POST_UNARY: @@ -203,18 +209,17 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr) case EXPR_EXPRESSION_LIST: case EXPR_CAST: case EXPR_TYPEOF: + case EXPR_FAILABLE: + case EXPR_CATCH: + case EXPR_TRY: + case EXPR_EXPR_BLOCK: + case EXPR_DECL_LIST: + case EXPR_ELSE: UNREACHABLE } UNREACHABLE } -static inline LLVMValueRef gencontext_emit_error_cast(GenContext *context, LLVMValueRef value, Type *type) -{ - LLVMValueRef global = type->decl->error.start_value; - LLVMValueRef extend = LLVMBuildZExtOrBitCast(context->builder, value, llvm_type(type_usize), ""); - return LLVMBuildAdd(context->builder, global, extend, ""); -} - LLVMValueRef gencontext_emit_arr_to_subarray_cast(GenContext *context, LLVMValueRef value, Type *to_type, Type *from_type) { size_t size = from_type->pointer->array.len; @@ -230,6 +235,13 @@ LLVMValueRef gencontext_emit_subarray_to_ptr_cast(GenContext *context, LLVMValue return LLVMBuildExtractValue(context->builder, value, 0, ""); } +LLVMValueRef gencontext_emit_value_bitcast(GenContext *context, LLVMValueRef value, Type *to_type, Type *from_type) +{ + LLVMValueRef ptr = gencontext_emit_alloca(context, llvm_type(from_type), ""); + LLVMBuildStore(context->builder, value, ptr); + LLVMValueRef ptr_cast = gencontext_emit_bitcast(context, ptr, type_get_ptr(to_type)); + return gencontext_emit_load(context, to_type, ptr_cast); +} LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMValueRef value, Type *to_type, Type *from_type) { switch (cast_kind) @@ -253,14 +265,13 @@ LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMV TODO case CAST_STRPTR: TODO - case CAST_ERREU: - return gencontext_emit_error_cast(context, value, from_type); - case CAST_EUERR: - TODO + case CAST_EREU: + case CAST_EUER: + return gencontext_emit_value_bitcast(context, value, to_type, from_type); case CAST_EUBOOL: - return LLVMBuildICmp(context->builder, LLVMIntNE, value, llvm_int(type_usize, 0), "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(to_type->canonical->pointer)), "ptrbool"); + return LLVMBuildICmp(context->builder, LLVMIntNE, value, LLVMConstPointerNull(llvm_type(to_type->canonical)), "ptrbool"); case CAST_BOOLINT: return LLVMBuildTrunc(context->builder, value, llvm_type(to_type), "boolsi"); case CAST_FPBOOL: @@ -330,6 +341,14 @@ static inline LLVMValueRef gencontext_emit_initializer_list_expr_addr(GenContext gencontext_emit_memclear(context, ref, canonical); } + bool is_error = expr->type->canonical->type_kind == TYPE_ERRTYPE; + + if (is_error) + { + LLVMValueRef err_type = LLVMBuildStructGEP2(context->builder, type, ref, 0, ""); + LLVMBuildStore(context->builder, expr->type->canonical->backend_typeid, err_type); + } + if (expr->expr_initializer.init_type == INITIALIZER_ZERO) { return ref; @@ -338,6 +357,7 @@ static inline LLVMValueRef gencontext_emit_initializer_list_expr_addr(GenContext Expr **elements = expr->expr_initializer.initializer_expr; bool is_union = expr->type->canonical->type_kind == TYPE_UNION; + if (expr->expr_initializer.init_type == INITIALIZER_NORMAL) { if (is_union) @@ -352,7 +372,7 @@ static inline LLVMValueRef gencontext_emit_initializer_list_expr_addr(GenContext { Expr *element = elements[i]; LLVMValueRef init_value = gencontext_emit_expr(context, element); - LLVMValueRef subref = LLVMBuildStructGEP2(context->builder, type, ref, i, ""); + LLVMValueRef subref = LLVMBuildStructGEP2(context->builder, type, ref, i + (int)is_error, ""); LLVMBuildStore(context->builder, init_value, subref); } return ref; @@ -360,6 +380,7 @@ static inline LLVMValueRef gencontext_emit_initializer_list_expr_addr(GenContext VECEACH(elements, i) { + if (is_error) TODO Expr *element = elements[i]; DesignatedPath *path = element->designated_init_expr.path; LLVMValueRef sub_value = gencontext_emit_expr(context, element->designated_init_expr.value); @@ -455,8 +476,16 @@ LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr) case UNARYOP_ERROR: FATAL_ERROR("Illegal unary op %s", expr->unary_expr.operator); case UNARYOP_NOT: - return LLVMBuildXor(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), - llvm_int(type_bool, 1), "not"); + if (type_is_float(type)) + { + LLVMValueRef val = gencontext_emit_expr(context, expr->unary_expr.expr); + return LLVMBuildFCmp(context->builder, LLVMRealUNE, val, LLVMConstNull(llvm_type(type)), ""); + } + else + { + LLVMValueRef val = gencontext_emit_expr(context, expr->unary_expr.expr); + return LLVMBuildICmp(context->builder, LLVMIntEQ, val, LLVMConstNull(llvm_type(type)), ""); + } case UNARYOP_BITNEG: return LLVMBuildNot(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "bnot"); case UNARYOP_NEGMOD: @@ -813,15 +842,6 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM UNREACHABLE } -LLVMBasicBlockRef gencontext_get_try_target(GenContext *context, Expr *try_expr) -{ - if (!try_expr->try_expr.jump_target) - { - try_expr->try_expr.jump_target = gencontext_create_free_block(context, "tryjump"); - } - return try_expr->try_expr.jump_target; -} - LLVMValueRef gencontext_emit_post_unary_expr(GenContext *context, Expr *expr) { @@ -838,41 +858,45 @@ LLVMValueRef gencontext_emit_typeid(GenContext *context, Expr *expr) return expr->typeid_expr->type->backend_typeid; } -LLVMValueRef gencontext_emit_try_expr(GenContext *context, Expr *expr) +LLVMValueRef gencontext_emit_trycatch_expr(GenContext *context, Expr *expr) { - if (expr->try_expr.type == TRY_STMT) - { - gencontext_emit_stmt(context, expr->try_expr.stmt); - return NULL; - } - if (expr->try_expr.type == TRY_EXPR_ELSE_EXPR) - { - LLVMBasicBlockRef else_block = gencontext_get_try_target(context, expr); - LLVMBasicBlockRef after_catch = gencontext_create_free_block(context, "aftercatch"); - LLVMValueRef res = gencontext_emit_alloca(context, llvm_type(expr->try_expr.else_expr->type), "catch"); - LLVMValueRef normal_res = gencontext_emit_expr(context, expr->try_expr.expr); - LLVMBuildStore(context->builder, normal_res, res); - gencontext_emit_br(context, after_catch); - gencontext_emit_block(context, else_block); - LLVMValueRef catch_value = gencontext_emit_expr(context, expr->try_expr.else_expr); - LLVMBuildStore(context->builder, catch_value, res); - gencontext_emit_br(context, after_catch); - gencontext_emit_block(context, after_catch); - return gencontext_emit_load(context, expr->try_expr.else_expr->type, res); - } - if (expr->try_expr.type == TRY_EXPR_ELSE_JUMP) - { - LLVMBasicBlockRef else_block = gencontext_get_try_target(context, expr); - LLVMValueRef val = gencontext_emit_expr(context, expr->try_expr.expr); - LLVMBasicBlockRef after_catch = gencontext_create_free_block(context, "aftercatch"); - gencontext_emit_br(context, after_catch); - gencontext_emit_block(context, else_block); - gencontext_emit_stmt(context, expr->try_expr.else_stmt); - gencontext_emit_br(context, after_catch); - gencontext_emit_block(context, after_catch); - return val; - } - return gencontext_emit_expr(context, expr->try_expr.expr); + + LLVMBasicBlockRef error_block = gencontext_create_free_block(context, "error_block"); + LLVMBasicBlockRef no_err_block = gencontext_create_free_block(context, "noerr_block"); + LLVMBasicBlockRef phi_block = gencontext_create_free_block(context, "phi_block"); + + // Store catch/error var + PUSH_ERROR(); + + // Set the catch/error var + context->error_var = NULL; + context->catch_block = error_block; + + gencontext_emit_expr(context, expr->trycatch_expr); + + // Restore. + POP_ERROR(); + + // Emit success and jump to phi. + gencontext_emit_br(context, no_err_block); + gencontext_emit_block(context, no_err_block); + gencontext_emit_br(context, phi_block); + + // Emit error and jump to phi + gencontext_emit_block(context, error_block); + gencontext_emit_br(context, phi_block); + + gencontext_emit_block(context, phi_block); + + LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(expr->type), "val"); + LLVMValueRef lhs = llvm_int(type_bool, expr->expr_kind == EXPR_TRY ? 1 : 0); + LLVMValueRef rhs = llvm_int(type_bool, expr->expr_kind == EXPR_TRY ? 0 : 1); + + LLVMValueRef logic_values[2] = { lhs, rhs }; + LLVMBasicBlockRef blocks[2] = { no_err_block, error_block }; + LLVMAddIncoming(phi, logic_values, blocks, 2); + + return phi; } static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, Expr *expr) @@ -890,7 +914,12 @@ static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, Expr *expr) if (binary_op == BINARYOP_ASSIGN) { LLVMValueRef addr = gencontext_emit_address(context, expr->binary_expr.left); - return gencontext_emit_assign_expr(context, addr, expr->binary_expr.right); + LLVMValueRef failable_ref = NULL; + if (expr->binary_expr.left->expr_kind == EXPR_IDENTIFIER) + { + failable_ref = decl_failable_ref(expr->binary_expr.left->identifier_expr.decl); + } + return gencontext_emit_assign_expr(context, addr, expr->binary_expr.right, failable_ref); } return gencontext_emit_binary(context, expr, NULL, binary_op); } @@ -915,14 +944,17 @@ LLVMValueRef gencontext_emit_elvis_expr(GenContext *context, Expr *expr) gencontext_emit_block(context, rhs_block); LLVMValueRef rhs = gencontext_emit_expr(context, expr->ternary_expr.else_expr); + LLVMBasicBlockRef end_block = context->current_block; + gencontext_emit_br(context, phi_block); // Generate phi gencontext_emit_block(context, phi_block); + LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(expr->type), "val"); LLVMValueRef logic_values[2] = { lhs, rhs }; - LLVMBasicBlockRef blocks[2] = { current_block, rhs_block }; + LLVMBasicBlockRef blocks[2] = { current_block, end_block }; LLVMAddIncoming(phi, logic_values, blocks, 2); return phi; @@ -944,10 +976,12 @@ LLVMValueRef gencontext_emit_ternary_expr(GenContext *context, Expr *expr) gencontext_emit_block(context, lhs_block); LLVMValueRef lhs = gencontext_emit_expr(context, expr->ternary_expr.then_expr); + LLVMBasicBlockRef lhs_exit = context->current_block; gencontext_emit_br(context, phi_block); gencontext_emit_block(context, rhs_block); LLVMValueRef rhs = gencontext_emit_expr(context, expr->ternary_expr.else_expr); + LLVMBasicBlockRef rhs_exit = context->current_block; gencontext_emit_br(context, phi_block); // Generate phi @@ -955,7 +989,7 @@ LLVMValueRef gencontext_emit_ternary_expr(GenContext *context, Expr *expr) LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(expr->type), "val"); LLVMValueRef logic_values[2] = { lhs, rhs }; - LLVMBasicBlockRef blocks[2] = { lhs_block, rhs_block }; + LLVMBasicBlockRef blocks[2] = { lhs_exit, rhs_exit }; LLVMAddIncoming(phi, logic_values, blocks, 2); return phi; @@ -993,271 +1027,12 @@ LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr) 0)); return global_name; } - case TYPE_ERROR: - return llvm_int(type_error_base, expr->const_expr.error_constant->error_constant.value); + case TYPE_ERRTYPE: + TODO default: UNREACHABLE } } -/* -static inline void gencontext_emit_throw_union_branch(GenContext *context, CatchInfo *catches, LLVMValueRef value) -{ - LLVMBasicBlockRef after_block = gencontext_create_free_block(context, "throwafter"); - - type_error_union - value = LLVMBuildExtractValue(context->builder, value, 1, ""); - LLVMValueRef comparison = LLVMBuildICmp(context->builder, LLVMIntNE, llvm_int(error_type, 0), value, "checkerr"); - - VECEACH(catches, i) - { - LLVMBasicBlockRef ret_err_block = gencontext_create_free_block(context, "ret_err"); - gencontext_emit_cond_br(context, comparison, ret_err_block, after_block); - gencontext_emit_block(context, ret_err_block); - - if (catches->kind == CATCH_TRY_ELSE) - { - - } - } - - if (error_type == type_error_union) - size_t catch_index = context->catch_stack_index; - while (catch_index > 0) - { - catch_index--; - // TODO check error - Catch *current_catch = &context->catch_stack[catch_index]; - if (!current_catch->decl) - { - LLVMValueRef zero = llvm_int(type_reduced(type_error), 0); - // TODO emit defers. - if (error_type == type_error_union) - { - } - LLVMValueRef comparison = LLVMBuildICmp(context->builder, LLVMIntNE, zero, value, "checkerr"); - gencontext_emit_cond_br(context, comparison, current_catch->catch_block, after_block); - gencontext_emit_block(context, after_block); - return; - } - } - // Fix defers gencontext_emit_defer(context, ast->throw_stmt.defers.start, ast->throw_stmt.defers.end); - - if (error_type == type_error_union) - { - gencontext_emit_load(context, type_error_union, context->error_out); - TODO - } - LLVMBuildRet(context->builder, value); - context->current_block = NULL; - context->current_block_is_target = false; - gencontext_emit_block(context, after_block); -} -*/ - - - - -static inline void gencontext_emit_throw_branch(GenContext *context, LLVMValueRef value, TypeInfo** errors, ThrowInfo *throw_info, ErrorReturn error_return) -{ - Type *call_error_type; - switch (error_return) - { - case ERROR_RETURN_NONE: - // If there is no error return, this is a no-op. - return; - case ERROR_RETURN_ONE: - call_error_type = type_error_base; - break; - case ERROR_RETURN_MANY: - case ERROR_RETURN_ANY: - call_error_type = type_error_union; - break;; - } - - LLVMBasicBlockRef after_block = gencontext_create_free_block(context, "throwafter"); - LLVMValueRef comparison = LLVMBuildICmp(context->builder, LLVMIntNE, llvm_int(call_error_type, 0), value, "checkerr"); - - unsigned catch_count = vec_size(throw_info->catches); - assert(throw_info->is_completely_handled && catch_count); - - // Special handling for a single catch. - if (catch_count == 1) - { - CatchInfo *catch = &throw_info->catches[0]; - switch (catch->kind) - { - case CATCH_RETURN_ONE: - { - LLVMBasicBlockRef else_block = gencontext_create_free_block(context, "erret_one"); - gencontext_emit_cond_br(context, comparison, else_block, after_block); - gencontext_emit_block(context, else_block); - gencontext_emit_defer(context, throw_info->defer, NULL); - gencontext_emit_return_value(context, value); - gencontext_emit_block(context, after_block); - return; - } - case CATCH_RETURN_MANY: - { - LLVMBasicBlockRef else_block = gencontext_create_free_block(context, "erret_many"); - gencontext_emit_cond_br(context, comparison, else_block, after_block); - gencontext_emit_block(context, else_block); - if (call_error_type == type_error_base) - { - value = gencontext_emit_cast(context, CAST_ERREU, value, type_error_union, call_error_type); - } - gencontext_emit_defer(context, throw_info->defer, NULL); - gencontext_emit_return_value(context, value); - gencontext_emit_block(context, after_block); - return; - } - case CATCH_TRY_ELSE: - { - LLVMBasicBlockRef else_block = gencontext_get_try_target(context, catch->try_else); - if (throw_info->defer != catch->defer) - { - LLVMBasicBlockRef defer_block = gencontext_create_free_block(context, "defer"); - gencontext_emit_cond_br(context, comparison, defer_block, after_block); - gencontext_emit_defer(context, throw_info->defer, catch->defer); - gencontext_emit_br(context, else_block); - } - else - { - gencontext_emit_cond_br(context, comparison, else_block, after_block); - } - gencontext_emit_block(context, after_block); - return; - } - case CATCH_RETURN_ANY: - { - LLVMBasicBlockRef else_block = gencontext_create_free_block(context, "erret_any"); - gencontext_emit_cond_br(context, comparison, else_block, after_block); - gencontext_emit_block(context, else_block); - if (call_error_type == type_error_base) - { - value = gencontext_emit_cast(context, CAST_ERREU, value, type_error_union, errors[0]->type); - } - gencontext_emit_defer(context, throw_info->defer, NULL); - gencontext_emit_return_value(context, value); - gencontext_emit_block(context, after_block); - return; - } - case CATCH_REGULAR: - { - gencontext_generate_catch_block_if_needed(context, catch->catch); - LLVMBasicBlockRef else_block = gencontext_create_free_block(context, "catch_regular"); - gencontext_emit_cond_br(context, comparison, else_block, after_block); - gencontext_emit_block(context, else_block); - Decl *error_param = catch->catch->catch_stmt.error_param; - if (call_error_type == type_error_base) - { - if (error_param->type == type_error_union) - { - value = gencontext_emit_cast(context, CAST_ERREU, value, type_error_union, errors[0]->type); - } - } - LLVMBuildStore(context->builder, value, error_param->ref); - gencontext_emit_defer(context, throw_info->defer, catch->defer); - gencontext_emit_br(context, catch->catch->catch_stmt.block); - gencontext_emit_block(context, after_block); - return; - } - } - UNREACHABLE - } - // Here we handle multiple branches. - - LLVMBasicBlockRef err_handling_block = gencontext_create_free_block(context, "errhandlingblock"); - gencontext_emit_cond_br(context, comparison, err_handling_block, after_block); - gencontext_emit_block(context, err_handling_block); - err_handling_block = NULL; - - assert(error_return != ERROR_RETURN_ONE && "Should already be handled."); - - // Below here we can assume we're handling error unions. - VECEACH(throw_info->catches, i) - { - if (err_handling_block == NULL) - { - err_handling_block = gencontext_create_free_block(context, "thrownext"); - } - CatchInfo *catch = &throw_info->catches[i]; - switch (catch->kind) - { - case CATCH_RETURN_ONE: - { - // This is always the last catch, so we can assume that we have the correct error - // type already. - LLVMValueRef negated = LLVMBuildNeg(context->builder, catch->error->error.start_value, ""); - LLVMValueRef final_value = LLVMBuildAnd(context->builder, negated, value, ""); - gencontext_emit_defer(context, throw_info->defer, NULL); - gencontext_emit_return_value(context, final_value); - gencontext_emit_block(context, after_block); - assert(i == vec_size(throw_info->catches) - 1); - return; - } - case CATCH_RETURN_MANY: - case CATCH_RETURN_ANY: - { - // This is simple, just return our value. - gencontext_emit_defer(context, throw_info->defer, NULL); - gencontext_emit_return_value(context, value); - gencontext_emit_block(context, after_block); - assert(i == vec_size(throw_info->catches) - 1); - return; - } - case CATCH_TRY_ELSE: - { - // This should be the last catch. - gencontext_emit_defer(context, throw_info->defer, catch->defer); - LLVMBasicBlockRef else_block = gencontext_get_try_target(context, catch->try_else); - gencontext_emit_br(context, else_block); - gencontext_emit_block(context, after_block); - assert(i == vec_size(throw_info->catches) - 1); - return; - } - case CATCH_REGULAR: - { - gencontext_generate_catch_block_if_needed(context, catch->catch); - Decl *error_param = catch->catch->catch_stmt.error_param; - Type *error_type = error_param->type->canonical; - - // The wildcard catch is always the last one. - if (error_type == type_error_union) - { - // Store the value, then jump - LLVMBuildStore(context->builder, value, error_param->ref); - gencontext_emit_defer(context, throw_info->defer, catch->defer); - gencontext_emit_br(context, catch->catch->catch_stmt.block); - gencontext_emit_block(context, after_block); - assert(i == vec_size(throw_info->catches) - 1); - return; - } - - // Here we have a normal catch. - - // wrapping(value - offset) < entries + 1 – this handles both cases since wrapping will make - // values below the offset big. - Decl *error_decl = error_type->decl; - LLVMValueRef comp_value = LLVMBuildSub(context->builder, value, error_decl->error.start_value, ""); - LLVMValueRef entries_value = llvm_int(type_error_union, vec_size(error_decl->error.error_constants) + 1); - LLVMValueRef match = LLVMBuildICmp(context->builder, LLVMIntULT, comp_value, entries_value, "regmatch"); - - LLVMBasicBlockRef match_block = gencontext_create_free_block(context, "match"); - gencontext_emit_cond_br(context, match, match_block, err_handling_block); - gencontext_emit_block(context, match_block); - gencontext_emit_defer(context, throw_info->defer, catch->defer); - - LLVMBuildStore(context->builder, comp_value, error_param->ref); - gencontext_emit_br(context, catch->catch->catch_stmt.block); - gencontext_emit_block(context, err_handling_block); - err_handling_block = NULL; - break; - } - } - } - gencontext_emit_br(context, after_block); - gencontext_emit_block(context, after_block); -} LLVMValueRef gencontext_emit_call_expr(GenContext *context, Expr *expr) { @@ -1305,9 +1080,9 @@ LLVMValueRef gencontext_emit_call_expr(GenContext *context, Expr *expr) values[param_index++] = gencontext_emit_expr(context, expr->call_expr.arguments[i]); } - LLVMValueRef call = LLVMBuildCall2(context->builder, func_type, func, values, args, "call"); + LLVMValueRef call = LLVMBuildCall2(context->builder, func_type, func, values, args, ""); - gencontext_emit_throw_branch(context, call, signature->throws, expr->call_expr.throw_info, signature->error_return); + //gencontext_emit_throw_branch(context, call, signature->throws, expr->call_expr.throw_info, signature->error_return); // If we used a return param, then load that info here. if (return_param) @@ -1390,7 +1165,7 @@ static inline LLVMValueRef gencontext_emit_macro_block(GenContext *context, Expr Decl *decl = expr->macro_block.params[i]; decl->ref = gencontext_emit_alloca(context, llvm_type(decl->type), decl->name); LLVMValueRef value = gencontext_emit_expr(context, expr->macro_block.args[i]); - LLVMBuildStore(context->builder, value, decl->ref); + gencontext_emit_store(context, decl, value); } VECEACH(stmts, i) @@ -1416,23 +1191,119 @@ LLVMValueRef gencontext_emit_call_intrinsic(GenContext *context, unsigned intrin return LLVMBuildCall2(context->builder, type, decl, values, arg_count, ""); } -LLVMValueRef gencontext_emit_assign_expr(GenContext *context, LLVMValueRef ref, Expr *expr) +LLVMValueRef gencontext_emit_assign_expr(GenContext *context, LLVMValueRef ref, Expr *expr, LLVMValueRef failable_ref) { + LLVMBasicBlockRef assign_block = NULL; + + PUSH_ERROR(); + + if (failable_ref) + { + assign_block = gencontext_create_free_block(context, "after_assign"); + + context->error_var = failable_ref; + context->catch_block = assign_block; + + } + LLVMValueRef value; switch (expr->expr_kind) { case EXPR_INITIALIZER_LIST: - return gencontext_emit_load(context, + value = gencontext_emit_load(context, expr->type, gencontext_emit_initializer_list_expr_addr(context, expr, ref)); default: + value = gencontext_emit_expr(context, expr); + LLVMBuildStore(context->builder, value, ref); break; } - LLVMValueRef value = gencontext_emit_expr(context, expr); - LLVMBuildStore(context->builder, value, ref); + if (failable_ref) + { + LLVMBuildStore(context->builder, gencontext_emit_no_error_union(context), failable_ref); + } + POP_ERROR(); + + if (failable_ref) + { + gencontext_emit_br(context, assign_block); + gencontext_emit_block(context, assign_block); + + } + return value; } +static inline LLVMValueRef gencontext_emit_identifier_rvalue(GenContext *context, Decl *decl) +{ + if (decl->decl_kind != DECL_VAR || !decl->var.failable) + { + return gencontext_emit_load(context, decl->type, decl_ref(decl)); + } + + assert(context->catch_block); + LLVMBasicBlockRef after_block = gencontext_create_free_block(context, "after_check"); + LLVMValueRef error_value = gencontext_emit_load(context, type_error, decl_failable_ref(decl)); + LLVMValueRef comp = LLVMBuildICmp(context->builder, LLVMIntEQ, error_value, + gencontext_emit_no_error_union(context), ""); + if (context->error_var) + { + LLVMBasicBlockRef error_block = gencontext_create_free_block(context, "error"); + gencontext_emit_cond_br(context, comp, after_block, error_block); + gencontext_emit_block(context, error_block); + LLVMBuildStore(context->builder, error_value, gencontext_emit_bitcast(context, context->error_var, type_get_ptr(type_error))); + gencontext_emit_br(context, context->catch_block); + } + else + { + gencontext_emit_cond_br(context, comp, after_block, context->catch_block); + } + gencontext_emit_block(context, after_block); + return gencontext_emit_load(context, decl->type, decl_ref(decl)); +} + +static inline LLVMValueRef gencontext_emit_failable(GenContext *context, Expr *expr) +{ + Expr *fail = expr->failable_expr; + if (context->error_var) + { + assert(context->error_var); + LLVMValueRef value = gencontext_emit_expr(context, fail); + LLVMBuildStore(context->builder, value, gencontext_emit_bitcast(context, context->error_var, type_get_ptr(fail->type))); + } + gencontext_emit_br(context, context->catch_block); + LLVMBasicBlockRef ignored_block = gencontext_create_free_block(context, ""); + gencontext_emit_block(context, ignored_block); + return LLVMGetUndef(llvm_type(expr->type)); +} + +LLVMValueRef gencontext_emit_check_failable(GenContext *context, Expr *expr) +{ + PUSH_ERROR(); + + LLVMBasicBlockRef after_check_block = gencontext_create_free_block(context, "after_check"); + + context->error_var = NULL; + context->catch_block = after_check_block; + + gencontext_emit_expr(context, expr->fail_check_expr); + + POP_ERROR(); + + LLVMBasicBlockRef phi_block = gencontext_create_free_block(context, "checkphi"); + LLVMBasicBlockRef normal_block = context->current_block; + gencontext_emit_br(context, phi_block); + gencontext_emit_block(context, after_check_block); + gencontext_emit_br(context, phi_block); + + gencontext_emit_block(context, phi_block); + LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(type_bool), "val"); + LLVMValueRef logic_values[2] = { llvm_int(type_bool, 0), llvm_int(type_bool, 1) }; + LLVMBasicBlockRef blocks[2] = { after_check_block, normal_block }; + LLVMAddIncoming(phi, logic_values, blocks, 2); + return phi; +} + LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr) { NESTED_RETRY: @@ -1440,10 +1311,20 @@ NESTED_RETRY: { case EXPR_RANGE: case EXPR_POISONED: + case EXPR_DECL_LIST: UNREACHABLE case EXPR_DESIGNATED_INITIALIZER: // Should only appear when generating designated initializers. UNREACHABLE + case EXPR_FAIL_CHECK: + return gencontext_emit_check_failable(context, expr); + case EXPR_FAILABLE: + return gencontext_emit_failable(context, expr); + case EXPR_TRY: + case EXPR_CATCH: + return gencontext_emit_trycatch_expr(context, expr); + case EXPR_ELSE: + TODO case EXPR_MACRO_BLOCK: return gencontext_emit_macro_block(context, expr); case EXPR_COMPOUND_LITERAL: @@ -1465,8 +1346,8 @@ NESTED_RETRY: return gencontext_emit_ternary_expr(context, expr); case EXPR_POST_UNARY: return gencontext_emit_post_unary_expr(context, expr); - case EXPR_TRY: - return gencontext_emit_try_expr(context, expr); + case EXPR_GUARD: + return gencontext_emit_trycatch_expr(context, expr); case EXPR_TYPEID: return gencontext_emit_typeid(context, expr); case EXPR_TYPE_ACCESS: @@ -1474,6 +1355,7 @@ NESTED_RETRY: // These are folded in the semantic analysis step. UNREACHABLE case EXPR_IDENTIFIER: + return gencontext_emit_identifier_rvalue(context, expr->identifier_expr.decl); case EXPR_SUBSCRIPT: case EXPR_ACCESS: return gencontext_emit_load(context, expr->type, gencontext_emit_address(context, expr)); diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 1c6be4279..f29d5df6c 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -8,7 +8,7 @@ bool gencontext_check_block_branch_emit(GenContext *context) { - assert(context->current_block); + if (!context->current_block) return false; // If it's not used, we can delete the previous block and skip the branch. // Unless it is the entry block or a label target for jumps // These empty blocks will occur when doing branches. @@ -76,25 +76,24 @@ static inline void gencontext_emit_parameter(GenContext *context, Decl *decl, un // Allocate room on stack and copy. decl->ref = gencontext_emit_alloca(context, llvm_type(decl->type), decl->name); - LLVMBuildStore(context->builder, LLVMGetParam(context->function, index), decl->ref); + gencontext_emit_store(context, decl, LLVMGetParam(context->function, index)); } void gencontext_emit_implicit_return(GenContext *context) { - switch (context->cur_func_decl->func.function_signature.error_return) + if (context->cur_func_decl->func.function_signature.failable) { - case ERROR_RETURN_NONE: - LLVMBuildRetVoid(context->builder); - return; - case ERROR_RETURN_ANY: - case ERROR_RETURN_MANY: - LLVMBuildRet(context->builder, llvm_int(type_usize, 0)); - return; - case ERROR_RETURN_ONE: - LLVMBuildRet(context->builder, llvm_int(type_error_base, 0)); - return; + LLVMBuildRet(context->builder, gencontext_emit_no_error_union(context)); + } + else + { + if (context->cur_func_decl->func.function_signature.rtype->type != type_void && !context->cur_func_decl->func.function_signature.return_param) + { + LLVMBuildUnreachable(context->builder); + return; + } + LLVMBuildRetVoid(context->builder); } - UNREACHABLE } void gencontext_emit_function_body(GenContext *context, Decl *decl) @@ -105,6 +104,9 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl) LLVMValueRef prev_function = context->function; LLVMBuilderRef prev_builder = context->builder; + context->error_var = NULL; + context->catch_block = NULL; + context->function = decl->ref; context->cur_func_decl = decl; @@ -136,12 +138,6 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl) gencontext_emit_parameter(context, decl->func.function_signature.params[i], arg++); } - VECEACH(decl->func.labels, i) - { - Ast *label = decl->func.labels[i]; - label->label_stmt.backend_value = gencontext_create_free_block(context, label->label_stmt.name); - } - VECEACH(decl->func.body->compound_stmt.stmts, i) { gencontext_emit_stmt(context, decl->func.body->compound_stmt.stmts[i]); @@ -183,7 +179,7 @@ void gencontext_emit_function_decl(GenContext *context, Decl *decl) decl->ref = function; if (decl->func.function_signature.return_param) { - if (decl->func.function_signature.error_return == ERROR_RETURN_NONE) + if (!decl->func.function_signature.failable) { gencontext_add_attribute(context, function, sret_attribute, 1); } @@ -290,15 +286,12 @@ void gencontext_emit_extern_decl(GenContext *context, Decl *decl) TODO case DECL_STRUCT: case DECL_UNION: + case DECL_ERR: llvm_type(decl->type); TODO // Fix typeid break; case DECL_ENUM: TODO - case DECL_ERROR: - TODO - case DECL_ERROR_CONSTANT: - TODO case DECL_MEMBER: case DECL_ARRAY_VALUE: case DECL_IMPORT: @@ -308,6 +301,7 @@ void gencontext_emit_extern_decl(GenContext *context, Decl *decl) case DECL_CT_ELSE: case DECL_CT_ELIF: case DECL_ATTRIBUTE: + case DECL_LABEL: UNREACHABLE } } diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 3e21d4547..ee7d8d54a 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -18,6 +18,7 @@ #include #include #include +#include #include "dwarf.h" typedef struct @@ -54,6 +55,9 @@ typedef struct LLVMValueRef alloca_point; LLVMBuilderRef builder; LLVMBasicBlockRef current_block; + LLVMBasicBlockRef catch_block; + LLVMValueRef error_var; + Decl *cur_code_decl; Decl *cur_func_decl; TypeInfo *current_return_type; @@ -66,8 +70,6 @@ typedef struct Ast **defer_stack; DebugContext debug; Context *ast_context; - BreakContinue break_continue_stack[BREAK_STACK_MAX]; - size_t break_continue_stack_index; LLVMValueRef return_out; LLVMBasicBlockRef error_exit_block; LLVMBasicBlockRef expr_block_exit; @@ -118,9 +120,9 @@ LLVMValueRef gencontext_emit_call_intrinsic(GenContext *context, unsigned intrin LLVMValueRef *values, unsigned arg_count); void gencontext_emit_panic_on_true(GenContext *context, LLVMValueRef value, const char *panic_name); void gencontext_emit_defer(GenContext *context, Ast *defer_start, Ast *defer_end); -LLVMBasicBlockRef gencontext_get_try_target(GenContext *context, Expr *try_expr); + LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr); -LLVMValueRef gencontext_emit_assign_expr(GenContext *context, LLVMValueRef ref, Expr *expr); +LLVMValueRef gencontext_emit_assign_expr(GenContext *context, LLVMValueRef ref, Expr *expr, LLVMValueRef failable); LLVMMetadataRef gencontext_get_debug_type(GenContext *context, Type *type); void gencontext_emit_debug_location(GenContext *context, SourceRange location); LLVMMetadataRef gencontext_create_builtin_debug_type(GenContext *context, Type *builtin_type); @@ -136,6 +138,13 @@ static inline LLVMBasicBlockRef gencontext_create_free_block(GenContext *context { return LLVMCreateBasicBlockInContext(context->context, name); } +#define PUSH_ERROR() \ + LLVMBasicBlockRef _old_catch = context->catch_block; \ + LLVMValueRef _old_error_var = context->error_var +#define POP_ERROR() \ + context->catch_block = _old_catch; \ + context->error_var = _old_error_var + void gencontext_emit_function_body(GenContext *context, Decl *decl); void gencontext_emit_implicit_return(GenContext *context); void gencontext_emit_function_decl(GenContext *context, Decl *decl); @@ -147,12 +156,34 @@ static inline LLVMValueRef gencontext_emit_load(GenContext *context, Type *type, assert(gencontext_get_llvm_type(context, type) == LLVMGetElementType(LLVMTypeOf(value))); return LLVMBuildLoad2(context->builder, gencontext_get_llvm_type(context, type), value, ""); } + static inline void gencontext_emit_return_value(GenContext *context, LLVMValueRef value) { LLVMBuildRet(context->builder, value); context->current_block = NULL; context->current_block_is_target = false; } + +static inline LLVMValueRef decl_failable_ref(Decl *decl) +{ + assert(decl->decl_kind == DECL_VAR); + if (decl->var.kind == VARDECL_ALIAS) return decl_failable_ref(decl->var.alias); + return decl->var.failable_ref; +} + +static inline LLVMValueRef decl_ref(Decl *decl) +{ + assert(decl->decl_kind == DECL_VAR); + if (decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_ALIAS) return decl_ref(decl->var.alias); + return decl->ref; +} + +static inline void gencontext_emit_store(GenContext *context, Decl *decl, LLVMValueRef value) +{ + LLVMBuildStore(context->builder, value, decl_ref(decl)); +} + + LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMValueRef value, Type *to_type, Type *from_type); static inline bool gencontext_func_pass_return_by_param(GenContext *context, Type *first_param_type) { return false; }; static inline bool gencontext_func_pass_param_by_reference(GenContext *context, Type *param_type) { return false; } @@ -230,12 +261,18 @@ static inline LLVMCallConv llvm_call_convention_from_call(CallABI abi) #define llvm_type(type) gencontext_get_llvm_type(context, type) #define llvm_debug_type(type) gencontext_get_debug_type(context, type) +static inline LLVMValueRef gencontext_emit_no_error_union(GenContext *context) +{ + return LLVMConstInt(llvm_type(type_error), 0, false); +} + static inline LLVMValueRef gencontext_emit_const_int(GenContext *context, Type *type, uint64_t val) { type = type->canonical; - if (type == type_error_union) type = type_usize->canonical; + if (type == type_error) type = type_usize->canonical; assert(type_is_any_integer(type) || type->type_kind == TYPE_BOOL); return LLVMConstInt(llvm_type(type), val, type_is_signed_integer(type)); } #define llvm_int(_type, _val) gencontext_emit_const_int(context, _type, _val) +LLVMValueRef gencontext_emit_typeid(GenContext *context, Expr *expr); diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index eb0172b13..324179ba7 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -4,9 +4,7 @@ #include "llvm_codegen_internal.h" -static void gencontext_pop_break_continue(GenContext *context); -static void gencontext_push_break_continue(GenContext *context, LLVMBasicBlockRef break_block, LLVMBasicBlockRef continue_block, - LLVMBasicBlockRef next_block); +void gencontext_emit_try_stmt(GenContext *context, Ast *pAst); void gencontext_emit_compound_stmt(GenContext *context, Ast *ast) { @@ -24,6 +22,11 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast) Decl *decl = ast->declare_stmt; decl->ref = gencontext_emit_alloca(context, llvm_type(type_reduced(decl->type)), decl->name); + if (decl->var.failable) + { + decl->var.failable_ref = gencontext_emit_alloca(context, llvm_type(type_error), decl->name); + LLVMBuildStore(context->builder, gencontext_emit_no_error_union(context), decl->var.failable_ref); + } // TODO NRVO // TODO debug info /* @@ -42,31 +45,39 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast) */ if (decl->var.init_expr) { - gencontext_emit_assign_expr(context, decl->ref, decl->var.init_expr); - return decl->ref; + gencontext_emit_assign_expr(context, decl->ref, decl->var.init_expr, decl->var.failable_ref); } return decl->ref; } -void gencontext_emit_decl_expr_list_ignore_result(GenContext *context, Ast *ast) +void gencontext_emit_decl_expr_list_ignore_result(GenContext *context, Expr *expr) { - assert(ast->ast_kind == AST_DECL_EXPR_LIST); - VECEACH(ast->decl_expr_stmt, i) + assert(expr->expr_kind == EXPR_DECL_LIST); + VECEACH(expr->dexpr_list_expr, i) { - gencontext_emit_stmt(context, ast->decl_expr_stmt[i]); + gencontext_emit_stmt(context, expr->dexpr_list_expr[i]); } } -LLVMValueRef gencontext_emit_decl_expr_list(GenContext *context, Ast *ast, bool bool_cast) +LLVMValueRef gencontext_emit_decl_expr_list(GenContext *context, Expr *expr, bool bool_cast) { - assert(ast->ast_kind == AST_DECL_EXPR_LIST); - size_t size = vec_size(ast->decl_expr_stmt); + assert(expr->expr_kind == EXPR_DECL_LIST); + size_t size = vec_size(expr->dexpr_list_expr); size_t last_index = size - 1; for (size_t i = 0; i < last_index; i++) { - gencontext_emit_stmt(context, ast->decl_expr_stmt[i]); + Ast *ast = expr->dexpr_list_expr[i]; + if (ast->ast_kind == AST_DECLARE_STMT) + { + gencontext_emit_decl(context, ast); + } + else + { + assert(ast->ast_kind == AST_EXPR_STMT); + gencontext_emit_expr(context, ast->expr_stmt); + } } - Ast *last = ast->decl_expr_stmt[last_index]; + Ast *last = expr->dexpr_list_expr[last_index]; LLVMValueRef result; Type *type; switch (last->ast_kind) @@ -77,7 +88,14 @@ LLVMValueRef gencontext_emit_decl_expr_list(GenContext *context, Ast *ast, bool break; case AST_DECLARE_STMT: type = last->declare_stmt->var.type_info->type; - result = gencontext_emit_load(context, type, gencontext_emit_decl(context, last)); + { + LLVMValueRef value = gencontext_emit_decl(context, last); + if (bool_cast && last->declare_stmt->var.unwrap) + { + return llvm_int(type_bool, 1); + } + result = gencontext_emit_load(context, type, value); + } break; default: UNREACHABLE @@ -106,7 +124,23 @@ static inline void gencontext_emit_return(GenContext *context, Ast *ast) // Ensure we are on a branch that is non empty. if (!gencontext_check_block_branch_emit(context)) return; + bool in_expression_block = context->expr_block_exit != NULL; + + PUSH_ERROR(); + + LLVMBasicBlockRef error_return_block = NULL; + LLVMValueRef error_out = NULL; + if (!in_expression_block && context->cur_func_decl->func.function_signature.failable) + { + error_return_block = gencontext_create_free_block(context, "errretblock"); + error_out = gencontext_emit_alloca(context, llvm_type(type_error), "reterr"); + context->error_var = error_out; + context->catch_block = error_return_block; + } LLVMValueRef ret_value = ast->return_stmt.expr ? gencontext_emit_expr(context, ast->return_stmt.expr) : NULL; + + POP_ERROR(); + gencontext_emit_defer(context, ast->return_stmt.defer, NULL); // Are we in an expression block? @@ -137,38 +171,20 @@ static inline void gencontext_emit_return(GenContext *context, Ast *ast) } } context->current_block = NULL; + if (error_return_block && LLVMGetFirstUse(LLVMBasicBlockAsValue(error_return_block))) + { + gencontext_emit_block(context, error_return_block); + LLVMBuildRet(context->builder, gencontext_emit_load(context, type_error, error_out)); + context->current_block = NULL; + } LLVMBasicBlockRef post_ret_block = gencontext_create_free_block(context, "ret"); gencontext_emit_block(context, post_ret_block); } -static inline void gencontext_emit_throw(GenContext *context, Ast *ast) -{ - // Ensure we are on a branch that is non empty. - if (!gencontext_check_block_branch_emit(context)) return; - - LLVMValueRef error_val = gencontext_emit_expr(context, ast->throw_stmt.throw_value); - - gencontext_emit_defer(context, ast->throw_stmt.defer, NULL); - - // In the case that the throw actually contains a single error, but the function is throwing an error union, - // we must insert a conversion. - if (context->cur_func_decl->func.function_signature.error_return != ERROR_RETURN_ONE && - ast->throw_stmt.throw_value->type->type_kind == TYPE_ERROR) - { - error_val = gencontext_emit_cast(context, CAST_ERREU, error_val, type_error_union, ast->throw_stmt.throw_value->type); - } - - gencontext_emit_return_value(context, error_val); - LLVMBasicBlockRef post_throw_block = gencontext_create_free_block(context, "throw"); - gencontext_emit_block(context, post_throw_block); -} - void gencontext_emit_if(GenContext *context, Ast *ast) { - if (ast->if_stmt.decl) gencontext_emit_decl_expr_list_ignore_result(context, ast->if_stmt.decl); - // We need at least the exit block and the "then" block. LLVMBasicBlockRef exit_block = LLVMCreateBasicBlockInContext(context->context, "if.exit"); LLVMBasicBlockRef then_block = LLVMCreateBasicBlockInContext(context->context, "if.then"); @@ -179,20 +195,61 @@ void gencontext_emit_if(GenContext *context, Ast *ast) { else_block = LLVMCreateBasicBlockInContext(context->context, "if.else"); } + else + { + else_block = exit_block; + } + ast->if_stmt.break_block = exit_block; // Output boolean value and switch. + + PUSH_ERROR(); + + context->catch_block = else_block ?: exit_block; + context->error_var = NULL; + LLVMValueRef value = gencontext_emit_decl_expr_list(context, ast->if_stmt.cond, true); - gencontext_emit_cond_br(context, value, then_block, else_block ? else_block : exit_block); + + if (LLVMIsConstant(value)) + { + unsigned long v = LLVMConstIntGetZExtValue(value); + if (v) + { + gencontext_emit_br(context, then_block); + else_block = NULL; + } + else + { + gencontext_emit_br(context, else_block); + then_block = NULL; + } + } + else + { + gencontext_emit_cond_br(context, value, then_block, else_block); + } + + POP_ERROR(); + + Decl *label = ast->if_stmt.label; + if (label) + { + label->label.break_target = exit_block; + } // Emit the 'then' code. - gencontext_emit_block(context, then_block); - gencontext_emit_stmt(context, ast->if_stmt.then_body); + if (then_block) + { + gencontext_emit_block(context, then_block); + gencontext_emit_stmt(context, ast->if_stmt.then_body); + + // Jump to exit. + gencontext_emit_br(context, exit_block); + } - // Jump to exit. - gencontext_emit_br(context, exit_block); // Emit the 'else' branch if present. - if (else_block) + if (else_block && else_block != exit_block) { gencontext_emit_block(context, else_block); gencontext_emit_stmt(context, ast->if_stmt.else_body); @@ -204,33 +261,6 @@ void gencontext_emit_if(GenContext *context, Ast *ast) } -static void gencontext_push_break_continue(GenContext *context, LLVMBasicBlockRef break_block, - LLVMBasicBlockRef continue_block, LLVMBasicBlockRef next_block) -{ - size_t index = context->break_continue_stack_index++; - if (index == BREAK_STACK_MAX - 1) - { - error_exit("Exhausted break/continue stack - exceeded %d entries.", BREAK_STACK_MAX); - } - if (!index) - { - context->break_continue_stack[index].continue_block = continue_block; - context->break_continue_stack[index].break_block = break_block; - context->break_continue_stack[index].next_block = next_block; - return; - } - - context->break_continue_stack[index].continue_block = continue_block ? continue_block : context->break_continue_stack[index - 1].continue_block; - context->break_continue_stack[index].next_block = next_block ? next_block : context->break_continue_stack[index - 1].next_block; - context->break_continue_stack[index].break_block = break_block ? break_block : context->break_continue_stack[index - 1].break_block; -} - -static void gencontext_pop_break_continue(GenContext *context) -{ - assert(context->break_continue_stack_index); - context->break_continue_stack_index--; -} - void gencontext_emit_for_stmt(GenContext *context, Ast *ast) { // First, emit all inits. @@ -252,11 +282,10 @@ void gencontext_emit_for_stmt(GenContext *context, Ast *ast) // For continue: // 1. If there is inc, jump to the condition // 2. If there is no condition, jump to the body. - gencontext_push_break_continue(context, - exit_block, - inc_block ? inc_block : (cond_block ? cond_block : body_block), - NULL); + LLVMBasicBlockRef continue_block = inc_block ? inc_block : (cond_block ? cond_block : body_block); + ast->for_stmt.continue_block = continue_block; + ast->for_stmt.exit_block = exit_block; LLVMValueRef value = NULL; if (cond_block) @@ -305,31 +334,27 @@ void gencontext_emit_for_stmt(GenContext *context, Ast *ast) // And insert exit block gencontext_emit_block(context, exit_block); - gencontext_pop_break_continue(context); } void gencontext_emit_while_stmt(GenContext *context, Ast *ast) { // First, emit all inits. - - if (ast->while_stmt.decl) gencontext_emit_decl_expr_list_ignore_result(context, ast->while_stmt.decl); - - LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "while.exit"); LLVMBasicBlockRef begin_block = gencontext_create_free_block(context, "while.begin"); LLVMBasicBlockRef body_block = ast->while_stmt.body->compound_stmt.stmts ? gencontext_create_free_block(context, "while.body") : NULL; - gencontext_push_break_continue(context, exit_block, begin_block, NULL); + ast->while_stmt.continue_block = begin_block; + ast->while_stmt.break_block = exit_block; // Emit cond gencontext_emit_br(context, begin_block); gencontext_emit_block(context, begin_block); DeferList defers = { NULL, NULL }; - Ast *cond = ast->while_stmt.cond; - if (cond->ast_kind == AST_SCOPED_STMT) + Expr *cond = ast->while_stmt.cond; + if (cond->expr_kind == EXPR_SCOPED_EXPR) { - defers = cond->scoped_stmt.defers; - cond = cond->scoped_stmt.stmt; + defers = cond->expr_scope.defers; + cond = cond->expr_scope.expr; } LLVMValueRef value = gencontext_emit_decl_expr_list(context, cond, true); // If we have a body, conditionally jump to it. @@ -363,91 +388,170 @@ void gencontext_emit_while_stmt(GenContext *context, Ast *ast) // Emit defers gencontext_emit_defer(context, defers.start, defers.end); - gencontext_pop_break_continue(context); } void gencontext_emit_do_stmt(GenContext *context, Ast *ast) { LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "do.exit"); LLVMBasicBlockRef cond_block = ast->do_stmt.expr ? gencontext_create_free_block(context, "do.cond") : NULL; - LLVMBasicBlockRef body_block = ast->do_stmt.body ? gencontext_create_free_block(context, "do.body") : NULL; - - // A loop must either have a body or an inc. - // This type do-while for loop is forbidden: - // do { } while (1); - assert((cond_block || body_block) && "Do has no body and no cond."); + LLVMBasicBlockRef body_block = gencontext_create_free_block(context, "do.body"); // Break is simple it always jumps out. - // For continue: if there is no condition, jump to the body. - gencontext_push_break_continue(context, exit_block, cond_block ? cond_block : body_block, NULL); + // For continue: if there is no condition, exit. + LLVMBasicBlockRef cont_block = cond_block ? cond_block : exit_block; - if (body_block) - { - // Emit the body - gencontext_emit_br(context, body_block); - gencontext_emit_block(context, body_block); - gencontext_emit_stmt(context, ast->do_stmt.body); - } + Expr *do_expr = ast->do_stmt.expr; + Ast *do_body = ast->do_stmt.body; + + // Overwrite: + ast->do_stmt.break_block = exit_block; + ast->do_stmt.continue_block = cont_block; + + // Emit the body + gencontext_emit_br(context, body_block); + gencontext_emit_block(context, body_block); + gencontext_emit_stmt(context, do_body); if (cond_block) { gencontext_emit_br(context, cond_block); gencontext_emit_block(context, cond_block); - LLVMValueRef value = gencontext_emit_expr(context, ast->do_stmt.expr); - gencontext_emit_cond_br(context, value, body_block ? body_block : cond_block, exit_block); + LLVMValueRef value = gencontext_emit_expr(context, do_expr); + if (LLVMIsConstant(value)) + { + unsigned long v = LLVMConstIntGetZExtValue(value); + gencontext_emit_br(context, v ? body_block : exit_block); + } + else + { + gencontext_emit_cond_br(context, value, body_block, exit_block); + } } else { - // Branch to the beginning of the block - gencontext_emit_br(context, body_block); + // Branch to the exit + gencontext_emit_br(context, exit_block); } // Emit the exit block. gencontext_emit_block(context, exit_block); - gencontext_pop_break_continue(context); } -void gencontext_emit_label(GenContext *context, Ast *ast) -{ - gencontext_emit_br(context, ast->label_stmt.backend_value); - gencontext_emit_block(context, ast->label_stmt.backend_value); - context->current_block_is_target = true; -} -void gencontext_emit_switch(GenContext *context, Ast *ast) + +void gencontext_emit_if_switch_body(GenContext *context, LLVMValueRef switch_value, Ast **cases) { - // TODO check defer correctness - if (ast->switch_stmt.decl) gencontext_emit_decl_expr_list_ignore_result(context, ast->switch_stmt.decl); - LLVMValueRef switch_value = gencontext_emit_decl_expr_list(context, ast->switch_stmt.cond, false); - size_t cases = vec_size(ast->switch_stmt.cases); - if (!cases) + size_t case_count = vec_size(cases); + if (!case_count) { // No body or default is empty, just exit after the value. return; } Ast *default_case = NULL; - for (unsigned i = 0; i < cases; i++) + for (unsigned i = 0; i < case_count; i++) { - Ast *case_stmt = ast->switch_stmt.cases[i]; + Ast *case_stmt = cases[i]; if (!case_stmt->case_stmt.expr) { if (case_stmt->case_stmt.body) { - case_stmt->case_stmt.backend_value = gencontext_create_free_block(context, "switch.default"); + case_stmt->case_stmt.backend_block = gencontext_create_free_block(context, "switch.default"); } default_case = case_stmt; } else if (case_stmt->case_stmt.body) { - case_stmt->case_stmt.backend_value = gencontext_create_free_block(context, "switch.case"); + case_stmt->case_stmt.backend_block = gencontext_create_free_block(context, "switch.case"); + } + } + + + LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "switch.exit"); + + + for (unsigned i = 0; i < case_count; i++) + { + Ast *case_stmt = cases[i]; + if (case_stmt == default_case) continue; + LLVMValueRef case_value; + LLVMBasicBlockRef case_block = case_stmt->case_stmt.backend_block; + // TODO handle string. + if (case_stmt->case_stmt.is_type) + { + case_value = case_stmt->case_stmt.type_info->type->backend_typeid; + } + else + { + case_value = gencontext_emit_expr(context, case_stmt->case_stmt.expr); + } + LLVMValueRef cmp = LLVMBuildICmp(context->builder, LLVMIntNE, switch_value, case_value, ""); + LLVMBasicBlockRef next_block = gencontext_create_free_block(context, ""); + gencontext_emit_cond_br(context, cmp, next_block, case_block); + gencontext_emit_block(context, next_block); + } + if (default_case) + { + gencontext_emit_br(context, default_case->case_stmt.backend_block); + } + else + { + gencontext_emit_br(context, exit_block); + } + for (unsigned i = 0; i < case_count; i++) + { + Ast *case_stmt = cases[i]; + LLVMBasicBlockRef case_block = case_stmt->case_stmt.backend_block; + gencontext_emit_block(context, case_block); + if (!case_stmt->case_stmt.body) + { + gencontext_emit_br(context, i == case_count - 1 ? exit_block : cases[i + 1]->case_stmt.backend_block); + continue; + } + + // IMPORTANT! + context->current_block_is_target = true; + gencontext_emit_stmt(context, case_stmt->case_stmt.body); + gencontext_emit_br(context, exit_block); + + } + gencontext_emit_block(context, exit_block); +} + +void gencontext_emit_switch_body(GenContext *context, LLVMValueRef switch_value, Ast *switch_ast) +{ + Ast **cases = switch_ast->switch_stmt.cases; + size_t case_count = vec_size(cases); + if (!case_count) + { + // No body or default is empty, just exit after the value. + return; + } + + Ast *default_case = NULL; + for (unsigned i = 0; i < case_count; i++) + { + Ast *case_stmt = cases[i]; + if (!case_stmt->case_stmt.expr) + { + if (case_stmt->case_stmt.body) + { + case_stmt->case_stmt.backend_block = gencontext_create_free_block(context, "switch.default"); + } + default_case = case_stmt; + } + else if (case_stmt->case_stmt.body) + { + case_stmt->case_stmt.backend_block = gencontext_create_free_block(context, "switch.case"); } } LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "switch.exit"); - + LLVMBasicBlockRef switch_block = gencontext_create_free_block(context, "switch.entry"); + switch_ast->switch_stmt.retry_block = switch_block; + switch_ast->switch_stmt.exit_block = exit_block; // We will now treat the fallthrough cases: // switch (i) @@ -458,29 +562,46 @@ void gencontext_emit_switch(GenContext *context, Ast *ast) // default: // } LLVMBasicBlockRef next_block = exit_block; - for (unsigned i = cases; i > 0; i--) + for (unsigned i = case_count; i > 0; i--) { - Ast *case_stmt = ast->switch_stmt.cases[i - 1]; - if (case_stmt->case_stmt.backend_value) + Ast *case_stmt = cases[i - 1]; + if (case_stmt->case_stmt.backend_block) { - next_block = case_stmt->case_stmt.backend_value; + next_block = case_stmt->case_stmt.backend_block; continue; } - case_stmt->case_stmt.backend_value = next_block; + case_stmt->case_stmt.backend_block = next_block; } - gencontext_push_break_continue(context, exit_block, NULL, NULL); - LLVMValueRef switch_stmt = LLVMBuildSwitch(context->builder, switch_value, default_case ? default_case->case_stmt.backend_value : exit_block, cases); + Type *switch_type = switch_ast->switch_stmt.cond->type; + LLVMValueRef switch_var = gencontext_emit_alloca(context, llvm_type(switch_type), ""); + switch_ast->switch_stmt.retry_var = switch_var; + LLVMBuildStore(context->builder, switch_value, switch_var); + + gencontext_emit_br(context, switch_block); + gencontext_emit_block(context, switch_block); + + LLVMValueRef switch_load = gencontext_emit_load(context, switch_type, switch_var); + + LLVMValueRef switch_stmt = LLVMBuildSwitch(context->builder, switch_load, default_case ? default_case->case_stmt.backend_block : exit_block, case_count); context->current_block = NULL; - for (unsigned i = 0; i < cases; i++) + for (unsigned i = 0; i < case_count; i++) { - Ast *case_stmt = ast->switch_stmt.cases[i]; - LLVMBasicBlockRef block = case_stmt->case_stmt.backend_value; + Ast *case_stmt = cases[i]; + LLVMBasicBlockRef block = case_stmt->case_stmt.backend_block; if (case_stmt != default_case) { - LLVMValueRef case_value = gencontext_emit_expr(context, case_stmt->case_stmt.expr); + LLVMValueRef case_value; + if (case_stmt->case_stmt.is_type) + { + case_value = case_stmt->case_stmt.type_info->type->backend_typeid; + } + else + { + case_value = gencontext_emit_expr(context, case_stmt->case_stmt.expr); + } LLVMAddCase(switch_stmt, case_value, block); } @@ -490,23 +611,17 @@ void gencontext_emit_switch(GenContext *context, Ast *ast) gencontext_emit_block(context, block); // IMPORTANT! context->current_block_is_target = true; - gencontext_push_break_continue(context, NULL, NULL, i < cases - 1 ? ast->switch_stmt.cases[i + 1]->case_stmt.backend_value : exit_block); + gencontext_emit_stmt(context, case_stmt->case_stmt.body); - gencontext_pop_break_continue(context); gencontext_emit_br(context, exit_block); } - gencontext_pop_break_continue(context); gencontext_emit_block(context, exit_block); } -LLVMValueRef gencontext_get_defer_bool(GenContext *context, Ast *defer) +void gencontext_emit_switch(GenContext *context, Ast *ast) { - assert(defer->ast_kind == AST_DEFER_STMT && defer->defer_stmt.emit_boolean); - if (!defer->defer_stmt.bool_var) - { - defer->defer_stmt.bool_var = gencontext_emit_alloca(context, llvm_type(type_bool), "defer"); - } - return defer->defer_stmt.bool_var; + LLVMValueRef switch_value = gencontext_emit_decl_expr_list(context, ast->switch_stmt.cond, false); + return gencontext_emit_switch_body(context, switch_value, ast); } void gencontext_emit_defer(GenContext *context, Ast *defer_start, Ast *defer_end) @@ -515,70 +630,78 @@ void gencontext_emit_defer(GenContext *context, Ast *defer_start, Ast *defer_end Ast *defer = defer_start; while (defer && defer != defer_end) { - if (defer->defer_stmt.emit_boolean) - { - - // We need at least the exit block and the "then" block. - LLVMBasicBlockRef exit_block = LLVMCreateBasicBlockInContext(context->context, "skip.defer"); - LLVMBasicBlockRef defer_block = LLVMCreateBasicBlockInContext(context->context, "do.defer"); - - LLVMValueRef value = gencontext_emit_load(context, type_bool, gencontext_get_defer_bool(context, defer)); - - gencontext_emit_cond_br(context, value, defer_block, exit_block); - - // Emit the defer. - gencontext_emit_block(context, defer_block); - gencontext_emit_stmt(context, defer->defer_stmt.body); - - // Jump to exit. - gencontext_emit_br(context, exit_block); - - // And now we just emit the exit block. - gencontext_emit_block(context, exit_block); - } - else - { - gencontext_emit_stmt(context, defer->defer_stmt.body); - } + gencontext_emit_stmt(context, defer->defer_stmt.body); defer = defer->defer_stmt.prev_defer; } } -void gencontext_emit_goto(GenContext *context, Ast *ast) -{ - gencontext_emit_defer(context, ast->goto_stmt.defer.start, ast->goto_stmt.defer.end); - Ast *defer = ast->goto_stmt.label->label_stmt.defer; - while (defer != ast->goto_stmt.defer.end) - { - LLVMBuildStore(context->builder, LLVMConstInt(llvm_type(type_bool), 0, false), - gencontext_get_defer_bool(context, defer)); - defer = defer->defer_stmt.prev_defer; - } - gencontext_emit_jmp(context, ast->goto_stmt.label->label_stmt.backend_value); -} void gencontext_emit_break(GenContext *context, Ast *ast) { - gencontext_emit_defer(context, ast->break_stmt.defers.start, ast->break_stmt.defers.end); - - assert(context->break_continue_stack_index); - gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].break_block); + gencontext_emit_defer(context, ast->contbreak_stmt.defers.start, ast->contbreak_stmt.defers.end); + Ast *jump_target = ast->contbreak_stmt.ast; + LLVMBasicBlockRef jump; + switch (jump_target->ast_kind) + { + case AST_IF_STMT: + jump = jump_target->if_stmt.break_block; + break; + case AST_WHILE_STMT: + jump = jump_target->while_stmt.break_block; + break; + case AST_FOR_STMT: + jump = jump_target->for_stmt.exit_block; + break; + case AST_DO_STMT: + jump = jump_target->do_stmt.break_block; + break; + case AST_SWITCH_STMT: + jump = jump_target->switch_stmt.exit_block; + break; + default: + UNREACHABLE + } + gencontext_emit_jmp(context, jump); } void gencontext_emit_continue(GenContext *context, Ast *ast) -{ - gencontext_emit_defer(context, ast->continue_stmt.defers.start, ast->continue_stmt.defers.end); - - assert(context->break_continue_stack_index); - gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].continue_block); +{ gencontext_emit_defer(context, ast->contbreak_stmt.defers.start, ast->contbreak_stmt.defers.end); + Ast *jump_target = ast->contbreak_stmt.ast; + LLVMBasicBlockRef jump; + switch (jump_target->ast_kind) + { + case AST_IF_STMT: + case AST_SWITCH_STMT: + UNREACHABLE + break; + case AST_WHILE_STMT: + jump = jump_target->while_stmt.continue_block; + break; + case AST_DO_STMT: + jump = jump_target->do_stmt.continue_block; + break; + case AST_FOR_STMT: + jump = jump_target->for_stmt.continue_block; + break; + default: + UNREACHABLE + } + gencontext_emit_jmp(context, jump); } void gencontext_emit_next_stmt(GenContext *context, Ast *ast) { + Ast *jump_target = ast->next_stmt.case_switch_stmt; + if (jump_target->ast_kind != AST_SWITCH_STMT) + { + gencontext_emit_defer(context, ast->next_stmt.defers.start, ast->next_stmt.defers.end); + gencontext_emit_jmp(context, jump_target->case_stmt.backend_block); + return; + } + LLVMValueRef value = gencontext_emit_expr(context, ast->next_stmt.switch_expr); + LLVMBuildStore(context->builder, value, jump_target->switch_stmt.retry_var); gencontext_emit_defer(context, ast->next_stmt.defers.start, ast->next_stmt.defers.end); - - assert(context->break_continue_stack_index); - gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].next_block); + gencontext_emit_jmp(context, jump_target->switch_stmt.retry_block); } void gencontext_emit_scoped_stmt(GenContext *context, Ast *ast) @@ -594,6 +717,8 @@ void gencontext_generate_catch_block_if_needed(GenContext *context, Ast *ast) block = gencontext_create_free_block(context, "catchblock"); ast->catch_stmt.block = block; LLVMTypeRef type; + TODO +#if 0 if (ast->catch_stmt.error_param->type == type_error_union) { type = llvm_type(type_error_union); @@ -603,15 +728,124 @@ void gencontext_generate_catch_block_if_needed(GenContext *context, Ast *ast) type = llvm_type(type_error_base); } ast->catch_stmt.error_param->ref = gencontext_emit_alloca(context, type, ""); +#endif +} + +void gencontext_emit_try_stmt(GenContext *context, Ast *ast) +{ + // Create after try block + LLVMBasicBlockRef after_try = gencontext_create_free_block(context, "after_try"); + + // Store catch/error var + PUSH_ERROR(); + + // Set the catch/error var + context->error_var = NULL; + context->catch_block = after_try; + + // Emit the checks, which will create jumps like we want them. + Ast **decl_expr = ast->try_stmt.decl_expr->dexpr_list_expr; + VECEACH(decl_expr, i) + { + Ast *dexpr = decl_expr[i]; + switch (dexpr->ast_kind) + { + case AST_EXPR_STMT: + gencontext_emit_expr(context, dexpr->expr_stmt); + break; + case AST_DECLARE_STMT: + gencontext_emit_decl(context, dexpr); + break; + default: + UNREACHABLE + } + } + + // Restore. + POP_ERROR(); + + // Emit the statement + gencontext_emit_stmt(context, ast->try_stmt.body); + + // Jump to after. + gencontext_emit_br(context, after_try); + gencontext_emit_block(context, after_try); + +} + +void gencontext_emit_expr_stmt(GenContext *context, Ast *ast) +{ + if (ast->expr_stmt->failable) + { + PUSH_ERROR(); + LLVMBasicBlockRef discard_fail = gencontext_create_free_block(context, "voiderr"); + context->catch_block = discard_fail; + context->error_var = NULL; + gencontext_emit_expr(context, ast->expr_stmt); + gencontext_emit_br(context, discard_fail); + gencontext_emit_block(context, discard_fail); + POP_ERROR(); + return; + } + gencontext_emit_expr(context, ast->expr_stmt); } void gencontext_emit_catch_stmt(GenContext *context, Ast *ast) { - gencontext_generate_catch_block_if_needed(context, ast); + Expr *catch_expr; + LLVMValueRef error_result = NULL; + if (ast->catch_stmt.has_err_var) + { + Decl *error_var = ast->catch_stmt.err_var; + assert(error_var->type->canonical == type_error); + error_result = gencontext_emit_alloca(context, llvm_type(type_error), error_var->name); + error_var->ref = error_result; + catch_expr = error_var->var.init_expr; + + } + else + { + if (ast->catch_stmt.is_switch) + { + error_result = gencontext_emit_alloca(context, llvm_type(type_error), "catchval"); + } + catch_expr = ast->catch_stmt.catchable; + } + + // Create catch block. + LLVMBasicBlockRef catch_block = gencontext_create_free_block(context, "catch"); + + // Store catch/error var + PUSH_ERROR(); + + // Set the catch/error var + context->error_var = error_result; + context->catch_block = catch_block; + + // Emit the catch, which will create jumps like we want them. + gencontext_emit_expr(context, catch_expr); + + // Restore. + POP_ERROR(); + + // Create the bloch after and jump to it. LLVMBasicBlockRef after_catch = gencontext_create_free_block(context, "after_catch"); gencontext_emit_br(context, after_catch); - gencontext_emit_block(context, ast->catch_stmt.block); - gencontext_emit_stmt(context, ast->catch_stmt.body); + + // Emit catch. + gencontext_emit_block(context, catch_block); + + if (ast->catch_stmt.is_switch) + { + LLVMValueRef ref = gencontext_emit_bitcast(context, error_result, type_get_ptr(type_typeid)); + gencontext_emit_if_switch_body(context, gencontext_emit_load(context, type_typeid, ref), ast->catch_stmt.cases); + } + else + { + gencontext_emit_stmt(context, ast->catch_stmt.body); + } + + // Jump to after. gencontext_emit_br(context, after_catch); gencontext_emit_block(context, after_catch); } @@ -630,15 +864,23 @@ void gencontext_emit_panic_on_true(GenContext *context, LLVMValueRef value, cons void gencontext_emit_stmt(GenContext *context, Ast *ast) { + if (context->catch_block == NULL) + { + context->catch_block = gencontext_create_free_block(context, "stmt_catch"); + } switch (ast->ast_kind) { case AST_POISONED: + case AST_DEFINE_STMT: UNREACHABLE + case AST_TRY_STMT: + gencontext_emit_try_stmt(context, ast); + break; case AST_SCOPED_STMT: gencontext_emit_scoped_stmt(context, ast); break; case AST_EXPR_STMT: - gencontext_emit_expr(context, ast->expr_stmt); + gencontext_emit_expr_stmt(context, ast); break; case AST_DECLARE_STMT: gencontext_emit_decl(context, ast); @@ -671,20 +913,11 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast) gencontext_emit_next_stmt(context, ast); break; case AST_DEFER_STMT: - if (ast->defer_stmt.emit_boolean) - { - LLVMBuildStore(context->builder, LLVMConstInt(llvm_type(type_bool), 1, false), - gencontext_get_defer_bool(context, ast)); - } - break; case AST_NOP_STMT: break; case AST_CATCH_STMT: gencontext_emit_catch_stmt(context, ast); break; - case AST_THROW_STMT: - gencontext_emit_throw(context, ast); - break; case AST_ASM_STMT: TODO case AST_CT_IF_STMT: @@ -692,18 +925,7 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast) case AST_CT_ELSE_STMT: case AST_CT_FOR_STMT: case AST_CT_SWITCH_STMT: - case AST_CT_DEFAULT_STMT: - case AST_CT_CASE_STMT: - case AST_GENERIC_CASE_STMT: - case AST_GENERIC_DEFAULT_STMT: - case AST_DECL_EXPR_LIST: UNREACHABLE - case AST_LABEL: - gencontext_emit_label(context, ast); - break; - case AST_GOTO_STMT: - gencontext_emit_goto(context, ast); - break; case AST_SWITCH_STMT: gencontext_emit_switch(context, ast); break; @@ -714,3 +936,4 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast) TODO } } + diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 9acb3f023..6b7ba52fc 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -4,7 +4,7 @@ #include "llvm_codegen_internal.h" -LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *type); +LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *any_type); static inline LLVMTypeRef llvm_type_from_decl(LLVMContextRef context, Decl *decl) { @@ -20,10 +20,10 @@ static inline LLVMTypeRef llvm_type_from_decl(LLVMContextRef context, Decl *decl case DECL_CT_ELSE: case DECL_CT_ELIF: case DECL_VAR: - case DECL_ERROR_CONSTANT: case DECL_ARRAY_VALUE: case DECL_IMPORT: case DECL_MEMBER: + case DECL_LABEL: UNREACHABLE; case DECL_FUNC: { @@ -79,19 +79,31 @@ static inline LLVMTypeRef llvm_type_from_decl(LLVMContextRef context, Decl *decl } case DECL_ENUM: return llvm_get_type(context, decl->type); - case DECL_ERROR: - TODO - /* - if (!context->error_type) + case DECL_ERR: + { + LLVMTypeRef *types = NULL; + vec_add(types, llvm_get_type(context, type_typeid)); + unsigned size = type_size(type_typeid); + VECEACH(decl->strukt.members, i) { - LLVMTypeRef domain_type = LLVMInt64TypeInContext(LLVMCONTEXT(context)); - LLVMTypeRef pointer_type = BACKEND_TYPE(type_voidptr); - LLVMTypeRef error_type = LLVMStructCreateNamed(LLVMCONTEXT(context), "error"); - LLVMTypeRef types[2] = { domain_type, pointer_type }; - LLVMStructSetBody(error_type, types, 2, false); - context->error_type = error_type; + Type *type = decl->strukt.members[i]->type->canonical; + unsigned alignment = type_abi_alignment(type); + if (size % alignment != 0) + { + size += alignment - size % alignment; + } + size += type_size(type); + vec_add(types, llvm_get_type(context, type)); } - return context->error_type;*/ + unsigned padding = type_size(type_error) - size; + if (padding > 0) + { + vec_add(types, LLVMIntTypeInContext(context, padding * 8)); + } + LLVMTypeRef type = LLVMStructCreateNamed(context, decl->name); + LLVMStructSetBody(type, types, vec_size(types), false); + return type; + } } UNREACHABLE } @@ -138,92 +150,86 @@ LLVMTypeRef llvm_func_type(LLVMContextRef context, Type *type) } } LLVMTypeRef ret_type; - switch (signature->error_return) + if (signature->failable) { - case ERROR_RETURN_ANY: - case ERROR_RETURN_MANY: - ret_type = llvm_get_type(context, type_error_union); - break; - case ERROR_RETURN_ONE: - ret_type = llvm_get_type(context, type_error_base); - break;; - case ERROR_RETURN_NONE: - ret_type = signature->return_param ? llvm_get_type(context, type_void) : llvm_get_type(context, type->func.signature->rtype->type); - break; - default: - UNREACHABLE + ret_type = llvm_get_type(context, type_error); + } + else + { + ret_type = signature->return_param + ? llvm_get_type(context, type_void) + : llvm_get_type(context, type->func.signature->rtype->type); } return LLVMFunctionType(ret_type, params, parameters, signature->variadic); } -LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *type) +LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *any_type) { - if (type->backend_type && LLVMGetTypeContext(type->backend_type) == context) + if (any_type->backend_type && LLVMGetTypeContext(any_type->backend_type) == context) { - return type->backend_type; + return any_type->backend_type; } - DEBUG_LOG("Generating type %s", type->name); - switch (type->type_kind) + DEBUG_LOG("Generating type %s", any_type->name); + switch (any_type->type_kind) { case TYPE_MEMBER: case TYPE_POISONED: UNREACHABLE case TYPE_TYPEID: - return type->backend_type = LLVMIntTypeInContext(context, type->builtin.bitsize); - case TYPE_ERROR: - return type->backend_type = llvm_get_type(context, type_error_base); + return any_type->backend_type = LLVMIntTypeInContext(context, any_type->builtin.bitsize); case TYPE_TYPEDEF: - return type->backend_type = llvm_get_type(context, type->canonical); + return any_type->backend_type = llvm_get_type(context, any_type->canonical); case TYPE_ENUM: - return type->backend_type = llvm_get_type(context, type->decl->enums.type_info->type->canonical); - case TYPE_ERROR_UNION: - return type->backend_type = LLVMIntTypeInContext(context, type->builtin.bitsize); + return any_type->backend_type = llvm_get_type(context, any_type->decl->enums.type_info->type->canonical); + case TYPE_ERR_UNION: + return any_type->backend_type = LLVMIntTypeInContext(context, any_type->builtin.bitsize); case TYPE_STRUCT: case TYPE_UNION: - return type->backend_type = llvm_type_from_decl(context, type->decl); + case TYPE_ERRTYPE: + return any_type->backend_type = llvm_type_from_decl(context, any_type->decl); case TYPE_FUNC: - return type->backend_type = llvm_func_type(context, type); + return any_type->backend_type = llvm_func_type(context, any_type); case TYPE_VOID: - return type->backend_type = LLVMVoidTypeInContext(context); + return any_type->backend_type = LLVMVoidTypeInContext(context); case TYPE_F64: case TYPE_FXX: - return type->backend_type = LLVMDoubleTypeInContext(context); + return any_type->backend_type = LLVMDoubleTypeInContext(context); case TYPE_F32: - return type->backend_type = LLVMFloatTypeInContext(context); + return any_type->backend_type = LLVMFloatTypeInContext(context); case TYPE_U64: case TYPE_I64: - return type->backend_type = LLVMIntTypeInContext(context, 64U); + return any_type->backend_type = LLVMIntTypeInContext(context, 64U); case TYPE_U32: case TYPE_I32: case TYPE_IXX: - return type->backend_type = LLVMIntTypeInContext(context, 32U); + return any_type->backend_type = LLVMIntTypeInContext(context, 32U); case TYPE_U16: case TYPE_I16: - return type->backend_type = LLVMIntTypeInContext(context, 16U); + return any_type->backend_type = LLVMIntTypeInContext(context, 16U); case TYPE_U8: case TYPE_I8: - return type->backend_type = LLVMIntTypeInContext(context, 8U); + return any_type->backend_type = LLVMIntTypeInContext(context, 8U); case TYPE_BOOL: - return type->backend_type = LLVMIntTypeInContext(context, 1U); + return any_type->backend_type = LLVMIntTypeInContext(context, 1U); case TYPE_POINTER: - return type->backend_type = llvm_type_from_ptr(context, type); + return any_type->backend_type = llvm_type_from_ptr(context, any_type); case TYPE_STRING: // TODO - return type->backend_type = LLVMPointerType(llvm_get_type(context, type_char), 0); + return any_type->backend_type = LLVMPointerType(llvm_get_type(context, type_char), 0); case TYPE_ARRAY: - return type->backend_type = llvm_type_from_array(context, type); + return any_type->backend_type = llvm_type_from_array(context, any_type); case TYPE_SUBARRAY: { - LLVMTypeRef base_type = llvm_get_type(context, type_get_ptr(type->array.base)); + LLVMTypeRef base_type = llvm_get_type(context, type_get_ptr(any_type->array.base)); LLVMTypeRef size_type = llvm_get_type(context, type_usize); - LLVMTypeRef array_type = LLVMStructCreateNamed(context, type->name); + LLVMTypeRef array_type = LLVMStructCreateNamed(context, any_type->name); LLVMTypeRef types[2] = { base_type, size_type }; LLVMStructSetBody(array_type, types, 2, false); - return type->backend_type = array_type; + return any_type->backend_type = array_type; } case TYPE_VARARRAY: - return type->backend_type = llvm_get_type(context, type_get_ptr(type->array.base)); + return any_type->backend_type = llvm_get_type(context, type_get_ptr(any_type->array.base)); } UNREACHABLE; } diff --git a/src/compiler/number.c b/src/compiler/number.c index 6311b5955..ad66b6fd7 100644 --- a/src/compiler/number.c +++ b/src/compiler/number.c @@ -64,9 +64,6 @@ void expr_const_fprint(FILE *__restrict file, ExprConst *expr) case TYPE_ENUM: fprintf(file, "%s", expr->enum_constant->name); break; - case TYPE_ERROR: - fprintf(file, "%s", expr->error_constant->name); - break; case TYPE_STRING: fprintf(file, "%.*s", expr->string.len, expr->string.chars); break; @@ -252,9 +249,6 @@ const char *expr_const_to_error_string(const ExprConst *expr) case TYPE_ENUM: asprintf(&buff, "%s.%s", expr->enum_constant->type->name, expr->enum_constant->name); return buff; - case TYPE_ERROR: - asprintf(&buff, "%s.%s", expr->error_constant->type->name, expr->error_constant->name); - return buff; case TYPE_STRING: asprintf(&buff, "\"%*.s\"", expr->string.len, expr->string.chars); return buff; diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index c65591fc8..0a33e15b0 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -24,6 +24,11 @@ inline Expr *parse_precedence_with_left_side(Context *context, Expr *left_side, { if (!expr_ok(left_side)) return left_side; ParseFn infix_rule = rules[context->tok.type].infix; + if (!infix_rule) + { + SEMA_TOKEN_ERROR(context->tok, "An expression was expected."); + return poisoned_expr; + } left_side = infix_rule(context, left_side); } return left_side; @@ -67,7 +72,7 @@ inline Expr* parse_constant_expr(Context *context) * ; * */ -bool parse_param_list(Context *context, Expr ***result, bool allow_type) +bool parse_param_list(Context *context, Expr ***result, bool allow_type, TokenType param_end) { *result = NULL; while (1) @@ -94,6 +99,7 @@ bool parse_param_list(Context *context, Expr ***result, bool allow_type) { return true; } + if (context->tok.type == param_end) return true; } } @@ -128,7 +134,7 @@ static inline Expr* parse_non_assign_expr(Context *context) Expr *parse_expression_list(Context *context) { Expr *expr_list = EXPR_NEW_TOKEN(EXPR_EXPRESSION_LIST, context->tok); - if (!parse_param_list(context, &expr_list->expression_list, false)) return poisoned_expr; + if (!parse_param_list(context, &expr_list->expression_list, false, TOKEN_INVALID_TOKEN)) return poisoned_expr; return expr_list; } @@ -206,12 +212,42 @@ static Expr *parse_range_expr(Context *context, Expr *left_side) return range; } +static bool token_may_end_expression(TokenType type) +{ + switch (type) + { + case TOKEN_RPAREN: + case TOKEN_RBRACE: + case TOKEN_RBRACKET: + case TOKEN_EOS: + case TOKEN_COMMA: + return true; + default: + return false; + } +} +static inline Expr *parse_check_failable(Context *context, Expr *left_side) +{ + Expr *expr_unwrap = EXPR_NEW_EXPR(EXPR_FAIL_CHECK, left_side); + advance_and_verify(context, TOKEN_QUESTION); + expr_unwrap->fail_check_expr = left_side; + RANGE_EXTEND_PREV(expr_unwrap); + return expr_unwrap; +} + static Expr *parse_ternary_expr(Context *context, Expr *left_side) { assert(expr_ok(left_side)); + + if (context->tok.type == TOKEN_QUESTION && token_may_end_expression(context->next_tok.type)) + { + return parse_check_failable(context, left_side); + } + Expr *expr_ternary = EXPR_NEW_EXPR(EXPR_TERNARY, left_side); expr_ternary->ternary_expr.cond = left_side; + // Check for elvis if (try_consume(context, TOKEN_ELVIS)) { @@ -286,12 +322,20 @@ Expr *parse_initializer_list(Context *context) CONSUME_OR(TOKEN_LBRACE, poisoned_expr); if (!try_consume(context, TOKEN_RBRACE)) { - if (!parse_param_list(context, &initializer_list->expr_initializer.initializer_expr, false)) return poisoned_expr; + if (!parse_param_list(context, &initializer_list->expr_initializer.initializer_expr, false, TOKEN_RBRACE)) return poisoned_expr; CONSUME_OR(TOKEN_RBRACE, poisoned_expr); } return initializer_list; } +static Expr *parse_failable(Context *context, Expr *left_side) +{ + Expr *failable = expr_new(EXPR_FAILABLE, left_side->span); + advance_and_verify(context, TOKEN_BANG); + failable->failable_expr = left_side; + RANGE_EXTEND_PREV(failable); + return failable; +} static Expr *parse_binary(Context *context, Expr *left_side) { assert(left_side && expr_ok(left_side)); @@ -327,7 +371,7 @@ static Expr *parse_call_expr(Context *context, Expr *left) advance_and_verify(context, TOKEN_LPAREN); if (context->tok.type != TOKEN_RPAREN) { - if (!parse_param_list(context, ¶ms, 0)) return poisoned_expr; + if (!parse_param_list(context, ¶ms, 0, TOKEN_RPAREN)) return poisoned_expr; } TRY_CONSUME_OR(TOKEN_RPAREN, "Expected the ending ')' here", poisoned_expr); @@ -407,39 +451,50 @@ static Expr *parse_maybe_scope(Context *context, Expr *left) static Expr *parse_try_expr(Context *context, Expr *left) { assert(!left && "Unexpected left hand side"); - Expr *try_expr = EXPR_NEW_TOKEN(EXPR_TRY, context->tok); - advance_and_verify(context, TOKEN_TRY); - try_expr->try_expr.expr = TRY_EXPR_OR(parse_precedence(context, PREC_TRY + 1), poisoned_expr); - try_expr->try_expr.type = TRY_EXPR; - if (try_consume(context, TOKEN_ELSE)) - { - switch (context->tok.type) - { - case TOKEN_GOTO: - case TOKEN_RETURN: - case TOKEN_BREAK: - case TOKEN_CONTINUE: - case TOKEN_THROW: - { - Ast *ast = TRY_AST_OR(parse_jump_stmt_no_eos(context), poisoned_expr); - try_expr->try_expr.type = TRY_EXPR_ELSE_JUMP; - try_expr->try_expr.else_stmt = ast; - if (context->tok.type != TOKEN_EOS) - { - SEMA_ERROR(ast, "try-else jump statement must end with a ';'"); - return poisoned_expr; - } - break; - } - default: - try_expr->try_expr.type = TRY_EXPR_ELSE_EXPR; - try_expr->try_expr.else_expr = TRY_EXPR_OR(parse_precedence(context, PREC_ASSIGNMENT), poisoned_expr); - break; - } - } + Expr *try_expr = EXPR_NEW_TOKEN(context->tok.type == TOKEN_TRY ? EXPR_TRY : EXPR_CATCH, context->tok); + advance(context); + CONSUME_OR(TOKEN_LPAREN, poisoned_expr); + try_expr->trycatch_expr = TRY_EXPR_OR(parse_expr(context), poisoned_expr); + CONSUME_OR(TOKEN_RPAREN, poisoned_expr); return try_expr; } +static Expr *parse_bangbang_expr(Context *context, Expr *left) +{ + Expr *guard_expr = EXPR_NEW_TOKEN(EXPR_GUARD, context->tok); + advance_and_verify(context, TOKEN_BANGBANG); + guard_expr->guard_expr = left; + return guard_expr; +} + +static Expr *parse_else_expr(Context *context, Expr *left) +{ + Expr *else_expr = EXPR_NEW_TOKEN(EXPR_ELSE, context->tok); + advance_and_verify(context, TOKEN_ELSE); + else_expr->else_expr.expr = left; + switch (context->tok.type) + { + case TOKEN_RETURN: + case TOKEN_BREAK: + case TOKEN_CONTINUE: + { + Ast *ast = TRY_AST_OR(parse_jump_stmt_no_eos(context), poisoned_expr); + else_expr->else_expr.is_jump = true; + else_expr->else_expr.else_stmt = ast; + if (context->tok.type != TOKEN_EOS) + { + SEMA_ERROR(ast, "An else jump statement must end with a ';'"); + return poisoned_expr; + } + break; + } + default: + else_expr->else_expr.else_expr = TRY_EXPR_OR(parse_precedence(context, PREC_ASSIGNMENT), poisoned_expr); + break; + } + return else_expr; +} + static Expr *parse_integer(Context *context, Expr *left) { assert(!left && "Had left hand side"); @@ -809,6 +864,17 @@ Expr *parse_type_expression_with_path(Context *context, Path *path) { return parse_type_compound_literal_expr_after_type(context, type); } + if (try_consume(context, TOKEN_BANG)) + { + Expr *expr = expr_new(EXPR_COMPOUND_LITERAL, type->span); + expr->expr_compound_literal.type_info = type; + expr->expr_compound_literal.initializer = expr_new(EXPR_INITIALIZER_LIST, type->span); + + Expr *failable = expr_new(EXPR_FAILABLE, expr->span); + failable->failable_expr = expr; + RANGE_EXTEND_PREV(failable); + return failable; + } CONSUME_OR(TOKEN_DOT, poisoned_expr); return parse_type_access_expr_after_type(context, type); } @@ -835,6 +901,7 @@ static Expr* parse_expr_block(Context *context, Expr *left) } ParseRule rules[TOKEN_EOF + 1] = { + [TOKEN_ELSE] = { NULL, parse_else_expr, PREC_TRY_ELSE }, [TOKEN_ELLIPSIS] = { NULL, parse_range_expr, PREC_RANGE }, [TOKEN_QUESTION] = { NULL, parse_ternary_expr, PREC_TERNARY }, [TOKEN_ELVIS] = { NULL, parse_ternary_expr, PREC_TERNARY }, @@ -844,7 +911,9 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_LPARBRA] = { parse_expr_block, NULL, PREC_NONE }, [TOKEN_CAST] = { parse_cast_expr, NULL, PREC_NONE }, [TOKEN_TYPEOF] = { parse_typeof_expr, NULL, PREC_NONE }, - [TOKEN_TRY] = { parse_try_expr, NULL, PREC_TRY }, + [TOKEN_TRY] = { parse_try_expr, NULL, PREC_NONE }, + [TOKEN_CATCH] = { parse_try_expr, NULL, PREC_NONE }, + [TOKEN_BANGBANG] = { NULL, parse_bangbang_expr, PREC_CALL }, [TOKEN_LBRACKET] = { NULL, parse_subscript_expr, PREC_CALL }, [TOKEN_MINUS] = { parse_unary_expr, parse_binary, PREC_ADDITIVE }, [TOKEN_MINUS_MOD] = { parse_unary_expr, parse_binary, PREC_ADDITIVE }, @@ -855,7 +924,7 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_STAR] = { parse_unary_expr, parse_binary, PREC_MULTIPLICATIVE }, [TOKEN_MULT_MOD] = { NULL, parse_binary, PREC_MULTIPLICATIVE }, [TOKEN_DOT] = { NULL, parse_access_expr, PREC_CALL }, - [TOKEN_NOT] = { parse_unary_expr, NULL, PREC_UNARY }, + [TOKEN_BANG] = { parse_unary_expr, parse_failable, PREC_UNARY }, [TOKEN_BIT_NOT] = { parse_unary_expr, NULL, PREC_UNARY }, [TOKEN_BIT_XOR] = { NULL, parse_binary, PREC_BIT }, [TOKEN_BIT_OR] = { NULL, parse_binary, PREC_BIT }, diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 6b9f3c156..fae6e38a9 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -21,36 +21,153 @@ static inline Ast *parse_declaration_stmt(Context *context) return decl_stmt; } -/** - * control_expression - * : decl_or_expr_list - * | declaration_list ';' decl_or_expr_list - * ; - */ -static inline bool parse_control_expression(Context *context, Ast **decls, Ast **exprs) +static inline Decl *parse_optional_label(Context *context, Ast *parent) { - *exprs = TRY_AST_OR(parse_decl_expr_list(context), false); - - if (!try_consume(context, TOKEN_EOS)) + if (context->tok.type != TOKEN_CONST_IDENT) return NULL; + Decl *decl = decl_new(DECL_LABEL, context->tok, VISIBLE_LOCAL); + decl->label.parent = parent; + advance_and_verify(context, TOKEN_CONST_IDENT); + if (!try_consume(context, TOKEN_COLON)) { - *decls = NULL; - return true; + SEMA_ERROR(decl, "The name must be followed by a ':', did you forget it?"); + return poisoned_decl; } + return decl; +} - *decls = *exprs; +static inline void parse_optional_label_target(Context *context, Label *label) +{ + if (context->tok.type == TOKEN_CONST_IDENT) + { + label->span = context->tok.span; + label->name = context->tok.string; + advance_and_verify(context, TOKEN_CONST_IDENT); + } +} - *exprs = TRY_AST_OR(parse_decl_expr_list(context), false); +static inline bool parse_asm_param(Context *context, AsmOperand **list) +{ + AsmOperand operand; + // Reset parser + context->lexer.current = context->tok.span.loc + context->lexer.file_begin; + operand.constraints = lexer_scan_asm_constraint(&context->lexer); + if (operand.constraints.type == TOKEN_INVALID_TOKEN) return false; + // Restore state + context->tok = lexer_scan_token(&context->lexer); + context->next_tok = lexer_scan_token(&context->lexer); + + operand.expr = TRY_EXPR_OR(parse_expr(context), false); + + if (try_consume(context, TOKEN_AS)) + { + EXPECT_OR(TOKEN_IDENT, false); + operand.alias = context->tok; + advance(context); + } + vec_add(*list, operand); return true; } +static inline bool parse_asm_paramlist(Context *context, AsmOperand **list) +{ + if (context->tok.type == TOKEN_EOS || context->tok.type == TOKEN_RPAREN) return true; + while (1) + { + if (!parse_asm_param(context, list)) return false; + if (context->tok.type == TOKEN_EOS || context->tok.type == TOKEN_RPAREN) return true; + CONSUME_OR(TOKEN_COMMA, false); + } +} + +static inline bool parse_asm_params(Context *context, Ast *asm_ast) +{ + // Might be empty. + if (try_consume(context, TOKEN_RPAREN)) return true; + + AsmParams *params = malloc_arena(sizeof(AsmParams)); + asm_ast->asm_stmt.params = params; + + // Parse outputs + if (!parse_asm_paramlist(context, ¶ms->inputs)) return false; + + // Might not have any more params + if (try_consume(context, TOKEN_RPAREN)) return true; + + // Consume the ';' + advance_and_verify(context, TOKEN_EOS); + + // Parse inputs + if (!parse_asm_paramlist(context, ¶ms->inputs)) return false; + + // Might not have any more params + if (try_consume(context, TOKEN_RPAREN)) return true; + + while (1) + { + EXPECT_OR(TOKEN_IDENT, false); + vec_add(params->clobbers, context->tok); + if (!try_consume(context, TOKEN_COMMA)) break; + } + + // Might not have any more params + if (try_consume(context, TOKEN_RPAREN)) return true; + + // Consume the ';' + CONSUME_OR(TOKEN_EOS, false); + + while (1) + { + EXPECT_OR(TOKEN_IDENT, false); + vec_add(params->labels, context->tok); + if (!try_consume(context, TOKEN_COMMA)) break; + } + + // Consume the ')' + CONSUME_OR(TOKEN_RPAREN, false); + + return true; +} +/** + * asm { ... } + * @param context + * @return + */ static inline Ast* parse_asm_stmt(Context *context) { - // TODO - SEMA_TOKEN_ERROR(context->tok, "ASM not supported yet."); - return poisoned_ast; + Ast *ast = AST_NEW_TOKEN(AST_ASM_STMT, context->tok); + advance_and_verify(context, TOKEN_ASM); + if (try_consume(context, TOKEN_LPAREN)) + { + if (!parse_asm_params(context, ast)) return poisoned_ast; + } + + if (context->tok.type != TOKEN_LBRACE) + { + SEMA_TOKEN_ERROR(context->tok, "Expected '{' to start asm segment."); + return poisoned_ast; + } + context->lexer.current = context->next_tok.span.loc + context->lexer.file_begin; + while (1) + { + context->tok = lexer_scan_asm(&context->lexer); + TokenType type = context->tok.type; + if (type == TOKEN_RBRACE || type == TOKEN_EOF) break; + context->prev_tok_end = context->tok.span.end_loc; + vec_add(ast->asm_stmt.instructions, context->tok); + } + if (context->tok.type == TOKEN_EOF) + { + sema_error_at(context->prev_tok_end, "Unexpected end of file while parsing asm, did you forget a '}'?"); + return poisoned_ast; + } + assert(context->tok.type == TOKEN_RBRACE); + context->tok = lexer_scan_token(&context->lexer); + context->next_tok = lexer_scan_token(&context->lexer); + return ast; } + /** * do_stmt * : DO statement WHILE '(' expression ')' ';' @@ -61,24 +178,42 @@ static inline Ast* parse_do_stmt(Context *context) advance_and_verify(context, TOKEN_DO); + do_ast->do_stmt.label = TRY_DECL_OR(parse_optional_label(context, do_ast), poisoned_ast); do_ast->do_stmt.body = TRY_AST(parse_stmt(context)); - CONSUME_OR(TOKEN_WHILE, poisoned_ast); - - CONSUME_OR(TOKEN_LPAREN, poisoned_ast); - do_ast->do_stmt.expr = TRY_EXPR_OR(parse_expr(context), poisoned_ast); - CONSUME_OR(TOKEN_RPAREN, poisoned_ast); - - CONSUME_OR(TOKEN_EOS, poisoned_ast); + if (try_consume(context, TOKEN_WHILE)) + { + CONSUME_OR(TOKEN_LPAREN, poisoned_ast); + do_ast->do_stmt.expr = TRY_EXPR_OR(parse_expr(context), poisoned_ast); + CONSUME_OR(TOKEN_RPAREN, poisoned_ast); + CONSUME_OR(TOKEN_EOS, poisoned_ast); + } return do_ast; } +static inline bool token_type_ends_case(TokenType type) +{ + return type == TOKEN_CASE || type == TOKEN_DEFAULT || type == TOKEN_RBRACE; +} + +static inline Ast *parse_case_stmts(Context *context) +{ + if (token_type_ends_case(context->tok.type)) return NULL; + Ast *compound = AST_NEW_TOKEN(AST_COMPOUND_STMT, context->tok); + while (!token_type_ends_case(context->tok.type)) + { + Ast *stmt = TRY_AST(parse_stmt(context)); + vec_add(compound->compound_stmt.stmts, stmt); + } + return compound; +} + /** * catch_stmt - * : CATCH '(' ERROR ident ')' statement - * | CATCH '(' type_expression ident ')' statement + * : CATCH '(' expression ')' catch_body + * | CATCH '(' expression ')' compound_stmt * ; */ static inline Ast* parse_catch_stmt(Context *context) @@ -86,25 +221,23 @@ static inline Ast* parse_catch_stmt(Context *context) Ast *catch_stmt = AST_NEW_TOKEN(AST_CATCH_STMT, context->tok); advance_and_verify(context, TOKEN_CATCH); + catch_stmt->catch_stmt.label = TRY_DECL_OR(parse_optional_label(context, catch_stmt), false); + CONSUME_OR(TOKEN_LPAREN, poisoned_ast); - TypeInfo *type = NULL; - if (context->tok.type == TOKEN_ERROR_TYPE) - { - type = type_info_new_base(type_error_union, context->tok.span); - advance(context); - } - else - { - type = TRY_TYPE_OR(parse_type(context), poisoned_ast); - } - EXPECT_IDENT_FOR_OR("error parameter", poisoned_ast); - Decl *decl = decl_new_var(context->tok, type, VARDECL_LOCAL, VISIBLE_LOCAL); - advance(context); - catch_stmt->catch_stmt.error_param = decl; + catch_stmt->catch_stmt.catchable = TRY_EXPR_OR(parse_expr(context), poisoned_ast); CONSUME_OR(TOKEN_RPAREN, poisoned_ast); + + if (context->tok.type == TOKEN_LBRACE && (context->next_tok.type == TOKEN_CASE || context->next_tok.type == TOKEN_DEFAULT)) + { + catch_stmt->catch_stmt.is_switch = true; + if (!parse_switch_body(context, &catch_stmt->catch_stmt.cases, TOKEN_CASE, TOKEN_DEFAULT)) return poisoned_ast; + return catch_stmt; + } + catch_stmt->catch_stmt.body = TRY_AST(parse_stmt(context)); + return catch_stmt; } @@ -127,16 +260,31 @@ static inline Ast* parse_defer_stmt(Context *context) static inline Ast* parse_while_stmt(Context *context) { Ast *while_ast = AST_NEW_TOKEN(AST_WHILE_STMT, context->tok); - advance_and_verify(context, TOKEN_WHILE); + + while_ast->while_stmt.label = TRY_DECL_OR(parse_optional_label(context, while_ast), poisoned_ast); + CONSUME_OR(TOKEN_LPAREN, poisoned_ast); - if (!parse_control_expression(context, &while_ast->while_stmt.decl, &while_ast->while_stmt.cond)) return poisoned_ast; + while_ast->while_stmt.cond = TRY_EXPR_OR(parse_decl_expr_list(context), poisoned_ast); CONSUME_OR(TOKEN_RPAREN, poisoned_ast); while_ast->while_stmt.body = TRY_AST(parse_stmt(context)); return while_ast; } + + /** + * if_expr + * : failable_type IDENT '=' initializer + * | failable_type IDENT NOFAIL_ASSIGN expression + * | expression + * ; + * + * if_cond_expr + * : if_expr + * | if_cond_expr ',' if_expr + * ; + * * if_stmt * : IF '(' control_expression ')' statement * | IF '(' control_expression ')' compound_statement ELSE compound_statement @@ -146,8 +294,9 @@ static inline Ast* parse_if_stmt(Context *context) { Ast *if_ast = AST_NEW_TOKEN(AST_IF_STMT, context->tok); advance_and_verify(context, TOKEN_IF); + if_ast->if_stmt.label = TRY_DECL_OR(parse_optional_label(context, if_ast), poisoned_ast); CONSUME_OR(TOKEN_LPAREN, poisoned_ast); - if (!parse_control_expression(context, &if_ast->if_stmt.decl, &if_ast->if_stmt.cond)) return poisoned_ast; + if_ast->if_stmt.cond = TRY_EXPR_OR(parse_decl_expr_list(context), poisoned_ast); CONSUME_OR(TOKEN_RPAREN, poisoned_ast); Ast *stmt = TRY_AST(parse_stmt(context)); if_ast->if_stmt.then_body = stmt; @@ -159,35 +308,43 @@ static inline Ast* parse_if_stmt(Context *context) return if_ast; } -static inline bool token_type_ends_case(TokenType type) -{ - return type == TOKEN_CASE || type == TOKEN_DEFAULT || type == TOKEN_RBRACE; -} -static inline Ast *parse_case_stmts(Context *context) + +static bool parse_type_or_expr(Context *context, TypeInfo **type_info, Expr **expr) { - if (token_type_ends_case(context->tok.type)) return NULL; - Ast *compound = AST_NEW_TOKEN(AST_COMPOUND_STMT, context->tok); - while (!token_type_ends_case(context->tok.type)) + if (parse_next_is_case_type(context)) { - Ast *stmt = TRY_AST(parse_stmt(context)); - vec_add(compound->compound_stmt.stmts, stmt); + *type_info = TRY_TYPE_OR(parse_type(context), false); + return true; } - return compound; + *expr = TRY_EXPR_OR(parse_constant_expr(context), false);; + return true; } - /** + * * case_stmt * : CASE constant_expression ':' case_stmts * | CASE constant_expression ELLIPSIS constant_expression ':' cast_stmts + * | CAST type ':' cast_stmts + * ; */ static inline Ast* parse_case_stmt(Context *context) { Ast *ast = AST_NEW_TOKEN(AST_CASE_STMT, context->tok); advance(context); - Expr *expr = TRY_EXPR_OR(parse_constant_expr(context), poisoned_ast); - ast->case_stmt.expr = expr; + TypeInfo *type = NULL; + Expr *expr = NULL; + if (!parse_type_or_expr(context, &type, &expr)) return poisoned_ast; + if (type) + { + ast->case_stmt.is_type = true; + ast->case_stmt.type_info = type; + } + else + { + ast->case_stmt.expr = expr; + } TRY_CONSUME(TOKEN_COLON, "Missing ':' after case"); extend_ast_with_prev_token(context, ast); ast->case_stmt.body = TRY_AST(parse_case_stmts(context)); @@ -201,7 +358,7 @@ static inline Ast* parse_case_stmt(Context *context) static inline Ast *parse_default_stmt(Context *context) { Ast *ast = AST_NEW_TOKEN(AST_DEFAULT_STMT, context->tok); - advance_and_verify(context, TOKEN_DEFAULT); + advance(context); TRY_CONSUME_OR(TOKEN_COLON, "Expected ':' after 'default'.", poisoned_ast); extend_ast_with_prev_token(context, ast); ast->case_stmt.body = TRY_AST(parse_case_stmts(context)); @@ -217,45 +374,50 @@ static inline Ast *parse_default_stmt(Context *context) * | default_stmt switch body * ; */ -static inline bool parse_switch_body(Context *context, Ast *switch_ast) +bool parse_switch_body(Context *context, Ast ***cases, TokenType case_type, TokenType default_type) { - Ast *result; - switch (context->tok.type) + CONSUME_OR(TOKEN_LBRACE, false); + while (!try_consume(context, TOKEN_RBRACE)) { - case TOKEN_CASE: + Ast *result; + TokenType next = context->tok.type; + if (next == case_type) + { result = TRY_AST_OR(parse_case_stmt(context), false); - break; - case TOKEN_DEFAULT: + } + else if (next == default_type) + { result = TRY_AST_OR(parse_default_stmt(context), false); - break; - default: + } + else + { SEMA_TOKEN_ERROR(context->tok, "A 'case' or 'default' would be needed here, '%.*s' is not allowed.", source_range_len(context->tok.span), context->tok.start); return false; + } + vec_add((*cases), result); } - vec_add(switch_ast->switch_stmt.cases, result); return true; } /** * switch - * : SWITCH '(' control_expression ')' '{' switch_body '}' + * : SWITCH '(' decl_expr_list ')' '{' switch_body '}' */ static inline Ast* parse_switch_stmt(Context *context) { Ast *switch_ast = AST_NEW_TOKEN(AST_SWITCH_STMT, context->tok); advance_and_verify(context, TOKEN_SWITCH); + switch_ast->switch_stmt.label = TRY_DECL_OR(parse_optional_label(context, switch_ast), poisoned_ast); CONSUME_OR(TOKEN_LPAREN, poisoned_ast); - if (!parse_control_expression(context, &switch_ast->switch_stmt.decl, &switch_ast->switch_stmt.cond)) return poisoned_ast; + switch_ast->switch_stmt.cond = TRY_EXPR_OR(parse_decl_expr_list(context), poisoned_ast); CONSUME_OR(TOKEN_RPAREN, poisoned_ast); - CONSUME_OR(TOKEN_LBRACE, poisoned_ast); - while (!try_consume(context, TOKEN_RBRACE)) - { - if (!parse_switch_body(context, switch_ast)) return poisoned_ast; - } + + if (!parse_switch_body(context, &switch_ast->switch_stmt.cases, TOKEN_CASE, TOKEN_DEFAULT)) return poisoned_ast; return switch_ast; } + /** * for_statement * : FOR '(' decl_expr_list ';' expression ';' ')' statement @@ -270,11 +432,12 @@ static inline Ast* parse_for_stmt(Context *context) { Ast *ast = AST_NEW_TOKEN(AST_FOR_STMT, context->tok); advance_and_verify(context, TOKEN_FOR); + ast->for_stmt.label = TRY_DECL_OR(parse_optional_label(context, ast), poisoned_ast); CONSUME_OR(TOKEN_LPAREN, poisoned_ast); if (context->tok.type != TOKEN_EOS) { - ast->for_stmt.init = TRY_AST(parse_decl_expr_list(context)); + ast->for_stmt.init = TRY_EXPR_OR(parse_decl_expr_list(context), poisoned_ast); } else { @@ -302,20 +465,6 @@ static inline Ast* parse_for_stmt(Context *context) return ast; } -/** - * goto - * : GOTO ct_ident - * ; - */ -static inline Ast* parse_goto(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_GOTO_STMT, context->tok); - advance_and_verify(context, TOKEN_GOTO); - ast->goto_stmt.label_name = context->tok.string; - if (!consume_const_name(context, "label")) return poisoned_ast; - return ast; -} - /** * continue_stmt * : CONTINUE @@ -324,6 +473,8 @@ static inline Ast* parse_continue(Context *context) { Ast *ast = AST_NEW_TOKEN(AST_CONTINUE_STMT, context->tok); advance_and_verify(context, TOKEN_CONTINUE); + parse_optional_label_target(context, &ast->contbreak_stmt.label); + if (ast->contbreak_stmt.label.name) ast->contbreak_stmt.is_label = true; return ast; } @@ -331,11 +482,32 @@ static inline Ast* parse_continue(Context *context) /** * next * : NEXT + * | NEXT expr */ static inline Ast* parse_next(Context *context) { Ast *ast = AST_NEW_TOKEN(AST_NEXT_STMT, context->tok); advance_and_verify(context, TOKEN_NEXT); + if (context->tok.type != TOKEN_EOS) + { + if (context->tok.type == TOKEN_CONST_IDENT && context->next_tok.type == TOKEN_COLON) + { + parse_optional_label_target(context, &ast->next_stmt.label); + advance_and_verify(context, TOKEN_COLON); + } + TypeInfo *type = NULL; + Expr *expr = NULL; + if (!parse_type_or_expr(context, &type, &expr)) return poisoned_ast; + if (type) + { + ast->next_stmt.is_type = true; + ast->next_stmt.type_info = type; + } + else + { + ast->next_stmt.target = expr; + } + } return ast; } @@ -347,12 +519,15 @@ static inline Ast* parse_break(Context *context) { Ast *ast = AST_NEW_TOKEN(AST_BREAK_STMT, context->tok); advance_and_verify(context, TOKEN_BREAK); + parse_optional_label_target(context, &ast->contbreak_stmt.label); + if (ast->contbreak_stmt.label.name) ast->contbreak_stmt.is_label = true; return ast; } /** * expr_stmt * : expression EOS + * ; */ static inline Ast *parse_expr_stmt(Context *context) { @@ -362,6 +537,63 @@ static inline Ast *parse_expr_stmt(Context *context) return stmt; } + +/** + * try_stmt + * : try '(' decl_expr ')' statement + * ; + */ +static inline Ast *parse_try_stmt(Context *context) +{ + Ast *stmt = AST_NEW_TOKEN(AST_TRY_STMT, context->tok); + advance_and_verify(context, TOKEN_TRY); + TRY_CONSUME(TOKEN_LPAREN, "Expected a '(' after 'try'."); + stmt->try_stmt.decl_expr = TRY_EXPR_OR(parse_decl_expr_list(context), poisoned_ast); + TRY_CONSUME(TOKEN_RPAREN, "Expected a ')' after 'try'."); + stmt->try_stmt.body = TRY_AST(parse_stmt(context)); + return stmt; +} + +/** + * define_stmt + * : define CT_IDENT '=' const_expr EOS + * | define CT_TYPE '=' const_expr EOS + * | define CT_IDENT EOS + * | define CT_TYPE EOS + * ; + */ +static inline Ast *parse_define_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_DEFINE_STMT, context->tok); + + advance_and_verify(context, TOKEN_DEFINE); + Decl *decl = decl_new_var(context->tok, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL); + ast->define_stmt = decl; + + switch (context->tok.type) + { + case TOKEN_CT_IDENT: + advance(context); + if (try_consume(context, TOKEN_EQ)) + { + decl->var.init_expr = TRY_EXPR_OR(parse_expr(context), poisoned_ast); + } + break; + case TOKEN_CT_TYPE_IDENT: + advance(context); + if (try_consume(context, TOKEN_EQ)) + { + decl->var.type_info = TRY_TYPE_OR(parse_type(context), poisoned_ast); + } + break; + default: + SEMA_TOKEN_ERROR(context->tok, "Expected a compile time variable name ('$Foo' or '$foo')."); + return poisoned_ast; + } + TRY_CONSUME_EOS(); + return ast; +} + /** * ct_else_stmt * : CT_ELSE compound_stmt @@ -421,18 +653,6 @@ static inline Ast* parse_ct_if_stmt(Context *context) return ast; } -/** - * label_stmt - * : ct_label ':' - */ -static inline Ast *parse_label_stmt(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_LABEL, context->tok); - ast->label_stmt.name = context->tok.string; - advance_and_verify(context, TOKEN_CONST_IDENT); - advance_and_verify(context, TOKEN_COLON); - return extend_ast_with_prev_token(context, ast); -} /** @@ -453,18 +673,7 @@ static inline Ast *parse_return(Context *context) return ast; } -/** - * throw - * : THROW expr - */ -static inline Ast *parse_throw(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_THROW_STMT, context->tok); - advance_and_verify(context, TOKEN_THROW); - ast->throw_stmt.throw_value = TRY_EXPR_OR(parse_expr(context), poisoned_ast); - RANGE_EXTEND_PREV(ast); - return ast; -} + /** * volatile_stmt @@ -544,43 +753,7 @@ static inline Ast* parse_ct_switch_stmt(Context *context) Ast *ast = AST_NEW_TOKEN(AST_CT_SWITCH_STMT, context->tok); advance_and_verify(context, TOKEN_CT_SWITCH); ast->ct_switch_stmt.cond = TRY_EXPR_OR(parse_paren_expr(context), poisoned_ast); - CONSUME_OR(TOKEN_LBRACE, poisoned_ast); - Ast **switch_statements = NULL; - Ast *stmt = poisoned_ast; - while (stmt) - { - switch (context->tok.type) - { - case TOKEN_CT_CASE: - stmt = AST_NEW_TOKEN(AST_CT_CASE_STMT, context->tok); - advance(context); - while (1) - { - TypeInfo *type = TRY_TYPE_OR(parse_type(context), poisoned_ast); - vec_add(stmt->ct_case_stmt.types, type); - if (!try_consume(context, TOKEN_COMMA)) break; - } - CONSUME_OR(TOKEN_COLON, poisoned_ast); - stmt->ct_case_stmt.body = TRY_AST_OR(parse_stmt(context), poisoned_ast); - vec_add(switch_statements, stmt); - break; - case TOKEN_CT_DEFAULT: - stmt = AST_NEW_TOKEN(AST_CT_CASE_STMT, context->tok); - advance(context); - CONSUME_OR(TOKEN_COLON, poisoned_ast); - stmt->ct_default_stmt = TRY_AST_OR(parse_stmt(context), poisoned_ast); - vec_add(switch_statements, stmt); - break; - case TOKEN_RBRACE: - stmt = NULL; - break; - default: - SEMA_TOKEN_ERROR(context->tok, "Expected $case or $default."); - return poisoned_ast; - } - } - CONSUME_OR(TOKEN_RBRACE, poisoned_ast); - ast->ct_switch_stmt.body = switch_statements; + if (!parse_switch_body(context, &ast->ct_switch_stmt.body, TOKEN_CT_CASE, TOKEN_CT_DEFAULT)) return poisoned_ast; return ast; } @@ -590,6 +763,9 @@ Ast *parse_stmt(Context *context) { switch (context->tok.type) { + case TOKEN_ASM_STRING: + case TOKEN_ASM_CONSTRAINT: + UNREACHABLE case TOKEN_LBRACE: return parse_compound_stmt(context); case TOKEN_HALF: @@ -622,7 +798,7 @@ Ast *parse_stmt(Context *context) case TOKEN_TYPEID: case TOKEN_CT_TYPE_IDENT: case TOKEN_TYPE_IDENT: - case TOKEN_ERROR_TYPE: + case TOKEN_ERR: case TOKEN_IDENT: if (parse_next_is_decl(context)) { @@ -632,14 +808,14 @@ Ast *parse_stmt(Context *context) { return parse_expr_stmt(context); } + case TOKEN_TRY: + return parse_try_stmt(context); + case TOKEN_DEFINE: + return parse_define_stmt(context); case TOKEN_LOCAL: // Local means declaration! case TOKEN_CONST: // Const means declaration! return parse_declaration_stmt(context); case TOKEN_CONST_IDENT: - if (context->next_tok.type == TOKEN_COLON) - { - return parse_label_stmt(context); - } return parse_expr_stmt(context); case TOKEN_AT: return parse_expr_stmt(context); @@ -656,31 +832,12 @@ Ast *parse_stmt(Context *context) return parse_defer_stmt(context); case TOKEN_SWITCH: return parse_switch_stmt(context); - case TOKEN_GOTO: - { - Ast *ast = TRY_AST(parse_goto(context)); - RETURN_AFTER_EOS(ast); - } case TOKEN_DO: return parse_do_stmt(context); case TOKEN_FOR: return parse_for_stmt(context); case TOKEN_CATCH: return parse_catch_stmt(context); - case TOKEN_TRY: - if (is_valid_try_statement(context->next_tok.type)) - { - Expr *try_expr = EXPR_NEW_TOKEN(EXPR_TRY, context->tok); - advance(context); - Ast *stmt = TRY_AST(parse_stmt(context)); - try_expr->try_expr.type = TRY_STMT; - try_expr->try_expr.stmt = stmt; - RANGE_EXTEND_PREV(try_expr); - Ast *ast = AST_NEW(AST_EXPR_STMT, try_expr->span); - ast->expr_stmt = try_expr; - return ast; - } - return parse_expr_stmt(context); case TOKEN_CONTINUE: { Ast *ast = TRY_AST(parse_continue(context)); @@ -712,11 +869,6 @@ Ast *parse_stmt(Context *context) return parse_ct_switch_stmt(context); case TOKEN_CT_FOR: return parse_ct_for_stmt(context); - case TOKEN_THROW: - { - Ast *ast = TRY_AST(parse_throw(context)); - RETURN_AFTER_EOS(ast); - } case TOKEN_VOLATILE: return parse_volatile_stmt(context); case TOKEN_STAR: @@ -727,7 +879,7 @@ Ast *parse_stmt(Context *context) case TOKEN_BIT_XOR: case TOKEN_LPAREN: case TOKEN_MINUS: - case TOKEN_NOT: + case TOKEN_BANG: case TOKEN_OR: case TOKEN_PLUS: case TOKEN_MINUSMINUS: @@ -798,8 +950,6 @@ Ast *parse_stmt(Context *context) case TOKEN_PUBLIC: case TOKEN_EXTERN: case TOKEN_STRUCT: - case TOKEN_THROWS: - case TOKEN_ERRSET: case TOKEN_TYPEDEF: case TOKEN_UNION: case TOKEN_UNTIL: @@ -817,6 +967,7 @@ Ast *parse_stmt(Context *context) case TOKEN_CT_DEFAULT: case TOKEN_RPARBRA: case TOKEN_IN: + case TOKEN_BANGBANG: SEMA_TOKEN_ERROR(context->tok, "Unexpected '%s' found when expecting a statement.", token_type_to_string(context->tok.type)); advance(context); return poisoned_ast; @@ -841,16 +992,12 @@ Ast *parse_jump_stmt_no_eos(Context *context) { switch (context->tok.type) { - case TOKEN_GOTO: - return parse_goto(context); case TOKEN_RETURN: return parse_return(context); case TOKEN_BREAK: return parse_break(context); case TOKEN_CONTINUE: return parse_continue(context); - case TOKEN_THROW: - return parse_throw(context); default: UNREACHABLE } diff --git a/src/compiler/parser.c b/src/compiler/parser.c index 246168c25..ec1139fba 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -190,7 +190,6 @@ static void recover_top_level(Context *context) case TOKEN_STRUCT: case TOKEN_IMPORT: case TOKEN_UNION: - case TOKEN_ERRSET: case TOKEN_MACRO: case TOKEN_EXTERN: return; @@ -336,8 +335,8 @@ static inline TypeInfo *parse_base_type(Context *context) type_info = type_info_new(TYPE_INFO_IDENTIFIER, context->tok.span); type_info->unresolved.name_loc = context->tok; break; - case TOKEN_ERROR_TYPE: - type_found = type_error_union; + case TOKEN_ERR: + type_found = type_error; break; case TOKEN_VOID: type_found = type_void; @@ -509,6 +508,7 @@ TypeInfo *parse_type(Context *context) return type_info; } + #pragma mark --- Decl parsing /** @@ -543,7 +543,6 @@ Decl *parse_decl_after_type(Context *context, bool local, TypeInfo *type) advance_and_verify(context, TOKEN_EQ); decl->var.init_expr = TRY_EXPR_OR(parse_initializer(context), poisoned_decl); } - return decl; } @@ -561,8 +560,14 @@ Decl *parse_decl(Context *context) TypeInfo *type_info = parse_type(context); TypeInfo *type = TRY_TYPE_OR(type_info, poisoned_decl); + bool failable = try_consume(context, TOKEN_BANG); Decl *decl = TRY_DECL_OR(parse_decl_after_type(context, local, type), poisoned_decl); - + if (failable && decl->var.unwrap) + { + SEMA_ERROR(decl, "You cannot use unwrap with a failable variable."); + return poisoned_decl; + } + decl->var.failable = failable; if (constant) decl->var.kind = VARDECL_CONST; return decl; @@ -591,10 +596,12 @@ static inline Decl *parse_const_declaration(Context *context, Visibility visibil } else { - if (token_is_type(context->tok.type) || context->tok.type == TOKEN_CT_TYPE_IDENT || context->tok.type == TOKEN_TYPE_IDENT) + if (token_is_any_type(context->tok.type)) { decl->var.type_info = TRY_TYPE_OR(parse_type(context), poisoned_decl); } + decl->name = context->tok.string; + decl->name_span = context->tok.span; if (!consume_const_name(context, "constant")) return poisoned_decl; } @@ -608,8 +615,8 @@ static inline Decl *parse_const_declaration(Context *context, Visibility visibil /** * global_declaration - * : type_expression IDENT ';' - * | type_expression IDENT '=' expression ';' + * : failable_type IDENT ';' + * | failable_type IDENT '=' expression ';' * ; * * @param visibility @@ -643,6 +650,11 @@ static inline Decl *parse_incremental_array(Context *context) Token name = context->tok; advance_and_verify(context, TOKEN_IDENT); + if (!try_consume(context, TOKEN_PLUS_ASSIGN)) + { + SEMA_TOKEN_ERROR(name, "Did you miss a declaration before the variable name?"); + return poisoned_decl; + } CONSUME_OR(TOKEN_PLUS_ASSIGN, poisoned_decl); Decl *decl = decl_new(DECL_ARRAY_VALUE, name, VISIBLE_LOCAL); decl->incr_array_decl = TRY_EXPR_OR(parse_initializer(context), poisoned_decl); @@ -661,34 +673,36 @@ static inline Decl *parse_incremental_array(Context *context) * * @return bool */ -Ast *parse_decl_expr_list(Context *context) +Expr *parse_decl_expr_list(Context *context) { - Ast *decl_expr = AST_NEW_TOKEN(AST_DECL_EXPR_LIST, context->tok); - decl_expr->decl_expr_stmt = NULL; + Expr *decl_expr = EXPR_NEW_TOKEN(EXPR_DECL_LIST, context->tok); + decl_expr->dexpr_list_expr = NULL; while (1) { if (parse_next_is_decl(context)) { - Decl *decl = TRY_DECL_OR(parse_decl(context), poisoned_ast); + Decl *decl = TRY_DECL_OR(parse_decl(context), poisoned_expr); Ast *stmt = AST_NEW(AST_DECLARE_STMT, decl->span); stmt->declare_stmt = decl; - vec_add(decl_expr->decl_expr_stmt, stmt); + vec_add(decl_expr->dexpr_list_expr, stmt); } else { - Expr *expr = TRY_EXPR_OR(parse_expr(context), poisoned_ast); + Expr *expr = TRY_EXPR_OR(parse_expr(context), poisoned_expr); Ast *stmt = AST_NEW(AST_EXPR_STMT, expr->span); stmt->expr_stmt = expr; - vec_add(decl_expr->decl_expr_stmt, stmt); + vec_add(decl_expr->dexpr_list_expr, stmt); } if (!try_consume(context, TOKEN_COMMA)) break; } - return extend_ast_with_prev_token(context, decl_expr); + RANGE_EXTEND_PREV(decl_expr); + return decl_expr; } bool parse_next_is_decl(Context *context) { + TokenType next_tok = context->next_tok.type; switch (context->tok.type) { case TOKEN_VOID: @@ -716,11 +730,11 @@ bool parse_next_is_decl(Context *context) case TOKEN_C_ULONGLONG: case TOKEN_TYPE_IDENT: case TOKEN_CT_TYPE_IDENT: - case TOKEN_ERROR_TYPE: + case TOKEN_ERR: case TOKEN_TYPEID: - return context->next_tok.type == TOKEN_STAR || context->next_tok.type == TOKEN_LBRACKET || context->next_tok.type == TOKEN_IDENT; + return (next_tok == TOKEN_BANG) | (next_tok == TOKEN_STAR) | (next_tok == TOKEN_LBRACKET) | (next_tok == TOKEN_IDENT); case TOKEN_IDENT: - if (context->next_tok.type != TOKEN_SCOPE) return false; + if (next_tok != TOKEN_SCOPE) return false; // We need a little lookahead to see if this is type or expression. context_store_lexer_state(context); do @@ -737,6 +751,56 @@ bool parse_next_is_decl(Context *context) +bool parse_next_is_case_type(Context *context) +{ + TokenType next_tok = context->next_tok.type; + switch (context->tok.type) + { + case TOKEN_VOID: + case TOKEN_BYTE: + case TOKEN_BOOL: + case TOKEN_CHAR: + case TOKEN_DOUBLE: + case TOKEN_FLOAT: + case TOKEN_INT: + case TOKEN_ISIZE: + case TOKEN_LONG: + case TOKEN_SHORT: + case TOKEN_UINT: + case TOKEN_ULONG: + case TOKEN_USHORT: + case TOKEN_USIZE: + case TOKEN_QUAD: + case TOKEN_C_SHORT: + case TOKEN_C_INT: + case TOKEN_C_LONG: + case TOKEN_C_LONGLONG: + case TOKEN_C_USHORT: + case TOKEN_C_UINT: + case TOKEN_C_ULONG: + case TOKEN_C_ULONGLONG: + case TOKEN_TYPE_IDENT: + case TOKEN_CT_TYPE_IDENT: + case TOKEN_ERR: + case TOKEN_TYPEID: + return (next_tok == TOKEN_STAR) | (next_tok == TOKEN_LBRACKET | next_tok == TOKEN_COLON | next_tok == TOKEN_EOS); + case TOKEN_IDENT: + if (next_tok != TOKEN_SCOPE) return false; + // We need a little lookahead to see if this is type or expression. + context_store_lexer_state(context); + do + { + advance(context); advance(context); + } while (context->tok.type == TOKEN_IDENT && context->next_tok.type == TOKEN_SCOPE); + bool is_type = context->tok.type == TOKEN_TYPE_IDENT; + context_restore_lexer_state(context); + return is_type; + default: + return false; + } +} + + #pragma mark --- Parse parameters & throws & attributes @@ -794,55 +858,6 @@ static inline bool parse_attributes(Context *context, Decl *parent_decl) -/** - * throw_declaration - * : THROWS - * | THROWS error_list - * ; - * - * opt_throw_declaration - * : throw_declaration - * | - * ; - * - */ -static inline bool parse_opt_throw_declaration(Context *context, Visibility visibility, FunctionSignature *signature) -{ - if (context->tok.type == TOKEN_THROW) - { - SEMA_TOKEN_ERROR(context->tok, "Did you mean 'throws'?"); - return false; - } - - if (!try_consume(context, TOKEN_THROWS)) return true; - if (context->tok.type != TOKEN_TYPE_IDENT && context->tok.type != TOKEN_IDENT) - { - signature->throw_any = true; - return true; - } - TypeInfo **throws = NULL; - while (1) - { - TypeInfo *throw = parse_base_type(context); - if (!type_info_ok(throw)) return false; - vec_add(throws, throw); - if (!try_consume(context, TOKEN_COMMA)) break; - } - switch (context->tok.type) - { - case TOKEN_TYPE_IDENT: - SEMA_TOKEN_ERROR(context->tok, "Expected ',' between each error type."); - return false; - case TOKEN_IDENT: - case TOKEN_CONST_IDENT: - SEMA_TOKEN_ERROR(context->tok, "Expected an error type."); - return false; - default: - break; - } - signature->throws = throws; - return true; -} /** @@ -1093,7 +1108,7 @@ static inline Ast *parse_generics_statements(Context *context) /** * generics_declaration * : GENERIC opt_path IDENT '(' macro_argument_list ')' '{' generics_body '}' - * | GENERIC type_expression opt_path IDENT '(' macro_argument_list ')' '{' generics_body '}' + * | GENERIC failable_type opt_path IDENT '(' macro_argument_list ')' '{' generics_body '}' * ; * * opt_path @@ -1134,45 +1149,47 @@ static inline Decl *parse_generics_declaration(Context *context, Visibility visi } CONSUME_OR(TOKEN_LBRACE, poisoned_decl); Ast **cases = NULL; - while (!try_consume(context, TOKEN_RBRACE)) - { - if (context->tok.type == TOKEN_CASE) - { - Ast *generic_case = AST_NEW_TOKEN(AST_GENERIC_CASE_STMT, context->tok); - advance_and_verify(context, TOKEN_CASE); - TypeInfo **types = NULL; - while (!try_consume(context, TOKEN_COLON)) - { - TypeInfo *type = TRY_TYPE_OR(parse_type(context), poisoned_decl); - types = VECADD(types, type); - if (!try_consume(context, TOKEN_COMMA) && context->tok.type != TOKEN_COLON) - { - SEMA_TOKEN_ERROR(context->tok, "Expected ',' or ':'."); - return poisoned_decl; - } - } - generic_case->generic_case_stmt.types = types; - generic_case->generic_case_stmt.body = TRY_AST_OR(parse_generics_statements(context), poisoned_decl); - cases = VECADD(cases, generic_case); - continue; - } - if (context->tok.type == TOKEN_DEFAULT) - { - Ast *generic_case = AST_NEW_TOKEN(AST_GENERIC_DEFAULT_STMT, context->tok); - advance_and_verify(context, TOKEN_DEFAULT); - CONSUME_OR(TOKEN_COLON, poisoned_decl); - generic_case->generic_default_stmt = TRY_AST_OR(parse_generics_statements(context), poisoned_decl); - cases = VECADD(cases, generic_case); - continue; - } - SEMA_TOKEN_ERROR(context->tok, "Expected 'case' or 'default'."); - return poisoned_decl; - } + if (!parse_switch_body(context, &cases, TOKEN_CASE, TOKEN_DEFAULT)) return poisoned_decl; decl->generic_decl.cases = cases; decl->generic_decl.parameters = parameters; return decl; } +static inline Decl *parse_define(Context *context, Visibility visibility) +{ + advance_and_verify(context, TOKEN_DEFINE); + TypeInfo *rtype = NULL; + if (context->tok.type != TOKEN_IDENT) + { + rtype = TRY_TYPE_OR(parse_type(context), poisoned_decl); + } + bool had_error; + Path *path = parse_path_prefix(context, &had_error); + if (had_error) return poisoned_decl; + Decl *decl = decl_new(DECL_GENERIC, context->tok, visibility); + decl->generic_decl.path = path; + if (!consume_ident(context, "generic function name")) return poisoned_decl; + decl->generic_decl.rtype = rtype; + Token *parameters = NULL; + CONSUME_OR(TOKEN_LPAREN, poisoned_decl); + while (!try_consume(context, TOKEN_RPAREN)) + { + if (context->tok.type != TOKEN_IDENT) + { + SEMA_TOKEN_ERROR(context->tok, "Expected an identifier."); + return false; + } + parameters = VECADD(parameters, context->tok); + advance(context); + COMMA_RPAREN_OR(poisoned_decl); + } + CONSUME_OR(TOKEN_LBRACE, poisoned_decl); + Ast **cases = NULL; + if (!parse_switch_body(context, &cases, TOKEN_CASE, TOKEN_DEFAULT)) return poisoned_decl; + decl->generic_decl.cases = cases; + decl->generic_decl.parameters = parameters; + return decl; +} static AttributeDomain TOKEN_TO_ATTR[TOKEN_EOF + 1] = { @@ -1183,7 +1200,7 @@ static AttributeDomain TOKEN_TO_ATTR[TOKEN_EOF + 1] = { [TOKEN_UNION] = ATTR_UNION, [TOKEN_CONST] = ATTR_CONST, [TOKEN_TYPEDEF] = ATTR_TYPEDEF, - [TOKEN_ERRSET] = ATTR_ERROR, + [TOKEN_ERR] = ATTR_ERROR, }; /** @@ -1246,8 +1263,8 @@ static inline Decl *parse_attribute_declaration(Context *context, Visibility vis */ /** * func_typedef - * : FUNC type_expression opt_parameter_type_list - * | FUNC type_expression opt_parameter_type_list throw_declaration + * : FUNC failable_type opt_parameter_type_list + * | FUNC failable_type opt_parameter_type_list throw_declaration * ; */ static inline bool parse_func_typedef(Context *context, Decl *decl, Visibility visibility) @@ -1255,12 +1272,16 @@ static inline bool parse_func_typedef(Context *context, Decl *decl, Visibility v decl->typedef_decl.is_func = true; advance_and_verify(context, TOKEN_FUNC); TypeInfo *type_info = TRY_TYPE_OR(parse_type(context), false); + if (try_consume(context, TOKEN_BANG)) + { + decl->typedef_decl.function_signature.failable = true; + } decl->typedef_decl.function_signature.rtype = type_info; if (!parse_opt_parameter_type_list(context, visibility, &(decl->typedef_decl.function_signature), true)) { return false; } - return parse_opt_throw_declaration(context, visibility, &(decl->typedef_decl.function_signature)); + return true; } @@ -1291,12 +1312,15 @@ static inline Decl *parse_macro_declaration(Context *context, Visibility visibil advance_and_verify(context, TOKEN_MACRO); TypeInfo *rtype = NULL; + bool failable = false; if (context->tok.type != TOKEN_IDENT) { rtype = TRY_TYPE_OR(parse_type(context), poisoned_decl); + failable = try_consume(context, TOKEN_BANG); } Decl *decl = decl_new(DECL_MACRO, context->tok, visibility); decl->macro_decl.rtype = rtype; + decl->macro_decl.failable = failable; TRY_CONSUME_OR(TOKEN_IDENT, "Expected a macro name here", poisoned_decl); CONSUME_OR(TOKEN_LPAREN, poisoned_decl); @@ -1332,48 +1356,37 @@ static inline Decl *parse_macro_declaration(Context *context, Visibility visibil /** * error_declaration - * : ERROR TYPE_IDENT '{' error_list '}' + * : ERROR TYPE_IDENT ';' + * | ERROR TYPE_IDENT '{' error_data '}' * ; - * */ static inline Decl *parse_error_declaration(Context *context, Visibility visibility) { - advance_and_verify(context, TOKEN_ERRSET); + advance_and_verify(context, TOKEN_ERR); - Decl *error_decl = decl_new_with_type(context->tok, DECL_ERROR, visibility); + Decl *err_decl = decl_new_with_type(context->tok, DECL_ERR, visibility); if (!consume_type_name(context, "error type")) return poisoned_decl; - CONSUME_OR(TOKEN_LBRACE, poisoned_decl); - - while (context->tok.type == TOKEN_CONST_IDENT) - { - Decl *err_constant = decl_new(DECL_ERROR_CONSTANT, context->tok, error_decl->visibility); - - err_constant->error_constant.parent = error_decl; - VECEACH(error_decl->error.error_constants, i) - { - Decl *other_constant = error_decl->error.error_constants[i]; - if (other_constant->name == context->tok.string) - { - SEMA_TOKEN_ERROR(context->tok, "This error is declared twice."); - SEMA_PREV(other_constant, "The previous declaration was here."); - decl_poison(err_constant); - decl_poison(error_decl); - break; - } - } - error_decl->error.error_constants = VECADD(error_decl->error.error_constants, err_constant); - advance_and_verify(context, TOKEN_CONST_IDENT); - if (!try_consume(context, TOKEN_COMMA)) break; - } - if (context->tok.type == TOKEN_TYPE_IDENT || context->tok.type == TOKEN_IDENT) - { - SEMA_TOKEN_ERROR(context->tok, "Errors must be all upper case."); - return poisoned_decl; - } - CONSUME_OR(TOKEN_RBRACE, poisoned_decl); - return error_decl; + if (try_consume(context, TOKEN_LBRACE)) + { + while (!try_consume(context, TOKEN_RBRACE)) + { + TypeInfo *type = TRY_TYPE_OR(parse_type(context), poisoned_decl); + if (context->tok.type != TOKEN_IDENT) + { + SEMA_TOKEN_ERROR(context->tok, "Expected an identifier here."); + return poisoned_decl; + } + Decl *member = decl_new(DECL_MEMBER, context->tok, visibility); + advance(context); + add_struct_member(err_decl, err_decl, member, type); + TRY_CONSUME_EOS_OR(poisoned_decl); + } + return err_decl; + } + TRY_CONSUME_EOS_OR(poisoned_decl); + return err_decl; } /** @@ -1457,7 +1470,7 @@ static inline Decl *parse_enum_declaration(Context *context, Visibility visibili if (try_consume(context, TOKEN_LPAREN)) { Expr **result = NULL; - if (!parse_param_list(context, &result, true)) return poisoned_decl; + if (!parse_param_list(context, &result, true, TOKEN_RPAREN)) return poisoned_decl; enum_const->enum_constant.args = result; CONSUME_OR(TOKEN_RPAREN, poisoned_decl); } @@ -1494,8 +1507,8 @@ static inline Decl *parse_enum_declaration(Context *context, Visibility visibili * ; * * func_declaration - * : FUNC type_expression func_name '(' opt_parameter_type_list ')' opt_attributes - * | FUNC type_expression func_name '(' opt_parameter_type_list ')' throw_declaration opt_attributes + * : FUNC failable_type func_name '(' opt_parameter_type_list ')' opt_attributes + * | FUNC failable_type func_name '(' opt_parameter_type_list ')' throw_declaration opt_attributes * ; * * @param visibility @@ -1506,10 +1519,9 @@ static inline Decl *parse_func_definition(Context *context, Visibility visibilit advance_and_verify(context, TOKEN_FUNC); TypeInfo *return_type = TRY_TYPE_OR(parse_type(context), poisoned_decl); - Decl *func = decl_new(DECL_FUNC, context->tok, visibility); func->func.function_signature.rtype = return_type; - + func->func.function_signature.failable = try_consume(context, TOKEN_BANG); SourceRange start = context->tok.span; bool had_error; Path *path = parse_path_prefix(context, &had_error); @@ -1535,8 +1547,6 @@ static inline Decl *parse_func_definition(Context *context, Visibility visibilit RANGE_EXTEND_PREV(func); if (!parse_opt_parameter_type_list(context, visibility, &(func->func.function_signature), is_interface)) return poisoned_decl; - if (!parse_opt_throw_declaration(context, visibility, &(func->func.function_signature))) return poisoned_decl; - if (!parse_attributes(context, func)) return poisoned_decl; // TODO remove @@ -1671,6 +1681,8 @@ static inline Decl *parse_top_level(Context *context) switch (context->tok.type) { + case TOKEN_DEFINE: + return parse_define(context, visibility); case TOKEN_ATTRIBUTE: return parse_attribute_declaration(context, visibility); case TOKEN_FUNC: @@ -1689,7 +1701,7 @@ static inline Decl *parse_top_level(Context *context) return parse_macro_declaration(context, visibility); case TOKEN_ENUM: return parse_enum_declaration(context, visibility); - case TOKEN_ERRSET: + case TOKEN_ERR: return parse_error_declaration(context, visibility); case TOKEN_TYPEDEF: return parse_typedef_declaration(context, visibility); diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index 8d6776002..454915580 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -29,6 +29,11 @@ do { if (!try_consume(context, TOKEN_COMMA) && context->tok.type != TOKEN_RPAREN SEMA_TOKEN_ERROR(context->tok, "Expected ',' or ')'"); return _res; } } while(0) +typedef enum +{ + DECL_PARSE_NORMAL, + DECL_PARSE_UNWRAP +} DeclParse; Ast *parse_stmt(Context *context); Path *parse_path_prefix(Context *context, bool *had_error); Expr *parse_type_expression_with_path(Context *context, Path *path); @@ -38,15 +43,17 @@ Expr* parse_constant_expr(Context *context); Expr *parse_initializer_list(Context *context); Expr *parse_initializer(Context *context); Decl *parse_decl(Context *context); -Ast *parse_decl_expr_list(Context *context); +Expr *parse_decl_expr_list(Context *context); Ast* parse_compound_stmt(Context *context); Ast *parse_jump_stmt_no_eos(Context *context); +bool parse_switch_body(Context *context, Ast ***cases, TokenType case_type, TokenType default_type); Expr *parse_expression_list(Context *context); Decl *parse_decl_after_type(Context *context, bool local, TypeInfo *type); -bool parse_param_list(Context *context, Expr ***result, bool allow_type); +bool parse_param_list(Context *context, Expr ***result, bool allow_type, TokenType end_type); Expr *parse_type_compound_literal_expr_after_type(Context *context, TypeInfo *type_info); Expr *parse_type_access_expr_after_type(Context *context, TypeInfo *type_info); bool parse_next_is_decl(Context *context); +bool parse_next_is_case_type(Context *context); void error_at_current(Context *context, const char* message, ...); bool try_consume(Context *context, TokenType type); bool consume(Context *context, TokenType type, const char *message, ...); diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index bfd6de10a..2f20e4253 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -15,11 +15,10 @@ static inline void insert_cast(Expr *expr, CastKind kind, Type *canonical) { - Expr *inner = malloc_arena(sizeof(Expr)); assert(expr->resolve_status == RESOLVE_DONE); assert(expr->type); assert(canonical->canonical == canonical); - *inner = *expr; + Expr *inner = COPY(expr); expr->expr_kind = EXPR_CAST; expr->cast_expr.kind = kind; expr->cast_expr.expr = inner; @@ -226,6 +225,7 @@ bool bofp(Expr *left, Type *canonical, Type *type, CastType cast_type) */ bool xibo(Expr *left, Type *canonical, Type *type, CastType cast_type) { + if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true; // if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH(); RETURN_NON_CONST_CAST(CAST_INTBOOL); @@ -328,40 +328,6 @@ bool ixxen(Expr *left, Type *canonical, Type *type, CastType cast_type) return ixxxi(left, canonical, type, cast_type); } -/** - * Convert from compile time int to error value - */ -bool ixxer(Expr *left, Type *canonical, Type *type, CastType cast_type) -{ - // Assigning zero = no value is always ok. - REQUIRE_EXPLICIT_CAST(cast_type); - - if (left->expr_kind == EXPR_CONST) - { - if (bigint_cmp_zero(&left->const_expr.i) != CMP_GT) - { - SEMA_ERROR(left, "Cannot cast '%s' to an error value.", expr_const_to_error_string(&left->const_expr)); - return false; - } - BigInt comp; - bigint_init_unsigned(&comp, vec_size(canonical->decl->error.error_constants)); - if (bigint_cmp(&left->const_expr.i, &comp) == CMP_GT) - { - SEMA_ERROR(left, "Cannot cast '%s' to a valid '%s' error value.", expr_const_to_error_string(&left->const_expr), canonical->decl->name); - return false; - } - left->type = type; - return true; - } - assert(canonical->type_kind == TYPE_ERROR); - - if (!ixxxi(left, type_error_base->canonical, type_error_base, cast_type)) return false; - - insert_cast(left, CAST_XIERR, canonical); - - return true; -} - /** * Convert from compile time int to error union */ @@ -613,16 +579,6 @@ bool enxi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ // 3. Dispatch to the right cast: return xixi(left, enum_type_canonical, canonical, type, cast_type); } -bool erxi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) -{ - TODO -} - -bool ereu(Expr *left) -{ - insert_cast(left, CAST_ERREU, type_error_union); - return true; -} bool vava(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) { @@ -648,17 +604,6 @@ bool vasa(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ -bool euxi(Expr *left, Type *canonical, Type *type, CastType cast_type) -{ - if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true; - if (cast_type == CAST_TYPE_IMPLICIT) - { - SEMA_ERROR(left, "Cannot implictly cast an error to '%s'.", type_to_error_string(type)); - return false; - } - left->type = type; - return true; -} bool xieu(Expr *left, Type *canonical, Type *type, CastType cast_type) { @@ -670,23 +615,6 @@ bool xierr(Expr *left, Type *canonical, Type *type, CastType cast_type) TODO } -/** - * Convert error union to error. This is always a required cast. - * @return false if an error was reported. - */ -bool euer(Expr *left, Type *canonical, Type *type, CastType cast_type) -{ - TODO - if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true; - if (cast_type == CAST_TYPE_IMPLICIT) - { - SEMA_ERROR(left, "Cannot implictly cast an error union back to '%s'.", type_to_error_string(type)); - return false; - } - insert_cast(left, CAST_EUERR, canonical); - return true; -} - /** * Convert error union to error. This is always a required cast. @@ -694,11 +622,24 @@ bool euer(Expr *left, Type *canonical, Type *type, CastType cast_type) */ bool eubool(Expr *left, Type *canonical, Type *type, CastType cast_type) { - TODO insert_cast(left, CAST_EUBOOL, canonical); return true; } + +bool euer(Expr *left, Type *canonical, Type *type, CastType cast_type) +{ + REQUIRE_EXPLICIT_CAST(cast_type); + insert_cast(left, CAST_EUER, canonical); + return true; +} + + +bool ereu(Expr *left, Type *canonical, Type *type, CastType cast_type) +{ + insert_cast(left, CAST_EREU, canonical); + return true; +} bool ptva(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type) { TODO @@ -733,10 +674,10 @@ bool vapt(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ } - -bool cast_to_runtime(Expr *expr) +bool cast_implicitly_to_runtime(Expr *expr) { Type *canonical = expr->type->canonical; + int success; switch (canonical->type_kind) { case TYPE_IXX: @@ -746,23 +687,6 @@ bool cast_to_runtime(Expr *expr) default: return true; } -} - -void cast_to_smallest_runtime(Expr *expr) -{ - Type *canonical = expr->type->canonical; - int success; - switch (canonical->type_kind) - { - case TYPE_IXX: - success = cast(expr, type_long, CAST_TYPE_IMPLICIT); - break; - case TYPE_FXX: - success = cast(expr, type_double, CAST_TYPE_IMPLICIT); - break; - default: - return; - } assert(success && "This should always work"); } @@ -780,11 +704,11 @@ CastKind cast_to_bool_kind(Type *type) return cast_to_bool_kind(type->canonical); case TYPE_POISONED: case TYPE_VOID: - case TYPE_ERROR_UNION: + case TYPE_ERR_UNION: case TYPE_STRUCT: case TYPE_UNION: case TYPE_STRING: - case TYPE_ERROR: + case TYPE_ERRTYPE: case TYPE_ENUM: case TYPE_FUNC: case TYPE_MEMBER: @@ -834,10 +758,9 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type) if (type_is_integer(canonical)) return boxi(expr, canonical, to_type, cast_type); if (type_is_float(canonical)) return bofp(expr, canonical, to_type, cast_type); break; - case TYPE_ERROR_UNION: + case TYPE_ERR_UNION: if (to_type->type_kind == TYPE_BOOL) return eubool(expr, canonical, to_type, cast_type); - if (type_is_integer(canonical)) return euxi(expr, canonical, to_type, cast_type); - if (to_type->type_kind == TYPE_ERROR) return euer(expr, canonical, to_type, cast_type); + if (to_type->type_kind == TYPE_ERRTYPE) return euer(expr, canonical, to_type, cast_type); break; case TYPE_IXX: // Compile time integers may convert into ints, floats, bools @@ -846,7 +769,6 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type) if (canonical == type_bool) return ixxbo(expr, to_type); if (canonical->type_kind == TYPE_POINTER) return xipt(expr, from_type, canonical, to_type, cast_type); if (canonical->type_kind == TYPE_ENUM) return ixxen(expr, canonical, to_type, cast_type); - if (canonical->type_kind == TYPE_ERROR) return ixxer(expr, canonical, to_type, cast_type); break; case TYPE_I8: case TYPE_I16: @@ -857,8 +779,6 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type) if (type_is_float(canonical)) return sifp(expr, canonical, to_type); if (canonical == type_bool) return xibo(expr, canonical, to_type, cast_type); if (canonical->type_kind == TYPE_POINTER) return xipt(expr, from_type, canonical, to_type, cast_type); - if (canonical->type_kind == TYPE_ERROR_UNION) return xieu(expr, canonical, to_type, cast_type); - if (canonical->type_kind == TYPE_ERROR) return xierr(expr, canonical, to_type, cast_type); break; case TYPE_U8: case TYPE_U16: @@ -887,9 +807,8 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type) case TYPE_ENUM: if (type_is_integer(canonical)) return enxi(expr, from_type, canonical, to_type, cast_type); break; - case TYPE_ERROR: - if (type_is_integer(canonical)) return erxi(expr, from_type, canonical, to_type, cast_type); - if (canonical == type_error_union) return ereu(expr); + case TYPE_ERRTYPE: + if (canonical->type_kind == TYPE_ERR_UNION) return ereu(expr, canonical, to_type, cast_type); break; case TYPE_FUNC: SEMA_ERROR(expr, "The function call is missing (...), if you want to take the address of a function it must be prefixed with '&'."); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 9b48fddc3..cf25c1054 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -6,37 +6,6 @@ #include "bigint.h" -static inline bool sema_analyse_error(Context *context __unused, Decl *decl) -{ - Decl **constants = decl->error.error_constants; - unsigned size = vec_size(constants); - if (size > MAX_ERRORS) - { - SEMA_ERROR(decl, "More than %d errors declared in a single error type.", MAX_ERRORS); - return false; - } - bool success = true; - for (unsigned i = 0; i < size; i++) - { - Decl *constant = constants[i]; - for (unsigned j = 0; j < i; j++) - { - if (constant->name == constants[j]->name) - { - SEMA_ERROR(constant, "Duplicate error names, please remove one of them."); - SEMA_PREV(constants[j], "The previous declaration was here."); - decl_poison(constant); - decl_poison(constants[j]); - success = false; - break; - } - } - constant->type = decl->type; - constant->error_constant.value = i + 1; - constant->resolve_status = RESOLVE_DONE; - } - return success; -} static inline void sema_set_struct_size(Decl *decl) @@ -142,7 +111,7 @@ static inline bool sema_analyse_function_param(Context *context, Decl *param, bo if (param->var.init_expr) { Expr *expr = param->var.init_expr; - if (!sema_analyse_expr_of_required_type(context, param->type, expr)) return false; + if (!sema_analyse_expr_of_required_type(context, param->type, expr, false)) return false; if (expr->expr_kind != EXPR_CONST) { SEMA_ERROR(expr, "Only constant expressions may be used as default values."); @@ -162,6 +131,7 @@ static inline Type *sema_analyse_function_signature(Context *context, FunctionSi if (all_ok) { type_append_signature_name(signature->rtype->type, buffer, &buffer_write_offset); + if (signature->failable) buffer[buffer_write_offset++] = '!'; buffer[buffer_write_offset++] = '('; } if (vec_size(signature->params) > MAX_PARAMS) @@ -212,47 +182,13 @@ static inline Type *sema_analyse_function_signature(Context *context, FunctionSi buffer[buffer_write_offset++] = '.'; } buffer[buffer_write_offset++] = ')'; - if (signature->throw_any) - { - assert(!signature->throws); - buffer[buffer_write_offset++] = '!'; - } - if (vec_size(signature->throws)) - { - buffer[buffer_write_offset++] = '!'; - VECEACH(signature->throws, i) - { - TypeInfo *err_decl = signature->throws[i]; - if (!sema_resolve_type_info(context, err_decl)) - { - return false; - } - if (i > 0 && all_ok) - { - buffer[buffer_write_offset++] = '|'; - } - type_append_signature_name(err_decl->type, buffer, &buffer_write_offset); - } - } - - unsigned error_types = vec_size(signature->throws); - ErrorReturn error_return = ERROR_RETURN_NONE; - if (signature->throw_any) - { - error_return = ERROR_RETURN_ANY; - } - else if (error_types) - { - error_return = error_types > 1 ? ERROR_RETURN_MANY : ERROR_RETURN_ONE; - } - signature->error_return = error_return; Type *return_type = signature->rtype->type->canonical; signature->return_param = false; if (return_type->type_kind != TYPE_VOID) { // TODO fix this number with ABI compatibility - if (signature->error_return != ERROR_RETURN_NONE || type_size(return_type) > 8 * 2) + if (signature->failable || type_size(return_type) > 8 * 2) { signature->return_param = true; } @@ -339,7 +275,7 @@ static inline bool sema_analyse_enum(Context *context, Decl *decl) } // We try to convert to the desired type. - if (!sema_analyse_expr_of_required_type(context, type, expr)) + if (!sema_analyse_expr_of_required_type(context, type, expr, false)) { success = false; enum_value->resolve_status = RESOLVE_DONE; @@ -608,7 +544,7 @@ static inline bool sema_analyse_global(Context *context, Decl *decl) decl->type = decl->var.type_info->type; if (decl->var.init_expr) { - if (!sema_analyse_expr_of_required_type(context, decl->type, decl->var.init_expr)) return false; + if (!sema_analyse_expr_of_required_type(context, decl->type, decl->var.init_expr, false)) return false; if (decl->var.init_expr->expr_kind != EXPR_CONST) { SEMA_ERROR(decl->var.init_expr, "The expression must be a constant value."); @@ -671,6 +607,49 @@ static inline bool sema_analyse_generic(Context *context, Decl *decl) } + +static inline bool sema_analyse_error(Context *context __unused, Decl *decl) +{ + Decl **members = decl->strukt.members; + unsigned member_count = vec_size(members); + bool success = true; + unsigned error_size = 0; + for (unsigned i = 0; i < member_count; i++) + { + Decl *member = members[i]; + for (unsigned j = 0; j < i; j++) + { + if (member->name == members[j]->name) + { + SEMA_ERROR(member, "Duplicate error names, please remove one of them."); + SEMA_PREV(members[j], "The previous declaration was here."); + decl_poison(member); + decl_poison(members[j]); + success = false; + break; + } + } + sema_analyse_struct_member(context, member); + unsigned alignment = type_abi_alignment(member->type); + unsigned size = type_size(member->type); + if (error_size % alignment != 0) + { + error_size += alignment - (error_size % alignment); + } + error_size += size; + } + sema_set_struct_size(decl); + if (decl->strukt.size > type_size(type_usize)) + { + SEMA_ERROR(decl, "Error type may exceed pointer size (%d bytes) it was %d bytes.", type_size(type_usize), error_size); + return false; + } + decl->strukt.abi_alignment = type_abi_alignment(type_voidptr); + decl->strukt.size = type_size(type_error); + return success; +} + + bool sema_analyse_decl(Context *context, Decl *decl) { if (decl->resolve_status == RESOLVE_DONE) return decl_ok(decl); @@ -715,7 +694,7 @@ bool sema_analyse_decl(Context *context, Decl *decl) if (!sema_analyse_enum(context, decl)) return decl_poison(decl); decl_set_external_name(decl); break; - case DECL_ERROR: + case DECL_ERR: if (!sema_analyse_error(context, decl)) return decl_poison(decl); decl_set_external_name(decl); break; @@ -727,11 +706,11 @@ bool sema_analyse_decl(Context *context, Decl *decl) case DECL_POISONED: case DECL_IMPORT: case DECL_ENUM_CONSTANT: - case DECL_ERROR_CONSTANT: case DECL_ARRAY_VALUE: case DECL_CT_ELSE: case DECL_CT_ELIF: case DECL_MEMBER: + case DECL_LABEL: UNREACHABLE case DECL_CT_IF: // Handled elsewhere diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index af3dfb4a6..5272cba12 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -69,7 +69,7 @@ static inline Ast **context_push_returns(Context *context) int sema_check_comp_time_bool(Context *context, Expr *expr) { - if (!sema_analyse_expr_of_required_type(context, type_bool, expr)) return -1; + if (!sema_analyse_expr_of_required_type(context, type_bool, expr, false)) return -1; if (expr->expr_kind != EXPR_CONST) { SEMA_ERROR(expr, "$if requires a compile time constant value."); @@ -83,10 +83,20 @@ static bool expr_is_ltype(Expr *expr) switch (expr->expr_kind) { case EXPR_IDENTIFIER: - return - (expr->identifier_expr.decl->decl_kind == DECL_VAR && (expr->identifier_expr.decl->var.kind == VARDECL_LOCAL - || expr->identifier_expr.decl->var.kind == VARDECL_GLOBAL - || expr->identifier_expr.decl->var.kind == VARDECL_PARAM)); + { + Decl *decl = expr->identifier_expr.decl; + if (decl->decl_kind != DECL_VAR) return false; + decl = decl_raw(decl); + switch (decl->var.kind) + { + case VARDECL_LOCAL: + case VARDECL_GLOBAL: + case VARDECL_PARAM: + return true; + default: + return false; + } + } case EXPR_UNARY: return expr->unary_expr.operator == UNARYOP_DEREF; case EXPR_ACCESS: @@ -101,15 +111,22 @@ static bool expr_is_ltype(Expr *expr) } +static ExprFailableStatus expr_is_failable(Expr *expr) +{ + if (expr->expr_kind != EXPR_IDENTIFIER) return FAILABLE_NO; + Decl *decl = expr->identifier_expr.decl; + if (decl->decl_kind != DECL_VAR) return FAILABLE_NO; + if (decl->var.kind == VARDECL_ALIAS && decl->var.alias->var.failable) return FAILABLE_UNWRAPPED; + return decl->var.failable ? FAILABLE_YES : FAILABLE_NO; +} + + static inline bool sema_type_error_on_binop(Expr *expr) { const char *c = token_type_to_string(binaryop_to_token(expr->binary_expr.operator)); - SEMA_ERROR(expr, - "%s is not defined in the expression '%s' %s '%s'.", - c, - type_to_error_string(expr->binary_expr.left->type), - c, - type_to_error_string(expr->binary_expr.right->type)); + SEMA_ERROR(expr, "%s is not defined in the expression '%s' %s '%s'.", + c, type_to_error_string(expr->binary_expr.left->type), + c, type_to_error_string(expr->binary_expr.right->type)); return false; } @@ -131,12 +148,15 @@ static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *e if (left) { if (!sema_analyse_expr(context, type_bool, cond)) return expr_poison(expr); + if (!cast_implicit(cond, type_bool)) return expr_poison(expr); if (!sema_analyse_expr(context, to, left)) return expr_poison(expr); + expr->failable = left->failable | cond->failable; } else { // Elvis if (!sema_analyse_expr(context, to, cond)) return expr_poison(expr); + expr->failable = cond->failable; Type *type = cond->type->canonical; if (type->type_kind != TYPE_BOOL && cast_to_bool_kind(type) == CAST_ERROR) { @@ -149,8 +169,16 @@ static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *e Expr *right = expr->ternary_expr.else_expr; if (!sema_analyse_expr(context, to, right)) return expr_poison(expr); + expr->failable |= right->failable; Type *left_canonical = left->type->canonical; Type *right_canonical = right->type->canonical; + if (type_is_ct(left_canonical) && type_is_ct(right_canonical)) + { + if (!cast_implicitly_to_runtime(left)) return false; + if (!cast_implicitly_to_runtime(right)) return false; + left_canonical = left->type->canonical; + right_canonical = right->type->canonical; + } if (left_canonical != right_canonical) { Type *max = type_find_max_type(left_canonical, right_canonical); @@ -169,14 +197,22 @@ static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *e static inline Decl *decl_copy_local_from_macro(Context *context, Decl *to_copy) { + if (!to_copy) return NULL; assert(to_copy->decl_kind == DECL_VAR); - Decl *copy = malloc_arena(sizeof(Decl)); - memcpy(copy, to_copy, sizeof(Decl)); + Decl *copy = COPY(to_copy); MACRO_COPY_TYPE(copy->var.type_info); MACRO_COPY_EXPR(copy->var.init_expr); return copy; } +static inline Decl *decl_copy_label_from_macro(Context *context, Decl *to_copy, Ast *ast) +{ + if (!to_copy) return NULL; + to_copy = decl_copy_local_from_macro(context, to_copy); + to_copy->label.parent = ast; + return to_copy; +} + static inline bool sema_expr_analyse_enum_constant(Expr *expr, const char *name, Decl *decl) { VECEACH(decl->enums.values, i) @@ -194,34 +230,15 @@ static inline bool sema_expr_analyse_enum_constant(Expr *expr, const char *name, return false; } -static inline bool sema_expr_analyse_error_constant(Expr *expr, const char *name, Decl *decl) -{ - VECEACH(decl->error.error_constants, i) - { - Decl *error_constant = decl->error.error_constants[i]; - if (error_constant->name == name) - { - assert(error_constant->resolve_status == RESOLVE_DONE); - expr->type = decl->type; - expr->expr_kind = EXPR_CONST; - expr->const_expr.error_constant = error_constant; - expr->const_expr.kind = TYPE_ERROR; - return true; - } - } - return false; -} static inline bool find_possible_inferred_identifier(Type *to, Expr *expr) { - if (to->canonical->type_kind != TYPE_ENUM && to->canonical->type_kind != TYPE_ERROR) return false; + if (to->canonical->type_kind != TYPE_ENUM && to->canonical->type_kind != TYPE_ERRTYPE) return false; Decl *parent_decl = to->canonical->decl; switch (parent_decl->decl_kind) { case DECL_ENUM: return sema_expr_analyse_enum_constant(expr, expr->identifier_expr.identifier, parent_decl); - case DECL_ERROR: - return sema_expr_analyse_error_constant(expr, expr->identifier_expr.identifier, parent_decl); case DECL_UNION: case DECL_STRUCT: return false; @@ -234,7 +251,6 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr { Decl *ambiguous_decl; Decl *decl = sema_resolve_symbol(context, expr->identifier_expr.identifier, expr->identifier_expr.path, &ambiguous_decl); - if (!decl && !expr->identifier_expr.path && to) { if (find_possible_inferred_identifier(to, expr)) return true; @@ -279,9 +295,18 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr { SEMA_ERROR(expr, "Only macro expansions can be prefixed with '@', please try to remove it.", decl->name); } + if (decl->decl_kind == DECL_VAR && decl->var.failable) + { + expr->failable = true; + } if (decl->decl_kind == DECL_VAR && decl->var.constant) { assert(decl->var.init_expr && decl->var.init_expr->resolve_status == RESOLVE_DONE); + if (decl->var.failable) + { + SEMA_ERROR(expr, "Constants may never be 'failable', please remove the '!'."); + return false; + } // Todo, maybe make a copy? expr_replace(expr, decl->var.init_expr); return true; @@ -316,29 +341,7 @@ static inline int find_index_of_named_parameter(Decl** func_params, Expr *expr) static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionSignature *signature, Expr *expr, Decl *decl, Type *to, Expr *struct_var) { Decl **func_params = signature->params; - expr->call_expr.throw_info = CALLOCS(ThrowInfo); - expr->call_expr.throw_info->defer = context->current_scope->defers.start; Expr **args = expr->call_expr.arguments; - unsigned error_params = signature->throw_any || signature->throws; - if (error_params) - { - if (context->try_nesting == 0) - { - SEMA_ERROR(expr, "Function '%s' throws errors, this call must be prefixed 'try'.", decl->name); - return false; - } - // Add errors to current context. - if (signature->throw_any) - { - vec_add(context->error_calls, throw_new_union(expr->span, THROW_TYPE_CALL_ANY, expr->call_expr.throw_info)); - } - else - { - vec_add(context->error_calls, throw_new(expr->span, - vec_size(signature->throws) == 1 ? THROW_TYPE_CALL_THROW_ONE : THROW_TYPE_CALL_THROW_MANY, - expr->call_expr.throw_info, signature->throws)); - } - } unsigned struct_args = struct_var == NULL ? 0 : 1; unsigned func_param_count = vec_size(func_params); unsigned num_args = vec_size(args) + struct_args; @@ -363,8 +366,9 @@ static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionS SEMA_ERROR(arg, "The parameter '%s' was already set once.", func_params[index]->name); return false; } - if (!sema_analyse_expr_of_required_type(context, func_params[index]->type, arg->binary_expr.right)) return false; + if (!sema_analyse_expr_of_required_type(context, func_params[index]->type, arg->binary_expr.right, 0)) return false; actual_args[index] = arg->binary_expr.right; + expr->failable |= arg->binary_expr.right->failable; continue; } @@ -375,8 +379,9 @@ static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionS SEMA_ERROR(expr, "Too many parameters for this function."); return false; } - if (!sema_analyse_expr_of_required_type(context, NULL, arg)) return false; + if (!sema_analyse_expr_of_required_type(context, NULL, arg, true)) return false; actual_args[i] = arg; + expr->failable |= arg->failable; continue; } @@ -385,7 +390,8 @@ static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionS SEMA_ERROR(expr, "A regular parameter cannot follow a named parameter."); return false; } - if (!sema_analyse_expr_of_required_type(context, func_params[i]->type, arg)) return false; + if (!sema_analyse_expr_of_required_type(context, func_params[i]->type, arg, true)) return false; + expr->failable |= arg->failable; actual_args[i] = arg; } for (unsigned i = 0; i < entries_needed; i++) @@ -403,12 +409,14 @@ static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionS } expr->type = signature->rtype->type; expr->call_expr.arguments = actual_args; + expr->failable |= signature->failable; return true; } static inline bool sema_expr_analyse_var_call(Context *context, Type *to, Expr *expr, Decl *var_decl) { Type *func_ptr_type = var_decl->type->canonical; + expr->failable |= var_decl->var.failable; if (func_ptr_type->type_kind != TYPE_POINTER || func_ptr_type->pointer->type_kind != TYPE_FUNC) { SEMA_ERROR(expr, "Only macros, functions and function pointers maybe invoked, this is of type '%s'.", type_to_error_string(var_decl->type)); @@ -422,6 +430,7 @@ static inline bool sema_expr_analyse_var_call(Context *context, Type *to, Expr * to, NULL); } + static inline bool sema_expr_analyse_generic_call(Context *context, Type *to, Expr *expr) { TODO }; @@ -496,6 +505,7 @@ static inline bool sema_expr_analyse_func_call(Context *context, Type *to, Expr static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *call_expr, Decl *decl) { + // TODO failable if (context->macro_nesting >= MAX_MACRO_NESTING) { SEMA_ERROR(call_expr, "Too deep nesting (more than %d levels) when evaluating this macro.", MAX_MACRO_NESTING); @@ -601,6 +611,7 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr { Expr *func_expr = expr->call_expr.function; if (!sema_analyse_expr_may_be_function(context, func_expr)) return false; + expr->failable = func_expr->failable; Decl *decl; Expr *struct_var = NULL; switch (func_expr->expr_kind) @@ -750,6 +761,7 @@ static inline bool sema_expr_analyse_subscript_after_parent_resolution(Context * // Check range if (!expr_check_index_in_range(type, index)) return false; + expr->failable |= index->failable; if (index->expr_kind == EXPR_RANGE) { expr->type = type_get_subarray(inner_type); @@ -762,7 +774,7 @@ static inline bool sema_expr_analyse_subscript_after_parent_resolution(Context * static inline bool sema_expr_analyse_subscript(Context *context, Type *to, Expr *expr) { if (!sema_analyse_expr(context, NULL, expr->subscript_expr.expr)) return false; - + expr->failable = expr->subscript_expr.expr->failable; return sema_expr_analyse_subscript_after_parent_resolution(context, NULL, expr); } @@ -868,22 +880,7 @@ static bool sema_expr_analyse_type_access(Context *context, Type *to, Expr *expr return true; } break; - case DECL_ERROR: - if (expr->type_access.name.type == TOKEN_CONST_IDENT) - { - if (!sema_expr_analyse_error_constant(expr, expr->type_access.name.string, decl)) - { - SEMA_ERROR(expr, "'%s' has no error type '%s'.", decl->name, expr->type_access.name.string); - return false; - } - return true; - } - if (expr->type_access.name.start == kw_sizeof) - { - expr_rewrite_to_int_const(expr, type_usize, type_size(type_error_base->canonical)); - return true; - } - break; + case DECL_ERR: case DECL_UNION: case DECL_STRUCT: if (expr->type_access.name.start == kw_sizeof) @@ -960,17 +957,7 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) return true; } break; - case DECL_ERROR: - if (expr->type_access.name.type == TOKEN_CONST_IDENT) - { - if (!sema_expr_analyse_error_constant(expr, sub_element, decl)) - { - SEMA_ERROR(expr, "'%s' has no error type '%s'.", decl->name, sub_element); - return false; - } - return true; - } - break; + case DECL_ERR: case DECL_UNION: case DECL_STRUCT: break; @@ -1014,6 +1001,8 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) { if (!sema_analyse_expr(context, NULL, expr->access_expr.parent)) return false; + expr->failable = expr->access_expr.parent->failable; + assert(expr->expr_kind == EXPR_ACCESS); assert(expr->access_expr.parent->resolve_status == RESOLVE_DONE); @@ -1038,8 +1027,8 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) switch (decl->decl_kind) { case DECL_ENUM: - case DECL_ERROR: return sema_expr_analyse_method(context, expr, decl, is_pointer); + case DECL_ERR: case DECL_STRUCT: case DECL_UNION: break; @@ -1059,6 +1048,7 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) deref->unary_expr.expr = expr->access_expr.parent; deref->resolve_status = RESOLVE_DONE; deref->type = type; + deref->failable = expr->access_expr.parent->failable; expr->access_expr.parent = deref; } expr->type = member->type; @@ -1222,10 +1212,11 @@ static bool sema_expr_analyse_designated_initializer(Context *context, Type *ass return false; } Expr *value = expr->binary_expr.right; - if (!sema_analyse_expr_of_required_type(context, last_path->type, value)) return false; + if (!sema_analyse_expr_of_required_type(context, last_path->type, value, true)) return false; expr->expr_kind = EXPR_DESIGNATED_INITIALIZER; expr->designated_init_expr.path = path.sub_path; expr->designated_init_expr.value = value; + expr->failable |= value->failable; expr->resolve_status = RESOLVE_DONE; } initializer->expr_initializer.init_type = INITIALIZER_DESIGNATED; @@ -1271,7 +1262,8 @@ static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, return false; } // 5. We know the required type, so resolve the expression. - if (!sema_analyse_expr_of_required_type(context, members[i]->type, elements[i])) return false; + if (!sema_analyse_expr_of_required_type(context, members[i]->type, elements[i], 0)) return false; + initializer->failable = elements[i]->failable; } // 6. There's the case of too few values as well. Mark the last element as wrong. @@ -1319,7 +1311,8 @@ static inline bool sema_expr_analyse_array_plain_initializer(Context *context, T SEMA_ERROR(elements[i], "Too many elements in initializer, expected only %d.", expected_members); return false; } - if (!sema_analyse_expr_of_required_type(context, inner_type, elements[i])) return false; + if (!sema_analyse_expr_of_required_type(context, inner_type, elements[i], true)) return false; + initializer->failable |= elements[i]->failable; } if (expected_members > size) @@ -1373,6 +1366,7 @@ static inline bool sema_expr_analyse_initializer_list(Context *context, Type *to case TYPE_STRUCT: case TYPE_UNION: case TYPE_ARRAY: + case TYPE_ERRTYPE: return sema_expr_analyse_initializer(context, assigned, expr); case TYPE_VARARRAY: TODO @@ -1392,7 +1386,8 @@ static inline bool sema_expr_analyse_expr_list(Context *context, Type *to, Expr size_t last = vec_size(expr->expression_list) - 1; VECEACH(expr->expression_list, i) { - success &= sema_analyse_expr_of_required_type(context, i == last ? to : NULL, expr->expression_list[i]); + success &= sema_analyse_expr_of_required_type(context, i == last ? to : NULL, expr->expression_list[i], 0); + expr->failable |= expr->expression_list[i]->failable; } return success; } @@ -1401,7 +1396,7 @@ static inline bool sema_expr_analyse_cast(Context *context, Type *to, Expr *expr { Expr *inner = expr->cast_expr.expr; if (!sema_resolve_type_info(context, expr->cast_expr.type_info)) return false; - if (!sema_analyse_expr_of_required_type(context, NULL, inner)) return false; + if (!sema_analyse_expr_of_required_type(context, NULL, inner, true)) return false; if (!cast(inner, expr->cast_expr.type_info->type, CAST_TYPE_EXPLICIT)) return false; @@ -1410,10 +1405,61 @@ static inline bool sema_expr_analyse_cast(Context *context, Type *to, Expr *expr SourceRange loc = expr->span; *expr = *inner; expr->span = loc; + expr->failable = expr->failable; + return true; +} + +static inline bool sema_expr_analyse_fail_check(Context *context, Expr *expr) +{ + Expr *inner = expr->fail_check_expr; + switch (inner->expr_kind) + { + case EXPR_IDENTIFIER: + case EXPR_CALL: + case EXPR_CAST: + case EXPR_EXPR_BLOCK: + case EXPR_GROUP: + case EXPR_TYPE_ACCESS: + case EXPR_SUBSCRIPT: + case EXPR_ACCESS: + case EXPR_TYPEID: + case EXPR_TYPEOF: + break; + default: + SEMA_ERROR(expr, "Ambiguous use of unwrapping operator '?', use '()' around the expression to indicate intent."); + return false; + } + if (!sema_analyse_expr(context, NULL, inner)) return false; + if (!inner->failable) + { + SEMA_ERROR(expr, "You can only check a failable type e.g. '%s!' not '%s'.", + type_to_error_string(inner->type), type_to_error_string(inner->type)); + return false; + } + if (inner->expr_kind == EXPR_IDENTIFIER && (context->current_scope->flags & SCOPE_COND)) + { + Decl *var = inner->identifier_expr.decl; + Decl *decl = COPY(var); + decl->var.kind = VARDECL_ALIAS; + decl->var.alias = var; + sema_unwrap_var(context, decl); + } + expr->type = type_bool; + return true; +} + +bool sema_expr_analyse_assign_right_side(Context *context, Expr *expr, Type *left_type, Expr *right, ExprFailableStatus lhs_is_failable) +{ + // 1. Evaluate right side to required type. + if (!sema_analyse_expr_of_required_type(context, left_type, right, lhs_is_failable != FAILABLE_NO)) return false; + + // 2. Set the result to the type on the right side. + if (expr) expr->type = right->type; return true; } + /** * Analyse a = b * @return true if analysis works @@ -1430,12 +1476,15 @@ static bool sema_expr_analyse_assign(Context *context, Expr *expr, Expr *left, E return false; } + ExprFailableStatus failable_status = expr_is_failable(left); + // 3. Evaluate right side to required type. - if (!sema_analyse_expr_of_required_type(context, left->type, right)) return false; - - // 4. Set the result to the type on the right side. - expr->type = right->type; + if (!sema_expr_analyse_assign_right_side(context, expr, left->type, right, failable_status)) return false; + if (failable_status == FAILABLE_UNWRAPPED && right->failable) + { + return sema_rewrap_var(context, left->identifier_expr.decl); + } return true; } @@ -1472,7 +1521,7 @@ static bool sema_expr_analyse_common_assign(Context *context, Expr *expr, Expr * } // 5. Cast the right hand side to the one on the left - if (!sema_analyse_expr_of_required_type(context, left->type->canonical, right)) return false; + if (!sema_analyse_expr_of_required_type(context, left->type->canonical, right, expr_is_failable(left))) return false; // 6. Check for zero in case of div or mod. if (right->expr_kind == EXPR_CONST) @@ -1580,7 +1629,7 @@ static bool sema_expr_analyse_add_sub_assign(Context *context, Expr *expr, Expr } // 5. Convert any compile time values to runtime - cast_to_smallest_runtime(right); + if (!cast_implicitly_to_runtime(right)) return false; // 6. Finally, check that the right side is indeed an integer. if (!type_is_integer(right->type->canonical)) @@ -1666,8 +1715,8 @@ static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr * return true; } - // 5. Cast any compile time int into smallest runtime version if we have a compile time constant. - cast_to_smallest_runtime(right); + // 5. Cast any compile time int into runtime version if we have a compile time constant. + if (!cast_implicitly_to_runtime(right)) return false; // 6. No need for further casts, just it is an integer. if (!type_is_integer(right_type)) @@ -1827,6 +1876,7 @@ static bool sema_expr_analyse_add(Context *context, Type *to, Expr *expr, Expr * */ static bool sema_expr_analyse_mult(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) { + bool is_mod = expr->binary_expr.operator == BINARYOP_MULT_MOD; // 1. Analyse the sub expressions. @@ -2076,9 +2126,6 @@ static bool sema_expr_analyse_shift(Context *context, Type *to, Expr *expr, Expr return false; } - // 2c. Cast the value to the smallest runtime type. - cast_to_smallest_runtime(right); - // 4. Fold constant expressions. if (is_const(left)) { @@ -2108,8 +2155,15 @@ static bool sema_expr_analyse_shift(Context *context, Type *to, Expr *expr, Expr // 5. We might have the case 2 << x. In that case we will to cast the left hand side to the receiving type. if (!cast_implicit(left, to)) return false; - // 6. As a last out, we make sure that a comptime int has a real type. We pick i64 for this. - if (!cast_to_runtime(left)) return false; + // 6. As a last out, we make sure that a comptime int has a real type by casting to the right side (which must be non constant) + if (type_is_ct(left->type)) + { + assert(!type_is_ct(right->type)); + cast(left, right->type, CAST_TYPE_EXPLICIT); + } + + // 7. On LLVM, left and right types must match. + cast(right, left->type, CAST_TYPE_EXPLICIT); expr->type = left->type; return true; @@ -2153,20 +2207,21 @@ static bool sema_expr_analyse_shift_assign(Context *context, Expr *expr, Expr *l SEMA_ERROR(right, "A shift must be a positive number."); return false; } - - // 3c. Cast the rhs to the smallest runtime type. - cast_to_smallest_runtime(right); } // 4. Set the type expr->type = left->type; + + // 5. This is already checked as ok + cast(right, left->type, CAST_TYPE_EXPLICIT); + return true; } static bool sema_expr_analyse_and(Context *context, Expr *expr, Expr *left, Expr *right) { - if (!sema_expr_analyse_binary_sub_expr(context, NULL, left, right)) return false; + if (!sema_analyse_expr(context, type_bool, left) & sema_analyse_expr(context, type_bool, right)) return false; if (!cast_implicit(left, type_bool) || !cast_implicit(right, type_bool)) return false; expr->type = type_bool; @@ -2265,11 +2320,11 @@ static bool sema_expr_analyse_comp(Context *context, Expr *expr, Expr *left, Exp UNREACHABLE case TYPE_BOOL: case TYPE_ENUM: - case TYPE_ERROR: + case TYPE_ERRTYPE: case TYPE_FUNC: case TYPE_STRUCT: case TYPE_UNION: - case TYPE_ERROR_UNION: + case TYPE_ERR_UNION: case TYPE_STRING: case TYPE_ARRAY: case TYPE_VARARRAY: @@ -2474,7 +2529,7 @@ static bool sema_expr_analyse_not(Context *context, Type *to, Expr *expr, Expr * case TYPE_STRING: expr->const_expr.b = !inner->const_expr.string.len; break; - case TYPE_ERROR: + case TYPE_ERRTYPE: case TYPE_ENUM: TODO default: @@ -2491,7 +2546,7 @@ static bool sema_expr_analyse_not(Context *context, Type *to, Expr *expr, Expr * case TYPE_IXX: case TYPE_FXX: case TYPE_TYPEDEF: - case TYPE_ERROR_UNION: + case TYPE_ERR_UNION: UNREACHABLE case TYPE_FUNC: case TYPE_ARRAY: @@ -2515,7 +2570,7 @@ static bool sema_expr_analyse_not(Context *context, Type *to, Expr *expr, Expr * case TYPE_VOID: case TYPE_STRING: case TYPE_ENUM: - case TYPE_ERROR: + case TYPE_ERRTYPE: case TYPE_TYPEID: case TYPE_MEMBER: SEMA_ERROR(expr, "Cannot use 'not' on %s", type_to_error_string(inner->type)); @@ -2547,6 +2602,8 @@ static inline bool sema_expr_analyse_binary(Context *context, Type *to, Expr *ex assert(expr->resolve_status == RESOLVE_RUNNING); Expr *left = expr->binary_expr.left; Expr *right = expr->binary_expr.right; + // Don't push down bool conversions for example. + if (to && !type_is_numeric(to)) to = NULL; switch (expr->binary_expr.operator) { case BINARYOP_ASSIGN: @@ -2649,88 +2706,72 @@ static inline bool sema_expr_analyse_post_unary(Context *context, Type *to, Expr } -static inline bool sema_expr_analyse_try(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_try(Context *context, Expr *expr) { - context->try_nesting++; - // Duplicates code in try for statements.. :( - unsigned prev_throws = vec_size(context->error_calls); - bool success = sema_analyse_expr(context, to, expr->try_expr.expr); - context->try_nesting--; + Expr *inner = expr->trycatch_expr; + bool success = sema_analyse_expr(context, NULL, inner); if (!success) return false; - unsigned new_throws = vec_size(context->error_calls); - if (new_throws == prev_throws) + if (!inner->failable) { - if (expr->try_expr.type == TRY_STMT) - { - SEMA_ERROR(expr->try_expr.stmt, "No error to 'try' in the statement that follows, please remove the 'try'."); - } - else - { - SEMA_ERROR(expr->try_expr.expr, "No error to 'try' in the expression, please remove the 'try'."); - } - return false; - } - expr->type = expr->try_expr.expr->type; - bool found = false; - for (unsigned i = prev_throws; i < new_throws; i++) - { - // At least one uncaught error found! - if (!context->error_calls[i].throw_info->is_completely_handled) - { - found = true; - break; - } - } - if (expr->try_expr.type != TRY_STMT && expr->try_expr.type != TRY_EXPR) - { - CatchInfo info = { .kind = CATCH_TRY_ELSE, .try_else = expr, .defer = context->current_scope->defers.start }; - // Absorb all errors. - for (unsigned i = prev_throws; i < new_throws; i++) - { - Throw *throw = &context->error_calls[i]; - // Skip handled errors - if (throw[i].throw_info->is_completely_handled) continue; - throw->throw_info->is_completely_handled = true; - vec_add(throw->throw_info->catches, info); - } - // Resize to remove the throws from consideration. - vec_resize(context->error_calls, prev_throws); - if (expr->try_expr.type == TRY_EXPR_ELSE_JUMP) - { - if (!sema_analyse_statement(context, expr->try_expr.else_stmt)) return false; - } - else - { - if (!sema_analyse_expr(context, to, expr->try_expr.else_expr)) return false; - } - } - if (!found) - { - if (expr->try_expr.type == TRY_STMT) - { - SEMA_ERROR(expr->try_expr.stmt, "All errors in the following statement was caught, please remove the 'try'."); - } - else - { - SEMA_ERROR(expr->try_expr.expr, "All errors in the expression was caught, please remove the 'try'."); - } + SEMA_ERROR(expr->trycatch_expr, "Expected a failable expression to 'try'."); return false; } + expr->type = type_bool; return true; } +static inline bool sema_expr_analyse_catch(Context *context, Expr *expr) +{ + Expr *inner = expr->trycatch_expr; + bool success = sema_analyse_expr(context, NULL, inner); + if (!success) return false; + if (!inner->failable) + { + SEMA_ERROR(expr->trycatch_expr, "Expected a failable expression to 'catch'."); + return false; + } + expr->type = type_bool; + return true; +} + + +static inline bool sema_expr_analyse_else(Context *context, Type *to, Expr *expr) +{ + Expr *inner = expr->else_expr.expr; + bool success = sema_analyse_expr(context, to, inner); + if (!success) return false; + Type *type = inner->type; + if (!inner->failable) + { + SEMA_ERROR(inner, "No failable to 'else' in the expression, please remove the 'else'."); + return false; + } + if (expr->else_expr.is_jump) + { + if (!sema_analyse_statement(context, expr->else_expr.else_stmt)) return false; + expr->type = type; + return true; + } + + // First we analyse the "else" and try to implictly cast. + if (!sema_analyse_expr(context, type, expr->else_expr.else_expr)) return false; + + // Here we might need to insert casts. + Type *common = type_find_max_type(type, expr->else_expr.else_expr->type); + if (!cast_implicit(expr->else_expr.else_expr, common)) return false; + + expr->type = common; + return cast_implicit(expr->else_expr.expr, common); +} + static Ast *ast_shallow_copy(Ast *source) { - Ast *copy = malloc_arena(sizeof(Ast)); - memcpy(copy, source, sizeof(Ast)); - return copy; + return COPY(source); } static Expr *expr_shallow_copy(Expr *source) { - Expr *copy = malloc_arena(sizeof(Expr)); - memcpy(copy, source, sizeof(Expr)); - return copy; + return COPY(source); } @@ -2787,6 +2828,30 @@ static Expr *expr_copy_from_macro(Context *context, Expr *source_expr) Expr *expr = expr_shallow_copy(source_expr); switch (source_expr->expr_kind) { + case EXPR_CATCH: + case EXPR_TRY: + MACRO_COPY_EXPR(expr->trycatch_expr); + return expr; + case EXPR_DECL_LIST: + MACRO_COPY_AST_LIST(expr->dexpr_list_expr); + return expr; + case EXPR_FAIL_CHECK: + MACRO_COPY_EXPR(expr->fail_check_expr); + return expr; + case EXPR_FAILABLE: + MACRO_COPY_EXPR(expr->failable_expr); + return expr; + case EXPR_ELSE: + MACRO_COPY_EXPR(expr->else_expr.expr); + if (expr->else_expr.is_jump) + { + MACRO_COPY_EXPR(expr->else_expr.else_expr); + } + else + { + MACRO_COPY_AST(expr->else_expr.else_stmt); + } + return expr; case EXPR_MACRO_BLOCK: UNREACHABLE case EXPR_TYPEOF: @@ -2808,9 +2873,8 @@ static Expr *expr_copy_from_macro(Context *context, Expr *source_expr) return expr; case EXPR_POISONED: return source_expr; - case EXPR_TRY: - MACRO_COPY_EXPR(expr->try_expr.expr); - MACRO_COPY_EXPR(expr->try_expr.else_expr); + case EXPR_GUARD: + MACRO_COPY_EXPR(expr->guard_expr); return expr; case EXPR_CONST: return expr; @@ -2901,13 +2965,42 @@ static Ast *ast_copy_from_macro(Context *context, Ast *source) TODO case AST_BREAK_STMT: return ast; + case AST_TRY_STMT: + MACRO_COPY_EXPR(ast->try_stmt.decl_expr); + MACRO_COPY_AST(ast->try_stmt.body); + return ast; + case AST_DEFINE_STMT: + ast->define_stmt = decl_copy_local_from_macro(context, ast->define_stmt); + return ast; case AST_CASE_STMT: MACRO_COPY_AST(ast->case_stmt.body); - MACRO_COPY_EXPR(ast->case_stmt.expr); + if (ast->case_stmt.is_type) + { + MACRO_COPY_TYPE(ast->case_stmt.type_info); + } + else + { + MACRO_COPY_EXPR(ast->case_stmt.expr); + } return ast; break; case AST_CATCH_STMT: - MACRO_COPY_AST(ast->catch_stmt.body); + if (ast->catch_stmt.has_err_var) + { + TODO + } + else + { + MACRO_COPY_EXPR(ast->catch_stmt.catchable); + } + if (ast->catch_stmt.is_switch) + { + MACRO_COPY_AST_LIST(ast->catch_stmt.cases); + } + else + { + MACRO_COPY_AST(ast->catch_stmt.body); + } return ast; case AST_COMPOUND_STMT: MACRO_COPY_AST_LIST(ast->compound_stmt.stmts); @@ -2935,13 +3028,6 @@ static Ast *ast_copy_from_macro(Context *context, Ast *source) MACRO_COPY_EXPR(ast->ct_switch_stmt.cond); MACRO_COPY_AST_LIST(ast->ct_switch_stmt.body); return ast; - case AST_CT_DEFAULT_STMT: - MACRO_COPY_AST(ast->ct_default_stmt); - return ast; - case AST_CT_CASE_STMT: - MACRO_COPY_AST(ast->ct_case_stmt.body); - MACRO_COPY_TYPE_LIST(ast->ct_case_stmt.types); - return ast; case AST_DECLARE_STMT: ast->declare_stmt = decl_copy_local_from_macro(context, ast->declare_stmt); return ast; @@ -2953,6 +3039,7 @@ static Ast *ast_copy_from_macro(Context *context, Ast *source) MACRO_COPY_AST(ast->defer_stmt.body); return ast; case AST_DO_STMT: + ast->do_stmt.label = decl_copy_label_from_macro(context, ast->do_stmt.label, ast); MACRO_COPY_AST(ast->do_stmt.body); MACRO_COPY_EXPR(ast->do_stmt.expr); return ast; @@ -2960,36 +3047,18 @@ static Ast *ast_copy_from_macro(Context *context, Ast *source) MACRO_COPY_EXPR(ast->expr_stmt); return ast; case AST_FOR_STMT: + ast->for_stmt.label = decl_copy_label_from_macro(context, ast->for_stmt.label, ast); MACRO_COPY_EXPR(ast->for_stmt.cond); MACRO_COPY_EXPR(ast->for_stmt.incr); MACRO_COPY_AST(ast->for_stmt.body); - MACRO_COPY_AST(ast->for_stmt.init); - return ast; - case AST_GENERIC_CASE_STMT: - MACRO_COPY_AST(ast->generic_case_stmt.body); - // ast->generic_case_stmt.types = ... - TODO - return ast; - case AST_GENERIC_DEFAULT_STMT: - MACRO_COPY_AST(ast->generic_default_stmt); - return ast; - case AST_GOTO_STMT: - MACRO_COPY_AST(ast->goto_stmt.label); - // TODO fixup name, which needs to be macro local. - TODO + MACRO_COPY_EXPR(ast->for_stmt.init); return ast; case AST_IF_STMT: - MACRO_COPY_AST(ast->if_stmt.cond); - MACRO_COPY_AST(ast->if_stmt.decl); + ast->if_stmt.label = decl_copy_label_from_macro(context, ast->if_stmt.label, ast); + MACRO_COPY_EXPR(ast->if_stmt.cond); MACRO_COPY_AST(ast->if_stmt.else_body); MACRO_COPY_AST(ast->if_stmt.then_body); return ast; - case AST_LABEL: - assert(!ast->label_stmt.defer); - assert(!ast->label_stmt.in_defer); - // TODO fixup name which needs to be macro local. - TODO - return ast; case AST_NOP_STMT: return ast; case AST_RETURN_STMT: @@ -2997,17 +3066,11 @@ static Ast *ast_copy_from_macro(Context *context, Ast *source) // TODO handle conversions? TODO return ast; - case AST_DECL_EXPR_LIST: - MACRO_COPY_AST_LIST(ast->decl_expr_stmt); - return ast; case AST_SWITCH_STMT: - MACRO_COPY_AST(ast->switch_stmt.decl); - MACRO_COPY_AST(ast->switch_stmt.cond); + ast->switch_stmt.label = decl_copy_label_from_macro(context, ast->switch_stmt.label, ast); + MACRO_COPY_EXPR(ast->switch_stmt.cond); MACRO_COPY_AST_LIST(ast->switch_stmt.cases); return ast; - case AST_THROW_STMT: - MACRO_COPY_EXPR(ast->throw_stmt.throw_value); - return ast; case AST_NEXT_STMT: TODO return ast; @@ -3015,8 +3078,8 @@ static Ast *ast_copy_from_macro(Context *context, Ast *source) TODO return ast; case AST_WHILE_STMT: - MACRO_COPY_AST(ast->while_stmt.cond); - MACRO_COPY_AST(ast->while_stmt.decl); + ast->while_stmt.label = decl_copy_label_from_macro(context, ast->for_stmt.label, ast); + MACRO_COPY_EXPR(ast->while_stmt.cond); MACRO_COPY_AST(ast->while_stmt.body); return ast; case AST_SCOPED_STMT: @@ -3082,6 +3145,7 @@ static inline bool sema_expr_analyse_expr_block(Context *context, Type *to, Expr { bool success = true; expr->type = type_void; + bool saved_expr_failable_return = context->expr_failable_return; Type *prev_expected_block_type = context->expected_block_type; Ast **saved_returns = context_push_returns(context); context->expected_block_type = to; @@ -3119,6 +3183,8 @@ static inline bool sema_expr_analyse_expr_block(Context *context, Type *to, Expr EXIT: context_pop_scope(context); context_pop_returns(context, saved_returns); + expr->failable = context->expr_failable_return; + context->expr_failable_return = saved_expr_failable_return; context->expected_block_type = prev_expected_block_type; return success; } @@ -3130,6 +3196,7 @@ static inline bool sema_expr_analyse_compound_literal(Context *context, Type *to Type *type = expr->expr_compound_literal.type_info->type; if (!sema_expr_analyse_initializer_list(context, type, expr->expr_compound_literal.initializer)) return false; expr->type = type; + expr->failable = expr->expr_compound_literal.initializer->failable; return true; } @@ -3143,10 +3210,46 @@ static inline bool sema_expr_analyse_typeof(Context *context, Expr *expr) return true; } +static inline bool sema_expr_analyse_failable(Context *context, Type *to, Expr *expr) +{ + Expr *inner = expr->failable_expr; + if (!sema_analyse_expr(context, NULL, inner)) return false; + if (inner->failable) + { + SEMA_ERROR(inner, "The inner expression is already a failable."); + } + Type *type = inner->type->canonical; + if (inner->expr_kind == EXPR_FAILABLE) + { + SEMA_ERROR(inner, "It looks like you added one too many '!' after the error."); + return false; + } + if (type->type_kind != TYPE_ERRTYPE) + { + SEMA_ERROR(inner, "You cannot use the '!' operator on expressions of type '%s'", type_to_error_string(type)); + return false; + } + if (!to) + { + expr->type = type_void; + return true; + } + expr->failable = true; + expr->type = to; + return true; +} + + static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *expr) { switch (expr->expr_kind) { + case EXPR_DECL_LIST: + UNREACHABLE + case EXPR_FAIL_CHECK: + return sema_expr_analyse_fail_check(context, expr); + case EXPR_FAILABLE: + return sema_expr_analyse_failable(context, to, expr); case EXPR_POISONED: return false; case EXPR_DESIGNATED_INITIALIZER: @@ -3155,27 +3258,42 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * case EXPR_MACRO_BLOCK: case EXPR_SCOPED_EXPR: UNREACHABLE + case EXPR_CATCH: + return sema_expr_analyse_catch(context, expr); + case EXPR_TRY: + return sema_expr_analyse_try(context, expr); case EXPR_TYPEOF: return sema_expr_analyse_typeof(context, expr); + case EXPR_ELSE: + return sema_expr_analyse_else(context, to, expr); case EXPR_COMPOUND_LITERAL: return sema_expr_analyse_compound_literal(context, to, expr); case EXPR_EXPR_BLOCK: return sema_expr_analyse_expr_block(context, to, expr); - case EXPR_TRY: - return sema_expr_analyse_try(context, to, expr); + case EXPR_GUARD: + return sema_expr_analyse_try(context, expr); case EXPR_RANGE: SEMA_ERROR(expr, "Range expression was not expected here."); return false; case EXPR_CONST: return true; case EXPR_BINARY: - return sema_expr_analyse_binary(context, to, expr); + if (!sema_expr_analyse_binary(context, to, expr)) return false; + if (expr->expr_kind == EXPR_BINARY) + { + expr->failable = expr->binary_expr.left->failable | expr->binary_expr.right->failable; + } + return true; case EXPR_TERNARY: return sema_expr_analyse_ternary(context, to, expr); case EXPR_UNARY: - return sema_expr_analyse_unary(context, to, expr); + if (!sema_expr_analyse_unary(context, to, expr)) return false; + if (expr->expr_kind == EXPR_UNARY) expr->failable = expr->unary_expr.expr->failable; + return true; case EXPR_POST_UNARY: - return sema_expr_analyse_post_unary(context, to, expr); + if (!sema_expr_analyse_post_unary(context, to, expr)) return false; + if (expr->expr_kind == EXPR_UNARY) expr->failable = expr->unary_expr.expr->failable; + return true; case EXPR_TYPEID: return sema_expr_analyse_type(context, to, expr); case EXPR_IDENTIFIER: @@ -3201,9 +3319,15 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * } -bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr) +bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr, bool may_be_failable) { if (!sema_analyse_expr(context, to, expr)) return false; + if (expr->failable && !may_be_failable) + { + if (!to) to = expr->type; + SEMA_ERROR(expr, "'%s!' cannot be implicitly cast to '%s'.", type_to_error_string(expr->type), type_to_error_string(to)); + return false; + } return cast_implicit(expr, to); } diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 5fc29737e..7cbe18b2d 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -11,3 +11,20 @@ int sema_check_comp_time_bool(Context *context, Expr *expr); bool sema_analyse_function_body(Context *context, Decl *func); void context_pop_scope(Context *context); void context_push_scope_with_flags(Context *context, ScopeFlags flags); +#define PUSH_X(ast, X) Ast *_old_##X##_defer = context->X##_defer; Ast *_old_##X = context->X##_target; context->X##_target = ast; context->X##_defer = context->current_scope->defers.start +#define POP_X(X) context->X##_target = _old_##X; context->X##_defer = _old_##X##_defer +#define PUSH_CONTINUE(ast) PUSH_X(ast, continue) +#define POP_CONTINUE() POP_X(continue) +#define PUSH_BREAK(ast) PUSH_X(ast, break) +#define POP_BREAK() POP_X(break) +#define PUSH_NEXT(ast, sast) PUSH_X(ast, next); Ast *_old_next_switch = context->next_switch; context->next_switch = sast +#define POP_NEXT() POP_X(next); context->next_switch = _old_next_switch +#define PUSH_BREAKCONT(ast) PUSH_CONTINUE(ast); PUSH_BREAK(ast) +#define POP_BREAKCONT() POP_CONTINUE(); POP_BREAK() + +#define PUSH_FAILABLES() \ + unsigned _prev_failables = context->failables_found; \ + context->failables_found = 0; + +#define POP_FAILABLES() \ + ({ unsigned _failables = context->failables_found; context->failables_found = _prev_failables; _failables; }) diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index f9871ce1f..f6a86842f 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -117,6 +117,21 @@ Decl *sema_resolve_symbol(Context *context, const char *symbol, Path *path, Decl return decl; } +static inline bool sema_append_local(Context *context, Decl *decl) +{ + Decl *** vars = &context->active_function_for_analysis->func.annotations->vars; + unsigned num_vars = vec_size(*vars); + if (num_vars == MAX_LOCALS - 1 || context->last_local == &context->locals[MAX_LOCALS - 1]) + { + SEMA_ERROR(decl, "Reached the maximum number of locals."); + return false; + } + *vars = VECADD(*vars, decl); + context->last_local[0] = decl; + context->last_local++; + return true; +} + bool sema_add_local(Context *context, Decl *decl) { Decl *dummy; @@ -128,29 +143,26 @@ bool sema_add_local(Context *context, Decl *decl) decl_poison(other); return false; } - Decl *** vars = &context->active_function_for_analysis->func.annotations->vars; - unsigned num_vars = vec_size(*vars); - if (num_vars == MAX_LOCALS - 1 || context->last_local == &context->locals[MAX_LOCALS - 1]) - { - SEMA_ERROR(decl, "Reached the maximum number of locals."); - return false; - } - decl->var.id = num_vars; - *vars = VECADD(*vars, decl); - context->last_local[0] = decl; - context->last_local++; - return true; + return sema_append_local(context, decl); +} + +bool sema_unwrap_var(Context *context, Decl *decl) +{ + Decl *alias = COPY(decl); + alias->var.kind = VARDECL_ALIAS; + alias->var.alias = decl; + alias->var.failable = false; + return sema_append_local(context, decl); +} + +bool sema_rewrap_var(Context *context, Decl *decl) +{ + assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_ALIAS && decl->var.alias->var.failable); + return sema_append_local(context, decl->var.alias); } bool sema_add_macro_local(Context *context, Decl *decl) { - if (context->last_local == &context->locals[MAX_LOCALS - 1]) - { - SEMA_ERROR(decl, "Reached the maximum number of locals."); - return false; - } - context->last_local[0] = decl; - context->last_local++; - return true; + return sema_add_local(context, decl); } diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index 0568e4c6f..b8ebbbc16 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -110,10 +110,6 @@ void sema_analysis_pass_decls(Context *context) { sema_analyse_decl(context, context->types[i]); } - VECEACH(context->error_types, i) - { - sema_analyse_decl(context, context->error_types[i]); - } VECEACH(context->methods, i) { sema_analyse_decl(context, context->methods[i]); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 62c27e073..3a848739d 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -14,13 +14,21 @@ void context_push_scope_with_flags(Context *context, ScopeFlags flags) FATAL_ERROR("Too deeply nested scopes."); } ScopeFlags previous_flags = context->current_scope->flags; + Ast *previous_defer = context->current_scope->current_defer; Ast *parent_defer = context->current_scope->defers.start; context->current_scope++; + context->current_scope->scope_id = ++context->scope_id; + if (context->scope_id == 0) + { + FATAL_ERROR("Too many scopes."); + } context->current_scope->exit = EXIT_NONE; context->current_scope->local_decl_start = context->last_local; + context->current_scope->current_defer = previous_defer; context->current_scope->defers.start = parent_defer; context->current_scope->defers.end = parent_defer; - if (flags & (SCOPE_DEFER | SCOPE_EXPR_BLOCK | SCOPE_NEXT)) + + if (flags & (SCOPE_DEFER | SCOPE_EXPR_BLOCK)) { context->current_scope->flags = flags; } @@ -35,6 +43,17 @@ void context_push_scope_with_flags(Context *context, ScopeFlags flags) context->current_scope->flags_created = flags; } +void context_push_scope_with_label(Context *context, Decl *label) +{ + context_push_scope_with_flags(context, SCOPE_NONE); + if (label) + { + label->label.defer = context->current_scope->defers.end; + sema_add_local(context, label); + label->label.scope_id = context->current_scope->scope_id; + } +} + static inline void context_push_scope(Context *context) { context_push_scope_with_flags(context, SCOPE_NONE); @@ -56,6 +75,7 @@ static inline void context_add_exit(Context *context, ExitType exit) if (!context->current_scope->exit) context->current_scope->exit = exit; } + void context_pop_scope(Context *context) { assert(context->current_scope != &context->scopes[0]); @@ -94,8 +114,7 @@ static void context_pop_defers_and_replace_ast(Context *context, Ast *ast) return; } assert(ast->ast_kind != AST_COMPOUND_STMT); - Ast *replacement = malloc_arena(sizeof(Ast)); - *replacement = *ast; + Ast *replacement = COPY(ast); ast->ast_kind = AST_SCOPED_STMT; ast->scoped_stmt.stmt = replacement; ast->scoped_stmt.defers = defers; @@ -106,6 +125,7 @@ static void context_pop_defers_and_replace_ast(Context *context, Ast *ast) #define UPDATE_EXIT(exit_type) \ do { if (!context->current_scope->exit) context->current_scope->exit = exit_type; } while(0) + #pragma mark --- Sema analyse stmts @@ -113,12 +133,12 @@ static inline bool sema_analyse_block_return_stmt(Context *context, Ast *stateme { assert(context->current_scope->flags & SCOPE_EXPR_BLOCK); UPDATE_EXIT(EXIT_RETURN); - if (statement->return_stmt.expr - && !sema_analyse_expr_of_required_type(context, - context->expected_block_type, - statement->return_stmt.expr)) + if (statement->return_stmt.expr) { - return false; + if (!sema_analyse_expr_of_required_type(context, + context->expected_block_type, + statement->return_stmt.expr, true)) return false; + context->expr_failable_return |= statement->return_stmt.expr->failable; } vec_add(context->returns, statement); return true; @@ -157,7 +177,7 @@ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) SEMA_ERROR(statement, "You can't return a value from a void function, you need to add a return type."); return false; } - if (!sema_analyse_expr_of_required_type(context, expected_rtype, return_expr)) return false; + if (!sema_analyse_expr_of_required_type(context, expected_rtype, return_expr, context->failable_return)) return false; if (!expected_rtype) { assert(context->evaluating_macro); @@ -169,50 +189,63 @@ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) return true; } - -static inline bool sema_analyse_var_decl(Context *context, Decl *decl) +static inline bool sema_analyse_decl_expr_list(Context *context, Expr *expr) { - assert(decl->decl_kind == DECL_VAR); - if (!sema_resolve_type_info(context, decl->var.type_info)) return decl_poison(decl); - decl->type = decl->var.type_info->type; - if (decl->var.init_expr) + assert(expr->expr_kind == EXPR_DECL_LIST); + + Ast **dexprs = expr->dexpr_list_expr; + unsigned entries = vec_size(dexprs); + for (unsigned i = 0; i < entries; i++) { - if (!sema_analyse_expr_of_required_type(context, decl->type, decl->var.init_expr)) return decl_poison(decl); + Ast *ast = dexprs[i]; + if (!sema_analyse_statement(context, ast)) + { + return false; + } + } + if (entries == 0) + { + expr->type = type_void; + return true; + } + Ast *last = dexprs[entries - 1]; + switch (last->ast_kind) + { + case AST_DECLARE_STMT: + expr->type = last->declare_stmt->type; + break; + case AST_EXPR_STMT: + expr->type = last->expr_stmt->type; + break; + default: + UNREACHABLE } - if (!sema_add_local(context, decl)) return decl_poison(decl); return true; } -static inline bool sema_analyse_decl_expr_list(Context *context, Ast *stmt) +static inline bool sema_analyse_cond(Context *context, Expr *expr, bool cast_to_bool) { - assert(stmt->ast_kind == AST_DECL_EXPR_LIST); - - VECEACH(stmt->decl_expr_stmt, i) - { - if (!sema_analyse_statement(context, stmt->decl_expr_stmt[i])) return false; - } - - return true; -} - -static inline bool sema_analyse_cond(Context *context, Ast *stmt, bool cast_to_bool) -{ - assert(stmt->ast_kind == AST_DECL_EXPR_LIST); - - size_t size = vec_size(stmt->decl_expr_stmt); + assert(expr->expr_kind == EXPR_DECL_LIST); + size_t size = vec_size(expr->dexpr_list_expr); if (!size) { - SEMA_ERROR(stmt, "Expected a boolean expression"); + SEMA_ERROR(expr, "Expected a boolean expression"); return false; } - if (!sema_analyse_decl_expr_list(context, stmt)) return false; + if (!sema_analyse_decl_expr_list(context, expr)) return false; - Ast *last = stmt->decl_expr_stmt[size - 1]; + Ast *last = expr->dexpr_list_expr[size - 1]; switch (last->ast_kind) { case AST_EXPR_STMT: + if (last->expr_stmt->failable) + { + SEMA_ERROR(last, "'%s!' cannot be converted into '%s'.", + type_to_error_string(last->expr_stmt->type), + cast_to_bool ? "bool" : type_to_error_string(last->expr_stmt->type)); + } if (cast_to_bool) { if (!cast_implicit(last->expr_stmt, type_bool)) return false; @@ -220,14 +253,21 @@ static inline bool sema_analyse_cond(Context *context, Ast *stmt, bool cast_to_b return true; case AST_DECLARE_STMT: { - Expr *init = last->declare_stmt->var.init_expr; + Decl *decl = last->declare_stmt; + Expr *init = decl->var.init_expr; if (!init) { SEMA_ERROR(last, "Expected a declaration with initializer."); return false; } - if (cast_to_bool && init->type->type_kind != TYPE_BOOL && - cast_to_bool_kind(last->declare_stmt->var.type_info->type) == CAST_ERROR) + if (init->failable && !decl->var.unwrap) + { + SEMA_ERROR(last, "'%s!' cannot be converted into '%s'.", + type_to_error_string(last->expr_stmt->type), + cast_to_bool ? "bool" : type_to_error_string(init->type)); + } + if (!decl->var.unwrap && cast_to_bool && init->type->type_kind != TYPE_BOOL && + cast_to_bool_kind(decl->var.type_info->type) == CAST_ERROR) { SEMA_ERROR(last->declare_stmt->var.init_expr, "The expression needs to be convertible to a boolean."); return false; @@ -241,19 +281,17 @@ static inline bool sema_analyse_cond(Context *context, Ast *stmt, bool cast_to_b static inline bool sema_analyse_while_stmt(Context *context, Ast *statement) { - Ast *decl = statement->while_stmt.decl; - Ast *cond = statement->while_stmt.cond; + Expr *cond = statement->while_stmt.cond; Ast *body = statement->while_stmt.body; context_push_scope(context); - bool success = !decl || sema_analyse_statement(context, decl); - context_push_scope(context); + bool success = sema_analyse_cond(context, cond, true); + context_push_scope_with_label(context, statement->while_stmt.label); - success = success && sema_analyse_cond(context, cond, true); - context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) + PUSH_BREAKCONT(statement); success = success && sema_analyse_statement(context, body); context_pop_defers_and_replace_ast(context, body); - context_pop_scope(context); - context_pop_defers_and_replace_ast(context, cond); + POP_BREAKCONT(); + context_pop_scope(context); context_pop_defers_and_replace_ast(context, statement); context_pop_scope(context); @@ -266,13 +304,16 @@ static inline bool sema_analyse_do_stmt(Context *context, Ast *statement) Expr *expr = statement->do_stmt.expr; Ast *body = statement->do_stmt.body; bool success; - context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) + context_push_scope_with_label(context, statement->do_stmt.label); + PUSH_BREAKCONT(statement); success = sema_analyse_statement(context, body); context_pop_defers_and_replace_ast(context, body); + POP_BREAKCONT(); context_pop_scope(context); if (!success) return false; + if (!statement->do_stmt.expr) return success; context_push_scope(context); - success = sema_analyse_expr_of_required_type(context, type_bool, expr); + success = sema_analyse_expr_of_required_type(context, type_bool, expr, false); statement->do_stmt.expr = context_pop_defers_and_wrap_expr(context, expr); context_pop_scope(context); return success; @@ -282,7 +323,37 @@ static inline bool sema_analyse_do_stmt(Context *context, Ast *statement) static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement) { Decl *decl = statement->declare_stmt; - return sema_analyse_var_decl(context, decl); + assert(decl->decl_kind == DECL_VAR); + if (!sema_resolve_type_info(context, decl->var.type_info)) return decl_poison(decl); + decl->type = decl->var.type_info->type; + if (decl->var.init_expr) + { + if (!sema_expr_analyse_assign_right_side(context, NULL, decl->type, decl->var.init_expr, decl->var.failable || decl->var.unwrap ? FAILABLE_YES : FAILABLE_NO)) return decl_poison(decl); + if (decl->var.unwrap && !decl->var.init_expr->failable) + { + SEMA_ERROR(decl->var.init_expr, "A failable expression was expected here."); + return false; + } + } + if (!sema_add_local(context, decl)) return decl_poison(decl); + return true; +} + +static inline bool sema_analyse_define_stmt(Context *context, Ast *statement) +{ + Decl *decl = statement->declare_stmt; + assert(decl->decl_kind == DECL_VAR); + switch (decl->var.kind) + { + case VARDECL_LOCAL_CT_TYPE: + if (decl->var.type_info && !sema_resolve_type_info(context, decl->var.type_info)) return false; + break; + case VARDECL_LOCAL: + if (decl->var.init_expr) TODO + default: + UNREACHABLE + } + TODO; } static inline bool sema_analyse_expr_stmt(Context *context, Ast *statement) @@ -294,12 +365,23 @@ static inline bool sema_analyse_expr_stmt(Context *context, Ast *statement) static inline bool sema_analyse_defer_stmt(Context *context, Ast *statement) { // TODO special parsing of "catch" - context_push_scope_with_flags(context, SCOPE_DEFER | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) + context_push_scope_with_flags(context, SCOPE_DEFER); // NOLINT(hicpp-signed-bitwise) + context->current_scope->defers.start = NULL; + context->current_scope->defers.end = NULL; + context->current_scope->current_defer = statement; + + PUSH_CONTINUE(statement); + PUSH_BREAK(NULL); + PUSH_NEXT(NULL, NULL); + // Only ones allowed. - context->current_scope->flags &= SCOPE_DEFER | SCOPE_CONTINUE; // NOLINT(hicpp-signed-bitwise) + context->current_scope->flags &= SCOPE_DEFER; bool success = sema_analyse_statement(context, statement->defer_stmt.body); + POP_BREAKCONT(); + POP_NEXT(); + context_pop_scope(context); if (!success) return false; @@ -318,14 +400,14 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) context_push_scope(context); if (statement->for_stmt.init) { - success = sema_analyse_statement(context, statement->for_stmt.init); + success = sema_analyse_decl_expr_list(context, statement->for_stmt.init); } if (success && statement->for_stmt.cond) { // Conditional scope start context_push_scope(context); Expr *cond = statement->for_stmt.cond; - success = sema_analyse_expr_of_required_type(context, type_bool, cond); + success = sema_analyse_expr_of_required_type(context, type_bool, cond, false); statement->for_stmt.cond = context_pop_defers_and_wrap_expr(context, cond); // Conditional scope end context_pop_scope(context); @@ -340,11 +422,17 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) // Incr scope end context_pop_scope(context); } - if (!success) return false; + if (!success) + { + context_pop_scope(context); + return false; + } // Create the for body scope. - context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) + context_push_scope_with_label(context, statement->for_stmt.label); + PUSH_BREAKCONT(statement); success = sema_analyse_statement(context, statement->for_stmt.body); + POP_BREAKCONT(); // End for body scope context_pop_defers_and_replace_ast(context, statement->for_stmt.body); context_pop_scope(context); @@ -354,23 +442,8 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) return success; } -static inline bool sema_analyse_goto_stmt(Context *context, Ast *statement) -{ - statement->goto_stmt.defer = context->current_scope->defers; - VECEACH(context->labels, i) - { - Ast *label = context->labels[i]; - if (statement->goto_stmt.label_name == label->label_stmt.name) - { - label->label_stmt.is_used = true; - statement->goto_stmt.label = label; - } - } - vec_add(context->gotos, statement); - context_add_exit(context, EXIT_GOTO); - return true; -} + static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) { @@ -379,7 +452,7 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) // if (!x) A(); else B(); // into // if (x) B(); else A(); - Ast *cond = statement->if_stmt.cond; + Expr *cond = statement->if_stmt.cond; context_push_scope(context); bool success = sema_analyse_cond(context, cond, true); if (statement->if_stmt.else_body) @@ -398,14 +471,18 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) } } ExitType prev_exit = context->current_scope->exit; + context_push_scope_with_label(context, statement->if_stmt.label); success = success && sema_analyse_statement(context, statement->if_stmt.then_body); + context_pop_scope(context); ExitType if_exit = context->current_scope->exit; ExitType else_exit = prev_exit; if (statement->if_stmt.else_body) { + context_push_scope_with_label(context, statement->if_stmt.label); context->current_scope->exit = prev_exit; success = success && sema_analyse_statement(context, statement->if_stmt.else_body); else_exit = context->current_scope->exit; + context_pop_scope(context); } context->current_scope->exit = else_exit < if_exit ? else_exit : if_exit; context_pop_defers_and_replace_ast(context, statement); @@ -413,210 +490,278 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) return success; } -static inline bool sema_analyse_label(Context *context, Ast *statement) -{ - statement->label_stmt.defer = context->current_scope->defers.start; - VECEACH(context->labels, i) - { - Ast *label = context->labels[i]; - if (label->label_stmt.name == statement->label_stmt.name) - { - SEMA_ERROR(statement, "This duplicate label '%s'.", statement->label_stmt.name); - sema_prev_at_range(label->span, "The previous declaration was here."); - ast_poison(label); - ast_poison(statement); - return false; - } - } - context->current_scope->exit = EXIT_NONE; - vec_add(context->labels, statement); - VECEACH(context->gotos, i) - { - Ast *the_goto = context->gotos[i]; - if (the_goto->goto_stmt.label_name == statement->label_stmt.name) - { - the_goto->goto_stmt.label = statement; - statement->label_stmt.is_used = true; - break; - } - } - return true; -} -static inline bool throw_completely_handled_call_throw_many(Throw *throw) -{ - assert(throw->kind == THROW_TYPE_CALL_THROW_MANY && "Only for throw many"); - assert(!throw->throw_info->is_completely_handled && "Expected unhandled"); - TypeInfo **throws = throw->throws; - CatchInfo *catched = throw->throw_info->catches; - unsigned catches = 0; - unsigned throw_count = vec_size(throws); - for (unsigned i = 0; i < throw_count; i++) - { - TypeInfo *throw_decl = throws[i]; - if (throw_completely_caught(throw_decl, catched)) - { - catches++; - } - } - return catches == throw_count; -} -/** - * Handle the catch statement - * @return true if error checking succeeds. - */ -static bool sema_analyse_catch_stmt(Context *context, Ast *statement) -{ - unsigned throws = vec_size(context->error_calls); - - // 1. If no errors are found we don't have a try to match. - if (throws <= context->current_scope->throws) - { - SEMA_ERROR(statement, "Unexpected 'catch' without a matching 'try'."); - return false; - } - - // 2. Let's check that we haven't caught all errors. - bool found = false; - for (unsigned i = context->current_scope->throws; i < throws; i++) - { - if (!context->error_calls[i].throw_info->is_completely_handled) - { - found = true; - break;; - } - } - // IMPROVE: Suppress for macro? - if (!found) - { - SEMA_ERROR(statement, "All errors are already caught, so this catch will not handle any errors."); - return false; - } - - // 3. Resolve variable - Decl *variable = statement->catch_stmt.error_param; - assert(variable->var.kind == VARDECL_LOCAL); - if (!sema_resolve_type_info(context, variable->var.type_info)) return false; - variable->type = variable->var.type_info->type; - Type *error_type = variable->type->canonical; - - CatchInfo catch = { .kind = CATCH_REGULAR, .catch = statement, .defer = context->current_scope->defers.start }; - // 4. Absorb all errors in case of a type error union. - if (error_type == type_error_union) - { - for (unsigned i = context->current_scope->throws; i < throws; i++) - { - Throw *throw = &context->error_calls[i]; - // Skip handled errors - if (throw->throw_info->is_completely_handled) continue; - vec_add(throw->throw_info->catches, catch); - throw->throw_info->is_completely_handled = true; - } - // Resize to remove the throws from consideration. - vec_resize(context->error_calls, context->current_scope->throws); - } - else - { - // 5. Otherwise, go through the list of errors and null the errors matching the current type. - for (unsigned i = context->current_scope->throws; i < throws; i++) - { - Throw *throw = &context->error_calls[i]; - // Skip handled errors - if (throw->throw_info->is_completely_handled) continue; - - switch (throw->kind) - { - case THROW_TYPE_CALL_ANY: - vec_add(throw->throw_info->catches, catch); - // An error union can never be completely handled. - break; - case THROW_TYPE_CALL_THROW_ONE: - // If there is no match, ignore. - if (throw->throws[0]->type != error_type) continue; - // Otherwise add and set to completely handled. - vec_add(throw->throw_info->catches, catch); - throw->throw_info->is_completely_handled = true; - break; - case THROW_TYPE_CALL_THROW_MANY: - // The most complex situation, add and handle below. - vec_add(throw->throw_info->catches, catch); - throw->throw_info->is_completely_handled = throw_completely_handled_call_throw_many(throw); - break; - } - } - } - context_push_scope(context); - // Push the error variable - if (!sema_add_local(context, variable)) goto ERR_END_SCOPE; - if (!sema_analyse_statement(context, statement->catch_stmt.body)) goto ERR_END_SCOPE; - context_pop_scope(context); - return true; - - ERR_END_SCOPE: - context_pop_scope(context); - return false; -} static bool sema_analyse_asm_stmt(Context *context __unused, Ast *statement __unused) { TODO } +static DynamicScope *context_find_scope_by_id(Context *context, unsigned scope_id) +{ + DynamicScope *scope = context->current_scope; + while (1) + { + if (scope->scope_id == scope_id) return scope; + assert(scope != &context->scopes[0]); + scope--; + } + UNREACHABLE +} + +static inline Decl *sema_analyse_label(Context *context, Ast *stmt) +{ + Decl *ambiguous; + Decl *target = sema_resolve_symbol(context, stmt->contbreak_stmt.label.name, NULL, &ambiguous); + if (!target) + { + SEMA_ERROR(stmt, "Cannot find a labelled statement with the name '%s'.", stmt->contbreak_stmt.label.name); + return poisoned_decl; + } + if (target->decl_kind != DECL_LABEL) + { + sema_error_range(stmt->contbreak_stmt.label.span, "Expected the name to match a label, not a constant."); + return poisoned_decl; + } + if (context->current_scope->current_defer) + { + DynamicScope *scope = context_find_scope_by_id(context, target->label.scope_id); + if (scope->current_defer != context->current_scope->current_defer) + { + SEMA_ERROR(stmt, stmt->ast_kind == AST_BREAK_STMT ? "You cannot break out of a defer." : "You cannot use continue out of a defer."); + return false; + } + } + return target; +} + +static bool context_labels_exist_in_scope(Context *context) +{ + for (Decl **from = &context->locals[0]; from < context->last_local; from++) + { + if ((*from)->decl_kind == DECL_LABEL) return true; + } + return false; +} static bool sema_analyse_break_stmt(Context *context, Ast *statement) { - if (!(context->current_scope->flags & SCOPE_BREAK)) // NOLINT(hicpp-signed-bitwise) + if (!context->break_target && !statement->contbreak_stmt.label.name) { - SEMA_ERROR(statement, "'break' is not allowed here."); + if (context_labels_exist_in_scope(context)) + { + SEMA_ERROR(statement, "Unlabelled 'break' is not allowed here."); + } + else + { + SEMA_ERROR(statement, "'break' is not allowed here."); + } return false; } + UPDATE_EXIT(EXIT_BREAK); - DynamicScope *scope = context->current_scope; - statement->break_stmt.defers.start = scope->defers.start; - while (!(scope->flags_created & SCOPE_BREAK)) // NOLINT(hicpp-signed-bitwise) + + statement->contbreak_stmt.defers.start = context->current_scope->defers.start; + + if (statement->contbreak_stmt.label.name) { - scope--; + Decl *target = sema_analyse_label(context, statement); + if (!decl_ok(target)) return false; + + statement->contbreak_stmt.ast = target->label.parent; + statement->contbreak_stmt.defers.end = target->label.defer; + return true; } - statement->break_stmt.defers.end = scope->defers.end; + statement->contbreak_stmt.defers.end = context->break_defer; + statement->contbreak_stmt.ast = context->break_target; return true; } static bool sema_analyse_next_stmt(Context *context, Ast *statement) { - if (!(context->current_scope->flags & SCOPE_NEXT)) // NOLINT(hicpp-signed-bitwise) + if (!context->next_target && !statement->next_stmt.label.name) { SEMA_ERROR(statement, "'next' is not allowed here."); return false; } UPDATE_EXIT(EXIT_NEXT); - DynamicScope *scope = context->current_scope; - statement->next_stmt.defers.start = scope->defers.start; - while (!(scope->flags_created & SCOPE_NEXT)) // NOLINT(hicpp-signed-bitwise) + + Ast *parent = context->next_switch; + + if (statement->next_stmt.label.name) { - scope--; + Decl *ambiguous; + Decl *target = sema_resolve_symbol(context, statement->next_stmt.label.name, NULL, &ambiguous); + if (!target) + { + SEMA_ERROR(statement, "Cannot find a switch statement with the name '%s'.", statement->next_stmt.label.name); + return false; + } + if (target->decl_kind != DECL_LABEL) + { + sema_error_range(statement->next_stmt.label.span, "Expected the name to match a label, not a constant."); + return false; + } + if (target->label.parent->ast_kind != AST_SWITCH_STMT && target->label.parent->ast_kind != AST_CATCH_STMT) + { + sema_error_range(statement->next_stmt.label.span, "Expected the label to match a 'switch' or 'catch' statement."); + return false; + } + parent = target->label.parent; + bool defer_mismatch = false; + if (parent->ast_kind == AST_SWITCH_STMT) + { + defer_mismatch = context->current_scope->current_defer != parent->switch_stmt.defer; + } + else + { + defer_mismatch = context->current_scope->current_defer != parent->catch_stmt.defer; + } + if (defer_mismatch) + { + SEMA_ERROR(statement, "This 'next' would jump out of a defer which isn't possible."); + return false; + } + assert(statement->next_stmt.target); } - statement->next_stmt.defers.end = scope->defers.end; + + statement->next_stmt.defers.start = context->current_scope->defers.start; + statement->next_stmt.defers.end = parent->switch_stmt.defer; + + if (!statement->next_stmt.target) + { + if (!context->next_target) + { + SEMA_ERROR(statement, "Unexpected 'next' statement outside of a switch."); + return false; + } + statement->next_stmt.case_switch_stmt = context->next_target; + return true; + } + + if (statement->next_stmt.is_type) + { + if (!sema_resolve_type_info(context, statement->next_stmt.type_info)) return false; + Ast **cases; + if (parent->ast_kind == AST_SWITCH_STMT) + { + statement->next_stmt.defers.end = parent->switch_stmt.defer; + if (parent->switch_stmt.cond->type->canonical != type_typeid) + { + SEMA_ERROR(statement, "Unexpected 'type' in as an 'next' destination."); + SEMA_PREV(statement, "The 'switch' here uses expected a type '%s'.", type_to_error_string(parent->switch_stmt.cond->type)); + return false; + } + cases = parent->switch_stmt.cases; + } + else + { + statement->next_stmt.defers.end = parent->catch_stmt.defer; + cases = parent->catch_stmt.cases; + } + + Ast *default_stmt = NULL; + VECEACH(cases, i) + { + Ast *case_stmt = cases[i]; + if (case_stmt->ast_kind == AST_DEFAULT_STMT) + { + default_stmt = case_stmt; + break; + } + if (case_stmt->case_stmt.type_info->type->canonical == statement->next_stmt.type_info->type->canonical) + { + statement->next_stmt.case_switch_stmt = case_stmt; + return true; + } + } + if (default_stmt) + { + statement->next_stmt.case_switch_stmt = default_stmt; + return true; + } + SEMA_ERROR(statement->next_stmt.type_info, "There is no case for type '%s'.", type_to_error_string(statement->next_stmt.type_info->type)); + return false; + } + + if (parent->ast_kind != AST_SWITCH_STMT) + { + SEMA_ERROR(statement, "The 'next' expected a type."); + } + Expr *target = statement->next_stmt.target; + + if (!sema_analyse_expr(context, parent->switch_stmt.cond->type, target)) return false; + + if (!cast_implicit(target, parent->switch_stmt.cond->type)) return false; + + if (target->expr_kind == EXPR_CONST) + { + Ast *default_stmt = NULL; + statement->next_stmt.defers.end = parent->switch_stmt.defer; + VECEACH(parent->switch_stmt.cases, i) + { + Ast *case_stmt = parent->switch_stmt.cases[i]; + if (case_stmt->ast_kind == AST_DEFAULT_STMT) + { + default_stmt = case_stmt; + break; + } + if (expr_const_compare(&target->const_expr, &case_stmt->case_stmt.expr->const_expr, BINARYOP_EQ)) + { + statement->next_stmt.case_switch_stmt = case_stmt; + return true; + } + } + if (default_stmt) + { + statement->next_stmt.case_switch_stmt = default_stmt; + return true; + } + SEMA_ERROR(statement, "The 'next' needs to jump to an exact case statement."); + return false; + } + + statement->next_stmt.case_switch_stmt = parent; + statement->next_stmt.switch_expr = target; return true; } - static bool sema_analyse_continue_stmt(Context *context, Ast *statement) { - if (!(context->current_scope->flags & SCOPE_CONTINUE)) // NOLINT(hicpp-signed-bitwise) + + if (!context->break_target && !statement->contbreak_stmt.label.name) { SEMA_ERROR(statement, "'continue' is not allowed here."); return false; } + UPDATE_EXIT(EXIT_CONTINUE); - DynamicScope *scope = context->current_scope; - statement->continue_stmt.defers.start = scope->defers.start; - while (!(scope->flags_created & SCOPE_CONTINUE)) // NOLINT(hicpp-signed-bitwise) + + statement->contbreak_stmt.defers.start = context->current_scope->defers.start; + + if (statement->contbreak_stmt.label.name) { - scope--; + Decl *target = sema_analyse_label(context, statement); + if (!decl_ok(target)) return false; + switch (target->label.parent->ast_kind) + { + case AST_FOR_STMT: + case AST_WHILE_STMT: + break; + case AST_DO_STMT: + if (target->label.parent->do_stmt.expr) break; + default: + SEMA_ERROR(statement, "'continue' may only be used with 'for', 'while' and 'do-while' statements."); + return false; + } + statement->contbreak_stmt.ast = target->label.parent; + statement->contbreak_stmt.defers.end = target->label.defer; + return true; } - statement->continue_stmt.defers.end = scope->defers.end; + statement->contbreak_stmt.defers.end = context->continue_defer; + statement->contbreak_stmt.ast = context->break_target; return true; } @@ -716,10 +861,10 @@ static inline bool sema_analyse_compound_statement_no_scope(Context *context, As return all_ok; } -static inline Type *ast_cond_type(Ast *ast) +static inline Type *ast_cond_type(Expr *expr) { - assert(ast->ast_kind == AST_DECL_EXPR_LIST); - Ast *last = VECLAST(ast->decl_expr_stmt); + assert(expr->expr_kind == EXPR_DECL_LIST); + Ast *last = VECLAST(expr->dexpr_list_expr); switch (last->ast_kind) { case AST_EXPR_STMT: @@ -731,26 +876,78 @@ static inline Type *ast_cond_type(Ast *ast) } } - -static bool sema_analyse_switch_stmt(Context *context, Ast *statement) +static inline bool sema_check_type_case(Context *context, Type *switch_type, Ast *case_stmt, Ast **cases, unsigned index, bool use_type_id) { - context_push_scope(context); - Ast *cond = statement->switch_stmt.cond; - bool success = sema_analyse_cond(context, cond, false); + if (!sema_resolve_type_info(context, case_stmt->case_stmt.type_info)) return false; + Type *case_type = case_stmt->case_stmt.type_info->type; + if (!use_type_id) + { + SEMA_ERROR(case_stmt, "Unexpected '%s' given case when a normal expression was expected.", type_to_error_string(case_type)); + return false; + } + if (switch_type == type_error && case_type->canonical->type_kind != TYPE_ERRTYPE) + { + if (case_type->canonical == type_error) + { + SEMA_ERROR(case_stmt, "In a catch, only use specific error types, never 'error'."); + return false; + } + SEMA_ERROR(case_stmt, "Expected an error type here, not '%s'", type_to_error_string(case_type)); + return false; + } + for (unsigned i = 0; i < index; i++) + { + Ast *other = cases[i]; + if (other->ast_kind == AST_CASE_STMT && other->case_stmt.type_info->type->canonical == case_stmt->case_stmt.type_info->type->canonical) + { + SEMA_ERROR(case_stmt, "The same type appears more than once."); + SEMA_PREV(other, "Here is the case with that type."); + return false; + } + } + return true; +} - Type *switch_type = ast_cond_type(cond)->canonical; +static inline bool sema_check_value_case(Context *context, Type *switch_type, Ast *case_stmt, Ast **cases, unsigned index, bool use_type_id) +{ + if (!sema_analyse_case_expr(context, switch_type, case_stmt)) return false; + Expr *expr = case_stmt->case_stmt.expr; + if (use_type_id) + { + SEMA_ERROR(case_stmt, "Unexpected value of type '%s' when expecting a type.", type_to_error_string(expr->type)); + return false; + } + for (unsigned i = 0; i < index; i++) + { + Ast *other = cases[i]; + if (other->ast_kind == AST_CASE_STMT && expr_const_compare(&other->case_stmt.expr->const_expr, &expr->const_expr, BINARYOP_EQ)) + { + SEMA_ERROR(case_stmt, "The same case value appears more than once."); + SEMA_PREV(other, "Here is the previous use of that value."); + return false; + } + } + return true; +} + +static bool sema_analyse_switch_body(Context *context, Ast *statement, SourceRange expr_span, Type *switch_type, Ast **cases) +{ + // TODO switch next/break labels + bool use_type_id = false; switch (switch_type->type_kind) { + case TYPE_TYPEID: + case TYPE_ERR_UNION: + use_type_id = true; + break; case ALL_INTS: assert(switch_type->type_kind != TYPE_IXX); case TYPE_BOOL: - case TYPE_ERROR: - case TYPE_TYPEID: case TYPE_ENUM: case TYPE_STRING: break; default: - SEMA_ERROR(cond, "It is not possible to switch over '%s'.", type_to_error_string(switch_type)); + sema_error_range(expr_span, "It is not possible to switch over '%s'.", type_to_error_string(switch_type)); return false; } Ast *default_case = NULL; @@ -759,27 +956,29 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement) ExitType prev_exit = context->current_scope->exit; bool exhaustive = false; ExitType lowest_exit = EXIT_NONE; - unsigned cases = vec_size(statement->switch_stmt.cases); - for (unsigned i = 0; i < cases; i++) + unsigned case_count = vec_size(cases); + bool success = true; + for (unsigned i = 0; i < case_count; i++) { context->current_scope->exit = prev_exit; - Ast *stmt = statement->switch_stmt.cases[i]; + Ast *stmt = cases[i]; switch (stmt->ast_kind) { case AST_CASE_STMT: - if (!sema_analyse_case_expr(context, switch_type, stmt)) + if (stmt->case_stmt.is_type) { - success = false; - break; - } - for (unsigned j = 0; j < i; j++) - { - Ast *other = statement->switch_stmt.cases[j]; - if (other->ast_kind == AST_CASE_STMT && expr_const_compare(&other->case_stmt.expr->const_expr, &stmt->case_stmt.expr->const_expr, BINARYOP_EQ)) + if (!sema_check_type_case(context, switch_type, stmt, cases, i, use_type_id)) { - SEMA_ERROR(stmt, "The same case value appears more than once."); - SEMA_PREV(other, "Here is the previous use of that value."); success = false; + break;; + } + } + else + { + if (!sema_check_value_case(context, switch_type, stmt, cases, i, use_type_id)) + { + success = false; + break; } } break; @@ -796,14 +995,15 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement) default: UNREACHABLE; } - if (i == cases - 1) - { - context_push_scope_with_flags(context, SCOPE_BREAK); - } - else - { - context_push_scope_with_flags(context, SCOPE_NEXT | SCOPE_BREAK); - } + } + for (unsigned i = 0; i < case_count; i++) + { + context->current_scope->exit = prev_exit; + Ast *stmt = cases[i]; + context_push_scope(context); + PUSH_BREAK(statement); + Ast *next = (i < case_count - 1) ? cases[i + 1] : NULL; + PUSH_NEXT(next, statement); success = success && (!stmt->case_stmt.body || sema_analyse_compound_statement_no_scope(context, stmt->case_stmt.body)); ExitType case_exit = context->current_scope->exit; if (case_exit != lowest_exit) @@ -822,66 +1022,185 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement) break; } } + POP_BREAK(); + POP_NEXT(); context_pop_scope(context); } - context_pop_defers_and_replace_ast(context, statement); if (lowest_exit <= EXIT_BREAK) lowest_exit = prev_exit; // Check exhaustive use. context->current_scope->exit = exhaustive ? lowest_exit : EXIT_NONE; - context_pop_scope(context); if (!success) return false; return success; } - - -static bool sema_analyse_throw_stmt(Context *context, Ast *statement) +static bool sema_analyse_switch_stmt(Context *context, Ast *statement) { - Expr *throw_value = statement->throw_stmt.throw_value; - statement->throw_stmt.defer = context->current_scope->defers.end; - UPDATE_EXIT(EXIT_THROW); - if (!sema_analyse_expr(context, NULL, throw_value)) return false; - Type *type = throw_value->type->canonical; - if (type->type_kind != TYPE_ERROR && type->type_kind != TYPE_ERROR_UNION) - { - SEMA_ERROR(throw_value, "Only 'error' types can be thrown, this is a '%s'.", type->name); - return false; - } - FunctionSignature *sig = &context->active_function_for_analysis->func.function_signature; - if (sig->error_return == ERROR_RETURN_NONE) - { - SEMA_ERROR(statement, "This throw requires that the function adds 'throws %s' to its declaration.", type->name); - return false; - } + context_push_scope_with_label(context, statement->switch_stmt.label); + Expr *cond = statement->switch_stmt.cond; + if (!sema_analyse_cond(context, cond, false)) return false; - // Check if the error is actually in the list. - if (sig->error_return == ERROR_RETURN_MANY || sig->error_return == ERROR_RETURN_ONE) - { - bool found = false; - VECEACH(sig->throws, i) - { - if (sig->throws[i]->type == type) - { - found = true; - } - } - if (!found) - { - if (type != type_error_union) - { - SEMA_ERROR(statement->throw_stmt.throw_value, "'%s' must be added to the list of errors after 'throws'.", type->name); - } - else - { - SEMA_ERROR(statement, "This throw requires the function to use a wildcard 'throws' without types.", type->name); - } - return false; - } - } - vec_add(context->throw, statement); - return true; + Type *switch_type = ast_cond_type(cond)->canonical; + statement->switch_stmt.defer = context->current_scope->defers.start; + bool success = sema_analyse_switch_body(context, statement, cond->span, + switch_type->canonical, + statement->switch_stmt.cases); + if (success) context_pop_defers_and_replace_ast(context, statement); + + context_pop_scope(context); + return success; } +/** + * Handle the catch statement + * @return true if error checking succeeds. + */ +static bool sema_analyse_catch_stmt(Context *context, Ast *statement) +{ + Expr *catch_expr = statement->catch_stmt.catchable; + Decl *error_var = NULL; + Expr *error_expr = catch_expr; + Decl *unwrapped = NULL; + + context_push_scope_with_label(context, statement->catch_stmt.label); + + Expr *maybe_unwrapped = NULL; + statement->catch_stmt.defer = context->current_scope->defers.start; + if (catch_expr->expr_kind == EXPR_BINARY && catch_expr->binary_expr.operator == BINARYOP_ASSIGN) + { + Expr *left = catch_expr->binary_expr.left; + if (left->expr_kind == EXPR_IDENTIFIER) + { + Decl *ambiguous_decl; + Decl *error_var_decl = sema_resolve_symbol(context, + left->identifier_expr.identifier, + left->identifier_expr.path, + &ambiguous_decl); + if (!error_var_decl) + { + + error_var = decl_new_var((Token) { .span = left->span, .string = left->identifier_expr.identifier }, + type_info_new_base(type_error, left->span), + VARDECL_LOCAL, + VISIBLE_LOCAL); + error_var->type = type_error; + Expr *right = catch_expr->binary_expr.right; + error_var->var.init_expr = right; + error_expr = right; + statement->catch_stmt.has_err_var = true; + statement->catch_stmt.err_var = error_var; + } + } + } + bool success = sema_analyse_expr(context, NULL, error_expr); + + if (!success) goto EXIT; + + if (error_var) + { + sema_add_local(context, error_var); + } + + if (!error_expr->failable) + { + const char *error_type = type_to_error_string(error_expr->type); + if (error_expr->expr_kind == EXPR_IDENTIFIER + && error_expr->identifier_expr.decl->decl_kind == DECL_VAR + && error_expr->identifier_expr.decl->var.kind == VARDECL_ALIAS) + { + SEMA_ERROR(error_expr, "'%s' is unwrapped to '%s' here, so it cannot be caught.", error_expr->identifier_expr.decl->name, error_type); + success = false; + goto EXIT; + } + SEMA_ERROR(error_expr, "Expected a failable '%s!' not '%s'.", error_type, error_type); + success = false; + goto EXIT; + } + + if (catch_expr->expr_kind == EXPR_IDENTIFIER) + { + unwrapped = catch_expr->identifier_expr.decl; + } + else if (error_var) + { + Expr *right = catch_expr->binary_expr.right; + if (right->expr_kind == EXPR_IDENTIFIER) unwrapped = right->identifier_expr.decl; + } + + if (statement->catch_stmt.is_switch) + { + success = sema_analyse_switch_body(context, statement, error_expr->span, type_error, statement->catch_stmt.cases); + } + else + { + success = sema_analyse_statement(context, statement->catch_stmt.body); + } + bool was_exit = context->current_scope->exit == EXIT_RETURN; + if (success) context_pop_defers_and_replace_ast(context, statement); + context_pop_scope(context); + + if (error_var) + { + } + +EXIT: + if (success) + { + if (unwrapped && was_exit) + { + Decl *decl = COPY(unwrapped); + decl->var.kind = VARDECL_ALIAS; + decl->var.alias = unwrapped; + decl->var.failable = false; + sema_unwrap_var(context, decl); + } + } + return success; + +} + + +static bool sema_analyse_try_stmt(Context *context, Ast *stmt) +{ + assert(stmt->try_stmt.decl_expr->expr_kind == EXPR_DECL_LIST); + + Ast **dexprs = stmt->try_stmt.decl_expr->dexpr_list_expr; + context_push_scope(context); + unsigned entries = vec_size(dexprs); + for (unsigned i = 0; i < entries; i++) + { + Ast *ast = dexprs[i]; + if (ast->ast_kind == AST_DECLARE_STMT) + { + ast->declare_stmt->var.unwrap = true; + if (!sema_analyse_statement(context, ast)) goto ERR; + continue; + } + if (!sema_analyse_statement(context, ast)) goto ERR; + Expr *expr = ast->expr_stmt; + if (!expr->failable) + { + SEMA_ERROR(expr, "The expression to 'try' must be failable."); + goto ERR; + } + if (expr->expr_kind == EXPR_IDENTIFIER) + { + Decl *var = expr->identifier_expr.decl; + Decl *decl = COPY(var); + decl->var.kind = VARDECL_ALIAS; + decl->var.alias = var; + decl->var.failable = false; + sema_unwrap_var(context, decl); + } + } + if (!sema_analyse_statement(context, stmt->try_stmt.body)) goto ERR; + + context_pop_scope(context); + return true; + + ERR: + context_pop_scope(context); + return false; +} static bool sema_analyse_volatile_stmt(Context *context, Ast *statement) { @@ -908,6 +1227,8 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement return false; case AST_SCOPED_STMT: UNREACHABLE + case AST_DEFINE_STMT: + return sema_analyse_define_stmt(context, statement); case AST_ASM_STMT: return sema_analyse_asm_stmt(context, statement); case AST_BREAK_STMT: @@ -936,114 +1257,41 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement return sema_analyse_expr_stmt(context, statement); case AST_FOR_STMT: return sema_analyse_for_stmt(context, statement); - case AST_GOTO_STMT: - return sema_analyse_goto_stmt(context, statement); + case AST_TRY_STMT: + return sema_analyse_try_stmt(context, statement); case AST_IF_STMT: return sema_analyse_if_stmt(context, statement); - case AST_LABEL: - return sema_analyse_label(context, statement); case AST_NOP_STMT: return true; case AST_RETURN_STMT: return sema_analyse_return_stmt(context, statement); case AST_SWITCH_STMT: return sema_analyse_switch_stmt(context, statement); - case AST_THROW_STMT: - return sema_analyse_throw_stmt(context, statement); case AST_NEXT_STMT: return sema_analyse_next_stmt(context, statement); case AST_VOLATILE_STMT: return sema_analyse_volatile_stmt(context, statement); case AST_WHILE_STMT: return sema_analyse_while_stmt(context, statement); - case AST_DECL_EXPR_LIST: - return sema_analyse_decl_expr_list(context, statement); case AST_CT_ELIF_STMT: case AST_CT_ELSE_STMT: UNREACHABLE case AST_CT_FOR_STMT: case AST_CT_SWITCH_STMT: - case AST_CT_DEFAULT_STMT: - case AST_CT_CASE_STMT: - case AST_GENERIC_CASE_STMT: - case AST_GENERIC_DEFAULT_STMT: TODO } + UNREACHABLE } + bool sema_analyse_statement(Context *context, Ast *statement) { if (sema_analyse_statement_inner(context, statement)) return true; return ast_poison(statement); } -static inline int defer_depth(Ast *defer_stmt) -{ - int depth = 0; - while (defer_stmt) - { - defer_stmt = defer_stmt->defer_stmt.prev_defer; - depth++; - } - return depth; -} -static inline void defer_list_walk_to_common_depth(Ast **defer_stmt, int this_depth, int other_depth) -{ - int steps = this_depth - other_depth; - for (int i = 0; i < steps; i++) - { - *defer_stmt = (*defer_stmt)->defer_stmt.prev_defer; - } -} - -static inline bool throw_add_error_return_catch(Throw *throw, TypeInfo **func_throws) -{ - assert(throw->kind != THROW_TYPE_CALL_ANY); - TypeInfo **throws = throw->throws; - unsigned throw_count = vec_size(throws); - unsigned func_throw_count = vec_size(func_throws); - assert(func_throw_count); - bool catch_added = false; - for (unsigned i = 0; i < func_throw_count; i++) - { - TypeInfo *func_throw = func_throws[i]; - for (unsigned j = 0; j < throw_count; j++) - { - if (throws[j]->type == func_throw->type) - { - // If the throw was already caught, ignore it. - if (throw_completely_caught(throws[j], throw->throw_info->catches)) continue; - - // One of the throws was caught - if (func_throw_count > 1) - { - CatchInfo info = { .kind = CATCH_RETURN_MANY, .error = func_throw->type->decl }; - vec_add(throw->throw_info->catches, info); - } - else - { - CatchInfo info = { .kind = CATCH_RETURN_ONE, .error = func_throw->type->decl }; - vec_add(throw->throw_info->catches, info); - } - // If we only have one count, then we're done! - if (throw_count == 1) - { - throw->throw_info->is_completely_handled = true; - return true; - } - // Otherwise we simply continue. - } - } - } - // If we have already caught some, then we might have completely caught all throws. - if (throw_count > 1 && catch_added) - { - throw->throw_info->is_completely_handled = throw_completely_handled_call_throw_many(throw); - } - return catch_added; -} bool sema_analyse_function_body(Context *context, Decl *func) { @@ -1051,17 +1299,15 @@ bool sema_analyse_function_body(Context *context, Decl *func) context->active_function_for_analysis = func; context->rtype = signature->rtype->type; context->current_scope = &context->scopes[0]; + context->current_scope->scope_id = 0; + context->failable_return = signature->failable; + // Clean out the current scope. memset(context->current_scope, 0, sizeof(*context->current_scope)); - // Clear try handling - vec_resize(context->throw, 0); - vec_resize(context->error_calls, 0); // Clear returns vec_resize(context->returns, 0); - context->try_nesting = 0; - context->labels = NULL; - context->gotos = NULL; + context->scope_id = 0; context->returns = NULL; context->expected_block_type = NULL; context->last_local = &context->locals[0]; @@ -1069,7 +1315,11 @@ bool sema_analyse_function_body(Context *context, Decl *func) context->in_macro = 0; context->macro_counter = 0; context->macro_nesting = 0; - func->func.annotations = CALLOCS(*func->func.annotations); + context->continue_target = NULL; + context->next_target = NULL; + context->next_switch = NULL; + context->break_target = NULL; + func->func.annotations = CALLOCS(FuncAnnotations); context_push_scope(context); Decl **params = signature->params; assert(context->current_scope == &context->scopes[1]); @@ -1079,9 +1329,10 @@ bool sema_analyse_function_body(Context *context, Decl *func) } if (!sema_analyse_compound_statement_no_scope(context, func->func.body)) return false; assert(context->current_scope == &context->scopes[1]); - if (context->current_scope->exit != EXIT_RETURN && context->current_scope->exit != EXIT_THROW && context->current_scope->exit != EXIT_GOTO) + if (context->current_scope->exit != EXIT_RETURN) { - if (signature->rtype->type->canonical != type_void) + Type *canonical_rtype = signature->rtype->type->canonical; + if (canonical_rtype != type_void) { // IMPROVE better pointer to end. SEMA_ERROR(func, "Missing return statement at the end of the function."); @@ -1090,86 +1341,6 @@ bool sema_analyse_function_body(Context *context, Decl *func) } - VECEACH(context->gotos, i) - { - Ast *goto_stmt = context->gotos[i]; - Ast *label_target = goto_stmt->goto_stmt.label; - if (!label_target) - { - SEMA_ERROR(goto_stmt, "Goto to a missing label %s.", goto_stmt->goto_stmt.label_name); - return false; - } - - // If there are no defers, then that's fine. - if (!goto_stmt->goto_stmt.defer.start && !label_target->label_stmt.defer) continue; - - Ast *common_depth_label = label_target->label_stmt.defer; - Ast *common_depth_goto = goto_stmt->goto_stmt.defer.start; - - // First we need to search for the common depth. - int label_depth = defer_depth(common_depth_label); - int goto_depth = defer_depth(common_depth_goto); - - - // Now walk up to the common depth. - defer_list_walk_to_common_depth(&common_depth_label, label_depth, goto_depth); - defer_list_walk_to_common_depth(&common_depth_goto, goto_depth, label_depth); - - // We might still not match, so walk upwards until we have a match: - while (common_depth_goto != common_depth_label) - { - assert(common_depth_goto && common_depth_label); - common_depth_goto = common_depth_goto->defer_stmt.prev_defer; - common_depth_label = common_depth_label->defer_stmt.prev_defer; - } - - // We now know the top defer (which we won't actually generate) - goto_stmt->goto_stmt.defer.end = common_depth_goto; - - // Mark all defers that occur on the way "up" to the common depth conditional. - Ast *current = label_target->label_stmt.defer; - while (current != common_depth_goto) - { - current->defer_stmt.emit_boolean = true; - current = current->defer_stmt.prev_defer; - } - } - bool error_was_useful = signature->error_return == ERROR_RETURN_NONE; - VECEACH(context->error_calls, i) - { - Throw *throw = &context->error_calls[i]; - if (throw->throw_info->is_completely_handled) continue; - switch (signature->error_return) - { - case ERROR_RETURN_NONE: - // Nothing to do, will result in error. - break; - case ERROR_RETURN_ANY: - // Any return, then any throw is ok, add - // an implicit catch. - vec_add(throw->throw_info->catches, (CatchInfo) { .kind = CATCH_RETURN_ANY }); - throw->throw_info->is_completely_handled = true; - error_was_useful = true; - continue; - case ERROR_RETURN_MANY: - case ERROR_RETURN_ONE: - // Try to add a catch. - error_was_useful = true; - throw_add_error_return_catch(throw, signature->throws); - break; - } - // If it's fully caught, then fine. - if (throw->throw_info->is_completely_handled) continue; - // Otherwise error. - SEMA_ERROR(throw, "The errors returned by the call must be completely caught in a catch or else the function current must be declared to throw."); - return false; - } - - if (!error_was_useful && !vec_size(context->throw)) - { - // Warning here? - } - func->func.labels = context->labels; context_pop_scope(context); context->current_scope = NULL; return true; diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 41305c7c1..525cd9901 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -18,28 +18,6 @@ static inline bool sema_resolve_ptr_type(Context *context, TypeInfo *type_info) return true; } -bool throw_completely_caught(TypeInfo *throw, CatchInfo *catches) -{ - VECEACH(catches, i) - { - CatchInfo *catch_info = &catches[i]; - switch (catch_info->kind) - { - case CATCH_REGULAR: - if (throw->type == catch_info->catch->catch_stmt.error_param->type) return true; - break; - case CATCH_TRY_ELSE: - case CATCH_RETURN_ANY: - return true; - case CATCH_RETURN_MANY: - case CATCH_RETURN_ONE: - if (throw->type == catch_info->error->type) return true; - break; - } - } - return false; -} - static inline bool sema_resolve_array_type(Context *context, TypeInfo *type) { @@ -57,7 +35,10 @@ static inline bool sema_resolve_array_type(Context *context, TypeInfo *type) type->type = type_get_subarray(type->array.base->type); break;; case TYPE_INFO_ARRAY: - if (!sema_analyse_expr_of_required_type(context, type_usize, type->array.len)) return type_info_poison(type); + if (!sema_analyse_expr_of_required_type(context, + type_usize, + type->array.len, + false)) return type_info_poison(type); if (type->array.len->expr_kind != EXPR_CONST) { SEMA_ERROR(type->array.len, "Expected a constant value as array size."); @@ -82,7 +63,6 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info) type_info->unresolved.name_loc.string, type_info->unresolved.path, &ambiguous_decl); - if (!decl) { SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, "Unknown type '%s'.", type_info->unresolved.name_loc.string); @@ -109,7 +89,7 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info) { case DECL_STRUCT: case DECL_UNION: - case DECL_ERROR: + case DECL_ERR: case DECL_ENUM: case DECL_TYPEDEF: if (decl->resolve_status == RESOLVE_NOT_DONE) @@ -125,11 +105,11 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info) case DECL_FUNC: case DECL_VAR: case DECL_ENUM_CONSTANT: - case DECL_ERROR_CONSTANT: case DECL_ARRAY_VALUE: case DECL_IMPORT: case DECL_MACRO: case DECL_GENERIC: + case DECL_LABEL: SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, "This is not a type."); return type_info_poison(type_info); case DECL_CT_ELSE: @@ -164,7 +144,8 @@ bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info) case TYPE_INFO_INC_ARRAY: UNREACHABLE case TYPE_INFO_IDENTIFIER: - return sema_resolve_type_identifier(context, type_info); + if (!sema_resolve_type_identifier(context, type_info)) return false; + break; case TYPE_INFO_EXPRESSION: if (!sema_analyse_expr(context, NULL, type_info->unresolved_type_expr)) { @@ -174,9 +155,11 @@ bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info) case TYPE_INFO_SUBARRAY: case TYPE_INFO_VARARRAY: case TYPE_INFO_ARRAY: - return sema_resolve_array_type(context, type_info); + if (!sema_resolve_array_type(context, type_info)) return false; + break; case TYPE_INFO_POINTER: - return sema_resolve_ptr_type(context, type_info); + if (!sema_resolve_ptr_type(context, type_info)) return false; + break; } - UNREACHABLE + return true; } \ No newline at end of file diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 89039067a..7802c2abb 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -52,7 +52,7 @@ const char *token_type_to_string(TokenType type) return "-"; case TOKEN_MOD: return "%"; - case TOKEN_NOT: + case TOKEN_BANG: return "!"; case TOKEN_PLUS: return "+"; @@ -90,6 +90,8 @@ const char *token_type_to_string(TokenType type) return ">="; case TOKEN_LESS_EQ: return "<="; + case TOKEN_LPARBRA: + return "({"; case TOKEN_MINUS_ASSIGN: return "-="; case TOKEN_MINUS_MOD: @@ -112,16 +114,16 @@ const char *token_type_to_string(TokenType type) return "+%"; case TOKEN_PLUSPLUS: return "++"; + case TOKEN_RPARBRA: + return "})"; case TOKEN_SCOPE: return "::"; case TOKEN_SHL: return "<<"; case TOKEN_SHR: return ">>"; - case TOKEN_LPARBRA: - return "({"; - case TOKEN_RPARBRA: - return "})"; + case TOKEN_BANGBANG: + return "!!"; // Three character tokens case TOKEN_ELLIPSIS: @@ -151,6 +153,12 @@ const char *token_type_to_string(TokenType type) case TOKEN_TYPE_IDENT: return "TYPE_IDENT"; + // Asm + case TOKEN_ASM_STRING: + return "ASM_STRING"; + case TOKEN_ASM_CONSTRAINT: + return "ASM_CONSTRAINT"; + // Values case TOKEN_STRING: return "STRING"; @@ -190,6 +198,8 @@ const char *token_type_to_string(TokenType type) return "default"; case TOKEN_DEFER: return "defer"; + case TOKEN_DEFINE: + return "define"; case TOKEN_DO: return "do"; case TOKEN_ELSE: @@ -198,10 +208,8 @@ const char *token_type_to_string(TokenType type) return "enum"; case TOKEN_EXTERN: return "extern"; - case TOKEN_ERROR_TYPE: + case TOKEN_ERR: return "error"; - case TOKEN_ERRSET: - return "errset"; case TOKEN_FALSE: return "false"; case TOKEN_FOR: @@ -210,8 +218,6 @@ const char *token_type_to_string(TokenType type) return "func"; case TOKEN_GENERIC: return "generic"; - case TOKEN_GOTO: - return "goto"; case TOKEN_IF: return "if"; case TOKEN_IMPORT: @@ -236,10 +242,6 @@ const char *token_type_to_string(TokenType type) return "struct"; case TOKEN_SWITCH: return "switch"; - case TOKEN_THROW: - return "throw"; - case TOKEN_THROWS: - return "throws"; case TOKEN_TRUE: return "true"; case TOKEN_TRY: @@ -362,3 +364,7 @@ bool token_is_type(TokenType type) return type >= TOKEN_VOID && type <= TOKEN_C_ULONGLONG; } +bool token_is_any_type(TokenType type) +{ + return (type >= TOKEN_VOID && type <= TOKEN_C_ULONGLONG) || type == TOKEN_CT_TYPE_IDENT || type == TOKEN_TYPE_IDENT; +} diff --git a/src/compiler/types.c b/src/compiler/types.c index 69a729db2..24ff41a16 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -10,7 +10,7 @@ static Type t_f32, t_f64, t_fxx; static Type t_usz, t_isz; static Type t_cus, t_cui, t_cul, t_cull; static Type t_cs, t_ci, t_cl, t_cll; -static Type t_voidstar, t_typeid, t_error_union; +static Type t_voidstar, t_typeid, t_error; Type *type_bool = &t_u1; Type *type_void = &t_u0; @@ -39,8 +39,7 @@ Type *type_c_ushort = &t_cus; Type *type_c_uint = &t_cui; Type *type_c_ulong = &t_cul; Type *type_c_ulonglong = &t_cull; -Type *type_error_union = &t_error_union; -Type *type_error_base = &t_ci; +Type *type_error = &t_error; static unsigned size_subarray; static unsigned alignment_subarray; @@ -100,29 +99,19 @@ const char *type_to_error_string(Type *type) case TYPE_F64: case TYPE_FXX: case TYPE_UNION: - case TYPE_ERROR: + case TYPE_ERRTYPE: return type->name; case TYPE_FUNC: { - asprintf(&buffer, "func %s(", type_to_error_string(type->func.signature->rtype->type)); + asprintf(&buffer, type->func.signature->failable ? "func %s!(" : "func %s(", + type_to_error_string(type->func.signature->rtype->type)); VECEACH(type->func.signature->params, i) { if (i != 0) buffer = strcat_arena(buffer, ", "); strcat_arena(buffer, type_to_error_string(type->func.signature->params[i]->type)); } - buffer = strcat_arena(buffer, ")"); - if (type->func.signature->throw_any) - { - return strcat_arena(buffer, " throws"); - } - if (!vec_size(type->func.signature->throws)) return buffer; - buffer = strcat_arena(buffer, " throws "); - VECEACH(type->func.signature->throws, i) - { - if (i != 0) buffer = strcat_arena(buffer, ", "); - buffer = strcat_arena(buffer, type_to_error_string(type->func.signature->throws[i]->type)); - } - return buffer; + + return strcat_arena(buffer, ")"); } case TYPE_TYPEID: return "typeid"; @@ -146,7 +135,7 @@ const char *type_to_error_string(Type *type) case TYPE_SUBARRAY: asprintf(&buffer, "%s[]", type_to_error_string(type->array.base)); return buffer; - case TYPE_ERROR_UNION: + case TYPE_ERR_UNION: return "error"; } UNREACHABLE @@ -167,7 +156,6 @@ static void type_append_signature_name_user_defined(Decl *decl, char *dst, size_ case DECL_VAR: case DECL_ENUM_CONSTANT: case DECL_TYPEDEF: - case DECL_ERROR_CONSTANT: case DECL_ARRAY_VALUE: case DECL_IMPORT: case DECL_MACRO: @@ -177,11 +165,12 @@ static void type_append_signature_name_user_defined(Decl *decl, char *dst, size_ case DECL_CT_ELIF: case DECL_ATTRIBUTE: case DECL_MEMBER: + case DECL_LABEL: UNREACHABLE case DECL_STRUCT: case DECL_UNION: case DECL_ENUM: - case DECL_ERROR: + case DECL_ERR: { unsigned len = source_range_len(decl->name_span); memcpy(dst + *offset, decl->name, len); @@ -202,7 +191,7 @@ void type_append_signature_name(Type *type, char *dst, size_t *offset) case TYPE_POISONED: case TYPE_TYPEDEF: UNREACHABLE; - case TYPE_ERROR: + case TYPE_ERRTYPE: case TYPE_ENUM: case TYPE_STRUCT: case TYPE_UNION: @@ -218,76 +207,76 @@ void type_append_signature_name(Type *type, char *dst, size_t *offset) -size_t type_size(Type *canonical) +size_t type_size(Type *type) { - assert(canonical && canonical->canonical == canonical); - switch (canonical->type_kind) + switch (type->type_kind) { case TYPE_POISONED: - case TYPE_TYPEDEF: UNREACHABLE; + case TYPE_TYPEDEF: + return type_size(type->canonical); case TYPE_ENUM: - return canonical->decl->enums.type_info->type->canonical->builtin.bytesize; - case TYPE_ERROR: + return type->decl->enums.type_info->type->canonical->builtin.bytesize; + case TYPE_ERRTYPE: return alignment_error_code; case TYPE_STRUCT: case TYPE_UNION: - assert(canonical->decl->resolve_status == RESOLVE_DONE); - return canonical->decl->strukt.size; + assert(type->decl->resolve_status == RESOLVE_DONE); + return type->decl->strukt.size; case TYPE_VOID: return 1; case TYPE_BOOL: case TYPE_TYPEID: case ALL_INTS: case ALL_FLOATS: - return canonical->builtin.bytesize; + case TYPE_ERR_UNION: + return type->builtin.bytesize; case TYPE_MEMBER: - return type_size(canonical->decl->var.type_info->type); + return type_size(type->decl->var.type_info->type); case TYPE_FUNC: case TYPE_POINTER: case TYPE_VARARRAY: case TYPE_STRING: - case TYPE_ERROR_UNION: return t_usz.canonical->builtin.bytesize; case TYPE_ARRAY: - return type_size(canonical->array.base) * canonical->array.len; + return type_size(type->array.base) * type->array.len; case TYPE_SUBARRAY: return size_subarray; } UNREACHABLE } -unsigned int type_abi_alignment(Type *canonical) +unsigned int type_abi_alignment(Type *type) { - assert(canonical && canonical->canonical == canonical); - switch (canonical->type_kind) + switch (type->type_kind) { case TYPE_POISONED: - case TYPE_TYPEDEF: case TYPE_VOID: UNREACHABLE; + case TYPE_TYPEDEF: + return type_abi_alignment(type->canonical); case TYPE_ENUM: - return canonical->decl->enums.type_info->type->canonical->builtin.abi_alignment; - case TYPE_ERROR: + return type->decl->enums.type_info->type->canonical->builtin.abi_alignment; + case TYPE_ERRTYPE: return alignment_error_code; case TYPE_STRUCT: case TYPE_UNION: - return canonical->decl->strukt.abi_alignment; + return type->decl->strukt.abi_alignment; case TYPE_TYPEID: case TYPE_BOOL: case ALL_INTS: case ALL_FLOATS: - case TYPE_ERROR_UNION: - return canonical->builtin.abi_alignment; + case TYPE_ERR_UNION: + return type->builtin.abi_alignment; case TYPE_MEMBER: - return type_abi_alignment(canonical->decl->var.type_info->type); + return type_abi_alignment(type->decl->var.type_info->type); case TYPE_FUNC: case TYPE_POINTER: case TYPE_VARARRAY: case TYPE_STRING: return t_usz.canonical->builtin.abi_alignment; case TYPE_ARRAY: - return type_abi_alignment(canonical->array.base); + return type_abi_alignment(type->array.base); case TYPE_SUBARRAY: return alignment_subarray; } @@ -329,6 +318,7 @@ static Type *type_generate_ptr(Type *ptr_type, bool canonical) return ptr; } + static Type *type_generate_subarray(Type *arr_type, bool canonical) { if (canonical) arr_type = arr_type->canonical; @@ -534,7 +524,7 @@ type_create(#_name, &_shortname, _type, _bits, target->align_ ## _align, target- alignment_subarray = MAX(type_abi_alignment(&t_voidstar), type_abi_alignment(t_usz.canonical)); size_subarray = alignment_subarray * 2; - type_create("error", &t_error_union, TYPE_ERROR_UNION, target->width_pointer, target->align_pointer, target->align_pref_pointer); + type_create("error", &t_error, TYPE_ERR_UNION, target->width_pointer * 2, target->align_pointer, target->align_pref_pointer); } /** @@ -569,7 +559,7 @@ bool type_may_have_sub_elements(Type *type) case TYPE_UNION: case TYPE_STRUCT: case TYPE_ENUM: - case TYPE_ERROR: + case TYPE_ERRTYPE: return true; default: return false; @@ -743,13 +733,13 @@ Type *type_find_max_type(Type *type, Type *other) // IMPROVE: should there be implicit conversion between one enum and the other in // some way? return NULL; - case TYPE_ERROR: - if (other->type_kind == TYPE_ERROR) return type_error_union; + case TYPE_ERRTYPE: + if (other->type_kind == TYPE_ERRTYPE) return type_error; return NULL; case TYPE_MEMBER: case TYPE_FUNC: case TYPE_UNION: - case TYPE_ERROR_UNION: + case TYPE_ERR_UNION: case TYPE_TYPEID: return NULL; case TYPE_STRUCT: diff --git a/src/main.c b/src/main.c index 2d30e901c..4923ddb5b 100644 --- a/src/main.c +++ b/src/main.c @@ -43,6 +43,7 @@ int main(int argc, const char *argv[]) case COMMAND_BENCH: printf("TODO\n"); } + print_arena_status(); free_arena(); return 0; } diff --git a/src/utils/lib.h b/src/utils/lib.h index defecf7b0..a1db95483 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -16,12 +16,13 @@ void file_add_wildcard_files(const char ***files, const char *path, bool recursi void memory_init(void); void *malloc_arena(unsigned long mem); void free_arena(void); +void print_arena_status(void); void run_arena_allocator_tests(void); #define MALLOC(mem) malloc_arena(mem) -#define MALLOCS(type) malloc_arena(sizeof(type)) -#define CALLOCS(type) ({ void *__x = malloc_arena(sizeof(type)); memset(__x, 0, sizeof(type)); __x; }) +#define CALLOCS(type) ({ type *__x = malloc_arena(sizeof(type)); memset(__x, 0, sizeof(type)); __x; }) +#define COPY(value) ({ typeof(value) __x = malloc_arena(sizeof(*value)); memcpy(__x, value, sizeof(*value)); __x; }) static inline bool is_power_of_two(uint64_t x) { @@ -305,6 +306,7 @@ static inline bool is_all_lower(const char* string) char *strcat_arena(const char *a, const char *b); char *strformat(const char *var, ...) __printflike(1, 2); +char *strcopy(const char *start, size_t len); #define MAX(_a, _b) ({ \ typeof(_a) __a__ = (_a); \ diff --git a/src/utils/stringutils.c b/src/utils/stringutils.c index 255b31d26..7599477d6 100644 --- a/src/utils/stringutils.c +++ b/src/utils/stringutils.c @@ -23,6 +23,14 @@ char *strformat(const char *var, ...) return buffer; } +char *strcopy(const char *start, size_t len) +{ + char *buffer = malloc_arena(len + 1); + memcpy(buffer, start, len); + buffer[len] = '\0'; + return buffer; +} + char *strcat_arena(const char *a, const char *b) { unsigned a_len = strlen(a);