diff --git a/doc/langref.md b/doc/langref.md index 3481f3ffda..b2a8f26205 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -34,13 +34,13 @@ Root : many(TopLevelDecl) token(EOF) TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use | ContainerDecl | VariableDeclaration -VariableDeclaration : option(FnVisibleMod) (token(Var) | token(Const)) token(Symbol) (token(Eq) Expression | token(Colon) Type option(token(Eq) Expression)) +VariableDeclaration : option(FnVisibleMod) (token(Var) | token(Const)) token(Symbol) (token(Eq) Expression | token(Colon) UnwrapMaybeExpression option(token(Eq) Expression)) ContainerDecl : many(Directive) option(FnVisibleMod) (token(Struct) | token(Enum)) token(Symbol) token(LBrace) many(StructMember) token(RBrace) StructMember: StructField | FnDecl -StructField : token(Symbol) option(token(Colon) Type token(Comma)) +StructField : token(Symbol) option(token(Colon) Expression) token(Comma)) Use : many(Directive) token(Use) token(String) token(Semicolon) @@ -48,7 +48,7 @@ RootExportDecl : many(Directive) token(Export) token(Symbol) token(String) token ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnDecl) token(RBrace) -FnProto : many(Directive) option(FnVisibleMod) token(Fn) token(Symbol) ParamDeclList option(token(Arrow) Type) +FnProto : many(Directive) option(FnVisibleMod) token(Fn) token(Symbol) ParamDeclList option(Expression) Directive : token(NumberSign) token(Symbol) token(LParen) token(String) token(RParen) @@ -56,23 +56,11 @@ FnVisibleMod : token(Pub) | token(Export) FnDecl : FnProto token(Semicolon) -FnDef : FnProto Block +FnDef : FnProto token(FatArrow) Block ParamDeclList : token(LParen) list(ParamDecl, token(Comma)) token(RParen) -ParamDecl : token(Symbol) token(Colon) Type | token(Ellipsis) - -Type : token(Symbol) | token(Unreachable) | token(Void) | PointerType | ArrayType | MaybeType | CompilerFnExpr - -CompilerFnExpr : token(NumberSign) token(Symbol) token(LParen) Expression token(RParen) - -CompilerFnType : token(NumberSign) token(Symbol) token(LParen) Type token(RParen) - -PointerType : token(Ampersand) option(token(Const)) option(token(NoAlias)) Type - -MaybeType : token(Question) Type - -ArrayType : token(LBracket) option(Expression) token(RBracket) option(token(Const)) option(token(NoAlias)) Type +ParamDecl : option(token(NoAlias)) token(Symbol) token(Colon) UnwrapMaybeExpression | token(Ellipsis) Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace) @@ -80,11 +68,9 @@ Statement : Label | VariableDeclaration token(Semicolon) | NonBlockExpression to Label: token(Symbol) token(Colon) -VariableDeclaration : (token(Var) | token(Const)) token(Symbol) (token(Eq) Expression | token(Colon) Type option(token(Eq) Expression)) - Expression : BlockExpression | NonBlockExpression -NonBlockExpression : ReturnExpression | AssignmentExpression | AsmExpression +NonBlockExpression : ReturnExpression | AssignmentExpression AsmExpression : token(Asm) option(token(Volatile)) token(LParen) token(String) option(AsmOutput) token(RParen) @@ -92,13 +78,13 @@ AsmOutput : token(Colon) list(AsmOutputItem, token(Comma)) option(AsmInput) AsmInput : token(Colon) list(AsmInputItem, token(Comma)) option(AsmClobbers) -AsmOutputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) (token(Symbol) | token(Arrow) Type) token(RParen) +AsmOutputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) (token(Symbol) | token(Arrow) Expression) token(RParen) AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) Expression token(RParen) AsmClobbers: token(Colon) list(token(String), token(Comma)) -UnwrapMaybeExpression : BoolOrExpression token(DoubleQuestion) BoolOrExpression | BoolOrExpression +UnwrapMaybeExpression : BoolOrExpression token(DoubleQuestionMark) BoolOrExpression | BoolOrExpression AssignmentExpression : UnwrapMaybeExpression AssignmentOperator UnwrapMaybeExpression | UnwrapMaybeExpression @@ -116,7 +102,7 @@ IfExpression : IfVarExpression | IfBoolExpression IfBoolExpression : token(If) token(LParen) Expression token(RParen) Expression option(Else) -IfVarExpression : token(If) token(LParen) (token(Const) | token(Var)) token(Symbol) option(token(Colon) Type) Token(MaybeAssign) Expression token(RParen) Expression Option(Else) +IfVarExpression : token(If) token(LParen) (token(Const) | token(Var)) token(Symbol) option(token(Colon) Expression) Token(MaybeAssign) Expression token(RParen) Expression Option(Else) Else : token(Else) Expression @@ -144,11 +130,11 @@ MultiplyExpression : CastExpression MultiplyOperator MultiplyExpression | CastEx MultiplyOperator : token(Star) | token(Slash) | token(Percent) -CastExpression : CastExpression token(as) Type | PrefixOpExpression +CastExpression : CastExpression token(as) PrimaryExpression | PrefixOpExpression PrefixOpExpression : PrefixOp PrefixOpExpression | SuffixOpExpression -SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) +SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | ContainerInitExpression) FieldAccessExpression : token(Dot) token(Symbol) @@ -158,26 +144,30 @@ ArrayAccessExpression : token(LBracket) Expression token(RBracket) SliceExpression : token(LBracket) Expression token(Ellipsis) option(Expression) token(RBracket) option(token(Const)) -PrefixOp : token(Not) | token(Dash) | token(Tilde) | token(Star) | (token(Ampersand) option(token(Const))) +ContainerInitExpression : token(LBrace) ContainerInitBody token(RBrace) -PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType | (token(AtSign) token(Symbol) FnCallExpression) +ContainerInitBody : list(StructLiteralField, token(Comma)) | list(Expression, token(Comma)) -StructValueExpression : token(Type) token(LBrace) list(StructValueExpressionField, token(Comma)) token(RBrace) +StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression -StructValueExpressionField : token(Dot) token(Symbol) token(Eq) Expression +PrefixOp : token(Not) | token(Dash) | token(Tilde) | token(Star) | (token(Ampersand) option(token(Const))) | token(QuestionMark) -Goto: token(Goto) token(Symbol) +PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | token(Symbol) | (token(AtSign) token(Symbol) FnCallExpression) | ArrayType | AsmExpression + +ArrayType : token(LBracket) option(Expression) token(RBracket) option(token(Const)) Expression + +GotoExpression: token(Goto) token(Symbol) GroupedExpression : token(LParen) Expression token(RParen) -KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) | token(Null) +KeywordLiteral : token(True) | token(False) | token(Null) | token(Break) | token(Continue) ``` ## Operator Precedence ``` -x() x[] x.y -!x -x ~x *x &x &const x +x() x[] x{} x.y +!x -x ~x *x &x as * / % + - diff --git a/doc/vim/syntax/zig.vim b/doc/vim/syntax/zig.vim index b03aaa0ec5..c7024e7910 100644 --- a/doc/vim/syntax/zig.vim +++ b/doc/vim/syntax/zig.vim @@ -15,8 +15,8 @@ syn keyword zigConditional if else switch syn keyword zigRepeat while for syn keyword zigConstant null -syn keyword zigKeyword fn unreachable use void -syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 string +syn keyword zigKeyword fn use +syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 string void unreachable syn keyword zigBoolean true false diff --git a/example/arrays/arrays.zig b/example/arrays/arrays.zig deleted file mode 100644 index 957025efdd..0000000000 --- a/example/arrays/arrays.zig +++ /dev/null @@ -1,36 +0,0 @@ -export executable "arrays"; - -use "std.zig"; - -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { - var array : [5]u32; - - var i : u32 = 0; - while (i < 5) { - array[i] = i + 1; - i = array[i]; - } - - i = 0; - var accumulator : u32 = 0; - while (i < 5) { - accumulator += array[i]; - - i += 1; - } - - if (accumulator != 15) { - print_str("BAD\n"); - } - - if (get_array_len(array) != 5) { - print_str("BAD\n"); - } - - print_str("OK\n"); - return 0; -} - -fn get_array_len(a: []u32) -> usize { - a.len -} diff --git a/example/cat/main.zig b/example/cat/main.zig new file mode 100644 index 0000000000..a411853cf4 --- /dev/null +++ b/example/cat/main.zig @@ -0,0 +1,7 @@ +export executable "cat"; + +pub main(argv: [][]u8) -> i32 { + + + return 0; +} diff --git a/example/expressions/expressions.zig b/example/expressions/expressions.zig deleted file mode 100644 index 4a1090939a..0000000000 --- a/example/expressions/expressions.zig +++ /dev/null @@ -1,52 +0,0 @@ -export executable "expressions"; - -use "std.zig"; - -fn other_exit() -> unreachable { - if (true) { exit(0); } - // the unreachable statement is the programmer assuring the compiler that this code is impossible to execute. - unreachable; -} - -export fn main(argc: isize, argv: &&u8, env: &&u8) -> unreachable { - const a : i32 = 1; - const b = 2 as i32; - // const c : i32; // not yet support for const variables - // const d; // parse error - if (a + b == 3) { - const no_conflict : i32 = 5; - if (no_conflict == 5) { print_str("OK 1\n" as string); } - } - - const c = { - const no_conflict : i32 = 10; - no_conflict - }; - if (c == 10) { print_str("OK 2\n" as string); } - - void_fun(1, void, 2); - - test_mutable_vars(); - - other_exit(); -} - -fn void_fun(a : i32, b : void, c : i32) -> void { - const x = a + 1; // i32 - const y = c + 1; // i32 - const z = b; // void - const w : void = z; // void - if (x + y == 4) { return w; } -} - -fn test_mutable_vars() { - var i : i32 = 0; -loop_start: - if i == 3 { - goto done; - } - print_str("loop\n" as string); - i = i + 1; - goto loop_start; -done: -} diff --git a/example/guess_number/main.zig b/example/guess_number/main.zig index 30fbf04a1c..1212def80e 100644 --- a/example/guess_number/main.zig +++ b/example/guess_number/main.zig @@ -3,12 +3,12 @@ export executable "guess_number"; use "std.zig"; use "rand.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { print_str("Welcome to the Guess Number Game in Zig.\n"); var seed : u32; - const err = os_get_random_bytes(&seed as &u8, #sizeof(u32)); - if (err != #sizeof(u32)) { + const err = os_get_random_bytes(&seed as (&u8), @sizeof(u32)); + if (err != @sizeof(u32)) { // TODO full error message fprint_str(stderr_fileno, "unable to get random bytes\n"); return 1; diff --git a/example/hello_world/hello.zig b/example/hello_world/hello.zig index 31954f55e6..e273f5a812 100644 --- a/example/hello_world/hello.zig +++ b/example/hello_world/hello.zig @@ -2,7 +2,7 @@ export executable "hello"; use "std.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { print_str("Hello, world!\n"); return 0; } diff --git a/example/hello_world/hello_libc.zig b/example/hello_world/hello_libc.zig index 4184c11067..b2c9101f17 100644 --- a/example/hello_world/hello_libc.zig +++ b/example/hello_world/hello_libc.zig @@ -2,11 +2,10 @@ export executable "hello"; #link("c") extern { - fn printf(__format: &const u8, ...) -> i32; - fn exit(__status: i32) -> unreachable; + fn printf(__format: &const u8, ...) i32; } -export fn main(argc: i32, argv: &&u8, env: &&u8) -> i32 { +export fn main(argc: i32, argv: &&u8, env: &&u8) i32 => { printf(c"Hello, world!\n"); return 0; } diff --git a/example/list/list.zig b/example/list/list.zig index 9159ee389f..5fc6ed6464 100644 --- a/example/list/list.zig +++ b/example/list/list.zig @@ -1,16 +1,16 @@ pub struct List#(T: type) { items: ?&T, - length: usize, - capacity: usize, + length: isize, + capacity: isize, - pub fn (l: &List) deinit() { + pub fn deinit(l: &List) { free(l.items); - l.items = None; + l.items = null; } pub fn append(l: &List, item: T) -> error { const err = l.ensure_capacity(l.length + 1); - if err != Error.None { + if err != error.None { return err; } const raw_items = l.items ?? unreachable; @@ -47,11 +47,11 @@ pub struct List#(T: type) { better_capacity *= 2; } if better_capacity != l.capacity { - const new_items = realloc(l.items, better_capacity) ?? { return Error.NoMem }; + const new_items = realloc(l.items, better_capacity) ?? { return error.NoMem }; l.items = new_items; l.capacity = better_capacity; } - Error.None + error.None } } @@ -64,3 +64,41 @@ pub fn realloc#(T: type)(ptr: ?&T, new_count: usize) -> ?&T { pub fn free#(T: type)(ptr: ?&T) { } + + +////////////////// alternate + +// previously proposed, but with : instead of -> +// `:` means "parser should expect a type now" +fn max#(T :type)(a :T, b :T) :T { + if (a > b) a else b +} + +// andy's new idea +// parameters can talk about @typeof() for previous parameters. +// using :T here is equivalent to @child_type(@typeof(T)) +fn max(T :type, a :T, b :T) :T { + if (a > b) a else b +} + +fn f() { + const x :i32 = 1234; + const y :i32 = 5678; + const z = max(@typeof(x), x, y); +} + +// So, type-generic functions don't need any fancy syntax. type-generic +// containers still do, though: + +pub struct List(T :type) { + items :?&T, + length :isize, + capacity :isize, +} + +// Types are always marked with ':' so we don't need '#' to indicate type generic parameters. + +fn f() { + var list :List(:u8); +} + diff --git a/example/maybe_type/main.zig b/example/maybe_type/main.zig index df333821bf..426b5ea95a 100644 --- a/example/maybe_type/main.zig +++ b/example/maybe_type/main.zig @@ -2,7 +2,7 @@ export executable "maybe_type"; use "std.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { const x : ?bool = true; if (const y ?= x) { @@ -25,7 +25,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { const final_x : ?i32 = 13; - const num = final_x ?? unreachable; + const num = final_x ?? unreachable{}; if (num != 13) { print_str("BAD\n"); diff --git a/example/multiple_files/foo.zig b/example/multiple_files/foo.zig index 9b4ebcf3e2..5f7484f916 100644 --- a/example/multiple_files/foo.zig +++ b/example/multiple_files/foo.zig @@ -2,10 +2,10 @@ use "std.zig"; // purposefully conflicting function with main.zig // but it's private so it should be OK -fn private_function() { +fn private_function() => { print_str("OK 1\n"); } -pub fn print_text() { +pub fn print_text() => { private_function(); } diff --git a/example/multiple_files/main.zig b/example/multiple_files/main.zig index 67421dfa9b..a29f720b8a 100644 --- a/example/multiple_files/main.zig +++ b/example/multiple_files/main.zig @@ -3,12 +3,12 @@ export executable "test-multiple-files"; use "std.zig"; use "foo.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { private_function(); print_str("OK 2\n"); return 0; } -fn private_function() { +fn private_function() => { print_text(); } diff --git a/example/structs/structs.zig b/example/structs/structs.zig deleted file mode 100644 index 7140302bc6..0000000000 --- a/example/structs/structs.zig +++ /dev/null @@ -1,87 +0,0 @@ -export executable "structs"; - -use "std.zig"; - -pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { - var foo : Foo; - - foo.a = foo.a + 1; - - foo.b = foo.a == 1; - - test_foo(foo); - - modify_foo(&foo); - - if foo.c != 100 { - print_str("BAD\n"); - } - - test_point_to_self(); - - test_byval_assign(); - - test_initializer(); - - print_str("OK\n"); - return 0; -} - -struct Foo { - a : i32, - b : bool, - c : f32, -} - -struct Node { - val: Val, - next: &Node, -} - -struct Val { - x: i32, -} - -fn test_foo(foo : Foo) { - if !foo.b { - print_str("BAD\n"); - } -} - -fn modify_foo(foo : &Foo) { - foo.c = 100; -} - -fn test_point_to_self() { - var root : Node; - root.val.x = 1; - - var node : Node; - node.next = &root; - node.val.x = 2; - - root.next = &node; - - if node.next.next.next.val.x != 1 { - print_str("BAD\n"); - } -} - -fn test_byval_assign() { - var foo1 : Foo; - var foo2 : Foo; - - foo1.a = 1234; - - if foo2.a != 0 { print_str("BAD\n"); } - - foo2 = foo1; - - if foo2.a != 1234 { print_str("BAD - byval assignment failed\n"); } - -} - -fn test_initializer() { - const val = Val { .x = 42 }; - if val.x != 42 { print_str("BAD\n"); } -} diff --git a/src/all_types.hpp b/src/all_types.hpp index 88e31d65df..a914fcb001 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -100,7 +100,6 @@ enum NodeType { NodeTypeFnDef, NodeTypeFnDecl, NodeTypeParamDecl, - NodeTypeType, NodeTypeBlock, NodeTypeExternBlock, NodeTypeDirective, @@ -111,7 +110,6 @@ enum NodeType { NodeTypeNumberLiteral, NodeTypeStringLiteral, NodeTypeCharLiteral, - NodeTypeUnreachable, NodeTypeSymbol, NodeTypePrefixOpExpr, NodeTypeFnCallExpr, @@ -119,7 +117,6 @@ enum NodeType { NodeTypeSliceExpr, NodeTypeFieldAccessExpr, NodeTypeUse, - NodeTypeVoid, NodeTypeBoolLiteral, NodeTypeNullLiteral, NodeTypeIfBoolExpr, @@ -132,10 +129,9 @@ enum NodeType { NodeTypeAsmExpr, NodeTypeStructDecl, NodeTypeStructField, - NodeTypeStructValueExpr, + NodeTypeContainerInitExpr, NodeTypeStructValueField, - NodeTypeCompilerFnExpr, - NodeTypeCompilerFnType, + NodeTypeArrayType, }; struct AstNodeRoot { @@ -185,32 +181,12 @@ struct AstNodeFnDecl { struct AstNodeParamDecl { Buf name; AstNode *type; + bool is_noalias; // populated by semantic analyzer VariableTableEntry *variable; }; -enum AstNodeTypeType { - AstNodeTypeTypePrimitive, - AstNodeTypeTypePointer, - AstNodeTypeTypeArray, - AstNodeTypeTypeMaybe, - AstNodeTypeTypeCompilerExpr, -}; - -struct AstNodeType { - AstNodeTypeType type; - Buf primitive_name; - AstNode *child_type; - AstNode *array_size; // can be null - bool is_const; - bool is_noalias; - AstNode *compiler_expr; - - // populated by semantic analyzer - TypeTableEntry *entry; -}; - struct AstNodeBlock { ZigList statements; @@ -295,6 +271,7 @@ struct AstNodeFnCallExpr { // populated by semantic analyzer: BuiltinFnEntry *builtin_fn; Expr resolved_expr; + NumLitCodeGen resolved_num_lit; }; struct AstNodeArrayAccessExpr { @@ -360,6 +337,7 @@ enum PrefixOp { PrefixOpAddressOf, PrefixOpConstAddressOf, PrefixOpDereference, + PrefixOpMaybe, }; struct AstNodePrefixOpExpr { @@ -537,45 +515,24 @@ struct AstNodeStructValueField { TypeStructField *type_struct_field; }; -struct AstNodeStructValueExpr { +enum ContainerInitKind { + ContainerInitKindStruct, + ContainerInitKindArray, +}; + +struct AstNodeContainerInitExpr { AstNode *type; - ZigList fields; + ZigList entries; + ContainerInitKind kind; - // populated by semantic analyzer - StructValExprCodeGen codegen; - Expr resolved_expr; -}; - -struct AstNodeCompilerFnExpr { - Buf name; - AstNode *expr; - - // populated by semantic analyzer - Expr resolved_expr; -}; - -struct AstNodeCompilerFnType { - Buf name; - AstNode *type; - - // populated by semantic analyzer - Expr resolved_expr; - NumLitCodeGen resolved_num_lit; -}; - -struct AstNodeNullLiteral { // populated by semantic analyzer StructValExprCodeGen resolved_struct_val_expr; Expr resolved_expr; }; -struct AstNodeVoidExpr { - // populated by semantic analyzer - Expr resolved_expr; -}; - -struct AstNodeUnreachableExpr { +struct AstNodeNullLiteral { // populated by semantic analyzer + StructValExprCodeGen resolved_struct_val_expr; Expr resolved_expr; }; @@ -603,6 +560,15 @@ struct AstNodeContinueExpr { Expr resolved_expr; }; +struct AstNodeArrayType { + AstNode *size; + AstNode *child_type; + bool is_const; + + // populated by semantic analyzer + Expr resolved_expr; +}; + struct AstNode { enum NodeType type; int line; @@ -615,7 +581,6 @@ struct AstNode { AstNodeFnDef fn_def; AstNodeFnDecl fn_decl; AstNodeFnProto fn_proto; - AstNodeType type; AstNodeParamDecl param_decl; AstNodeBlock block; AstNodeReturnExpr return_expr; @@ -641,17 +606,14 @@ struct AstNode { AstNodeStringLiteral string_literal; AstNodeCharLiteral char_literal; AstNodeNumberLiteral number_literal; - AstNodeStructValueExpr struct_val_expr; + AstNodeContainerInitExpr container_init_expr; AstNodeStructValueField struct_val_field; - AstNodeCompilerFnExpr compiler_fn_expr; - AstNodeCompilerFnType compiler_fn_type; AstNodeNullLiteral null_literal; - AstNodeVoidExpr void_expr; - AstNodeUnreachableExpr unreachable_expr; AstNodeSymbolExpr symbol_expr; AstNodeBoolLiteral bool_literal; AstNodeBreakExpr break_expr; AstNodeContinueExpr continue_expr; + AstNodeArrayType array_type; } data; }; @@ -670,7 +632,6 @@ struct AsmToken { struct TypeTableEntryPointer { TypeTableEntry *child_type; bool is_const; - bool is_noalias; }; struct TypeTableEntryInt { @@ -770,8 +731,8 @@ struct TypeTableEntry { } data; // use these fields to make sure we don't duplicate type table entries for the same type - TypeTableEntry *pointer_parent[2][2]; // 0 - const. 1 - noalias - TypeTableEntry *unknown_size_array_parent[2][2]; // 0 - const. 1 - noalias + TypeTableEntry *pointer_parent[2]; + TypeTableEntry *unknown_size_array_parent[2]; HashMap arrays_by_size; TypeTableEntry *maybe_parent; TypeTableEntry *meta_parent; @@ -830,6 +791,11 @@ enum BuiltinFnId { BuiltinFnIdArithmeticWithOverflow, BuiltinFnIdMemcpy, BuiltinFnIdMemset, + BuiltinFnIdSizeof, + BuiltinFnIdMaxValue, + BuiltinFnIdMinValue, + BuiltinFnIdValueCount, + BuiltinFnIdTypeof, }; struct BuiltinFnEntry { diff --git a/src/analyze.cpp b/src/analyze.cpp index 04090d39b6..2bb0427ddb 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -15,10 +15,10 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, TypeTableEntry *expected_type, AstNode *node); static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context, AstNode *node, AstNodeNumberLiteral *out_number_literal); -static void collect_type_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *type_node, TopLevelDecl *decl_node); static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node); static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *struct_type); +static TypeTableEntry *unwrapped_node_type(AstNode *node); static AstNode *first_executing_node(AstNode *node) { switch (node->type) { @@ -40,7 +40,6 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeFnDef: case NodeTypeFnDecl: case NodeTypeParamDecl: - case NodeTypeType: case NodeTypeBlock: case NodeTypeExternBlock: case NodeTypeDirective: @@ -49,11 +48,9 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeNumberLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: - case NodeTypeUnreachable: case NodeTypeSymbol: case NodeTypePrefixOpExpr: case NodeTypeUse: - case NodeTypeVoid: case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeIfBoolExpr: @@ -65,11 +62,10 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeAsmExpr: case NodeTypeStructDecl: case NodeTypeStructField: - case NodeTypeStructValueExpr: case NodeTypeStructValueField: case NodeTypeWhileExpr: - case NodeTypeCompilerFnExpr: - case NodeTypeCompilerFnType: + case NodeTypeContainerInitExpr: + case NodeTypeArrayType: return node; } zig_panic("unreachable"); @@ -188,8 +184,9 @@ static TypeTableEntry *get_meta_type(CodeGen *g, TypeTableEntry *child_type) { } } -TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const, bool is_noalias) { - TypeTableEntry **parent_pointer = &child_type->pointer_parent[(is_const ? 1 : 0)][(is_noalias ? 1 : 0)]; +TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) { + assert(child_type->id != TypeTableEntryIdInvalid); + TypeTableEntry **parent_pointer = &child_type->pointer_parent[(is_const ? 1 : 0)]; if (*parent_pointer) { return *parent_pointer; } else { @@ -197,9 +194,8 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool entry->type_ref = LLVMPointerType(child_type->type_ref, 0); const char *const_str = is_const ? "const " : ""; - const char *noalias_str = is_noalias ? "noalias " : ""; buf_resize(&entry->name, 0); - buf_appendf(&entry->name, "&%s%s%s", const_str, noalias_str, buf_ptr(&child_type->name)); + buf_appendf(&entry->name, "&%s%s", const_str, buf_ptr(&child_type->name)); entry->size_in_bits = g->pointer_size_bytes * 8; entry->align_in_bits = g->pointer_size_bytes * 8; @@ -208,14 +204,13 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool entry->size_in_bits, entry->align_in_bits, buf_ptr(&entry->name)); entry->data.pointer.child_type = child_type; entry->data.pointer.is_const = is_const; - entry->data.pointer.is_noalias = is_noalias; *parent_pointer = entry; return entry; } } -static TypeTableEntry *get_maybe_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *child_type) { +static TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { if (child_type->maybe_parent) { TypeTableEntry *entry = child_type->maybe_parent; return entry; @@ -293,9 +288,10 @@ static TypeTableEntry *get_array_type(CodeGen *g, ImportTableEntry *import, } static TypeTableEntry *get_unknown_size_array_type(CodeGen *g, ImportTableEntry *import, - TypeTableEntry *child_type, bool is_const, bool is_noalias) + TypeTableEntry *child_type, bool is_const) { - TypeTableEntry **parent_pointer = &child_type->unknown_size_array_parent[(is_const ? 1 : 0)][(is_noalias ? 1 : 0)]; + assert(child_type->id != TypeTableEntryIdInvalid); + TypeTableEntry **parent_pointer = &child_type->unknown_size_array_parent[(is_const ? 1 : 0)]; if (*parent_pointer) { return *parent_pointer; } else { @@ -305,7 +301,7 @@ static TypeTableEntry *get_unknown_size_array_type(CodeGen *g, ImportTableEntry buf_appendf(&entry->name, "[]%s", buf_ptr(&child_type->name)); entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&entry->name)); - TypeTableEntry *pointer_type = get_pointer_to_type(g, child_type, is_const, is_noalias); + TypeTableEntry *pointer_type = get_pointer_to_type(g, child_type, is_const); unsigned element_count = 2; LLVMTypeRef element_types[] = { @@ -343,6 +339,25 @@ static TypeTableEntry *get_unknown_size_array_type(CodeGen *g, ImportTableEntry } } +// like analyze expression, but expects a type. creates an error if resulting type is +// not a meta type. unwraps it if it is. +static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, + AstNode *node) +{ + TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, node); + if (type_entry->id == TypeTableEntryIdInvalid) { + return type_entry; + } else if (type_entry->id == TypeTableEntryIdMetaType) { + return type_entry->data.meta_type.child_type; + } else { + add_node_error(g, first_executing_node(node), + buf_sprintf("expected type, found expression")); + get_resolved_expr(node)->type_entry = g->builtin_types.entry_invalid; + return g->builtin_types.entry_invalid; + } +} + + static TypeTableEntry *eval_const_expr_bin_op(CodeGen *g, BlockContext *context, AstNode *node, AstNodeNumberLiteral *out_number_literal) { @@ -436,6 +451,40 @@ static TypeTableEntry *eval_const_expr_bin_op(CodeGen *g, BlockContext *context, zig_unreachable(); } +static TypeTableEntry *eval_const_expr_fn_call(CodeGen *g, BlockContext *context, + AstNode *node, AstNodeNumberLiteral *out_number_literal) +{ + if (!node->data.fn_call_expr.is_builtin) { + return g->builtin_types.entry_invalid; + } + + switch (node->data.fn_call_expr.builtin_fn->id) { + case BuiltinFnIdInvalid: + zig_unreachable(); + case BuiltinFnIdArithmeticWithOverflow: + case BuiltinFnIdMemcpy: + case BuiltinFnIdMemset: + return g->builtin_types.entry_invalid; + case BuiltinFnIdSizeof: + { + AstNode *type_node = node->data.fn_call_expr.params.at(0); + TypeTableEntry *target_type = unwrapped_node_type(type_node); + out_number_literal->overflow = false; + out_number_literal->data.x_uint = target_type->size_in_bits / 8; + out_number_literal->kind = get_number_literal_kind_unsigned(out_number_literal->data.x_uint); + return get_resolved_expr(node)->type_entry; + } + case BuiltinFnIdMaxValue: + case BuiltinFnIdMinValue: + zig_panic("TODO eval_const_expr_fn_call max/min value"); + case BuiltinFnIdValueCount: + zig_panic("TODO eval_const_expr_fn_call value_count"); + case BuiltinFnIdTypeof: + return get_resolved_expr(node)->type_entry; + } + zig_unreachable(); +} + static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context, AstNode *node, AstNodeNumberLiteral *out_number_literal) { @@ -450,177 +499,27 @@ static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context, return get_resolved_expr(node)->type_entry; case NodeTypeBinOpExpr: return eval_const_expr_bin_op(g, context, node, out_number_literal); - case NodeTypeCompilerFnType: - { - Buf *name = &node->data.compiler_fn_type.name; - TypeTableEntry *expr_type = get_resolved_expr(node)->type_entry; - if (buf_eql_str(name, "sizeof")) { - TypeTableEntry *target_type = node->data.compiler_fn_type.type->data.type.entry; - out_number_literal->overflow = false; - out_number_literal->data.x_uint = target_type->size_in_bits / 8; - out_number_literal->kind = get_number_literal_kind_unsigned(out_number_literal->data.x_uint); - - return expr_type; - } else if (buf_eql_str(name, "max_value")) { - zig_panic("TODO eval_const_expr max_value"); - } else if (buf_eql_str(name, "min_value")) { - zig_panic("TODO eval_const_expr min_value"); - } else if (buf_eql_str(name, "value_count")) { - zig_panic("TODO eval_const_expr value_count"); - } else { - return g->builtin_types.entry_invalid; - } - break; - } case NodeTypeSymbol: { VariableTableEntry *var = find_variable(context, &node->data.symbol_expr.symbol); assert(var); AstNode *decl_node = var->decl_node; AstNode *expr_node = decl_node->data.variable_declaration.expr; - BlockContext *next_context = get_resolved_expr(expr_node)->block_context; - return eval_const_expr(g, next_context, expr_node, out_number_literal); + if (expr_node) { + BlockContext *next_context = get_resolved_expr(expr_node)->block_context; + return eval_const_expr(g, next_context, expr_node, out_number_literal); + } else { + // can't eval it + return g->builtin_types.entry_invalid; + } } + case NodeTypeFnCallExpr: + return eval_const_expr_fn_call(g, context, node, out_number_literal); default: return g->builtin_types.entry_invalid; } } -static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node, ImportTableEntry *import, - BlockContext *context, bool noalias_allowed) -{ - assert(node->type == NodeTypeType); - switch (node->data.type.type) { - case AstNodeTypeTypePrimitive: - { - Buf *name = &node->data.type.primitive_name; - auto table_entry = import->block_context->type_table.maybe_get(name); - if (!table_entry) { - table_entry = g->primitive_type_table.maybe_get(name); - } - if (table_entry) { - node->data.type.entry = table_entry->value; - } else { - add_node_error(g, node, - buf_sprintf("invalid type name: '%s'", buf_ptr(name))); - node->data.type.entry = g->builtin_types.entry_invalid; - } - return node->data.type.entry; - } - case AstNodeTypeTypePointer: - { - bool use_noalias = false; - if (node->data.type.is_noalias) { - if (!noalias_allowed) { - add_node_error(g, node, - buf_create_from_str("invalid noalias qualifier")); - } else { - use_noalias = true; - } - } - - resolve_type(g, node->data.type.child_type, import, context, false); - TypeTableEntry *child_type = node->data.type.child_type->data.type.entry; - assert(child_type); - if (child_type->id == TypeTableEntryIdUnreachable) { - add_node_error(g, node, - buf_create_from_str("pointer to unreachable not allowed")); - node->data.type.entry = g->builtin_types.entry_invalid; - return node->data.type.entry; - } else if (child_type->id == TypeTableEntryIdInvalid) { - node->data.type.entry = child_type; - return child_type; - } else { - node->data.type.entry = get_pointer_to_type(g, child_type, node->data.type.is_const, use_noalias); - return node->data.type.entry; - } - } - case AstNodeTypeTypeArray: - { - AstNode *size_node = node->data.type.array_size; - - bool use_noalias = false; - if (node->data.type.is_noalias) { - if (!noalias_allowed || size_node) { - add_node_error(g, node, - buf_create_from_str("invalid noalias qualifier")); - } else { - use_noalias = true; - } - } - - TypeTableEntry *child_type = resolve_type(g, node->data.type.child_type, import, context, false); - if (child_type->id == TypeTableEntryIdUnreachable) { - add_node_error(g, node, - buf_create_from_str("array of unreachable not allowed")); - node->data.type.entry = g->builtin_types.entry_invalid; - return node->data.type.entry; - } - - if (size_node) { - TypeTableEntry *size_type = analyze_expression(g, import, context, - g->builtin_types.entry_usize, size_node); - if (size_type->id == TypeTableEntryIdInvalid) { - node->data.type.entry = g->builtin_types.entry_invalid; - return node->data.type.entry; - } - - AstNodeNumberLiteral number_literal; - TypeTableEntry *resolved_type = eval_const_expr(g, context, size_node, &number_literal); - - if (resolved_type->id == TypeTableEntryIdInt) { - if (resolved_type->data.integral.is_signed) { - add_node_error(g, size_node, - buf_create_from_str("array size must be unsigned integer")); - node->data.type.entry = g->builtin_types.entry_invalid; - } else { - node->data.type.entry = get_array_type(g, import, child_type, number_literal.data.x_uint); - } - } else { - add_node_error(g, size_node, - buf_create_from_str("unable to resolve constant expression")); - node->data.type.entry = g->builtin_types.entry_invalid; - } - return node->data.type.entry; - } else { - node->data.type.entry = get_unknown_size_array_type(g, import, child_type, - node->data.type.is_const, use_noalias); - return node->data.type.entry; - } - - } - case AstNodeTypeTypeMaybe: - { - resolve_type(g, node->data.type.child_type, import, context, false); - TypeTableEntry *child_type = node->data.type.child_type->data.type.entry; - assert(child_type); - if (child_type->id == TypeTableEntryIdUnreachable) { - add_node_error(g, node, - buf_create_from_str("maybe unreachable type not allowed")); - } else if (child_type->id == TypeTableEntryIdInvalid) { - return child_type; - } - node->data.type.entry = get_maybe_type(g, import, child_type); - return node->data.type.entry; - } - case AstNodeTypeTypeCompilerExpr: - { - AstNode *compiler_expr_node = node->data.type.compiler_expr; - Buf *fn_name = &compiler_expr_node->data.compiler_fn_expr.name; - if (buf_eql_str(fn_name, "typeof")) { - node->data.type.entry = analyze_expression(g, import, context, nullptr, - compiler_expr_node->data.compiler_fn_expr.expr); - } else { - add_node_error(g, node, - buf_sprintf("invalid compiler function: '%s'", buf_ptr(fn_name))); - node->data.type.entry = g->builtin_types.entry_invalid; - } - return node->data.type.entry; - } - } - zig_unreachable(); -} - static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry, ImportTableEntry *import) { @@ -654,8 +553,9 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t for (int i = 0; i < node->data.fn_proto.params.length; i += 1) { AstNode *child = node->data.fn_proto.params.at(i); assert(child->type == NodeTypeParamDecl); - TypeTableEntry *type_entry = resolve_type(g, child->data.param_decl.type, - import, import->block_context, true); + TypeTableEntry *type_entry = analyze_type_expr(g, import, import->block_context, + child->data.param_decl.type); + if (type_entry->id == TypeTableEntryIdUnreachable) { add_node_error(g, child->data.param_decl.type, buf_sprintf("parameter of type 'unreachable' not allowed")); @@ -667,7 +567,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t } } - resolve_type(g, node->data.fn_proto.return_type, import, import->block_context, true); + analyze_type_expr(g, import, import->block_context, node->data.fn_proto.return_type); } static void preview_function_labels(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry) { @@ -729,8 +629,8 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt AstNode *field_node = decl_node->data.struct_decl.fields.at(i); TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; type_enum_field->name = &field_node->data.struct_field.name; - type_enum_field->type_entry = resolve_type(g, field_node->data.struct_field.type, - import, import->block_context, false); + type_enum_field->type_entry = analyze_type_expr(g, import, import->block_context, + field_node->data.struct_field.type); type_enum_field->value = i; di_enumerators[i] = LLVMZigCreateDebugEnumerator(g->dbuilder, buf_ptr(type_enum_field->name), i); @@ -815,7 +715,7 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt biggest_align_in_bits, tag_type_entry->size_in_bits, 0, union_di_type); - // create debug type for root struct + // create debug type for root struct LLVMZigDIType *di_root_members[] = { tag_member_di_type, union_member_di_type, @@ -893,8 +793,8 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE AstNode *field_node = decl_node->data.struct_decl.fields.at(i); TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; type_struct_field->name = &field_node->data.struct_field.name; - type_struct_field->type_entry = resolve_type(g, field_node->data.struct_field.type, - import, import->block_context, false); + type_struct_field->type_entry = analyze_type_expr(g, import, import->block_context, + field_node->data.struct_field.type); type_struct_field->src_index = i; type_struct_field->gen_index = -1; @@ -1144,7 +1044,6 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeFnDef: case NodeTypeDirective: case NodeTypeParamDecl: - case NodeTypeType: case NodeTypeFnDecl: case NodeTypeReturnExpr: case NodeTypeRoot: @@ -1156,8 +1055,6 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeNumberLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: - case NodeTypeUnreachable: - case NodeTypeVoid: case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeSymbol: @@ -1173,10 +1070,9 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: case NodeTypeStructField: - case NodeTypeStructValueExpr: case NodeTypeStructValueField: - case NodeTypeCompilerFnExpr: - case NodeTypeCompilerFnType: + case NodeTypeContainerInitExpr: + case NodeTypeArrayType: zig_unreachable(); } } @@ -1186,13 +1082,22 @@ static FnTableEntry *get_context_fn_entry(BlockContext *context) { return context->fn_entry; } +static TypeTableEntry *unwrapped_node_type(AstNode *node) { + TypeTableEntry *meta_type_entry = get_resolved_expr(node)->type_entry; + if (meta_type_entry->id == TypeTableEntryIdInvalid) { + return meta_type_entry; + } else { + assert(meta_type_entry->id == TypeTableEntryIdMetaType); + return meta_type_entry->data.meta_type.child_type; + } +} + static TypeTableEntry *get_return_type(BlockContext *context) { FnTableEntry *fn_entry = get_context_fn_entry(context); AstNode *fn_proto_node = fn_entry->proto_node; assert(fn_proto_node->type == NodeTypeFnProto); AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; - assert(return_type_node->type == NodeTypeType); - return return_type_node->data.type.entry; + return unwrapped_node_type(return_type_node); } static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type, TypeTableEntry *other_type) { @@ -1553,6 +1458,114 @@ static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *imp return enum_type; } +static TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name, int *index) { + assert(type_entry->id == TypeTableEntryIdStruct); + for (int i = 0; i < type_entry->data.structure.field_count; i += 1) { + TypeStructField *field = &type_entry->data.structure.fields[i]; + if (buf_eql_buf(field->name, name)) { + *index = i; + return field; + } + } + return nullptr; +} + +static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, + AstNode *node) +{ + assert(node->type == NodeTypeContainerInitExpr); + + AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr; + + ContainerInitKind kind = container_init_expr->kind; + + TypeTableEntry *container_type = analyze_type_expr(g, import, context, container_init_expr->type); + + if (container_type->id == TypeTableEntryIdInvalid) { + return container_type; + } else if (container_type->id == TypeTableEntryIdStruct) { + switch (kind) { + case ContainerInitKindStruct: + { + StructValExprCodeGen *codegen = &container_init_expr->resolved_struct_val_expr; + codegen->type_entry = container_type; + codegen->source_node = node; + context->struct_val_expr_alloca_list.append(codegen); + + + int expr_field_count = container_init_expr->entries.length; + int actual_field_count = container_type->data.structure.field_count; + + int *field_use_counts = allocate(actual_field_count); + for (int i = 0; i < expr_field_count; i += 1) { + AstNode *val_field_node = container_init_expr->entries.at(i); + assert(val_field_node->type == NodeTypeStructValueField); + + int field_index; + TypeStructField *type_field = find_struct_type_field(container_type, + &val_field_node->data.struct_val_field.name, &field_index); + + if (!type_field) { + add_node_error(g, val_field_node, + buf_sprintf("no member named '%s' in '%s'", + buf_ptr(&val_field_node->data.struct_val_field.name), buf_ptr(&container_type->name))); + continue; + } + + field_use_counts[field_index] += 1; + if (field_use_counts[field_index] > 1) { + add_node_error(g, val_field_node, buf_sprintf("duplicate field")); + continue; + } + + val_field_node->data.struct_val_field.type_struct_field = type_field; + + analyze_expression(g, import, context, type_field->type_entry, + val_field_node->data.struct_val_field.expr); + } + + for (int i = 0; i < actual_field_count; i += 1) { + if (field_use_counts[i] == 0) { + add_node_error(g, node, + buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i].name))); + } + } + break; + } + case ContainerInitKindArray: + add_node_error(g, node, + buf_sprintf("struct '%s' does not support array initialization syntax", + buf_ptr(&container_type->name))); + break; + } + return container_type; + } else if (container_type->id == TypeTableEntryIdArray) { + zig_panic("TODO array container init"); + return container_type; + } else if (container_type->id == TypeTableEntryIdEnum) { + zig_panic("TODO enum container init"); + return container_type; + } else if (container_type->id == TypeTableEntryIdVoid) { + if (container_init_expr->entries.length != 0) { + add_node_error(g, node, buf_sprintf("void expression expects no arguments")); + return g->builtin_types.entry_invalid; + } else { + return container_type; + } + } else if (container_type->id == TypeTableEntryIdUnreachable) { + if (container_init_expr->entries.length != 0) { + add_node_error(g, node, buf_sprintf("unreachable expression expects no arguments")); + return g->builtin_types.entry_invalid; + } else { + return container_type; + } + } else { + add_node_error(g, node, + buf_sprintf("type '%s' does not support initialization syntax", buf_ptr(&container_type->name))); + return g->builtin_types.entry_invalid; + } +} + static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { @@ -1585,7 +1598,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i return_type = g->builtin_types.entry_usize; } else if (buf_eql_str(name, "ptr")) { // TODO determine whether the pointer should be const - return_type = get_pointer_to_type(g, struct_type->data.array.child_type, false, false); + return_type = get_pointer_to_type(g, struct_type->data.array.child_type, false); } else { add_node_error(g, node, buf_sprintf("no member named '%s' in '%s'", buf_ptr(name), @@ -1623,16 +1636,16 @@ static TypeTableEntry *analyze_slice_expr(CodeGen *g, ImportTableEntry *import, return_type = g->builtin_types.entry_invalid; } else if (array_type->id == TypeTableEntryIdArray) { return_type = get_unknown_size_array_type(g, import, array_type->data.array.child_type, - node->data.slice_expr.is_const, false); + node->data.slice_expr.is_const); } else if (array_type->id == TypeTableEntryIdPointer) { return_type = get_unknown_size_array_type(g, import, array_type->data.pointer.child_type, - node->data.slice_expr.is_const, false); + node->data.slice_expr.is_const); } else if (array_type->id == TypeTableEntryIdStruct && array_type->data.structure.is_unknown_size_array) { return_type = get_unknown_size_array_type(g, import, array_type->data.structure.fields[0].type_entry->data.pointer.child_type, - node->data.slice_expr.is_const, false); + node->data.slice_expr.is_const); } else { add_node_error(g, node, buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name))); @@ -1687,19 +1700,24 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, TypeTableEntry *expected_type, AstNode *node) { Buf *variable_name = &node->data.symbol_expr.symbol; + + auto primitive_table_entry = g->primitive_type_table.maybe_get(variable_name); + if (primitive_table_entry) { + return get_meta_type(g, primitive_table_entry->value); + } + VariableTableEntry *var = find_variable(context, variable_name); if (var) { return var->type; - } else { - TypeTableEntry *container_type = find_container(context, variable_name); - if (container_type) { - return get_meta_type(g, container_type); - } else { - add_node_error(g, node, - buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name))); - return g->builtin_types.entry_invalid; - } } + + TypeTableEntry *container_type = find_container(context, variable_name); + if (container_type) { + return get_meta_type(g, container_type); + } + + add_node_error(g, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name))); + return g->builtin_types.entry_invalid; } static TypeTableEntry *analyze_variable_name(CodeGen *g, ImportTableEntry *import, BlockContext *context, @@ -1768,7 +1786,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B { assert(node->type == NodeTypeCastExpr); - TypeTableEntry *wanted_type = resolve_type(g, node->data.cast_expr.type, import, context, false); + TypeTableEntry *wanted_type = analyze_type_expr(g, import, context, node->data.cast_expr.type); TypeTableEntry *actual_type = analyze_expression(g, import, context, nullptr, node->data.cast_expr.expr); if (wanted_type->id == TypeTableEntryIdInvalid || @@ -1833,23 +1851,22 @@ static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, Bloc TypeTableEntry *expected_rhs_type = nullptr; if (lhs_node->type == NodeTypeSymbol) { Buf *name = &lhs_node->data.symbol_expr.symbol; - VariableTableEntry *var = find_variable(block_context, name); - if (var) { - if (purpose == LValPurposeAssign && var->is_const) { - add_node_error(g, lhs_node, - buf_sprintf("cannot assign to constant")); - expected_rhs_type = g->builtin_types.entry_invalid; - } else if (purpose == LValPurposeAddressOf && var->is_const && !is_ptr_const) { - add_node_error(g, lhs_node, - buf_sprintf("must use &const to get address of constant")); - expected_rhs_type = g->builtin_types.entry_invalid; - } else { - expected_rhs_type = var->type; - } + if (purpose == LValPurposeAddressOf) { + expected_rhs_type = analyze_symbol_expr(g, import, block_context, nullptr, lhs_node); } else { - add_node_error(g, lhs_node, - buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name))); - expected_rhs_type = g->builtin_types.entry_invalid; + VariableTableEntry *var = find_variable(block_context, name); + if (var) { + if (var->is_const) { + add_node_error(g, lhs_node, buf_sprintf("cannot assign to constant")); + expected_rhs_type = g->builtin_types.entry_invalid; + } else { + expected_rhs_type = var->type; + } + } else { + add_node_error(g, lhs_node, + buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name))); + expected_rhs_type = g->builtin_types.entry_invalid; + } } } else if (lhs_node->type == NodeTypeArrayAccessExpr) { expected_rhs_type = analyze_array_access_expr(g, import, block_context, lhs_node); @@ -1873,13 +1890,19 @@ static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, Bloc } } else { if (purpose == LValPurposeAssign) { - add_node_error(g, lhs_node, - buf_sprintf("invalid assignment target")); + add_node_error(g, lhs_node, buf_sprintf("invalid assignment target")); + expected_rhs_type = g->builtin_types.entry_invalid; } else if (purpose == LValPurposeAddressOf) { - add_node_error(g, lhs_node, - buf_sprintf("invalid addressof target")); + TypeTableEntry *type_entry = analyze_expression(g, import, block_context, nullptr, lhs_node); + if (type_entry->id == TypeTableEntryIdInvalid) { + expected_rhs_type = g->builtin_types.entry_invalid; + } else if (type_entry->id == TypeTableEntryIdMetaType) { + expected_rhs_type = type_entry; + } else { + add_node_error(g, lhs_node, buf_sprintf("invalid addressof target")); + expected_rhs_type = g->builtin_types.entry_invalid; + } } - expected_rhs_type = g->builtin_types.entry_invalid; } assert(expected_rhs_type); return expected_rhs_type; @@ -1988,7 +2011,7 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa { TypeTableEntry *explicit_type = nullptr; if (variable_declaration->type != nullptr) { - explicit_type = resolve_type(g, variable_declaration->type, import, context, false); + explicit_type = analyze_type_expr(g, import, context, variable_declaration->type); if (explicit_type->id == TypeTableEntryIdUnreachable) { add_node_error(g, variable_declaration->type, buf_sprintf("variable of type 'unreachable' not allowed")); @@ -2109,78 +2132,46 @@ static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry } } -static TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name, int *index) { - assert(type_entry->id == TypeTableEntryIdStruct); - for (int i = 0; i < type_entry->data.structure.field_count; i += 1) { - TypeStructField *field = &type_entry->data.structure.fields[i]; - if (buf_eql_buf(field->name, name)) { - *index = i; - return field; - } - } - return nullptr; -} - -static TypeTableEntry *analyze_struct_val_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, +static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { - assert(node->type == NodeTypeStructValueExpr); + AstNode *size_node = node->data.array_type.size; - AstNodeStructValueExpr *struct_val_expr = &node->data.struct_val_expr; + TypeTableEntry *child_type = analyze_type_expr(g, import, context, node->data.array_type.child_type); - TypeTableEntry *type_entry = resolve_type(g, struct_val_expr->type, import, context, false); - - if (type_entry->id == TypeTableEntryIdInvalid) { + if (child_type->id == TypeTableEntryIdUnreachable) { + add_node_error(g, node, buf_create_from_str("array of unreachable not allowed")); return g->builtin_types.entry_invalid; - } else if (type_entry->id != TypeTableEntryIdStruct) { - add_node_error(g, node, - buf_sprintf("type '%s' is not a struct", buf_ptr(&type_entry->name))); + } else if (child_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } - node->data.struct_val_expr.codegen.type_entry = type_entry; - node->data.struct_val_expr.codegen.source_node = node; - context->struct_val_expr_alloca_list.append(&node->data.struct_val_expr.codegen); - - int expr_field_count = struct_val_expr->fields.length; - int actual_field_count = type_entry->data.structure.field_count; - - int *field_use_counts = allocate(actual_field_count); - for (int i = 0; i < expr_field_count; i += 1) { - AstNode *val_field_node = struct_val_expr->fields.at(i); - assert(val_field_node->type == NodeTypeStructValueField); - - int field_index; - TypeStructField *type_field = find_struct_type_field(type_entry, - &val_field_node->data.struct_val_field.name, &field_index); - - if (!type_field) { - add_node_error(g, val_field_node, - buf_sprintf("no member named '%s' in '%s'", - buf_ptr(&val_field_node->data.struct_val_field.name), buf_ptr(&type_entry->name))); - continue; + if (size_node) { + TypeTableEntry *size_type = analyze_expression(g, import, context, + g->builtin_types.entry_usize, size_node); + if (size_type->id == TypeTableEntryIdInvalid) { + return g->builtin_types.entry_invalid; } - field_use_counts[field_index] += 1; - if (field_use_counts[field_index] > 1) { - add_node_error(g, val_field_node, buf_sprintf("duplicate field")); - continue; + AstNodeNumberLiteral number_literal; + TypeTableEntry *resolved_type = eval_const_expr(g, context, size_node, &number_literal); + + if (resolved_type->id == TypeTableEntryIdInt) { + if (resolved_type->data.integral.is_signed) { + add_node_error(g, size_node, + buf_create_from_str("array size must be unsigned integer")); + return g->builtin_types.entry_invalid; + } else { + return get_meta_type(g, get_array_type(g, import, child_type, number_literal.data.x_uint)); + } + } else { + add_node_error(g, size_node, + buf_create_from_str("unable to resolve constant expression")); + return g->builtin_types.entry_invalid; } - - val_field_node->data.struct_val_field.type_struct_field = type_field; - - analyze_expression(g, import, context, type_field->type_entry, - val_field_node->data.struct_val_field.expr); + } else { + return get_meta_type(g, get_unknown_size_array_type(g, import, child_type, node->data.array_type.is_const)); } - - for (int i = 0; i < actual_field_count; i += 1) { - if (field_use_counts[i] == 0) { - add_node_error(g, node, - buf_sprintf("missing field: '%s'", buf_ptr(type_entry->data.structure.fields[i].name))); - } - } - - return type_entry; } static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, @@ -2293,9 +2284,14 @@ static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, node->data.if_var_expr.then_block, node->data.if_var_expr.else_node, node); } -static TypeTableEntry *analyze_min_max_value(CodeGen *g, AstNode *node, TypeTableEntry *type_entry, - const char *err_format) +static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *import, BlockContext *context, + AstNode *node, const char *err_format) { + assert(node->type == NodeTypeFnCallExpr); + assert(node->data.fn_call_expr.params.length == 1); + + AstNode *type_node = node->data.fn_call_expr.params.at(0); + TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node); if (type_entry->id == TypeTableEntryIdInt || type_entry->id == TypeTableEntryIdFloat || type_entry->id == TypeTableEntryIdBool || @@ -2309,54 +2305,6 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, AstNode *node, TypeTabl } } -static TypeTableEntry *analyze_compiler_fn_type(CodeGen *g, ImportTableEntry *import, BlockContext *context, - TypeTableEntry *expected_type, AstNode *node) -{ - assert(node->type == NodeTypeCompilerFnType); - - Buf *name = &node->data.compiler_fn_type.name; - TypeTableEntry *type_entry = resolve_type(g, node->data.compiler_fn_type.type, import, context, false); - - if (buf_eql_str(name, "sizeof")) { - if (type_entry->id == TypeTableEntryIdInvalid) { - return type_entry; - } else if (type_entry->id == TypeTableEntryIdUnreachable) { - add_node_error(g, node, - buf_sprintf("no size available for type '%s'", buf_ptr(&type_entry->name))); - return g->builtin_types.entry_invalid; - } else { - uint64_t size_in_bytes = type_entry->size_in_bits / 8; - - TypeTableEntry *num_lit_type = get_number_literal_type_unsigned(g, size_in_bytes); - TypeTableEntry *resolved_type = resolve_rhs_number_literal(g, nullptr, expected_type, node, num_lit_type); - return resolved_type ? resolved_type : num_lit_type; - } - } else if (buf_eql_str(name, "min_value")) { - return analyze_min_max_value(g, node, type_entry, "no min value available for type '%s'"); - } else if (buf_eql_str(name, "max_value")) { - return analyze_min_max_value(g, node, type_entry, "no max value available for type '%s'"); - } else if (buf_eql_str(name, "value_count")) { - if (type_entry->id == TypeTableEntryIdInvalid) { - return type_entry; - } else if (type_entry->id == TypeTableEntryIdEnum) { - uint64_t value_count = type_entry->data.enumeration.field_count; - - TypeTableEntry *num_lit_type = get_number_literal_type_unsigned(g, value_count); - TypeTableEntry *resolved_type = resolve_rhs_number_literal(g, nullptr, expected_type, node, num_lit_type); - return resolved_type ? resolved_type : num_lit_type; - - } else { - add_node_error(g, node, - buf_sprintf("no value count available for type '%s'", buf_ptr(&type_entry->name))); - return g->builtin_types.entry_invalid; - } - } else { - add_node_error(g, node, - buf_sprintf("invalid compiler function: '%s'", buf_ptr(name))); - return g->builtin_types.entry_invalid; - } -} - static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { @@ -2445,6 +2393,62 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry return builtin_fn->return_type; } + case BuiltinFnIdSizeof: + { + AstNode *type_node = node->data.fn_call_expr.params.at(0); + TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node); + if (type_entry->id == TypeTableEntryIdInvalid) { + return g->builtin_types.entry_invalid; + } else if (type_entry->id == TypeTableEntryIdUnreachable) { + add_node_error(g, first_executing_node(type_node), + buf_sprintf("no size available for type '%s'", buf_ptr(&type_entry->name))); + return g->builtin_types.entry_invalid; + } else { + uint64_t size_in_bytes = type_entry->size_in_bits / 8; + + TypeTableEntry *num_lit_type = get_number_literal_type_unsigned(g, size_in_bytes); + TypeTableEntry *resolved_type = resolve_rhs_number_literal(g, nullptr, expected_type, node, num_lit_type); + return resolved_type ? resolved_type : num_lit_type; + } + } + case BuiltinFnIdMaxValue: + return analyze_min_max_value(g, import, context, node, "no max value available for type '%s'"); + case BuiltinFnIdMinValue: + return analyze_min_max_value(g, import, context, node, "no min value available for type '%s'"); + case BuiltinFnIdValueCount: + { + AstNode *type_node = node->data.fn_call_expr.params.at(0); + TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node); + + if (type_entry->id == TypeTableEntryIdInvalid) { + return type_entry; + } else if (type_entry->id == TypeTableEntryIdEnum) { + uint64_t value_count = type_entry->data.enumeration.field_count; + + TypeTableEntry *num_lit_type = get_number_literal_type_unsigned(g, value_count); + TypeTableEntry *resolved_type = resolve_rhs_number_literal(g, nullptr, expected_type, node, num_lit_type); + return resolved_type ? resolved_type : num_lit_type; + + } else { + add_node_error(g, node, + buf_sprintf("no value count available for type '%s'", buf_ptr(&type_entry->name))); + return g->builtin_types.entry_invalid; + } + } + case BuiltinFnIdTypeof: + { + AstNode *expr_node = node->data.fn_call_expr.params.at(0); + TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node); + + if (type_entry->id == TypeTableEntryIdInvalid) { + return g->builtin_types.entry_invalid; + } else if (type_entry->id == TypeTableEntryIdMetaType) { + add_node_error(g, node, buf_sprintf("expected expression, got type")); + } else { + return get_meta_type(g, type_entry); + } + } + } zig_unreachable(); } else { @@ -2560,15 +2564,127 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import AstNode *param_decl_node = fn_proto->params.at(fn_proto_i); assert(param_decl_node->type == NodeTypeParamDecl); AstNode *param_type_node = param_decl_node->data.param_decl.type; - assert(param_type_node->type == NodeTypeType); - if (param_type_node->data.type.entry) { - expected_param_type = param_type_node->data.type.entry; + TypeTableEntry *param_type_entry = get_resolved_expr(param_type_node)->type_entry; + if (param_type_entry) { + expected_param_type = unwrapped_node_type(param_type_node); } } analyze_expression(g, import, context, expected_param_type, child); } - return fn_proto->return_type->data.type.entry; + return unwrapped_node_type(fn_proto->return_type); + } +} + +static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, + TypeTableEntry *expected_type, AstNode *node) +{ + switch (node->data.prefix_op_expr.prefix_op) { + case PrefixOpInvalid: + zig_unreachable(); + case PrefixOpBoolNot: + analyze_expression(g, import, context, g->builtin_types.entry_bool, + node->data.prefix_op_expr.primary_expr); + return g->builtin_types.entry_bool; + case PrefixOpBinNot: + { + AstNode *operand_node = node->data.prefix_op_expr.primary_expr; + TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type, + operand_node); + if (expr_type->id == TypeTableEntryIdInvalid) { + return expr_type; + } else if (expr_type->id == TypeTableEntryIdInt || + (expr_type->id == TypeTableEntryIdNumberLiteral && + !is_num_lit_float(expr_type->data.num_lit.kind))) + { + return expr_type; + } else { + add_node_error(g, operand_node, buf_sprintf("invalid binary not type: '%s'", + buf_ptr(&expr_type->name))); + return g->builtin_types.entry_invalid; + } + } + case PrefixOpNegation: + { + AstNode *operand_node = node->data.prefix_op_expr.primary_expr; + TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type, + operand_node); + if (expr_type->id == TypeTableEntryIdInvalid) { + return expr_type; + } else if (expr_type->id == TypeTableEntryIdInt && + expr_type->data.integral.is_signed) + { + return expr_type; + } else if (expr_type->id == TypeTableEntryIdFloat) { + return expr_type; + } else if (expr_type->id == TypeTableEntryIdNumberLiteral) { + return expr_type; + } else { + add_node_error(g, operand_node, buf_sprintf("invalid negation type: '%s'", + buf_ptr(&expr_type->name))); + return g->builtin_types.entry_invalid; + } + } + case PrefixOpAddressOf: + case PrefixOpConstAddressOf: + { + bool is_const = (node->data.prefix_op_expr.prefix_op == PrefixOpConstAddressOf); + + TypeTableEntry *child_type = analyze_lvalue(g, import, context, + node->data.prefix_op_expr.primary_expr, LValPurposeAddressOf, is_const); + + if (child_type->id == TypeTableEntryIdInvalid) { + return g->builtin_types.entry_invalid; + } else if (child_type->id == TypeTableEntryIdMetaType) { + TypeTableEntry *meta_child_type = child_type->data.meta_type.child_type; + if (meta_child_type->id == TypeTableEntryIdUnreachable) { + add_node_error(g, node, + buf_create_from_str("pointer to unreachable not allowed")); + } else { + return get_meta_type(g, get_pointer_to_type(g, meta_child_type, is_const)); + } + } else { + return get_pointer_to_type(g, child_type, is_const); + } + } + case PrefixOpDereference: + { + TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, + node->data.prefix_op_expr.primary_expr); + if (type_entry->id == TypeTableEntryIdInvalid) { + return type_entry; + } else if (type_entry->id == TypeTableEntryIdPointer) { + return type_entry->data.pointer.child_type; + } else { + add_node_error(g, node->data.prefix_op_expr.primary_expr, + buf_sprintf("indirection requires pointer operand ('%s' invalid)", + buf_ptr(&type_entry->name))); + return g->builtin_types.entry_invalid; + } + } + case PrefixOpMaybe: + { + TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, + node->data.prefix_op_expr.primary_expr); + + if (type_entry->id == TypeTableEntryIdInvalid) { + return type_entry; + } else if (type_entry->id == TypeTableEntryIdMetaType) { + TypeTableEntry *child_type = type_entry->data.meta_type.child_type; + if (child_type->id == TypeTableEntryIdUnreachable) { + add_node_error(g, node, buf_create_from_str("maybe unreachable type not allowed")); + return g->builtin_types.entry_invalid; + } else { + return get_meta_type(g, get_maybe_type(g, child_type)); + } + } else if (type_entry->id == TypeTableEntryIdUnreachable) { + add_node_error(g, node->data.prefix_op_expr.primary_expr, + buf_sprintf("unable to wrap unreachable in maybe type")); + return g->builtin_types.entry_invalid; + } else { + return get_maybe_type(g, type_entry); + } + } } } @@ -2593,9 +2709,10 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, continue; } if (return_type->id == TypeTableEntryIdUnreachable) { - if (child->type == NodeTypeVoid) { + if (is_node_void_expr(child)) { // {unreachable;void;void} is allowed. // ignore void statements once we enter unreachable land. + analyze_expression(g, import, context, g->builtin_types.entry_void, child); continue; } add_node_error(g, first_executing_node(child), buf_sprintf("unreachable code")); @@ -2604,6 +2721,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, bool is_last = (i == node->data.block.statements.length - 1); TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr; return_type = analyze_expression(g, import, child_context, passed_expected_type, child); + if (!is_last && return_type->id == TypeTableEntryIdMetaType) { + add_node_error(g, child, buf_sprintf("expected expression, found type")); + } } break; } @@ -2664,7 +2784,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); if (asm_output->return_type) { node->data.asm_expr.return_count += 1; - return_type = resolve_type(g, asm_output->return_type, import, context, false); + return_type = analyze_type_expr(g, import, context, asm_output->return_type); if (node->data.asm_expr.return_count > 1) { add_node_error(g, node, buf_sprintf("inline assembly allows up to one output value")); @@ -2699,6 +2819,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeFieldAccessExpr: return_type = analyze_field_access_expr(g, import, context, node); break; + case NodeTypeContainerInitExpr: + return_type = analyze_container_init_expr(g, import, context, node); + break; case NodeTypeNumberLiteral: return_type = analyze_number_literal_expr(g, import, context, expected_type, node); break; @@ -2713,14 +2836,6 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeCharLiteral: return_type = g->builtin_types.entry_u8; break; - case NodeTypeUnreachable: - return_type = g->builtin_types.entry_unreachable; - break; - - case NodeTypeVoid: - return_type = g->builtin_types.entry_void; - break; - case NodeTypeBoolLiteral: return_type = g->builtin_types.entry_bool; break; @@ -2730,96 +2845,13 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, break; case NodeTypeSymbol: - { - return_type = analyze_symbol_expr(g, import, context, expected_type, node); - break; - } + return_type = analyze_symbol_expr(g, import, context, expected_type, node); + break; case NodeTypeCastExpr: return_type = analyze_cast_expr(g, import, context, expected_type, node); break; case NodeTypePrefixOpExpr: - switch (node->data.prefix_op_expr.prefix_op) { - case PrefixOpInvalid: - zig_unreachable(); - case PrefixOpBoolNot: - analyze_expression(g, import, context, g->builtin_types.entry_bool, - node->data.prefix_op_expr.primary_expr); - return_type = g->builtin_types.entry_bool; - break; - case PrefixOpBinNot: - { - AstNode *operand_node = node->data.prefix_op_expr.primary_expr; - TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type, - operand_node); - if (expr_type->id == TypeTableEntryIdInvalid) { - return_type = expr_type; - } else if (expr_type->id == TypeTableEntryIdInt || - (expr_type->id == TypeTableEntryIdNumberLiteral && - !is_num_lit_float(expr_type->data.num_lit.kind))) - { - return_type = expr_type; - } else { - add_node_error(g, operand_node, buf_sprintf("invalid binary not type: '%s'", - buf_ptr(&expr_type->name))); - return_type = g->builtin_types.entry_invalid; - } - break; - } - case PrefixOpNegation: - { - AstNode *operand_node = node->data.prefix_op_expr.primary_expr; - TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type, - operand_node); - if (expr_type->id == TypeTableEntryIdInvalid) { - return_type = expr_type; - } else if (expr_type->id == TypeTableEntryIdInt && - expr_type->data.integral.is_signed) - { - return_type = expr_type; - } else if (expr_type->id == TypeTableEntryIdFloat) { - return_type = expr_type; - } else if (expr_type->id == TypeTableEntryIdNumberLiteral) { - return_type = expr_type; - } else { - add_node_error(g, operand_node, buf_sprintf("invalid negation type: '%s'", - buf_ptr(&expr_type->name))); - return_type = g->builtin_types.entry_invalid; - } - break; - } - case PrefixOpAddressOf: - case PrefixOpConstAddressOf: - { - bool is_const = (node->data.prefix_op_expr.prefix_op == PrefixOpConstAddressOf); - - TypeTableEntry *child_type = analyze_lvalue(g, import, context, - node->data.prefix_op_expr.primary_expr, LValPurposeAddressOf, is_const); - - if (child_type->id == TypeTableEntryIdInvalid) { - return_type = g->builtin_types.entry_invalid; - break; - } - - return_type = get_pointer_to_type(g, child_type, is_const, false); - break; - } - case PrefixOpDereference: - { - TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, - node->data.prefix_op_expr.primary_expr); - if (type_entry->id == TypeTableEntryIdInvalid) { - return_type = type_entry; - } else if (type_entry->id == TypeTableEntryIdPointer) { - return_type = type_entry->data.pointer.child_type; - } else { - add_node_error(g, node->data.prefix_op_expr.primary_expr, - buf_sprintf("indirection requires pointer operand ('%s' invalid)", - buf_ptr(&type_entry->name))); - return_type = g->builtin_types.entry_invalid; - } - break; - } - } + return_type = analyze_prefix_op_expr(g, import, context, expected_type, node); break; case NodeTypeIfBoolExpr: return_type = analyze_if_bool_expr(g, import, context, expected_type, node); @@ -2830,17 +2862,13 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeWhileExpr: return_type = analyze_while_expr(g, import, context, expected_type, node); break; - case NodeTypeStructValueExpr: - return_type = analyze_struct_val_expr(g, import, context, expected_type, node); - break; - case NodeTypeCompilerFnType: - return_type = analyze_compiler_fn_type(g, import, context, expected_type, node); + case NodeTypeArrayType: + return_type = analyze_array_type(g, import, context, expected_type, node); break; case NodeTypeDirective: case NodeTypeFnDecl: case NodeTypeFnProto: case NodeTypeParamDecl: - case NodeTypeType: case NodeTypeRoot: case NodeTypeRootExportDecl: case NodeTypeExternBlock: @@ -2850,7 +2878,6 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeStructDecl: case NodeTypeStructField: case NodeTypeStructValueField: - case NodeTypeCompilerFnExpr: zig_unreachable(); } assert(return_type); @@ -2885,8 +2912,7 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo // define local variables for parameters AstNodeParamDecl *param_decl = ¶m_decl_node->data.param_decl; - assert(param_decl->type->type == NodeTypeType); - TypeTableEntry *type = param_decl->type->data.type.entry; + TypeTableEntry *type = unwrapped_node_type(param_decl->type); if (is_exported && type->id == TypeTableEntryIdStruct) { add_node_error(g, param_decl_node, @@ -2918,7 +2944,7 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo } } - TypeTableEntry *expected_type = fn_proto->return_type->data.type.entry; + TypeTableEntry *expected_type = unwrapped_node_type(fn_proto->return_type); TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body); node->data.fn_def.implicit_return_type = block_return_type; @@ -2963,7 +2989,6 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeDirective: case NodeTypeParamDecl: case NodeTypeFnProto: - case NodeTypeType: case NodeTypeFnDecl: case NodeTypeReturnExpr: case NodeTypeRoot: @@ -2975,8 +3000,6 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeNumberLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: - case NodeTypeUnreachable: - case NodeTypeVoid: case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeSymbol: @@ -2992,174 +3015,147 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: case NodeTypeStructField: - case NodeTypeStructValueExpr: case NodeTypeStructValueField: - case NodeTypeCompilerFnExpr: - case NodeTypeCompilerFnType: + case NodeTypeContainerInitExpr: + case NodeTypeArrayType: zig_unreachable(); } } -static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *expr_node, +static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *node, TopLevelDecl *decl_node) { - switch (expr_node->type) { + switch (node->type) { case NodeTypeNumberLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: - case NodeTypeVoid: case NodeTypeBoolLiteral: case NodeTypeNullLiteral: - case NodeTypeUnreachable: case NodeTypeGoto: case NodeTypeBreak: case NodeTypeContinue: // no dependencies on other top level declarations break; case NodeTypeSymbol: - decl_node->deps.put(&expr_node->data.symbol_expr.symbol, expr_node); - break; - case NodeTypeBinOpExpr: - collect_expr_decl_deps(g, import, expr_node->data.bin_op_expr.op1, decl_node); - collect_expr_decl_deps(g, import, expr_node->data.bin_op_expr.op2, decl_node); - break; - case NodeTypeReturnExpr: - collect_expr_decl_deps(g, import, expr_node->data.return_expr.expr, decl_node); - break; - case NodeTypeCastExpr: - collect_expr_decl_deps(g, import, expr_node->data.cast_expr.expr, decl_node); - collect_type_decl_deps(g, import, expr_node->data.cast_expr.type, decl_node); - break; - case NodeTypePrefixOpExpr: - collect_expr_decl_deps(g, import, expr_node->data.prefix_op_expr.primary_expr, decl_node); - break; - case NodeTypeFnCallExpr: - collect_expr_decl_deps(g, import, expr_node->data.fn_call_expr.fn_ref_expr, decl_node); - for (int i = 0; i < expr_node->data.fn_call_expr.params.length; i += 1) { - AstNode *arg_node = expr_node->data.fn_call_expr.params.at(i); - collect_expr_decl_deps(g, import, arg_node, decl_node); - } - break; - case NodeTypeArrayAccessExpr: - collect_expr_decl_deps(g, import, expr_node->data.array_access_expr.array_ref_expr, decl_node); - collect_expr_decl_deps(g, import, expr_node->data.array_access_expr.subscript, decl_node); - break; - case NodeTypeSliceExpr: - collect_expr_decl_deps(g, import, expr_node->data.slice_expr.array_ref_expr, decl_node); - collect_expr_decl_deps(g, import, expr_node->data.slice_expr.start, decl_node); - if (expr_node->data.slice_expr.end) { - collect_expr_decl_deps(g, import, expr_node->data.slice_expr.end, decl_node); - } - break; - case NodeTypeFieldAccessExpr: - collect_expr_decl_deps(g, import, expr_node->data.field_access_expr.struct_expr, decl_node); - break; - case NodeTypeIfBoolExpr: - collect_expr_decl_deps(g, import, expr_node->data.if_bool_expr.condition, decl_node); - collect_expr_decl_deps(g, import, expr_node->data.if_bool_expr.then_block, decl_node); - if (expr_node->data.if_bool_expr.else_node) { - collect_expr_decl_deps(g, import, expr_node->data.if_bool_expr.else_node, decl_node); - } - break; - case NodeTypeIfVarExpr: - if (expr_node->data.if_var_expr.var_decl.type) { - collect_type_decl_deps(g, import, expr_node->data.if_var_expr.var_decl.type, decl_node); - } - if (expr_node->data.if_var_expr.var_decl.expr) { - collect_expr_decl_deps(g, import, expr_node->data.if_var_expr.var_decl.expr, decl_node); - } - collect_expr_decl_deps(g, import, expr_node->data.if_var_expr.then_block, decl_node); - if (expr_node->data.if_bool_expr.else_node) { - collect_expr_decl_deps(g, import, expr_node->data.if_var_expr.else_node, decl_node); - } - break; - case NodeTypeWhileExpr: - collect_expr_decl_deps(g, import, expr_node->data.while_expr.condition, decl_node); - collect_expr_decl_deps(g, import, expr_node->data.while_expr.body, decl_node); - break; - case NodeTypeBlock: - for (int i = 0; i < expr_node->data.block.statements.length; i += 1) { - AstNode *stmt = expr_node->data.block.statements.at(i); - collect_expr_decl_deps(g, import, stmt, decl_node); - } - break; - case NodeTypeAsmExpr: - for (int i = 0; i < expr_node->data.asm_expr.output_list.length; i += 1) { - AsmOutput *asm_output = expr_node->data.asm_expr.output_list.at(i); - if (asm_output->return_type) { - collect_type_decl_deps(g, import, asm_output->return_type, decl_node); - } else { - decl_node->deps.put(&asm_output->variable_name, expr_node); - } - } - for (int i = 0; i < expr_node->data.asm_expr.input_list.length; i += 1) { - AsmInput *asm_input = expr_node->data.asm_expr.input_list.at(i); - collect_expr_decl_deps(g, import, asm_input->expr, decl_node); - } - break; - case NodeTypeStructValueExpr: - collect_type_decl_deps(g, import, expr_node->data.struct_val_expr.type, decl_node); - for (int i = 0; i < expr_node->data.struct_val_expr.fields.length; i += 1) { - AstNode *field_node = expr_node->data.struct_val_expr.fields.at(i); - assert(field_node->type == NodeTypeStructValueField); - collect_expr_decl_deps(g, import, field_node->data.struct_val_field.expr, decl_node); - } - break; - case NodeTypeCompilerFnExpr: - collect_expr_decl_deps(g, import, expr_node->data.compiler_fn_expr.expr, decl_node); - break; - case NodeTypeCompilerFnType: - collect_type_decl_deps(g, import, expr_node->data.compiler_fn_type.type, decl_node); - break; - case NodeTypeRoot: - case NodeTypeRootExportDecl: - case NodeTypeFnProto: - case NodeTypeFnDef: - case NodeTypeFnDecl: - case NodeTypeParamDecl: - case NodeTypeType: - case NodeTypeExternBlock: - case NodeTypeDirective: - case NodeTypeVariableDeclaration: - case NodeTypeUse: - case NodeTypeLabel: - case NodeTypeStructDecl: - case NodeTypeStructField: - case NodeTypeStructValueField: - zig_unreachable(); - } -} - -static void collect_type_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *type_node, TopLevelDecl *decl_node) { - assert(type_node->type == NodeTypeType); - switch (type_node->data.type.type) { - case AstNodeTypeTypePrimitive: { - Buf *name = &type_node->data.type.primitive_name; + Buf *name = &node->data.symbol_expr.symbol; auto table_entry = g->primitive_type_table.maybe_get(name); if (!table_entry) { table_entry = import->block_context->type_table.maybe_get(name); } if (!table_entry) { - decl_node->deps.put(name, type_node); + decl_node->deps.put(name, node); } break; } - case AstNodeTypeTypePointer: - collect_type_decl_deps(g, import, type_node->data.type.child_type, decl_node); + case NodeTypeBinOpExpr: + collect_expr_decl_deps(g, import, node->data.bin_op_expr.op1, decl_node); + collect_expr_decl_deps(g, import, node->data.bin_op_expr.op2, decl_node); break; - case AstNodeTypeTypeArray: - collect_type_decl_deps(g, import, type_node->data.type.child_type, decl_node); - if (type_node->data.type.array_size) { - collect_expr_decl_deps(g, import, type_node->data.type.array_size, decl_node); + case NodeTypeReturnExpr: + collect_expr_decl_deps(g, import, node->data.return_expr.expr, decl_node); + break; + case NodeTypeCastExpr: + collect_expr_decl_deps(g, import, node->data.cast_expr.expr, decl_node); + collect_expr_decl_deps(g, import, node->data.cast_expr.type, decl_node); + break; + case NodeTypePrefixOpExpr: + collect_expr_decl_deps(g, import, node->data.prefix_op_expr.primary_expr, decl_node); + break; + case NodeTypeFnCallExpr: + collect_expr_decl_deps(g, import, node->data.fn_call_expr.fn_ref_expr, decl_node); + for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) { + AstNode *arg_node = node->data.fn_call_expr.params.at(i); + collect_expr_decl_deps(g, import, arg_node, decl_node); } break; - case AstNodeTypeTypeMaybe: - collect_type_decl_deps(g, import, type_node->data.type.child_type, decl_node); + case NodeTypeArrayAccessExpr: + collect_expr_decl_deps(g, import, node->data.array_access_expr.array_ref_expr, decl_node); + collect_expr_decl_deps(g, import, node->data.array_access_expr.subscript, decl_node); break; - case AstNodeTypeTypeCompilerExpr: - collect_expr_decl_deps(g, import, type_node->data.type.compiler_expr, decl_node); + case NodeTypeSliceExpr: + collect_expr_decl_deps(g, import, node->data.slice_expr.array_ref_expr, decl_node); + collect_expr_decl_deps(g, import, node->data.slice_expr.start, decl_node); + if (node->data.slice_expr.end) { + collect_expr_decl_deps(g, import, node->data.slice_expr.end, decl_node); + } break; + case NodeTypeFieldAccessExpr: + collect_expr_decl_deps(g, import, node->data.field_access_expr.struct_expr, decl_node); + break; + case NodeTypeIfBoolExpr: + collect_expr_decl_deps(g, import, node->data.if_bool_expr.condition, decl_node); + collect_expr_decl_deps(g, import, node->data.if_bool_expr.then_block, decl_node); + if (node->data.if_bool_expr.else_node) { + collect_expr_decl_deps(g, import, node->data.if_bool_expr.else_node, decl_node); + } + break; + case NodeTypeIfVarExpr: + if (node->data.if_var_expr.var_decl.type) { + collect_expr_decl_deps(g, import, node->data.if_var_expr.var_decl.type, decl_node); + } + if (node->data.if_var_expr.var_decl.expr) { + collect_expr_decl_deps(g, import, node->data.if_var_expr.var_decl.expr, decl_node); + } + collect_expr_decl_deps(g, import, node->data.if_var_expr.then_block, decl_node); + if (node->data.if_bool_expr.else_node) { + collect_expr_decl_deps(g, import, node->data.if_var_expr.else_node, decl_node); + } + break; + case NodeTypeWhileExpr: + collect_expr_decl_deps(g, import, node->data.while_expr.condition, decl_node); + collect_expr_decl_deps(g, import, node->data.while_expr.body, decl_node); + break; + case NodeTypeBlock: + for (int i = 0; i < node->data.block.statements.length; i += 1) { + AstNode *stmt = node->data.block.statements.at(i); + collect_expr_decl_deps(g, import, stmt, decl_node); + } + break; + case NodeTypeAsmExpr: + for (int i = 0; i < node->data.asm_expr.output_list.length; i += 1) { + AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); + if (asm_output->return_type) { + collect_expr_decl_deps(g, import, asm_output->return_type, decl_node); + } else { + decl_node->deps.put(&asm_output->variable_name, node); + } + } + for (int i = 0; i < node->data.asm_expr.input_list.length; i += 1) { + AsmInput *asm_input = node->data.asm_expr.input_list.at(i); + collect_expr_decl_deps(g, import, asm_input->expr, decl_node); + } + break; + case NodeTypeContainerInitExpr: + collect_expr_decl_deps(g, import, node->data.container_init_expr.type, decl_node); + for (int i = 0; i < node->data.container_init_expr.entries.length; i += 1) { + AstNode *child_node = node->data.container_init_expr.entries.at(i); + collect_expr_decl_deps(g, import, child_node, decl_node); + } + break; + case NodeTypeStructValueField: + collect_expr_decl_deps(g, import, node->data.struct_val_field.expr, decl_node); + break; + case NodeTypeArrayType: + if (node->data.array_type.size) { + collect_expr_decl_deps(g, import, node->data.array_type.size, decl_node); + } + collect_expr_decl_deps(g, import, node->data.array_type.child_type, decl_node); + break; + case NodeTypeVariableDeclaration: + case NodeTypeFnProto: + case NodeTypeExternBlock: + case NodeTypeRootExportDecl: + case NodeTypeFnDef: + case NodeTypeRoot: + case NodeTypeFnDecl: + case NodeTypeParamDecl: + case NodeTypeDirective: + case NodeTypeUse: + case NodeTypeLabel: + case NodeTypeStructDecl: + case NodeTypeStructField: + zig_unreachable(); } } @@ -3231,7 +3227,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast for (int i = 0; i < node->data.struct_decl.fields.length; i += 1) { AstNode *field_node = node->data.struct_decl.fields.at(i); AstNode *type_node = field_node->data.struct_field.type; - collect_type_decl_deps(g, import, type_node, decl_node); + collect_expr_decl_deps(g, import, type_node, decl_node); } decl_node->name = name; decl_node->import = import; @@ -3271,7 +3267,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast TopLevelDecl *decl_node = &node->data.variable_declaration.top_level_decl; decl_node->deps.init(1); if (node->data.variable_declaration.type) { - collect_type_decl_deps(g, import, node->data.variable_declaration.type, decl_node); + collect_expr_decl_deps(g, import, node->data.variable_declaration.type, decl_node); } if (node->data.variable_declaration.expr) { collect_expr_decl_deps(g, import, node->data.variable_declaration.expr, decl_node); @@ -3294,8 +3290,9 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast for (int i = 0; i < node->data.fn_proto.params.length; i += 1) { AstNode *param_node = node->data.fn_proto.params.at(i); assert(param_node->type == NodeTypeParamDecl); - collect_type_decl_deps(g, import, param_node->data.param_decl.type, decl_node); + collect_expr_decl_deps(g, import, param_node->data.param_decl.type, decl_node); } + collect_expr_decl_deps(g, import, node->data.fn_proto.return_type, decl_node); Buf *name = &node->data.fn_proto.name; decl_node->name = name; @@ -3315,7 +3312,6 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast break; case NodeTypeDirective: case NodeTypeParamDecl: - case NodeTypeType: case NodeTypeFnDecl: case NodeTypeReturnExpr: case NodeTypeRoot: @@ -3327,8 +3323,6 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast case NodeTypeNumberLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: - case NodeTypeUnreachable: - case NodeTypeVoid: case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeSymbol: @@ -3344,10 +3338,9 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: case NodeTypeStructField: - case NodeTypeStructValueExpr: + case NodeTypeContainerInitExpr: case NodeTypeStructValueField: - case NodeTypeCompilerFnExpr: - case NodeTypeCompilerFnType: + case NodeTypeArrayType: zig_unreachable(); } } @@ -3526,18 +3519,14 @@ Expr *get_resolved_expr(AstNode *node) { return &node->data.while_expr.resolved_expr; case NodeTypeAsmExpr: return &node->data.asm_expr.resolved_expr; - case NodeTypeStructValueExpr: - return &node->data.struct_val_expr.resolved_expr; + case NodeTypeContainerInitExpr: + return &node->data.container_init_expr.resolved_expr; case NodeTypeNumberLiteral: return &node->data.number_literal.resolved_expr; case NodeTypeStringLiteral: return &node->data.string_literal.resolved_expr; case NodeTypeBlock: return &node->data.block.resolved_expr; - case NodeTypeVoid: - return &node->data.void_expr.resolved_expr; - case NodeTypeUnreachable: - return &node->data.unreachable_expr.resolved_expr; case NodeTypeSymbol: return &node->data.symbol_expr.resolved_expr; case NodeTypeVariableDeclaration: @@ -3554,19 +3543,16 @@ Expr *get_resolved_expr(AstNode *node) { return &node->data.break_expr.resolved_expr; case NodeTypeContinue: return &node->data.continue_expr.resolved_expr; - case NodeTypeCompilerFnExpr: - return &node->data.compiler_fn_expr.resolved_expr; - case NodeTypeCompilerFnType: - return &node->data.compiler_fn_type.resolved_expr; case NodeTypeLabel: return &node->data.label.resolved_expr; + case NodeTypeArrayType: + return &node->data.array_type.resolved_expr; case NodeTypeRoot: case NodeTypeRootExportDecl: case NodeTypeFnProto: case NodeTypeFnDef: case NodeTypeFnDecl: case NodeTypeParamDecl: - case NodeTypeType: case NodeTypeExternBlock: case NodeTypeDirective: case NodeTypeUse: @@ -3581,13 +3567,12 @@ NumLitCodeGen *get_resolved_num_lit(AstNode *node) { switch (node->type) { case NodeTypeNumberLiteral: return &node->data.number_literal.codegen; - case NodeTypeCompilerFnType: - return &node->data.compiler_fn_type.resolved_num_lit; + case NodeTypeFnCallExpr: + return &node->data.fn_call_expr.resolved_num_lit; case NodeTypeReturnExpr: case NodeTypeBinOpExpr: case NodeTypeCastExpr: case NodeTypePrefixOpExpr: - case NodeTypeFnCallExpr: case NodeTypeArrayAccessExpr: case NodeTypeSliceExpr: case NodeTypeFieldAccessExpr: @@ -3595,24 +3580,21 @@ NumLitCodeGen *get_resolved_num_lit(AstNode *node) { case NodeTypeIfVarExpr: case NodeTypeWhileExpr: case NodeTypeAsmExpr: - case NodeTypeStructValueExpr: + case NodeTypeContainerInitExpr: case NodeTypeRoot: case NodeTypeRootExportDecl: case NodeTypeFnProto: case NodeTypeFnDef: case NodeTypeFnDecl: case NodeTypeParamDecl: - case NodeTypeType: case NodeTypeBlock: case NodeTypeExternBlock: case NodeTypeDirective: case NodeTypeVariableDeclaration: case NodeTypeStringLiteral: case NodeTypeCharLiteral: - case NodeTypeUnreachable: case NodeTypeSymbol: case NodeTypeUse: - case NodeTypeVoid: case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeLabel: @@ -3622,7 +3604,7 @@ NumLitCodeGen *get_resolved_num_lit(AstNode *node) { case NodeTypeStructDecl: case NodeTypeStructField: case NodeTypeStructValueField: - case NodeTypeCompilerFnExpr: + case NodeTypeArrayType: zig_unreachable(); } } @@ -3648,22 +3630,19 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { case NodeTypeIfVarExpr: case NodeTypeWhileExpr: case NodeTypeAsmExpr: - case NodeTypeStructValueExpr: + case NodeTypeContainerInitExpr: case NodeTypeRoot: case NodeTypeRootExportDecl: case NodeTypeFnDef: case NodeTypeFnDecl: case NodeTypeParamDecl: - case NodeTypeType: case NodeTypeBlock: case NodeTypeExternBlock: case NodeTypeDirective: case NodeTypeStringLiteral: case NodeTypeCharLiteral: - case NodeTypeUnreachable: case NodeTypeSymbol: case NodeTypeUse: - case NodeTypeVoid: case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeLabel: @@ -3672,8 +3651,23 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { case NodeTypeContinue: case NodeTypeStructField: case NodeTypeStructValueField: - case NodeTypeCompilerFnExpr: - case NodeTypeCompilerFnType: + case NodeTypeArrayType: zig_unreachable(); } } + +bool is_node_void_expr(AstNode *node) { + if (node->type == NodeTypeContainerInitExpr && + node->data.container_init_expr.kind == ContainerInitKindArray) + { + AstNode *type_node = node->data.container_init_expr.type; + if (type_node->type == NodeTypeSymbol && + buf_eql_str(&type_node->data.symbol_expr.symbol, "void")) + { + return true; + } + } + + return false; +} + diff --git a/src/analyze.hpp b/src/analyze.hpp index b35311ad02..66da488a22 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -13,12 +13,13 @@ void semantic_analyze(CodeGen *g); void add_node_error(CodeGen *g, AstNode *node, Buf *msg); TypeTableEntry *new_type_table_entry(TypeTableEntryId id); -TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const, bool is_noalias); +TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const); VariableTableEntry *find_variable(BlockContext *context, Buf *name); TypeTableEntry *find_container(BlockContext *context, Buf *name); BlockContext *new_block_context(AstNode *node, BlockContext *parent); Expr *get_resolved_expr(AstNode *node); NumLitCodeGen *get_resolved_num_lit(AstNode *node); TopLevelDecl *get_resolved_top_level_decl(AstNode *node); +bool is_node_void_expr(AstNode *node); #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index cd6d43d661..344dfc27d9 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -72,35 +72,35 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType b LLVMValueRef target_ref, LLVMValueRef value, TypeTableEntry *op1_type, TypeTableEntry *op2_type); - -static TypeTableEntry *get_type_for_type_node(CodeGen *g, AstNode *type_node) { - assert(type_node->type == NodeTypeType); - return type_node->data.type.entry; +static TypeTableEntry *get_type_for_type_node(AstNode *node) { + TypeTableEntry *meta_type_entry = get_resolved_expr(node)->type_entry; + assert(meta_type_entry->id == TypeTableEntryIdMetaType); + return meta_type_entry->data.meta_type.child_type; } static TypeTableEntry *fn_proto_type_from_type_node(CodeGen *g, AstNode *type_node) { - TypeTableEntry *type_entry = get_type_for_type_node(g, type_node); + TypeTableEntry *type_entry = get_type_for_type_node(type_node); if (type_entry->id == TypeTableEntryIdStruct || type_entry->id == TypeTableEntryIdArray) { - return get_pointer_to_type(g, type_entry, true, true); + return get_pointer_to_type(g, type_entry, true); } else { return type_entry; } } static LLVMZigDIType *to_llvm_debug_type(CodeGen *g, AstNode *type_node) { - TypeTableEntry *type_entry = get_type_for_type_node(g, type_node); + TypeTableEntry *type_entry = get_type_for_type_node(type_node); return type_entry->di_type; } static bool type_is_unreachable(CodeGen *g, AstNode *type_node) { - return get_type_for_type_node(g, type_node)->id == TypeTableEntryIdUnreachable; + return get_type_for_type_node(type_node)->id == TypeTableEntryIdUnreachable; } static bool is_param_decl_type_void(CodeGen *g, AstNode *param_decl_node) { assert(param_decl_node->type == NodeTypeParamDecl); - return get_type_for_type_node(g, param_decl_node->data.param_decl.type)->id == TypeTableEntryIdVoid; + return get_type_for_type_node(param_decl_node->data.param_decl.type)->id == TypeTableEntryIdVoid; } static int count_non_void_params(CodeGen *g, ZigList *params) { @@ -146,6 +146,32 @@ static TypeTableEntry *get_expr_type(AstNode *node) { return expr->type_entry; } +static LLVMValueRef gen_number_literal_raw(CodeGen *g, AstNode *source_node, + NumLitCodeGen *codegen_num_lit, AstNodeNumberLiteral *num_lit_node) +{ + TypeTableEntry *type_entry = codegen_num_lit->resolved_type; + assert(type_entry); + + // override the expression type for number literals + get_resolved_expr(source_node)->type_entry = type_entry; + + if (type_entry->id == TypeTableEntryIdInt) { + // here the union has int64_t and uint64_t and we purposefully read + // the uint64_t value in either case, because we want the twos + // complement representation + + return LLVMConstInt(type_entry->type_ref, + num_lit_node->data.x_uint, + type_entry->data.integral.is_signed); + } else if (type_entry->id == TypeTableEntryIdFloat) { + + return LLVMConstReal(type_entry->type_ref, + num_lit_node->data.x_float); + } else { + zig_panic("bad number literal type"); + } +} + static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; @@ -154,6 +180,7 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { switch (builtin_fn->id) { case BuiltinFnIdInvalid: + case BuiltinFnIdTypeof: zig_unreachable(); case BuiltinFnIdArithmeticWithOverflow: { @@ -238,6 +265,74 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { LLVMBuildCall(g->builder, builtin_fn->fn_val, params, 5, ""); return nullptr; } + case BuiltinFnIdSizeof: + { + assert(node->data.fn_call_expr.params.length == 1); + AstNode *type_node = node->data.fn_call_expr.params.at(0); + TypeTableEntry *type_entry = get_type_for_type_node(type_node); + + NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(node); + AstNodeNumberLiteral num_lit_node; + num_lit_node.kind = NumLitU64; // this field isn't even read + num_lit_node.overflow = false; + num_lit_node.data.x_uint = type_entry->size_in_bits / 8; + return gen_number_literal_raw(g, node, codegen_num_lit, &num_lit_node); + } + case BuiltinFnIdMinValue: + { + assert(node->data.fn_call_expr.params.length == 1); + AstNode *type_node = node->data.fn_call_expr.params.at(0); + TypeTableEntry *type_entry = get_type_for_type_node(type_node); + + + if (type_entry->id == TypeTableEntryIdInt) { + if (type_entry->data.integral.is_signed) { + return LLVMConstInt(type_entry->type_ref, 1ULL << (type_entry->size_in_bits - 1), false); + } else { + return LLVMConstNull(type_entry->type_ref); + } + } else if (type_entry->id == TypeTableEntryIdFloat) { + zig_panic("TODO codegen min_value float"); + } else { + zig_unreachable(); + } + } + case BuiltinFnIdMaxValue: + { + assert(node->data.fn_call_expr.params.length == 1); + AstNode *type_node = node->data.fn_call_expr.params.at(0); + TypeTableEntry *type_entry = get_type_for_type_node(type_node); + + + if (type_entry->id == TypeTableEntryIdInt) { + if (type_entry->data.integral.is_signed) { + return LLVMConstInt(type_entry->type_ref, (1ULL << (type_entry->size_in_bits - 1)) - 1, false); + } else { + return LLVMConstAllOnes(type_entry->type_ref); + } + } else if (type_entry->id == TypeTableEntryIdFloat) { + zig_panic("TODO codegen max_value float"); + } else { + zig_unreachable(); + } + } + case BuiltinFnIdValueCount: + { + assert(node->data.fn_call_expr.params.length == 1); + AstNode *type_node = node->data.fn_call_expr.params.at(0); + TypeTableEntry *type_entry = get_type_for_type_node(type_node); + + if (type_entry->id == TypeTableEntryIdEnum) { + NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(node); + AstNodeNumberLiteral num_lit_node; + num_lit_node.kind = NumLitU64; // field ignored + num_lit_node.overflow = false; + num_lit_node.data.x_uint = type_entry->data.enumeration.field_count; + return gen_number_literal_raw(g, node, codegen_num_lit, &num_lit_node); + } else { + zig_unreachable(); + } + } } zig_unreachable(); } @@ -692,6 +787,10 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { add_debug_source_node(g, node); return LLVMBuildLoad(g->builder, expr, ""); } + case PrefixOpMaybe: + { + zig_panic("TODO codegen PrefixOpMaybe"); + } } zig_unreachable(); } @@ -1484,35 +1583,46 @@ static LLVMValueRef gen_null_literal(CodeGen *g, AstNode *node) { return tmp_struct_ptr; } -static LLVMValueRef gen_struct_val_expr(CodeGen *g, AstNode *node) { - assert(node->type == NodeTypeStructValueExpr); +static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) { + assert(node->type == NodeTypeContainerInitExpr); TypeTableEntry *type_entry = get_expr_type(node); - assert(type_entry->id == TypeTableEntryIdStruct); + if (type_entry->id == TypeTableEntryIdStruct) { + assert(node->data.container_init_expr.kind == ContainerInitKindStruct); - int field_count = type_entry->data.structure.field_count; - assert(field_count == node->data.struct_val_expr.fields.length); + int field_count = type_entry->data.structure.field_count; + assert(field_count == node->data.container_init_expr.entries.length); - StructValExprCodeGen *struct_val_expr_node = &node->data.struct_val_expr.codegen; - LLVMValueRef tmp_struct_ptr = struct_val_expr_node->ptr; + StructValExprCodeGen *struct_val_expr_node = &node->data.container_init_expr.resolved_struct_val_expr; + LLVMValueRef tmp_struct_ptr = struct_val_expr_node->ptr; - for (int i = 0; i < field_count; i += 1) { - AstNode *field_node = node->data.struct_val_expr.fields.at(i); - assert(field_node->type == NodeTypeStructValueField); - TypeStructField *type_struct_field = field_node->data.struct_val_field.type_struct_field; - if (type_struct_field->type_entry->id == TypeTableEntryIdVoid) { - continue; + for (int i = 0; i < field_count; i += 1) { + AstNode *field_node = node->data.container_init_expr.entries.at(i); + assert(field_node->type == NodeTypeStructValueField); + TypeStructField *type_struct_field = field_node->data.struct_val_field.type_struct_field; + if (type_struct_field->type_entry->id == TypeTableEntryIdVoid) { + continue; + } + assert(buf_eql_buf(type_struct_field->name, &field_node->data.struct_val_field.name)); + + add_debug_source_node(g, field_node); + LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, type_struct_field->gen_index, ""); + LLVMValueRef value = gen_expr(g, field_node->data.struct_val_field.expr); + LLVMBuildStore(g->builder, value, field_ptr); } - assert(buf_eql_buf(type_struct_field->name, &field_node->data.struct_val_field.name)); - add_debug_source_node(g, field_node); - LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, type_struct_field->gen_index, ""); - LLVMValueRef value = gen_expr(g, field_node->data.struct_val_field.expr); - LLVMBuildStore(g->builder, value, field_ptr); + return tmp_struct_ptr; + } else if (type_entry->id == TypeTableEntryIdUnreachable) { + assert(node->data.container_init_expr.entries.length == 0); + add_debug_source_node(g, node); + return LLVMBuildUnreachable(g->builder); + } else if (type_entry->id == TypeTableEntryIdVoid) { + assert(node->data.container_init_expr.entries.length == 0); + return nullptr; + } else { + zig_unreachable(); } - - return tmp_struct_ptr; } static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { @@ -1659,84 +1769,6 @@ static LLVMValueRef gen_var_decl_expr(CodeGen *g, AstNode *node) { get_resolved_expr(node)->block_context, false, &init_val); } -static LLVMValueRef gen_number_literal_raw(CodeGen *g, AstNode *source_node, - NumLitCodeGen *codegen_num_lit, AstNodeNumberLiteral *num_lit_node) -{ - TypeTableEntry *type_entry = codegen_num_lit->resolved_type; - assert(type_entry); - - // override the expression type for number literals - get_resolved_expr(source_node)->type_entry = type_entry; - - if (type_entry->id == TypeTableEntryIdInt) { - // here the union has int64_t and uint64_t and we purposefully read - // the uint64_t value in either case, because we want the twos - // complement representation - - return LLVMConstInt(type_entry->type_ref, - num_lit_node->data.x_uint, - type_entry->data.integral.is_signed); - } else if (type_entry->id == TypeTableEntryIdFloat) { - - return LLVMConstReal(type_entry->type_ref, - num_lit_node->data.x_float); - } else { - zig_panic("bad number literal type"); - } -} - -static LLVMValueRef gen_compiler_fn_type(CodeGen *g, AstNode *node) { - assert(node->type == NodeTypeCompilerFnType); - - Buf *name = &node->data.compiler_fn_type.name; - TypeTableEntry *type_entry = get_type_for_type_node(g, node->data.compiler_fn_type.type); - if (buf_eql_str(name, "sizeof")) { - NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(node); - AstNodeNumberLiteral num_lit_node; - num_lit_node.kind = type_entry->data.num_lit.kind; - num_lit_node.overflow = false; - num_lit_node.data.x_uint = type_entry->size_in_bits / 8; - return gen_number_literal_raw(g, node, codegen_num_lit, &num_lit_node); - } else if (buf_eql_str(name, "min_value")) { - if (type_entry->id == TypeTableEntryIdInt) { - if (type_entry->data.integral.is_signed) { - return LLVMConstInt(type_entry->type_ref, 1ULL << (type_entry->size_in_bits - 1), false); - } else { - return LLVMConstNull(type_entry->type_ref); - } - } else if (type_entry->id == TypeTableEntryIdFloat) { - zig_panic("TODO codegen min_value float"); - } else { - zig_unreachable(); - } - } else if (buf_eql_str(name, "max_value")) { - if (type_entry->id == TypeTableEntryIdInt) { - if (type_entry->data.integral.is_signed) { - return LLVMConstInt(type_entry->type_ref, (1ULL << (type_entry->size_in_bits - 1)) - 1, false); - } else { - return LLVMConstAllOnes(type_entry->type_ref); - } - } else if (type_entry->id == TypeTableEntryIdFloat) { - zig_panic("TODO codegen max_value float"); - } else { - zig_unreachable(); - } - } else if (buf_eql_str(name, "value_count")) { - if (type_entry->id == TypeTableEntryIdEnum) { - NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(node); - AstNodeNumberLiteral num_lit_node; - num_lit_node.kind = type_entry->data.num_lit.kind; - num_lit_node.overflow = false; - num_lit_node.data.x_uint = type_entry->data.enumeration.field_count; - return gen_number_literal_raw(g, node, codegen_num_lit, &num_lit_node); - } else { - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - static LLVMValueRef gen_number_literal(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeNumberLiteral); @@ -1746,6 +1778,30 @@ static LLVMValueRef gen_number_literal(CodeGen *g, AstNode *node) { return gen_number_literal_raw(g, node, codegen_num_lit, &node->data.number_literal); } +static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) { + VariableTableEntry *variable = find_variable( + get_resolved_expr(node)->block_context, + &node->data.symbol_expr.symbol); + assert(variable); + if (variable->type->id == TypeTableEntryIdVoid) { + return nullptr; + } else if (variable->is_ptr) { + assert(variable->value_ref); + if (variable->type->id == TypeTableEntryIdArray) { + return variable->value_ref; + } else if (variable->type->id == TypeTableEntryIdStruct || + variable->type->id == TypeTableEntryIdMaybe) + { + return variable->value_ref; + } else { + add_debug_source_node(g, node); + return LLVMBuildLoad(g->builder, variable->value_ref, ""); + } + } else { + return variable->value_ref; + } +} + static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { switch (node->type) { case NodeTypeBinOpExpr: @@ -1766,11 +1822,6 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { return gen_slice_expr(g, node); case NodeTypeFieldAccessExpr: return gen_field_access_expr(g, node, false); - case NodeTypeUnreachable: - add_debug_source_node(g, node); - return LLVMBuildUnreachable(g->builder); - case NodeTypeVoid: - return nullptr; case NodeTypeBoolLiteral: if (node->data.bool_literal.value) return LLVMConstAllOnes(LLVMInt1Type()); @@ -1802,29 +1853,7 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { case NodeTypeCharLiteral: return LLVMConstInt(LLVMInt8Type(), node->data.char_literal.value, false); case NodeTypeSymbol: - { - VariableTableEntry *variable = find_variable( - get_resolved_expr(node)->block_context, - &node->data.symbol_expr.symbol); - assert(variable); - if (variable->type->id == TypeTableEntryIdVoid) { - return nullptr; - } else if (variable->is_ptr) { - assert(variable->value_ref); - if (variable->type->id == TypeTableEntryIdArray) { - return variable->value_ref; - } else if (variable->type->id == TypeTableEntryIdStruct || - variable->type->id == TypeTableEntryIdMaybe) - { - return variable->value_ref; - } else { - add_debug_source_node(g, node); - return LLVMBuildLoad(g->builder, variable->value_ref, ""); - } - } else { - return variable->value_ref; - } - } + return gen_symbol(g, node); case NodeTypeBlock: return gen_block(g, node, nullptr); case NodeTypeGoto: @@ -1846,24 +1875,21 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { LLVMPositionBuilderAtEnd(g->builder, basic_block); return nullptr; } - case NodeTypeStructValueExpr: - return gen_struct_val_expr(g, node); - case NodeTypeCompilerFnType: - return gen_compiler_fn_type(g, node); + case NodeTypeContainerInitExpr: + return gen_container_init_expr(g, node); case NodeTypeRoot: case NodeTypeRootExportDecl: case NodeTypeFnProto: case NodeTypeFnDef: case NodeTypeFnDecl: case NodeTypeParamDecl: - case NodeTypeType: case NodeTypeExternBlock: case NodeTypeDirective: case NodeTypeUse: case NodeTypeStructDecl: case NodeTypeStructField: case NodeTypeStructValueField: - case NodeTypeCompilerFnExpr: + case NodeTypeArrayType: zig_unreachable(); } zig_unreachable(); @@ -1872,7 +1898,7 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { LLVMValueRef val = gen_expr_no_cast(g, node); - if (node->type == NodeTypeVoid) { + if (is_node_void_expr(node)) { return val; } @@ -1966,7 +1992,7 @@ static void do_code_gen(CodeGen *g) { assert(proto_node->type == NodeTypeFnProto); AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; - LLVMTypeRef ret_type = get_type_for_type_node(g, fn_proto->return_type)->type_ref; + LLVMTypeRef ret_type = get_type_for_type_node(fn_proto->return_type)->type_ref; int param_count = count_non_void_params(g, &fn_proto->params); LLVMTypeRef *param_types = allocate(param_count); int gen_param_index = 0; @@ -2009,7 +2035,7 @@ static void do_code_gen(CodeGen *g) { TypeTableEntry *param_type = fn_proto_type_from_type_node(g, type_node); LLVMValueRef argument_val = LLVMGetParam(fn, gen_param_index); if (param_type->id == TypeTableEntryIdPointer && - param_type->data.pointer.is_noalias) + false) // TODO test if parameter is noalias { LLVMAddAttribute(argument_val, LLVMNoAliasAttribute); } else if (param_type->id == TypeTableEntryIdPointer && @@ -2267,7 +2293,7 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_u64 = entry; g->primitive_type_table.put(&entry->name, entry); } - g->builtin_types.entry_c_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true, false); + g->builtin_types.entry_c_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true); { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); entry->type_ref = LLVMInt8Type(); @@ -2413,7 +2439,7 @@ static void define_builtin_fns_int(CodeGen *g, TypeTableEntry *type_entry) { builtin_fn->param_types = allocate(builtin_fn->param_count); builtin_fn->param_types[0] = type_entry; builtin_fn->param_types[1] = type_entry; - builtin_fn->param_types[2] = get_pointer_to_type(g, type_entry, false, false); + builtin_fn->param_types[2] = get_pointer_to_type(g, type_entry, false); const char *signed_str = type_entry->data.integral.is_signed ? @@ -2437,6 +2463,23 @@ static void define_builtin_fns_int(CodeGen *g, TypeTableEntry *type_entry) { } } +static BuiltinFnEntry *create_builtin_fn(CodeGen *g, BuiltinFnId id, const char *name) { + BuiltinFnEntry *builtin_fn = allocate(1); + buf_init_from_str(&builtin_fn->name, name); + builtin_fn->id = id; + g->builtin_fn_table.put(&builtin_fn->name, builtin_fn); + return builtin_fn; +} + +static BuiltinFnEntry *create_one_arg_builtin_fn(CodeGen *g, BuiltinFnId id, const char *name) { + BuiltinFnEntry *builtin_fn = create_builtin_fn(g, id, name); + builtin_fn->return_type = nullptr; // manually determined later + builtin_fn->param_count = 1; + builtin_fn->param_types = allocate(builtin_fn->param_count); + builtin_fn->param_types[0] = nullptr; // manually checked later + return builtin_fn; +} + static void define_builtin_fns(CodeGen *g) { define_builtin_fns_int(g, g->builtin_types.entry_u8); define_builtin_fns_int(g, g->builtin_types.entry_u16); @@ -2447,9 +2490,7 @@ static void define_builtin_fns(CodeGen *g) { define_builtin_fns_int(g, g->builtin_types.entry_i32); define_builtin_fns_int(g, g->builtin_types.entry_i64); { - BuiltinFnEntry *builtin_fn = allocate(1); - buf_init_from_str(&builtin_fn->name, "memcpy"); - builtin_fn->id = BuiltinFnIdMemcpy; + BuiltinFnEntry *builtin_fn = create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy"); builtin_fn->return_type = g->builtin_types.entry_void; builtin_fn->param_count = 3; builtin_fn->param_types = allocate(builtin_fn->param_count); @@ -2470,12 +2511,9 @@ static void define_builtin_fns(CodeGen *g) { assert(LLVMGetIntrinsicID(builtin_fn->fn_val)); g->memcpy_fn_val = builtin_fn->fn_val; - g->builtin_fn_table.put(&builtin_fn->name, builtin_fn); } { - BuiltinFnEntry *builtin_fn = allocate(1); - buf_init_from_str(&builtin_fn->name, "memset"); - builtin_fn->id = BuiltinFnIdMemset; + BuiltinFnEntry *builtin_fn = create_builtin_fn(g, BuiltinFnIdMemset, "memset"); builtin_fn->return_type = g->builtin_types.entry_void; builtin_fn->param_count = 3; builtin_fn->param_types = allocate(builtin_fn->param_count); @@ -2496,8 +2534,12 @@ static void define_builtin_fns(CodeGen *g) { assert(LLVMGetIntrinsicID(builtin_fn->fn_val)); g->memset_fn_val = builtin_fn->fn_val; - g->builtin_fn_table.put(&builtin_fn->name, builtin_fn); } + create_one_arg_builtin_fn(g, BuiltinFnIdSizeof, "sizeof"); + create_one_arg_builtin_fn(g, BuiltinFnIdMaxValue, "max_value"); + create_one_arg_builtin_fn(g, BuiltinFnIdMinValue, "min_value"); + create_one_arg_builtin_fn(g, BuiltinFnIdValueCount, "value_count"); + create_one_arg_builtin_fn(g, BuiltinFnIdTypeof, "typeof"); } @@ -2780,9 +2822,8 @@ void codegen_add_root_code(CodeGen *g, Buf *src_dir, Buf *src_basename, Buf *sou } static void to_c_type(CodeGen *g, AstNode *type_node, Buf *out_buf) { - assert(type_node->type == NodeTypeType); - - TypeTableEntry *type_entry = type_node->data.type.entry; + zig_panic("TODO this function needs some love"); + TypeTableEntry *type_entry = get_resolved_expr(type_node)->type_entry; assert(type_entry); if (type_entry == g->builtin_types.entry_u8) { diff --git a/src/parser.cpp b/src/parser.cpp index b9c55ef319..de9dcf0a17 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -62,6 +62,7 @@ static const char *prefix_op_str(PrefixOp prefix_op) { case PrefixOpAddressOf: return "&"; case PrefixOpConstAddressOf: return "&const"; case PrefixOpDereference: return "*"; + case PrefixOpMaybe: return "?"; } zig_unreachable(); } @@ -80,8 +81,6 @@ const char *node_type_str(NodeType node_type) { return "FnProto"; case NodeTypeParamDecl: return "ParamDecl"; - case NodeTypeType: - return "Type"; case NodeTypeBlock: return "Block"; case NodeTypeBinOpExpr: @@ -108,16 +107,12 @@ const char *node_type_str(NodeType node_type) { return "StringLiteral"; case NodeTypeCharLiteral: return "CharLiteral"; - case NodeTypeUnreachable: - return "Unreachable"; case NodeTypeSymbol: return "Symbol"; case NodeTypePrefixOpExpr: return "PrefixOpExpr"; case NodeTypeUse: return "Use"; - case NodeTypeVoid: - return "Void"; case NodeTypeBoolLiteral: return "BoolLiteral"; case NodeTypeNullLiteral: @@ -144,14 +139,12 @@ const char *node_type_str(NodeType node_type) { return "StructDecl"; case NodeTypeStructField: return "StructField"; - case NodeTypeStructValueExpr: - return "StructValueExpr"; case NodeTypeStructValueField: return "StructValueField"; - case NodeTypeCompilerFnExpr: - return "CompilerFnExpr"; - case NodeTypeCompilerFnType: - return "CompilerFnType"; + case NodeTypeContainerInitExpr: + return "ContainerInitExpr"; + case NodeTypeArrayType: + return "ArrayType"; } zig_unreachable(); } @@ -214,47 +207,6 @@ void ast_print(AstNode *node, int indent) { break; } - case NodeTypeType: - switch (node->data.type.type) { - case AstNodeTypeTypePrimitive: - { - Buf *name_buf = &node->data.type.primitive_name; - fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(name_buf)); - break; - } - case AstNodeTypeTypePointer: - { - const char *const_or_mut_str = node->data.type.is_const ? "const " : ""; - const char *noalias_or_not_str = node->data.type.is_noalias ? "noalias " : ""; - fprintf(stderr, "%s%s PointerType\n", const_or_mut_str, noalias_or_not_str); - - ast_print(node->data.type.child_type, indent + 2); - break; - } - case AstNodeTypeTypeArray: - { - const char *const_or_mut_str = node->data.type.is_const ? "const " : ""; - const char *noalias_or_not_str = node->data.type.is_noalias ? "noalias " : ""; - fprintf(stderr, "%s%s ArrayType\n", const_or_mut_str, noalias_or_not_str); - if (node->data.type.array_size) - ast_print(node->data.type.array_size, indent + 2); - ast_print(node->data.type.child_type, indent + 2); - break; - } - case AstNodeTypeTypeMaybe: - { - fprintf(stderr, "MaybeType\n"); - ast_print(node->data.type.child_type, indent + 2); - break; - } - case AstNodeTypeTypeCompilerExpr: - { - fprintf(stderr, "CompilerExprType\n"); - ast_print(node->data.type.compiler_expr, indent + 2); - break; - } - } - break; case NodeTypeReturnExpr: fprintf(stderr, "%s\n", node_type_str(node->type)); if (node->data.return_expr.expr) @@ -348,18 +300,12 @@ void ast_print(AstNode *node, int indent) { fprintf(stderr, "%s '%c'\n", node_type_str(node->type), node->data.char_literal.value); break; } - case NodeTypeUnreachable: - fprintf(stderr, "Unreachable\n"); - break; case NodeTypeSymbol: fprintf(stderr, "Symbol %s\n", buf_ptr(&node->data.symbol_expr.symbol)); break; case NodeTypeUse: fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.use.path)); break; - case NodeTypeVoid: - fprintf(stderr, "%s\n", node_type_str(node->type)); - break; case NodeTypeBoolLiteral: fprintf(stderr, "%s '%s'\n", node_type_str(node->type), node->data.bool_literal.value ? "true" : "false"); @@ -431,24 +377,28 @@ void ast_print(AstNode *node, int indent) { ast_print(node->data.struct_field.type, indent + 2); } break; - case NodeTypeStructValueExpr: - fprintf(stderr, "%s\n", node_type_str(node->type)); - ast_print(node->data.struct_val_expr.type, indent + 2); - for (int i = 0; i < node->data.struct_val_expr.fields.length; i += 1) { - AstNode *child = node->data.struct_val_expr.fields.at(i); - ast_print(child, indent + 2); - } - break; case NodeTypeStructValueField: fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.struct_val_field.name)); ast_print(node->data.struct_val_field.expr, indent + 2); break; - case NodeTypeCompilerFnExpr: - fprintf(stderr, "%s\n", node_type_str(node->type)); - break; - case NodeTypeCompilerFnType: + case NodeTypeContainerInitExpr: fprintf(stderr, "%s\n", node_type_str(node->type)); + ast_print(node->data.container_init_expr.type, indent + 2); + for (int i = 0; i < node->data.container_init_expr.entries.length; i += 1) { + AstNode *child = node->data.container_init_expr.entries.at(i); + ast_print(child, indent + 2); + } break; + case NodeTypeArrayType: + { + const char *const_str = node->data.array_type.is_const ? "const" : "var"; + fprintf(stderr, "%s %s\n", node_type_str(node->type), const_str); + if (node->data.array_type.size) { + ast_print(node->data.array_type.size, indent + 2); + } + ast_print(node->data.array_type.child_type, indent + 2); + break; + } } } @@ -539,9 +489,8 @@ static AstNode *ast_create_node_with_node(ParseContext *pc, NodeType type, AstNo } static AstNode *ast_create_void_type_node(ParseContext *pc, Token *token) { - AstNode *node = ast_create_node(pc, NodeTypeType, token); - node->data.type.type = AstNodeTypeTypePrimitive; - buf_init_from_str(&node->data.type.primitive_name, "void"); + AstNode *node = ast_create_node(pc, NodeTypeSymbol, token); + buf_init_from_str(&node->data.symbol_expr.symbol, "void"); return node; } @@ -961,7 +910,7 @@ static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool ma static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory); static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory); static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool mandatory); -static AstNode *ast_parse_type(ParseContext *pc, int *token_index); +static AstNode *ast_parse_unwrap_maybe_expr(ParseContext *pc, int *token_index, bool mandatory); static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) { if (token->id != token_id) { @@ -1022,170 +971,40 @@ static void ast_parse_directives(ParseContext *pc, int *token_index, zig_unreachable(); } -static void ast_parse_type_assume_amp(ParseContext *pc, int *token_index, AstNode *node) { - node->data.type.type = AstNodeTypeTypePointer; - Token *first_type_token = &pc->tokens->at(*token_index); - if (first_type_token->id == TokenIdKeywordConst) { - node->data.type.is_const = true; - *token_index += 1; - first_type_token = &pc->tokens->at(*token_index); - if (first_type_token->id == TokenIdKeywordNoAlias) { - node->data.type.is_noalias = true; - *token_index += 1; - } - } else if (first_type_token->id == TokenIdKeywordNoAlias) { - node->data.type.is_noalias = true; - *token_index += 1; - } - - node->data.type.child_type = ast_parse_type(pc, token_index); -} - /* -CompilerFnType : token(NumberSign) token(Symbol) token(LParen) Expression token(RParen) -*/ -static AstNode *ast_parse_compiler_fn_type(ParseContext *pc, int *token_index, bool mandatory) { - Token *token = &pc->tokens->at(*token_index); - - if (token->id == TokenIdNumberSign) { - *token_index += 1; - } else if (mandatory) { - ast_invalid_token_error(pc, token); - } else { - return nullptr; - } - - Token *name_symbol = ast_eat_token(pc, token_index, TokenIdSymbol); - ast_eat_token(pc, token_index, TokenIdLParen); - - AstNode *node = ast_create_node(pc, NodeTypeCompilerFnType, token); - ast_buf_from_token(pc, name_symbol, &node->data.compiler_fn_type.name); - node->data.compiler_fn_type.type = ast_parse_type(pc, token_index); - - ast_eat_token(pc, token_index, TokenIdRParen); - return node; -} - -/* -CompilerFnExpr : token(NumberSign) token(Symbol) token(LParen) Expression token(RParen) -*/ -static AstNode *ast_parse_compiler_fn_call(ParseContext *pc, int *token_index, bool mandatory) { - Token *token = &pc->tokens->at(*token_index); - - if (token->id == TokenIdNumberSign) { - *token_index += 1; - } else if (mandatory) { - ast_invalid_token_error(pc, token); - } else { - return nullptr; - } - - Token *name_symbol = ast_eat_token(pc, token_index, TokenIdSymbol); - ast_eat_token(pc, token_index, TokenIdLParen); - - AstNode *node = ast_create_node(pc, NodeTypeCompilerFnExpr, token); - ast_buf_from_token(pc, name_symbol, &node->data.compiler_fn_expr.name); - node->data.compiler_fn_expr.expr = ast_parse_expression(pc, token_index, true); - - ast_eat_token(pc, token_index, TokenIdRParen); - return node; -} - -/* -Type : token(Symbol) | token(Unreachable) | token(Void) | PointerType | ArrayType | MaybeType | CompilerFnExpr -PointerType : token(Ampersand) option(token(Const)) option(token(NoAlias)) Type -ArrayType : token(LBracket) option(Expression) token(RBracket) option(token(Const)) option(token(NoAlias)) Type -*/ -static AstNode *ast_parse_type(ParseContext *pc, int *token_index) { - Token *token = &pc->tokens->at(*token_index); - AstNode *node = ast_create_node(pc, NodeTypeType, token); - - AstNode *compiler_fn_expr = ast_parse_compiler_fn_call(pc, token_index, false); - if (compiler_fn_expr) { - node->data.type.type = AstNodeTypeTypeCompilerExpr; - node->data.type.compiler_expr = compiler_fn_expr; - return node; - } - - *token_index += 1; - - if (token->id == TokenIdKeywordUnreachable) { - node->data.type.type = AstNodeTypeTypePrimitive; - buf_init_from_str(&node->data.type.primitive_name, "unreachable"); - } else if (token->id == TokenIdKeywordVoid) { - node->data.type.type = AstNodeTypeTypePrimitive; - buf_init_from_str(&node->data.type.primitive_name, "void"); - } else if (token->id == TokenIdSymbol) { - node->data.type.type = AstNodeTypeTypePrimitive; - ast_buf_from_token(pc, token, &node->data.type.primitive_name); - } else if (token->id == TokenIdAmpersand) { - ast_parse_type_assume_amp(pc, token_index, node); - } else if (token->id == TokenIdMaybe) { - node->data.type.type = AstNodeTypeTypeMaybe; - node->data.type.child_type = ast_parse_type(pc, token_index); - } else if (token->id == TokenIdBoolAnd) { - // Pretend that we got 2 ampersand tokens - node->data.type.type = AstNodeTypeTypePointer; - - node->data.type.child_type = ast_create_node_no_line_info(pc, NodeTypeType); - node->data.type.child_type->line = token->start_line; - node->data.type.child_type->column = token->start_column + 1; - - ast_parse_type_assume_amp(pc, token_index, node->data.type.child_type); - } else if (token->id == TokenIdLBracket) { - node->data.type.type = AstNodeTypeTypeArray; - - node->data.type.array_size = ast_parse_expression(pc, token_index, false); - - ast_eat_token(pc, token_index, TokenIdRBracket); - - Token *const_tok = &pc->tokens->at(*token_index); - if (const_tok->id == TokenIdKeywordConst) { - *token_index += 1; - node->data.type.is_const = true; - - Token *next_tok = &pc->tokens->at(*token_index); - if (next_tok->id == TokenIdKeywordNoAlias) { - *token_index += 1; - node->data.type.is_noalias = true; - } - } else if (const_tok->id == TokenIdKeywordNoAlias) { - *token_index += 1; - node->data.type.is_noalias = true; - } - - node->data.type.child_type = ast_parse_type(pc, token_index); - } else { - ast_invalid_token_error(pc, token); - } - - return node; -} - -/* -ParamDecl : token(Symbol) token(Colon) Type | token(Ellipsis) +ParamDecl : option(token(NoAlias)) token(Symbol) token(Colon) UnwrapMaybeExpression | token(Ellipsis) */ static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) { - Token *param_name = &pc->tokens->at(*token_index); - *token_index += 1; + Token *first_token = &pc->tokens->at(*token_index); - if (param_name->id == TokenIdSymbol) { - AstNode *node = ast_create_node(pc, NodeTypeParamDecl, param_name); - - ast_buf_from_token(pc, param_name, &node->data.param_decl.name); - - Token *colon = &pc->tokens->at(*token_index); + if (first_token->id == TokenIdEllipsis) { *token_index += 1; - ast_expect_token(pc, colon, TokenIdColon); - - node->data.param_decl.type = ast_parse_type(pc, token_index); - - return node; - } else if (param_name->id == TokenIdEllipsis) { return nullptr; - } else { - ast_invalid_token_error(pc, param_name); } + + AstNode *node = ast_create_node(pc, NodeTypeParamDecl, first_token); + Token *name_token; + + if (first_token->id == TokenIdKeywordNoAlias) { + node->data.param_decl.is_noalias = true; + *token_index += 1; + name_token = ast_eat_token(pc, token_index, TokenIdSymbol); + } else if (first_token->id == TokenIdSymbol) { + name_token = first_token; + *token_index += 1; + } else { + ast_invalid_token_error(pc, first_token); + } + + ast_buf_from_token(pc, name_token, &node->data.param_decl.name); + + Token *colon = &pc->tokens->at(*token_index); + *token_index += 1; + ast_expect_token(pc, colon, TokenIdColon); + + node->data.param_decl.type = ast_parse_unwrap_maybe_expr(pc, token_index, true); + + return node; } @@ -1274,52 +1093,222 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool } /* -StructValueExpression : token(Symbol) token(LBrace) list(StructValueExpressionField, token(Comma)) token(RBrace) -StructValueExpressionField : token(Dot) token(Symbol) token(Eq) Expression +ArrayType : token(LBracket) option(Expression) token(RBracket) option(token(Const)) Expression */ -static AstNode *ast_parse_struct_val_expr(ParseContext *pc, int *token_index) { - Token *first_token = &pc->tokens->at(*token_index); - AstNode *node = ast_create_node(pc, NodeTypeStructValueExpr, first_token); +static AstNode *ast_parse_array_type_expr(ParseContext *pc, int *token_index, bool mandatory) { + Token *l_bracket = &pc->tokens->at(*token_index); + if (l_bracket->id != TokenIdLBracket) { + if (mandatory) { + ast_invalid_token_error(pc, l_bracket); + } else { + return nullptr; + } + } - node->data.struct_val_expr.type = ast_parse_type(pc, token_index); + *token_index += 1; - ast_eat_token(pc, token_index, TokenIdLBrace); + AstNode *node = ast_create_node(pc, NodeTypeArrayType, l_bracket); + node->data.array_type.size = ast_parse_expression(pc, token_index, false); + + ast_eat_token(pc, token_index, TokenIdRBracket); + + Token *const_tok = &pc->tokens->at(*token_index); + if (const_tok->id == TokenIdKeywordConst) { + *token_index += 1; + node->data.array_type.is_const = true; + } + + node->data.array_type.child_type = ast_parse_expression(pc, token_index, true); + + return node; +} + +/* +AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) Expression token(RParen) +*/ +static void ast_parse_asm_input_item(ParseContext *pc, int *token_index, AstNode *node) { + ast_eat_token(pc, token_index, TokenIdLBracket); + Token *alias = ast_eat_token(pc, token_index, TokenIdSymbol); + ast_eat_token(pc, token_index, TokenIdRBracket); + + Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral); + + ast_eat_token(pc, token_index, TokenIdLParen); + AstNode *expr_node = ast_parse_expression(pc, token_index, true); + ast_eat_token(pc, token_index, TokenIdRParen); + + AsmInput *asm_input = allocate(1); + ast_buf_from_token(pc, alias, &asm_input->asm_symbolic_name); + parse_string_literal(pc, constraint, &asm_input->constraint, nullptr, nullptr); + asm_input->expr = expr_node; + node->data.asm_expr.input_list.append(asm_input); +} + +/* +AsmOutputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) (token(Symbol) | token(Arrow) Expression) token(RParen) +*/ +static void ast_parse_asm_output_item(ParseContext *pc, int *token_index, AstNode *node) { + ast_eat_token(pc, token_index, TokenIdLBracket); + Token *alias = ast_eat_token(pc, token_index, TokenIdSymbol); + ast_eat_token(pc, token_index, TokenIdRBracket); + + Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral); + + AsmOutput *asm_output = allocate(1); + + ast_eat_token(pc, token_index, TokenIdLParen); + + Token *token = &pc->tokens->at(*token_index); + *token_index += 1; + if (token->id == TokenIdSymbol) { + ast_buf_from_token(pc, token, &asm_output->variable_name); + } else if (token->id == TokenIdArrow) { + asm_output->return_type = ast_parse_expression(pc, token_index, true); + } else { + ast_invalid_token_error(pc, token); + } + + ast_eat_token(pc, token_index, TokenIdRParen); + + ast_buf_from_token(pc, alias, &asm_output->asm_symbolic_name); + parse_string_literal(pc, constraint, &asm_output->constraint, nullptr, nullptr); + node->data.asm_expr.output_list.append(asm_output); +} + +/* +AsmClobbers: token(Colon) list(token(String), token(Comma)) +*/ +static void ast_parse_asm_clobbers(ParseContext *pc, int *token_index, AstNode *node) { + Token *colon_tok = &pc->tokens->at(*token_index); + + if (colon_tok->id != TokenIdColon) + return; + + *token_index += 1; for (;;) { - Token *token = &pc->tokens->at(*token_index); + Token *string_tok = &pc->tokens->at(*token_index); + ast_expect_token(pc, string_tok, TokenIdStringLiteral); *token_index += 1; - if (token->id == TokenIdRBrace) { - return node; - } else if (token->id == TokenIdDot) { - Token *field_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); - ast_eat_token(pc, token_index, TokenIdEq); + Buf *clobber_buf = buf_alloc(); + parse_string_literal(pc, string_tok, clobber_buf, nullptr, nullptr); + node->data.asm_expr.clobber_list.append(clobber_buf); - AstNode *field_node = ast_create_node(pc, NodeTypeStructValueField, token); + Token *comma = &pc->tokens->at(*token_index); - ast_buf_from_token(pc, field_name_tok, &field_node->data.struct_val_field.name); - field_node->data.struct_val_field.expr = ast_parse_expression(pc, token_index, true); - - node->data.struct_val_expr.fields.append(field_node); - - Token *comma_tok = &pc->tokens->at(*token_index); - if (comma_tok->id == TokenIdComma) { - *token_index += 1; - } else if (comma_tok->id != TokenIdRBrace) { - ast_invalid_token_error(pc, comma_tok); - } else { - *token_index += 1; - return node; - } + if (comma->id == TokenIdComma) { + *token_index += 1; + continue; } else { - ast_invalid_token_error(pc, token); + break; } } } /* -PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType | (token(AtSign) token(Symbol) FnCallExpression) -KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) | token(Null) +AsmInput : token(Colon) list(AsmInputItem, token(Comma)) option(AsmClobbers) +*/ +static void ast_parse_asm_input(ParseContext *pc, int *token_index, AstNode *node) { + Token *colon_tok = &pc->tokens->at(*token_index); + + if (colon_tok->id != TokenIdColon) + return; + + *token_index += 1; + + for (;;) { + ast_parse_asm_input_item(pc, token_index, node); + + Token *comma = &pc->tokens->at(*token_index); + + if (comma->id == TokenIdComma) { + *token_index += 1; + continue; + } else { + break; + } + } + + ast_parse_asm_clobbers(pc, token_index, node); +} + +/* +AsmOutput : token(Colon) list(AsmOutputItem, token(Comma)) option(AsmInput) +*/ +static void ast_parse_asm_output(ParseContext *pc, int *token_index, AstNode *node) { + Token *colon_tok = &pc->tokens->at(*token_index); + + if (colon_tok->id != TokenIdColon) + return; + + *token_index += 1; + + for (;;) { + ast_parse_asm_output_item(pc, token_index, node); + + Token *comma = &pc->tokens->at(*token_index); + + if (comma->id == TokenIdComma) { + *token_index += 1; + continue; + } else { + break; + } + } + + ast_parse_asm_input(pc, token_index, node); +} + +/* +AsmExpression : token(Asm) option(token(Volatile)) token(LParen) token(String) option(AsmOutput) token(RParen) +*/ +static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mandatory) { + Token *asm_token = &pc->tokens->at(*token_index); + + if (asm_token->id != TokenIdKeywordAsm) { + if (mandatory) { + ast_invalid_token_error(pc, asm_token); + } else { + return nullptr; + } + } + + AstNode *node = ast_create_node(pc, NodeTypeAsmExpr, asm_token); + + *token_index += 1; + Token *lparen_tok = &pc->tokens->at(*token_index); + + if (lparen_tok->id == TokenIdKeywordVolatile) { + node->data.asm_expr.is_volatile = true; + + *token_index += 1; + lparen_tok = &pc->tokens->at(*token_index); + } + + ast_expect_token(pc, lparen_tok, TokenIdLParen); + *token_index += 1; + + Token *template_tok = &pc->tokens->at(*token_index); + ast_expect_token(pc, template_tok, TokenIdStringLiteral); + *token_index += 1; + + parse_string_literal(pc, template_tok, &node->data.asm_expr.asm_template, nullptr, + &node->data.asm_expr.offset_map); + parse_asm_template(pc, node); + + ast_parse_asm_output(pc, token_index, node); + + Token *rparen_tok = &pc->tokens->at(*token_index); + ast_expect_token(pc, rparen_tok, TokenIdRParen); + *token_index += 1; + + return node; +} + +/* +PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | token(Symbol) | (token(AtSign) token(Symbol) FnCallExpression) | ArrayType | AsmExpression +KeywordLiteral : token(True) | token(False) | token(Null) | token(Break) | token(Continue) */ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -1339,14 +1328,6 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool node->data.char_literal.value = parse_char_literal(pc, token); *token_index += 1; return node; - } else if (token->id == TokenIdKeywordUnreachable) { - AstNode *node = ast_create_node(pc, NodeTypeUnreachable, token); - *token_index += 1; - return node; - } else if (token->id == TokenIdKeywordVoid) { - AstNode *node = ast_create_node(pc, NodeTypeVoid, token); - *token_index += 1; - return node; } else if (token->id == TokenIdKeywordTrue) { AstNode *node = ast_create_node(pc, NodeTypeBoolLiteral, token); node->data.bool_literal.value = true; @@ -1361,6 +1342,14 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool AstNode *node = ast_create_node(pc, NodeTypeNullLiteral, token); *token_index += 1; return node; + } else if (token->id == TokenIdKeywordBreak) { + AstNode *node = ast_create_node(pc, NodeTypeBreak, token); + *token_index += 1; + return node; + } else if (token->id == TokenIdKeywordContinue) { + AstNode *node = ast_create_node(pc, NodeTypeContinue, token); + *token_index += 1; + return node; } else if (token->id == TokenIdAtSign) { *token_index += 1; Token *name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); @@ -1374,16 +1363,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool node->data.fn_call_expr.is_builtin = true; return node; } else if (token->id == TokenIdSymbol) { - Token *next_token = &pc->tokens->at(*token_index + 1); - - if (next_token->id == TokenIdLBrace) { - return ast_parse_struct_val_expr(pc, token_index); - } else { - *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeSymbol, token); - ast_buf_from_token(pc, token, &node->data.symbol_expr.symbol); - return node; - } + *token_index += 1; + AstNode *node = ast_create_node(pc, NodeTypeSymbol, token); + ast_buf_from_token(pc, token, &node->data.symbol_expr.symbol); + return node; } else if (token->id == TokenIdKeywordGoto) { AstNode *node = ast_create_node(pc, NodeTypeGoto, token); *token_index += 1; @@ -1394,14 +1377,6 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool ast_buf_from_token(pc, dest_symbol, &node->data.goto_expr.name); return node; - } else if (token->id == TokenIdKeywordBreak) { - AstNode *node = ast_create_node(pc, NodeTypeBreak, token); - *token_index += 1; - return node; - } else if (token->id == TokenIdKeywordContinue) { - AstNode *node = ast_create_node(pc, NodeTypeContinue, token); - *token_index += 1; - return node; } AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false); @@ -1414,9 +1389,14 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool return block_expr_node; } - AstNode *compiler_fn_type = ast_parse_compiler_fn_type(pc, token_index, false); - if (compiler_fn_type) { - return compiler_fn_type; + AstNode *array_type_node = ast_parse_array_type_expr(pc, token_index, false); + if (array_type_node) { + return array_type_node; + } + + AstNode *asm_expr = ast_parse_asm_expr(pc, token_index, false); + if (asm_expr) { + return asm_expr; } if (!mandatory) @@ -1426,11 +1406,14 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool } /* -SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) +SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | ContainerInitExpression) FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) ArrayAccessExpression : token(LBracket) Expression token(RBracket) SliceExpression : token(LBracket) Expression token(Ellipsis) option(Expression) token(RBracket) option(token(Const)) FieldAccessExpression : token(Dot) token(Symbol) +ContainerInitExpression : token(LBrace) ContainerInitBody token(RBrace) +ContainerInitBody : list(StructLiteralField, token(Comma)) | list(Expression, token(Comma)) +StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression */ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, bool mandatory) { AstNode *primary_expr = ast_parse_primary_expr(pc, token_index, mandatory); @@ -1439,16 +1422,16 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, boo } while (true) { - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdLParen) { + Token *first_token = &pc->tokens->at(*token_index); + if (first_token->id == TokenIdLParen) { *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, token); + AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, first_token); node->data.fn_call_expr.fn_ref_expr = primary_expr; ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params); primary_expr = node; - } else if (token->id == TokenIdLBracket) { + } else if (first_token->id == TokenIdLBracket) { *token_index += 1; AstNode *expr_node = ast_parse_expression(pc, token_index, true); @@ -1458,7 +1441,7 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, boo if (ellipsis_or_r_bracket->id == TokenIdEllipsis) { *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeSliceExpr, token); + AstNode *node = ast_create_node(pc, NodeTypeSliceExpr, first_token); node->data.slice_expr.array_ref_expr = primary_expr; node->data.slice_expr.start = expr_node; node->data.slice_expr.end = ast_parse_expression(pc, token_index, false); @@ -1475,23 +1458,88 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, boo } else if (ellipsis_or_r_bracket->id == TokenIdRBracket) { *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeArrayAccessExpr, token); + AstNode *node = ast_create_node(pc, NodeTypeArrayAccessExpr, first_token); node->data.array_access_expr.array_ref_expr = primary_expr; node->data.array_access_expr.subscript = expr_node; primary_expr = node; } else { - ast_invalid_token_error(pc, token); + ast_invalid_token_error(pc, first_token); } - } else if (token->id == TokenIdDot) { + } else if (first_token->id == TokenIdDot) { *token_index += 1; Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol); - AstNode *node = ast_create_node(pc, NodeTypeFieldAccessExpr, token); + AstNode *node = ast_create_node(pc, NodeTypeFieldAccessExpr, first_token); node->data.field_access_expr.struct_expr = primary_expr; ast_buf_from_token(pc, name_token, &node->data.field_access_expr.field_name); + primary_expr = node; + } else if (first_token->id == TokenIdLBrace) { + *token_index += 1; + + AstNode *node = ast_create_node(pc, NodeTypeContainerInitExpr, first_token); + node->data.container_init_expr.type = primary_expr; + + Token *token = &pc->tokens->at(*token_index); + if (token->id == TokenIdDot) { + for (;;) { + if (token->id == TokenIdDot) { + ast_eat_token(pc, token_index, TokenIdDot); + Token *field_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); + ast_eat_token(pc, token_index, TokenIdEq); + + AstNode *field_node = ast_create_node(pc, NodeTypeStructValueField, token); + + ast_buf_from_token(pc, field_name_tok, &field_node->data.struct_val_field.name); + field_node->data.struct_val_field.expr = ast_parse_expression(pc, token_index, true); + + node->data.container_init_expr.entries.append(field_node); + + Token *comma_tok = &pc->tokens->at(*token_index); + if (comma_tok->id == TokenIdComma) { + *token_index += 1; + token = &pc->tokens->at(*token_index); + continue; + } else if (comma_tok->id != TokenIdRBrace) { + ast_invalid_token_error(pc, comma_tok); + } else { + *token_index += 1; + break; + } + } else if (token->id == TokenIdRBrace) { + *token_index += 1; + break; + } else { + ast_invalid_token_error(pc, token); + } + } + + } else { + for (;;) { + if (token->id == TokenIdRBrace) { + *token_index += 1; + break; + } else { + AstNode *elem_node = ast_parse_expression(pc, token_index, true); + node->data.container_init_expr.entries.append(elem_node); + + Token *comma_tok = &pc->tokens->at(*token_index); + if (comma_tok->id == TokenIdComma) { + *token_index += 1; + token = &pc->tokens->at(*token_index); + continue; + } else if (comma_tok->id != TokenIdRBrace) { + ast_invalid_token_error(pc, comma_tok); + } else { + *token_index += 1; + break; + } + } + } + } + primary_expr = node; } else { return primary_expr; @@ -1506,56 +1554,54 @@ static PrefixOp tok_to_prefix_op(Token *token) { case TokenIdTilde: return PrefixOpBinNot; case TokenIdAmpersand: return PrefixOpAddressOf; case TokenIdStar: return PrefixOpDereference; + case TokenIdMaybe: return PrefixOpMaybe; + case TokenIdBoolAnd: return PrefixOpAddressOf; default: return PrefixOpInvalid; } } -/* -PrefixOp : token(Not) | token(Dash) | token(Tilde) | token(Star) | (token(Ampersand) option(token(Const))) -*/ -static PrefixOp ast_parse_prefix_op(ParseContext *pc, int *token_index, bool mandatory) { - Token *token = &pc->tokens->at(*token_index); - PrefixOp result = tok_to_prefix_op(token); - if (result == PrefixOpInvalid) { - if (mandatory) { - ast_invalid_token_error(pc, token); - } else { - return PrefixOpInvalid; - } - } - *token_index += 1; - - if (result == PrefixOpAddressOf) { - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdKeywordConst) { - *token_index += 1; - result = PrefixOpConstAddressOf; - } - } - - return result; -} - /* PrefixOpExpression : PrefixOp PrefixOpExpression | SuffixOpExpression +PrefixOp : token(Not) | token(Dash) | token(Tilde) | token(Star) | (token(Ampersand) option(token(Const))) */ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); - PrefixOp prefix_op = ast_parse_prefix_op(pc, token_index, false); - if (prefix_op == PrefixOpInvalid) + PrefixOp prefix_op = tok_to_prefix_op(token); + if (prefix_op == PrefixOpInvalid) { return ast_parse_suffix_op_expr(pc, token_index, mandatory); + } + *token_index += 1; + + AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, token); + AstNode *parent_node = node; + if (token->id == TokenIdBoolAnd) { + // pretend that we got 2 ampersand tokens + + parent_node = ast_create_node(pc, NodeTypePrefixOpExpr, token); + parent_node->data.prefix_op_expr.primary_expr = node; + parent_node->data.prefix_op_expr.prefix_op = PrefixOpAddressOf; + + node->column += 1; + } + + if (prefix_op == PrefixOpAddressOf) { + Token *token = &pc->tokens->at(*token_index); + if (token->id == TokenIdKeywordConst) { + *token_index += 1; + prefix_op = PrefixOpConstAddressOf; + } + } AstNode *prefix_op_expr = ast_parse_prefix_op_expr(pc, token_index, true); - AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, token); node->data.prefix_op_expr.primary_expr = prefix_op_expr; node->data.prefix_op_expr.prefix_op = prefix_op; - return node; + return parent_node; } /* -CastExpression : CastExpression token(as) Type | PrefixOpExpression +CastExpression : CastExpression token(as) PrimaryExpression | PrefixOpExpression */ static AstNode *ast_parse_cast_expression(ParseContext *pc, int *token_index, bool mandatory) { AstNode *operand_1 = ast_parse_prefix_op_expr(pc, token_index, mandatory); @@ -1571,7 +1617,7 @@ static AstNode *ast_parse_cast_expression(ParseContext *pc, int *token_index, bo AstNode *node = ast_create_node(pc, NodeTypeCastExpr, as_kw); node->data.cast_expr.expr = operand_1; - node->data.cast_expr.type = ast_parse_type(pc, token_index); + node->data.cast_expr.type = ast_parse_primary_expr(pc, token_index, true); operand_1 = node; } @@ -1899,7 +1945,7 @@ static AstNode *ast_parse_else(ParseContext *pc, int *token_index, bool mandator /* IfExpression : IfVarExpression | IfBoolExpression IfBoolExpression : token(If) token(LParen) Expression token(RParen) Expression option(Else) -IfVarExpression : token(If) token(LParen) (token(Const) | token(Var)) token(Symbol) option(token(Colon) Type) Token(MaybeAssign) Expression token(RParen) Expression Option(Else) +IfVarExpression : token(If) token(LParen) (token(Const) | token(Var)) token(Symbol) option(Expression) Token(MaybeAssign) Expression token(RParen) Expression Option(Else) */ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *if_tok = &pc->tokens->at(*token_index); @@ -1924,11 +1970,12 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda ast_buf_from_token(pc, name_token, &node->data.if_var_expr.var_decl.symbol); Token *eq_or_colon = &pc->tokens->at(*token_index); - *token_index += 1; if (eq_or_colon->id == TokenIdMaybeAssign) { + *token_index += 1; node->data.if_var_expr.var_decl.expr = ast_parse_expression(pc, token_index, true); } else if (eq_or_colon->id == TokenIdColon) { - node->data.if_var_expr.var_decl.type = ast_parse_type(pc, token_index); + *token_index += 1; + node->data.if_var_expr.var_decl.type = ast_parse_expression(pc, token_index, true); ast_eat_token(pc, token_index, TokenIdMaybeAssign); node->data.if_var_expr.var_decl.expr = ast_parse_expression(pc, token_index, true); @@ -1967,7 +2014,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m } /* -VariableDeclaration : option(FnVisibleMod) (token(Var) | token(Const)) token(Symbol) (token(Eq) Expression | token(Colon) Type option(token(Eq) Expression)) +VariableDeclaration : option(FnVisibleMod) (token(Var) | token(Const)) token(Symbol) (token(Eq) Expression | token(Colon) UnwrapMaybeExpression option(token(Eq) Expression)) */ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *first_token = &pc->tokens->at(*token_index); @@ -2008,8 +2055,7 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token node->data.variable_declaration.expr = ast_parse_expression(pc, token_index, true); return node; } else if (eq_or_colon->id == TokenIdColon) { - node->data.variable_declaration.type = ast_parse_type(pc, token_index); - + node->data.variable_declaration.type = ast_parse_unwrap_maybe_expr(pc, token_index, true); Token *eq_token = &pc->tokens->at(*token_index); if (eq_token->id == TokenIdEq) { *token_index += 1; @@ -2138,6 +2184,7 @@ static BinOpType ast_parse_ass_op(ParseContext *pc, int *token_index, bool manda /* UnwrapMaybeExpression : BoolOrExpression token(DoubleQuestion) BoolOrExpression | BoolOrExpression */ +// this is currently the first child expression of assignment static AstNode *ast_parse_unwrap_maybe_expr(ParseContext *pc, int *token_index, bool mandatory) { AstNode *lhs = ast_parse_bool_or_expr(pc, token_index, mandatory); if (!lhs) @@ -2185,190 +2232,7 @@ static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mand } /* -AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) Expression token(RParen) -*/ -static void ast_parse_asm_input_item(ParseContext *pc, int *token_index, AstNode *node) { - ast_eat_token(pc, token_index, TokenIdLBracket); - Token *alias = ast_eat_token(pc, token_index, TokenIdSymbol); - ast_eat_token(pc, token_index, TokenIdRBracket); - - Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral); - - ast_eat_token(pc, token_index, TokenIdLParen); - AstNode *expr_node = ast_parse_expression(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdRParen); - - AsmInput *asm_input = allocate(1); - ast_buf_from_token(pc, alias, &asm_input->asm_symbolic_name); - parse_string_literal(pc, constraint, &asm_input->constraint, nullptr, nullptr); - asm_input->expr = expr_node; - node->data.asm_expr.input_list.append(asm_input); -} - -/* -AsmOutputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) (token(Symbol) | token(Arrow) Type) token(RParen) -*/ -static void ast_parse_asm_output_item(ParseContext *pc, int *token_index, AstNode *node) { - ast_eat_token(pc, token_index, TokenIdLBracket); - Token *alias = ast_eat_token(pc, token_index, TokenIdSymbol); - ast_eat_token(pc, token_index, TokenIdRBracket); - - Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral); - - AsmOutput *asm_output = allocate(1); - - ast_eat_token(pc, token_index, TokenIdLParen); - - Token *token = &pc->tokens->at(*token_index); - *token_index += 1; - if (token->id == TokenIdSymbol) { - ast_buf_from_token(pc, token, &asm_output->variable_name); - } else if (token->id == TokenIdArrow) { - asm_output->return_type = ast_parse_type(pc, token_index); - } else { - ast_invalid_token_error(pc, token); - } - - ast_eat_token(pc, token_index, TokenIdRParen); - - ast_buf_from_token(pc, alias, &asm_output->asm_symbolic_name); - parse_string_literal(pc, constraint, &asm_output->constraint, nullptr, nullptr); - node->data.asm_expr.output_list.append(asm_output); -} - -/* -AsmClobbers: token(Colon) list(token(String), token(Comma)) -*/ -static void ast_parse_asm_clobbers(ParseContext *pc, int *token_index, AstNode *node) { - Token *colon_tok = &pc->tokens->at(*token_index); - - if (colon_tok->id != TokenIdColon) - return; - - *token_index += 1; - - for (;;) { - Token *string_tok = &pc->tokens->at(*token_index); - ast_expect_token(pc, string_tok, TokenIdStringLiteral); - *token_index += 1; - - Buf *clobber_buf = buf_alloc(); - parse_string_literal(pc, string_tok, clobber_buf, nullptr, nullptr); - node->data.asm_expr.clobber_list.append(clobber_buf); - - Token *comma = &pc->tokens->at(*token_index); - - if (comma->id == TokenIdComma) { - *token_index += 1; - continue; - } else { - break; - } - } -} - -/* -AsmInput : token(Colon) list(AsmInputItem, token(Comma)) option(AsmClobbers) -*/ -static void ast_parse_asm_input(ParseContext *pc, int *token_index, AstNode *node) { - Token *colon_tok = &pc->tokens->at(*token_index); - - if (colon_tok->id != TokenIdColon) - return; - - *token_index += 1; - - for (;;) { - ast_parse_asm_input_item(pc, token_index, node); - - Token *comma = &pc->tokens->at(*token_index); - - if (comma->id == TokenIdComma) { - *token_index += 1; - continue; - } else { - break; - } - } - - ast_parse_asm_clobbers(pc, token_index, node); -} - -/* -AsmOutput : token(Colon) list(AsmOutputItem, token(Comma)) option(AsmInput) -*/ -static void ast_parse_asm_output(ParseContext *pc, int *token_index, AstNode *node) { - Token *colon_tok = &pc->tokens->at(*token_index); - - if (colon_tok->id != TokenIdColon) - return; - - *token_index += 1; - - for (;;) { - ast_parse_asm_output_item(pc, token_index, node); - - Token *comma = &pc->tokens->at(*token_index); - - if (comma->id == TokenIdComma) { - *token_index += 1; - continue; - } else { - break; - } - } - - ast_parse_asm_input(pc, token_index, node); -} - -/* -AsmExpression : token(Asm) option(token(Volatile)) token(LParen) token(String) option(AsmOutput) token(RParen) -*/ -static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mandatory) { - Token *asm_token = &pc->tokens->at(*token_index); - - if (asm_token->id != TokenIdKeywordAsm) { - if (mandatory) { - ast_invalid_token_error(pc, asm_token); - } else { - return nullptr; - } - } - - AstNode *node = ast_create_node(pc, NodeTypeAsmExpr, asm_token); - - *token_index += 1; - Token *lparen_tok = &pc->tokens->at(*token_index); - - if (lparen_tok->id == TokenIdKeywordVolatile) { - node->data.asm_expr.is_volatile = true; - - *token_index += 1; - lparen_tok = &pc->tokens->at(*token_index); - } - - ast_expect_token(pc, lparen_tok, TokenIdLParen); - *token_index += 1; - - Token *template_tok = &pc->tokens->at(*token_index); - ast_expect_token(pc, template_tok, TokenIdStringLiteral); - *token_index += 1; - - parse_string_literal(pc, template_tok, &node->data.asm_expr.asm_template, nullptr, - &node->data.asm_expr.offset_map); - parse_asm_template(pc, node); - - ast_parse_asm_output(pc, token_index, node); - - Token *rparen_tok = &pc->tokens->at(*token_index); - ast_expect_token(pc, rparen_tok, TokenIdRParen); - *token_index += 1; - - return node; -} - -/* -NonBlockExpression : ReturnExpression | AssignmentExpression | AsmExpression +NonBlockExpression : ReturnExpression | AssignmentExpression */ static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -2381,10 +2245,6 @@ static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, boo if (ass_expr) return ass_expr; - AstNode *asm_expr = ast_parse_asm_expr(pc, token_index, false); - if (asm_expr) - return asm_expr; - if (mandatory) ast_invalid_token_error(pc, token); @@ -2440,6 +2300,14 @@ static AstNode *ast_parse_label(ParseContext *pc, int *token_index, bool mandato return node; } +static AstNode *ast_create_void_expr(ParseContext *pc, Token *token) { + AstNode *node = ast_create_node(pc, NodeTypeContainerInitExpr, token); + node->data.container_init_expr.type = ast_create_node(pc, NodeTypeSymbol, token); + node->data.container_init_expr.kind = ContainerInitKindArray; + buf_init_from_str(&node->data.container_init_expr.type->data.symbol_expr.symbol, "void"); + return node; +} + /* Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace) Statement : Label | VariableDeclaration token(Semicolon) | NonBlockExpression token(Semicolon) | BlockExpression @@ -2478,7 +2346,7 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato if (!statement_node) { statement_node = ast_parse_non_block_expr(pc, token_index, false); if (!statement_node) { - statement_node = ast_create_node(pc, NodeTypeVoid, last_token); + statement_node = ast_create_void_expr(pc, last_token); } } } @@ -2501,7 +2369,7 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato } /* -FnProto : many(Directive) option(FnVisibleMod) token(Fn) token(Symbol) ParamDeclList option(token(Arrow) Type) +FnProto : many(Directive) option(FnVisibleMod) token(Fn) token(Symbol) ParamDeclList option(Expression) */ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory) { Token *first_token = &pc->tokens->at(*token_index); @@ -2552,19 +2420,17 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args); - Token *arrow = &pc->tokens->at(*token_index); - if (arrow->id == TokenIdArrow) { - *token_index += 1; - node->data.fn_proto.return_type = ast_parse_type(pc, token_index); - } else { - node->data.fn_proto.return_type = ast_create_void_type_node(pc, arrow); + Token *next_token = &pc->tokens->at(*token_index); + node->data.fn_proto.return_type = ast_parse_expression(pc, token_index, false); + if (!node->data.fn_proto.return_type) { + node->data.fn_proto.return_type = ast_create_void_type_node(pc, next_token); } return node; } /* -FnDef : FnProto Block +FnDef : FnProto token(FatArrow) Block */ static AstNode *ast_parse_fn_def(ParseContext *pc, int *token_index, bool mandatory) { AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, mandatory); @@ -2573,6 +2439,7 @@ static AstNode *ast_parse_fn_def(ParseContext *pc, int *token_index, bool mandat AstNode *node = ast_create_node_with_node(pc, NodeTypeFnDef, fn_proto); node->data.fn_def.fn_proto = fn_proto; + ast_eat_token(pc, token_index, TokenIdFatArrow); node->data.fn_def.body = ast_parse_block(pc, token_index, true); return node; @@ -2709,7 +2576,7 @@ static AstNode *ast_parse_use(ParseContext *pc, int *token_index) { /* ContainerDecl : many(Directive) option(FnVisibleMod) (token(Struct) | token(Enum)) token(Symbol) token(LBrace) many(StructMember) token(RBrace) StructMember: StructField | FnDecl -StructField : token(Symbol) token(Colon) Type token(Comma) +StructField : token(Symbol) option(token(Colon) Expression) token(Comma)) */ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) { Token *first_token = &pc->tokens->at(*token_index); @@ -2792,16 +2659,16 @@ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) { ast_buf_from_token(pc, token, &field_node->data.struct_field.name); - Token *colon_tok = &pc->tokens->at(*token_index); - if (colon_tok->id == TokenIdColon) { + Token *expr_or_comma = &pc->tokens->at(*token_index); + if (expr_or_comma->id == TokenIdComma) { + field_node->data.struct_field.type = ast_create_void_type_node(pc, expr_or_comma); *token_index += 1; - field_node->data.struct_field.type = ast_parse_type(pc, token_index); } else { - field_node->data.struct_field.type = ast_create_void_type_node(pc, colon_tok); + ast_eat_token(pc, token_index, TokenIdColon); + field_node->data.struct_field.type = ast_parse_expression(pc, token_index, true); + ast_eat_token(pc, token_index, TokenIdComma); } - ast_eat_token(pc, token_index, TokenIdComma); - node->data.struct_decl.fields.append(field_node); } else { ast_invalid_token_error(pc, token); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index b49166a126..a74c03358c 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -207,8 +207,6 @@ static void end_token(Tokenize *t) { t->cur_tok->id = TokenIdKeywordConst; } else if (mem_eql_str(token_mem, token_len, "extern")) { t->cur_tok->id = TokenIdKeywordExtern; - } else if (mem_eql_str(token_mem, token_len, "unreachable")) { - t->cur_tok->id = TokenIdKeywordUnreachable; } else if (mem_eql_str(token_mem, token_len, "pub")) { t->cur_tok->id = TokenIdKeywordPub; } else if (mem_eql_str(token_mem, token_len, "export")) { @@ -217,8 +215,6 @@ static void end_token(Tokenize *t) { t->cur_tok->id = TokenIdKeywordAs; } else if (mem_eql_str(token_mem, token_len, "use")) { t->cur_tok->id = TokenIdKeywordUse; - } else if (mem_eql_str(token_mem, token_len, "void")) { - t->cur_tok->id = TokenIdKeywordVoid; } else if (mem_eql_str(token_mem, token_len, "true")) { t->cur_tok->id = TokenIdKeywordTrue; } else if (mem_eql_str(token_mem, token_len, "false")) { @@ -553,6 +549,11 @@ void tokenize(Buf *buf, Tokenization *out) { end_token(&t); t.state = TokenizeStateStart; break; + case '>': + t.cur_tok->id = TokenIdFatArrow; + end_token(&t); + t.state = TokenizeStateStart; + break; default: t.pos -= 1; end_token(&t); @@ -1009,12 +1010,10 @@ static const char * token_name(Token *token) { case TokenIdKeywordVar: return "Var"; case TokenIdKeywordReturn: return "Return"; case TokenIdKeywordExtern: return "Extern"; - case TokenIdKeywordUnreachable: return "Unreachable"; case TokenIdKeywordPub: return "Pub"; case TokenIdKeywordExport: return "Export"; case TokenIdKeywordAs: return "As"; case TokenIdKeywordUse: return "Use"; - case TokenIdKeywordVoid: return "Void"; case TokenIdKeywordTrue: return "True"; case TokenIdKeywordFalse: return "False"; case TokenIdKeywordIf: return "If"; @@ -1044,6 +1043,7 @@ static const char * token_name(Token *token) { case TokenIdPlus: return "Plus"; case TokenIdColon: return "Colon"; case TokenIdArrow: return "Arrow"; + case TokenIdFatArrow: return "FatArrow"; case TokenIdDash: return "Dash"; case TokenIdNumberSign: return "NumberSign"; case TokenIdBinOr: return "BinOr"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 5c99d172a0..b5eac3f050 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -18,12 +18,10 @@ enum TokenId { TokenIdKeywordVar, TokenIdKeywordConst, TokenIdKeywordExtern, - TokenIdKeywordUnreachable, TokenIdKeywordPub, TokenIdKeywordExport, TokenIdKeywordAs, TokenIdKeywordUse, - TokenIdKeywordVoid, TokenIdKeywordTrue, TokenIdKeywordFalse, TokenIdKeywordIf, @@ -53,6 +51,7 @@ enum TokenId { TokenIdPlus, TokenIdColon, TokenIdArrow, + TokenIdFatArrow, TokenIdDash, TokenIdNumberSign, TokenIdBoolOr, diff --git a/std/bootstrap.zig b/std/bootstrap.zig index b7b52353f8..88b84fde52 100644 --- a/std/bootstrap.zig +++ b/std/bootstrap.zig @@ -3,10 +3,35 @@ use "syscall.zig"; // The compiler treats this file special by implicitly importing the function `main` // from the root source file. +var env: &&u8; + #attribute("naked") -export fn _start() -> unreachable { - const argc = asm("mov (%%rsp), %[argc]" : [argc] "=r" (-> isize)); - const argv = asm("lea 0x8(%%rsp), %[argv]" : [argv] "=r" (-> &&u8)); - const env = asm("lea 0x10(%%rsp,%%rdi,8), %[env]" : [env] "=r" (-> &&u8)); - exit(main(argc, argv, env)) +export fn _start() unreachable => { + const argc = asm("mov (%%rsp), %[argc]": [argc] "=r" (-> isize)); + const argv = asm("lea 0x8(%%rsp), %[argv]": [argv] "=r" (-> &&u8)); + env = asm("lea 0x10(%%rsp,%%rdi,8), %[env]": [env] "=r" (-> &&u8)); + + exit(main(argc, argv, env)); + +/* + var args = @alloca_array([]u8, argc); + var i : @typeof(argc) = 0; + // TODO for in loop over the array + while (i < argc) { + const ptr = argv[i]; + args[i] = ptr[0...strlen(ptr)]; + i += 1; + } + exit(main(args)) + */ } + +/* +fn strlen(ptr: &u8) isize => { + var count: isize = 0; + while (ptr[count]) { + count += 1; + } + return count; +} +*/ diff --git a/std/builtin.zig b/std/builtin.zig index eaaee2d6fa..3cfaebb31e 100644 --- a/std/builtin.zig +++ b/std/builtin.zig @@ -1,8 +1,8 @@ // These functions are provided when not linking against libc because LLVM // sometimes generates code that calls them. -export fn memset(dest: &u8, c: u8, n: usize) -> &u8 { - var index : #typeof(n) = 0; +export fn memset(dest: &u8, c: u8, n: usize) &u8 => { + var index : @typeof(n) = 0; while (index != n) { dest[index] = c; index += 1; @@ -10,8 +10,8 @@ export fn memset(dest: &u8, c: u8, n: usize) -> &u8 { return dest; } -export fn memcpy(dest: &noalias u8, src: &const noalias u8, n: usize) -> &u8 { - var index : #typeof(n) = 0; +export fn memcpy(noalias dest: &u8, noalias src: &const u8, n: usize) &u8 => { + var index : @typeof(n) = 0; while (index != n) { dest[index] = src[index]; index += 1; diff --git a/std/rand.zig b/std/rand.zig index 637f34443d..d014a746b5 100644 --- a/std/rand.zig +++ b/std/rand.zig @@ -4,13 +4,13 @@ const ARRAY_SIZE : u16 = 624; /// Use `rand_init` to initialize this state. pub struct Rand { array: [ARRAY_SIZE]u32, - index: #typeof(ARRAY_SIZE), + index: @typeof(ARRAY_SIZE), /// Initialize random state with the given seed. - pub fn init(r: &Rand, seed: u32) { + pub fn init(r: &Rand, seed: u32) => { r.index = 0; r.array[0] = seed; - var i : #typeof(ARRAY_SIZE) = 1; + var i : @typeof(ARRAY_SIZE) = 1; while (i < ARRAY_SIZE) { const prev_value : u64 = r.array[i - 1]; r.array[i] = ((prev_value ^ (prev_value << 30)) * 0x6c078965 + i) as u32; @@ -20,7 +20,7 @@ pub struct Rand { /// Get 32 bits of randomness. - pub fn get_u32(r: &Rand) -> u32 { + pub fn get_u32(r: &Rand) u32 => { if (r.index == 0) { r.generate_numbers(); } @@ -37,13 +37,13 @@ pub struct Rand { } /// Fill `buf` with randomness. - pub fn get_bytes(r: &Rand, buf: []u8) { + pub fn get_bytes(r: &Rand, buf: []u8) => { var bytes_left = r.get_bytes_aligned(buf); if (bytes_left > 0) { - var rand_val_array : [#sizeof(u32)]u8; - *(rand_val_array.ptr as &u32) = r.get_u32(); + var rand_val_array : [@sizeof(u32)]u8; + *(rand_val_array.ptr as (&u32)) = r.get_u32(); while (bytes_left > 0) { - buf[buf.len - bytes_left] = rand_val_array[#sizeof(u32) - bytes_left]; + buf[buf.len - bytes_left] = rand_val_array[@sizeof(u32) - bytes_left]; bytes_left -= 1; } } @@ -51,23 +51,23 @@ pub struct Rand { /// Get a random unsigned integer with even distribution between `start` /// inclusive and `end` exclusive. - pub fn range_u64(r: &Rand, start: u64, end: u64) -> u64 { + pub fn range_u64(r: &Rand, start: u64, end: u64) u64 => { const range = end - start; - const leftover = #max_value(u64) % range; - const upper_bound = #max_value(u64) - leftover; - var rand_val_array : [#sizeof(u64)]u8; + const leftover = @max_value(u64) % range; + const upper_bound = @max_value(u64) - leftover; + var rand_val_array : [@sizeof(u64)]u8; while (true) { r.get_bytes_aligned(rand_val_array); - const rand_val = *(rand_val_array.ptr as &u64); + const rand_val = *(rand_val_array.ptr as (&u64)); if (rand_val < upper_bound) { return start + (rand_val % range); } } } - fn generate_numbers(r: &Rand) { - var i : #typeof(ARRAY_SIZE) = 0; + fn generate_numbers(r: &Rand) => { + var i : @typeof(ARRAY_SIZE) = 0; while (i < ARRAY_SIZE) { const y : u32 = (r.array[i] & 0x80000000) + (r.array[(i + 1) % ARRAY_SIZE] & 0x7fffffff); const untempered : u32 = r.array[(i + 397) % ARRAY_SIZE] ^ (y >> 1); @@ -82,11 +82,11 @@ pub struct Rand { } // does not populate the remaining (buf.len % 4) bytes - fn get_bytes_aligned(r: &Rand, buf: []u8) -> usize { + fn get_bytes_aligned(r: &Rand, buf: []u8) usize => { var bytes_left = buf.len; while (bytes_left >= 4) { - *(&buf[buf.len - bytes_left] as &u32) = r.get_u32(); - bytes_left -= #sizeof(u32); + *(&buf[buf.len - bytes_left] as (&u32)) = r.get_u32(); + bytes_left -= @sizeof(u32); } return bytes_left; } diff --git a/std/std.zig b/std/std.zig index e1b66d9cb1..667210f433 100644 --- a/std/std.zig +++ b/std/std.zig @@ -5,25 +5,25 @@ pub const stdout_fileno : isize = 1; pub const stderr_fileno : isize = 2; // TODO error handling -pub fn os_get_random_bytes(buf: &u8, count: usize) -> isize { +pub fn os_get_random_bytes(buf: &u8, count: usize) isize => { getrandom(buf, count, 0) } // TODO error handling // TODO handle buffering and flushing (mutex protected) -pub fn print_str(str: []const u8) -> isize { +pub fn print_str(str: []const u8) isize => { fprint_str(stdout_fileno, str) } // TODO error handling // TODO handle buffering and flushing (mutex protected) -pub fn fprint_str(fd: isize, str: []const u8) -> isize { +pub fn fprint_str(fd: isize, str: []const u8) isize => { write(fd, str.ptr, str.len) } // TODO handle buffering and flushing (mutex protected) // TODO error handling -pub fn print_u64(x: u64) -> isize { +pub fn print_u64(x: u64) isize => { var buf: [max_u64_base10_digits]u8; const len = buf_print_u64(buf, x); return write(stdout_fileno, buf.ptr, len); @@ -31,14 +31,14 @@ pub fn print_u64(x: u64) -> isize { // TODO handle buffering and flushing (mutex protected) // TODO error handling -pub fn print_i64(x: i64) -> isize { +pub fn print_i64(x: i64) isize => { var buf: [max_u64_base10_digits]u8; const len = buf_print_i64(buf, x); return write(stdout_fileno, buf.ptr, len); } // TODO error handling -pub fn readline(buf: []u8, out_len: &usize) -> bool { +pub fn readline(buf: []u8, out_len: &usize) bool => { const amt_read = read(stdin_fileno, buf.ptr, buf.len); if (amt_read < 0) { return true; @@ -48,10 +48,10 @@ pub fn readline(buf: []u8, out_len: &usize) -> bool { } // TODO return ?u64 when we support returning struct byval -pub fn parse_u64(buf: []u8, radix: u8, result: &u64) -> bool { +pub fn parse_u64(buf: []u8, radix: u8, result: &u64) bool => { var x : u64 = 0; - var i : #typeof(buf.len) = 0; + var i : @typeof(buf.len) = 0; while (i < buf.len) { const c = buf[i]; const digit = char_to_digit(c); @@ -77,7 +77,7 @@ pub fn parse_u64(buf: []u8, radix: u8, result: &u64) -> bool { return false; } -fn char_to_digit(c: u8) -> u8 { +fn char_to_digit(c: u8) u8 => { if ('0' <= c && c <= '9') { c - '0' } else if ('A' <= c && c <= 'Z') { @@ -85,13 +85,13 @@ fn char_to_digit(c: u8) -> u8 { } else if ('a' <= c && c <= 'z') { c - 'a' + 10 } else { - #max_value(u8) + @max_value(u8) } } const max_u64_base10_digits: usize = 20; -fn buf_print_i64(out_buf: []u8, x: i64) -> usize { +fn buf_print_i64(out_buf: []u8, x: i64) usize => { if (x < 0) { out_buf[0] = '-'; return 1 + buf_print_u64(out_buf[1...], ((-(x + 1)) as u64) + 1); @@ -100,7 +100,7 @@ fn buf_print_i64(out_buf: []u8, x: i64) -> usize { } } -fn buf_print_u64(out_buf: []u8, x: u64) -> usize { +fn buf_print_u64(out_buf: []u8, x: u64) usize => { var buf: [max_u64_base10_digits]u8; var a = x; var index = buf.len; diff --git a/std/syscall.zig b/std/syscall.zig index 97542363cb..436194778c 100644 --- a/std/syscall.zig +++ b/std/syscall.zig @@ -3,34 +3,34 @@ const SYS_write : usize = 1; const SYS_exit : usize = 60; const SYS_getrandom : usize = 318; -fn syscall1(number: usize, arg1: usize) -> usize { +fn syscall1(number: usize, arg1: usize) usize => { asm volatile ("syscall" : [ret] "={rax}" (-> usize) : [number] "{rax}" (number), [arg1] "{rdi}" (arg1) : "rcx", "r11") } -fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) -> usize { +fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize => { asm volatile ("syscall" : [ret] "={rax}" (-> usize) : [number] "{rax}" (number), [arg1] "{rdi}" (arg1), [arg2] "{rsi}" (arg2), [arg3] "{rdx}" (arg3) : "rcx", "r11") } -pub fn read(fd: isize, buf: &u8, count: usize) -> isize { +pub fn read(fd: isize, buf: &u8, count: usize) isize => { syscall3(SYS_read, fd as usize, buf as usize, count) as isize } -pub fn write(fd: isize, buf: &const u8, count: usize) -> isize { +pub fn write(fd: isize, buf: &const u8, count: usize) isize => { syscall3(SYS_write, fd as usize, buf as usize, count) as isize } -pub fn exit(status: i32) -> unreachable { +pub fn exit(status: i32) unreachable => { syscall1(SYS_exit, status as usize); - unreachable + unreachable{} } -pub fn getrandom(buf: &u8, count: usize, flags: u32) -> isize { +pub fn getrandom(buf: &u8, count: usize, flags: u32) isize => { syscall3(SYS_getrandom, buf as usize, count, flags as usize) as isize } diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 445433a863..04cf942c37 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -96,50 +96,50 @@ static TestCase *add_compile_fail_case(const char *case_name, const char *source static void add_compiling_test_cases(void) { add_simple_case("hello world with libc", R"SOURCE( - #link("c") - extern { - fn puts(s: &const u8) -> i32; - } +#link("c") +extern { + fn puts(s: &const u8) i32; +} - export fn main(argc: i32, argv: &&u8, env: &&u8) -> i32 { - puts(c"Hello, world!"); - return 0; - } +export fn main(argc: i32, argv: &&u8, env: &&u8) i32 => { + puts(c"Hello, world!"); + return 0; +} )SOURCE", "Hello, world!\n"); add_simple_case("function call", R"SOURCE( - use "std.zig"; - use "syscall.zig"; +use "std.zig"; +use "syscall.zig"; - fn empty_function_1() {} - fn empty_function_2() { return; } +fn empty_function_1() => {} +fn empty_function_2() => { return; } - pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { - empty_function_1(); - empty_function_2(); - this_is_a_function(); - } +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { + empty_function_1(); + empty_function_2(); + this_is_a_function(); +} - fn this_is_a_function() -> unreachable { - print_str("OK\n"); - exit(0); - } +fn this_is_a_function() unreachable => { + print_str("OK\n"); + exit(0); +} )SOURCE", "OK\n"); add_simple_case("comments", R"SOURCE( - use "std.zig"; +use "std.zig"; - /** - * multi line doc comment - */ - fn another_function() {} +/** + * multi line doc comment + */ +fn another_function() => {} - /// this is a documentation comment - /// doc comment line 2 - pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { - print_str(/* mid-line comment /* nested */ */ "OK\n"); - return 0; - } +/// this is a documentation comment +/// doc comment line 2 +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { + print_str(/* mid-line comment /* nested */ */ "OK\n"); + return 0; +} )SOURCE", "OK\n"); { @@ -147,13 +147,13 @@ static void add_compiling_test_cases(void) { use "std.zig"; use "foo.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { private_function(); print_str("OK 2\n"); return 0; } -fn private_function() { +fn private_function() => { print_text(); } )SOURCE", "OK 1\nOK 2\n"); @@ -163,11 +163,11 @@ use "std.zig"; // purposefully conflicting function with main.zig // but it's private so it should be OK -fn private_function() { +fn private_function() => { print_str("OK 1\n"); } -pub fn print_text() { +pub fn print_text() => { private_function(); } )SOURCE"); @@ -178,7 +178,7 @@ pub fn print_text() { use "foo.zig"; use "bar.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { foo_function(); bar_function(); return 0; @@ -187,7 +187,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { add_source_file(tc, "foo.zig", R"SOURCE( use "std.zig"; -pub fn foo_function() { +pub fn foo_function() => { print_str("OK\n"); } )SOURCE"); @@ -196,7 +196,7 @@ pub fn foo_function() { use "other.zig"; use "std.zig"; -pub fn bar_function() { +pub fn bar_function() => { if (foo_function()) { print_str("OK\n"); } @@ -204,7 +204,7 @@ pub fn bar_function() { )SOURCE"); add_source_file(tc, "other.zig", R"SOURCE( -pub fn foo_function() -> bool { +pub fn foo_function() bool => { // this one conflicts with the one from foo return true; } @@ -212,65 +212,65 @@ pub fn foo_function() -> bool { } add_simple_case("if statements", R"SOURCE( - use "std.zig"; +use "std.zig"; - pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { - if (1 != 0) { - print_str("1 is true\n"); - } else { - print_str("1 is false\n"); - } - if (0 != 0) { - print_str("0 is true\n"); - } else if (1 - 1 != 0) { - print_str("1 - 1 is true\n"); - } - if (!(0 != 0)) { - print_str("!0 is true\n"); - } - return 0; - } +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { + if (1 != 0) { + print_str("1 is true\n"); + } else { + print_str("1 is false\n"); + } + if (0 != 0) { + print_str("0 is true\n"); + } else if (1 - 1 != 0) { + print_str("1 - 1 is true\n"); + } + if (!(0 != 0)) { + print_str("!0 is true\n"); + } + return 0; +} )SOURCE", "1 is true\n!0 is true\n"); add_simple_case("params", R"SOURCE( - use "std.zig"; +use "std.zig"; - fn add(a: i32, b: i32) -> i32 { - a + b - } +fn add(a: i32, b: i32) i32 => { + a + b +} - pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { - if (add(22, 11) == 33) { - print_str("pass\n"); - } - return 0; - } +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { + if (add(22, 11) == 33) { + print_str("pass\n"); + } + return 0; +} )SOURCE", "pass\n"); add_simple_case("goto", R"SOURCE( - use "std.zig"; +use "std.zig"; - fn loop(a : i32) { - if (a == 0) { - goto done; - } - print_str("loop\n"); - loop(a - 1); +fn loop(a : i32) => { + if (a == 0) { + goto done; + } + print_str("loop\n"); + loop(a - 1); - done: - return; - } +done: + return; +} - pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { - loop(3); - return 0; - } +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { + loop(3); + return 0; +} )SOURCE", "loop\nloop\nloop\n"); add_simple_case("local variables", R"SOURCE( use "std.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { const a : i32 = 1; const b = 2 as i32; if (a + b == 3) { @@ -283,7 +283,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { add_simple_case("bool literals", R"SOURCE( use "std.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { if (true) { print_str("OK 1\n"); } if (false) { print_str("BAD 1\n"); } if (!true) { print_str("BAD 2\n"); } @@ -295,7 +295,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { add_simple_case("separate block scopes", R"SOURCE( use "std.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { if (true) { const no_conflict : i32 = 5; if (no_conflict == 5) { print_str("OK 1\n"); } @@ -313,12 +313,12 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { add_simple_case("void parameters", R"SOURCE( use "std.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { - void_fun(1, void, 2); +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { + void_fun(1, void{}, 2); return 0; } -fn void_fun(a : i32, b : void, c : i32) { +fn void_fun(a : i32, b : void, c : i32) => { const v = b; const vv : void = if (a == 1) {v} else {}; if (a + c == 3) { print_str("OK\n"); } @@ -333,16 +333,16 @@ struct Foo { b : i32, c : void, } -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { const foo = Foo { - .a = void, + .a = void{}, .b = 1, - .c = void, + .c = void{}, }; if (foo.b != 1) { print_str("BAD\n"); } - if (#sizeof(Foo) != 4) { + if (@sizeof(Foo) != 4) { print_str("BAD\n"); } print_str("OK\n"); @@ -354,7 +354,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { add_simple_case("mutable local variables", R"SOURCE( use "std.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { var zero : i32 = 0; if (zero == 0) { print_str("zero\n"); } @@ -374,7 +374,7 @@ done: add_simple_case("arrays", R"SOURCE( use "std.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { var array : [5]u32; var i : u32 = 0; @@ -401,7 +401,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { return 0; } -fn get_array_len(a: []u32) -> usize { +fn get_array_len(a: []u32) usize => { a.len } )SOURCE", "OK\n"); @@ -410,7 +410,7 @@ fn get_array_len(a: []u32) -> usize { add_simple_case("hello world without libc", R"SOURCE( use "std.zig"; -pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { +pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => { print_str("Hello, world!\n"); return 0; } @@ -420,7 +420,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { add_simple_case("a + b + c", R"SOURCE( use "std.zig"; -pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { +pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => { if (false || false || false) { print_str("BAD 1\n"); } if (true && true && false) { print_str("BAD 2\n"); } if (1 | 2 | 4 != 7) { print_str("BAD 3\n"); } @@ -442,7 +442,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { add_simple_case("short circuit", R"SOURCE( use "std.zig"; -pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { +pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => { if (true || { print_str("BAD 1\n"); false }) { print_str("OK 1\n"); } @@ -465,7 +465,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { add_simple_case("modify operators", R"SOURCE( use "std.zig"; -pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { +pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => { var i : i32 = 0; i += 5; if (i != 5) { print_str("BAD +=\n"); } i -= 2; if (i != 3) { print_str("BAD -=\n"); } @@ -488,10 +488,10 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { add_simple_case("number literals", R"SOURCE( #link("c") extern { - fn printf(__format: &const u8, ...) -> i32; + fn printf(__format: &const u8, ...) i32; } -export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { +export fn main(argc : isize, argv : &&u8, env : &&u8) i32 => { printf(c"\n"); printf(c"0: %llu\n", @@ -617,9 +617,9 @@ export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { add_simple_case("structs", R"SOURCE( use "std.zig"; -pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { +pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => { var foo : Foo; - @memset(&foo, 0, #sizeof(Foo)); + @memset(&foo, 0, @sizeof(Foo)); foo.a += 1; foo.b = foo.a == 1; test_foo(foo); @@ -638,12 +638,12 @@ struct Foo { b : bool, c : f32, } -fn test_foo(foo : Foo) { +fn test_foo(foo : Foo) => { if (!foo.b) { print_str("BAD\n"); } } -fn test_mutation(foo : &Foo) { +fn test_mutation(foo : &Foo) => { foo.c = 100; } struct Node { @@ -654,7 +654,7 @@ struct Node { struct Val { x: i32, } -fn test_point_to_self() { +fn test_point_to_self() => { var root : Node; root.val.x = 1; @@ -668,7 +668,7 @@ fn test_point_to_self() { print_str("BAD\n"); } } -fn test_byval_assign() { +fn test_byval_assign() => { var foo1 : Foo; var foo2 : Foo; @@ -680,7 +680,7 @@ fn test_byval_assign() { if (foo2.a != 1234) { print_str("BAD - byval assignment failed\n"); } } -fn test_initializer() { +fn test_initializer() => { const val = Val { .x = 42 }; if (val.x != 42) { print_str("BAD\n"); } } @@ -692,7 +692,7 @@ use "std.zig"; const g1 : i32 = 1233 + 1; var g2 : i32 = 0; -pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { +pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => { if (g2 != 0) { print_str("BAD\n"); } g2 = g1; if (g2 != 1234) { print_str("BAD\n"); } @@ -703,7 +703,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { add_simple_case("while loop", R"SOURCE( use "std.zig"; -pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { +pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => { var i : i32 = 0; while (i < 4) { print_str("loop\n"); @@ -711,7 +711,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { } return f(); } -fn f() -> i32 { +fn f() i32 => { while (true) { return 0; } @@ -720,7 +720,7 @@ fn f() -> i32 { add_simple_case("continue and break", R"SOURCE( use "std.zig"; -pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { +pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => { var i : i32 = 0; while (true) { print_str("loop\n"); @@ -736,7 +736,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { add_simple_case("maybe type", R"SOURCE( use "std.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { const x : ?bool = true; if (const y ?= x) { @@ -759,7 +759,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { const final_x : ?i32 = 13; - const num = final_x ?? unreachable; + const num = final_x ?? unreachable{}; if (num != 13) { print_str("BAD\n"); @@ -771,26 +771,26 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { add_simple_case("implicit cast after unreachable", R"SOURCE( use "std.zig"; -pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { +pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => { const x = outer(); if (x == 1234) { print_str("OK\n"); } return 0; } -fn inner() -> i32 { 1234 } -fn outer() -> isize { +fn inner() i32 => { 1234 } +fn outer() isize => { return inner(); } )SOURCE", "OK\n"); - add_simple_case("#sizeof() and #typeof()", R"SOURCE( + add_simple_case("@sizeof() and @typeof()", R"SOURCE( use "std.zig"; const x: u16 = 13; -const z: #typeof(x) = 19; -pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { - const y: #typeof(x) = 120; - print_u64(#sizeof(#typeof(y))); +const z: @typeof(x) = 19; +pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => { + const y: @typeof(x) = 120; + print_u64(@sizeof(@typeof(y))); print_str("\n"); return 0; } @@ -800,11 +800,11 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { use "std.zig"; struct Rand { seed: u32, - pub fn get_seed(r: Rand) -> u32 { + pub fn get_seed(r: Rand) u32 => { r.seed } } -pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { +pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => { const r = Rand {.seed = 1234}; if (r.get_seed() != 1234) { print_str("BAD seed\n"); @@ -817,7 +817,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { add_simple_case("pointer dereferencing", R"SOURCE( use "std.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { var x = 3 as i32; const y = &x; @@ -839,9 +839,9 @@ use "std.zig"; const ARRAY_SIZE : u8 = 20; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { var array : [ARRAY_SIZE]u8; - print_u64(#sizeof(#typeof(array))); + print_u64(@sizeof(@typeof(array))); print_str("\n"); return 0; } @@ -849,69 +849,69 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { add_simple_case("#min_value() and #max_value()", R"SOURCE( use "std.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { print_str("max u8: "); - print_u64(#max_value(u8)); + print_u64(@max_value(u8)); print_str("\n"); print_str("max u16: "); - print_u64(#max_value(u16)); + print_u64(@max_value(u16)); print_str("\n"); print_str("max u32: "); - print_u64(#max_value(u32)); + print_u64(@max_value(u32)); print_str("\n"); print_str("max u64: "); - print_u64(#max_value(u64)); + print_u64(@max_value(u64)); print_str("\n"); print_str("max i8: "); - print_i64(#max_value(i8)); + print_i64(@max_value(i8)); print_str("\n"); print_str("max i16: "); - print_i64(#max_value(i16)); + print_i64(@max_value(i16)); print_str("\n"); print_str("max i32: "); - print_i64(#max_value(i32)); + print_i64(@max_value(i32)); print_str("\n"); print_str("max i64: "); - print_i64(#max_value(i64)); + print_i64(@max_value(i64)); print_str("\n"); print_str("min u8: "); - print_u64(#min_value(u8)); + print_u64(@min_value(u8)); print_str("\n"); print_str("min u16: "); - print_u64(#min_value(u16)); + print_u64(@min_value(u16)); print_str("\n"); print_str("min u32: "); - print_u64(#min_value(u32)); + print_u64(@min_value(u32)); print_str("\n"); print_str("min u64: "); - print_u64(#min_value(u64)); + print_u64(@min_value(u64)); print_str("\n"); print_str("min i8: "); - print_i64(#min_value(i8)); + print_i64(@min_value(i8)); print_str("\n"); print_str("min i16: "); - print_i64(#min_value(i16)); + print_i64(@min_value(i16)); print_str("\n"); print_str("min i32: "); - print_i64(#min_value(i32)); + print_i64(@min_value(i32)); print_str("\n"); print_str("min i64: "); - print_i64(#min_value(i64)); + print_i64(@min_value(i64)); print_str("\n"); return 0; @@ -937,7 +937,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { add_simple_case("slicing", R"SOURCE( use "std.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { var array : [20]i32; array[5] = 1234; @@ -965,13 +965,13 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { add_simple_case("else if expression", R"SOURCE( use "std.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { if (f(1) == 1) { print_str("OK\n"); } return 0; } -fn f(c: u8) -> u8 { +fn f(c: u8) u8 => { if (c == 0) { 0 } else if (c == 1) { @@ -984,7 +984,7 @@ fn f(c: u8) -> u8 { add_simple_case("overflow intrinsics", R"SOURCE( use "std.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { var result: u8; if (!@add_with_overflow_u8(250, 100, &result)) { print_str("BAD\n"); @@ -1002,7 +1002,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { add_simple_case("memcpy and memset intrinsics", R"SOURCE( use "std.zig"; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { var foo : [20]u8; var bar : [20]u8; @@ -1020,13 +1020,13 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { add_simple_case("order-independent declarations", R"SOURCE( use "std.zig"; -const z : #typeof(stdin_fileno) = 0; -const x : #typeof(y) = 1234; +const z : @typeof(stdin_fileno) = 0; +const x : @typeof(y) = 1234; const y : u16 = 5678; -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { print_ok(x) } -fn print_ok(val: #typeof(x)) -> #typeof(foo) { +fn print_ok(val: @typeof(x)) @typeof(foo) => { print_str("OK\n"); return 0; } @@ -1054,7 +1054,7 @@ enum Bar { D, } -pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { +pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { const foo1 = Foo.One(13); const foo2 = Foo.Two(Point { .x = 1234, .y = 5678, }); const bar = Bar.B; @@ -1063,18 +1063,18 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { print_str("BAD\n"); } - if (#value_count(Foo) != 3) { + if (@value_count(Foo) != 3) { print_str("BAD\n"); } - if (#value_count(Bar) != 4) { + if (@value_count(Bar) != 4) { print_str("BAD\n"); } - if (#sizeof(Foo) != 17) { + if (@sizeof(Foo) != 17) { print_str("BAD\n"); } - if (#sizeof(Bar) != 1) { + if (@sizeof(Bar) != 1) { print_str("BAD\n"); } @@ -1090,8 +1090,8 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { static void add_compile_failure_test_cases(void) { add_compile_fail_case("multiple function definitions", R"SOURCE( -fn a() {} -fn a() {} +fn a() => {} +fn a() => {} )SOURCE", 1, ".tmp_source.zig:3:1: error: redefinition of 'a'"); add_compile_fail_case("bad directive", R"SOURCE( @@ -1100,46 +1100,46 @@ extern { fn b(); } #bogus2("") -fn a() {} +fn a() => {} )SOURCE", 2, ".tmp_source.zig:2:1: error: invalid directive: 'bogus1'", ".tmp_source.zig:6:1: error: invalid directive: 'bogus2'"); add_compile_fail_case("unreachable with return", R"SOURCE( -fn a() -> unreachable {return;} +fn a() unreachable => {return;} )SOURCE", 1, ".tmp_source.zig:2:24: error: expected type 'unreachable', got 'void'"); add_compile_fail_case("control reaches end of non-void function", R"SOURCE( -fn a() -> i32 {} +fn a() i32 => {} )SOURCE", 1, ".tmp_source.zig:2:15: error: expected type 'i32', got 'void'"); add_compile_fail_case("undefined function call", R"SOURCE( -fn a() { +fn a() => { b(); } )SOURCE", 1, ".tmp_source.zig:3:5: error: undefined function: 'b'"); add_compile_fail_case("wrong number of arguments", R"SOURCE( -fn a() { +fn a() => { b(1); } -fn b(a: i32, b: i32, c: i32) { } +fn b(a: i32, b: i32, c: i32) => { } )SOURCE", 1, ".tmp_source.zig:3:6: error: expected 3 arguments, got 1"); add_compile_fail_case("invalid type", R"SOURCE( -fn a() -> bogus {} - )SOURCE", 1, ".tmp_source.zig:2:11: error: invalid type name: 'bogus'"); +fn a() bogus => {} + )SOURCE", 1, ".tmp_source.zig:2:8: error: use of undeclared identifier 'bogus'"); add_compile_fail_case("pointer to unreachable", R"SOURCE( -fn a() -> &unreachable {} - )SOURCE", 1, ".tmp_source.zig:2:11: error: pointer to unreachable not allowed"); +fn a() &unreachable => {} + )SOURCE", 1, ".tmp_source.zig:2:8: error: pointer to unreachable not allowed"); add_compile_fail_case("unreachable code", R"SOURCE( -fn a() { +fn a() => { return; b(); } -fn b() {} +fn b() => {} )SOURCE", 1, ".tmp_source.zig:4:5: error: unreachable code"); add_compile_fail_case("bad version string", R"SOURCE( @@ -1152,7 +1152,7 @@ use "bogus-does-not-exist.zig"; )SOURCE", 1, ".tmp_source.zig:2:1: error: unable to find 'bogus-does-not-exist.zig'"); add_compile_fail_case("undeclared identifier", R"SOURCE( -fn a() { +fn a() => { b + c } @@ -1161,99 +1161,99 @@ fn a() { ".tmp_source.zig:4:5: error: use of undeclared identifier 'c'"); add_compile_fail_case("goto cause unreachable code", R"SOURCE( -fn a() { +fn a() => { goto done; b(); done: return; } -fn b() {} +fn b() => {} )SOURCE", 1, ".tmp_source.zig:4:5: error: unreachable code"); add_compile_fail_case("parameter redeclaration", R"SOURCE( -fn f(a : i32, a : i32) { +fn f(a : i32, a : i32) => { } )SOURCE", 1, ".tmp_source.zig:2:1: error: redeclaration of parameter 'a'"); add_compile_fail_case("local variable redeclaration", R"SOURCE( -fn f() { +fn f() => { const a : i32 = 0; const a = 0; } )SOURCE", 1, ".tmp_source.zig:4:5: error: redeclaration of variable 'a'"); add_compile_fail_case("local variable redeclares parameter", R"SOURCE( -fn f(a : i32) { +fn f(a : i32) => { const a = 0; } )SOURCE", 1, ".tmp_source.zig:3:5: error: redeclaration of variable 'a'"); add_compile_fail_case("variable has wrong type", R"SOURCE( -fn f() -> i32 { +fn f() i32 => { const a = c"a"; a } )SOURCE", 1, ".tmp_source.zig:2:15: error: expected type 'i32', got '&const u8'"); add_compile_fail_case("if condition is bool, not int", R"SOURCE( -fn f() { +fn f() => { if (0) {} } )SOURCE", 1, ".tmp_source.zig:3:9: error: expected type 'bool', got '(u8 literal)'"); add_compile_fail_case("assign unreachable", R"SOURCE( -fn f() { +fn f() => { const a = return; } )SOURCE", 1, ".tmp_source.zig:3:5: error: variable initialization is unreachable"); add_compile_fail_case("unreachable variable", R"SOURCE( -fn f() { +fn f() => { const a : unreachable = return; } )SOURCE", 1, ".tmp_source.zig:3:15: error: variable of type 'unreachable' not allowed"); add_compile_fail_case("unreachable parameter", R"SOURCE( -fn f(a : unreachable) {} +fn f(a : unreachable) => {} )SOURCE", 1, ".tmp_source.zig:2:10: error: parameter of type 'unreachable' not allowed"); add_compile_fail_case("exporting a void parameter", R"SOURCE( -export fn f(a : void) {} +export fn f(a : void) => {} )SOURCE", 1, ".tmp_source.zig:2:17: error: parameter of type 'void' not allowed on exported functions"); add_compile_fail_case("unused label", R"SOURCE( -fn f() { +fn f() => { a_label: } )SOURCE", 1, ".tmp_source.zig:3:1: error: label 'a_label' defined but not used"); add_compile_fail_case("bad assignment target", R"SOURCE( -fn f() { +fn f() => { 3 = 3; } )SOURCE", 1, ".tmp_source.zig:3:5: error: invalid assignment target"); add_compile_fail_case("assign to constant variable", R"SOURCE( -fn f() { +fn f() => { const a = 3; a = 4; } )SOURCE", 1, ".tmp_source.zig:4:5: error: cannot assign to constant"); add_compile_fail_case("use of undeclared identifier", R"SOURCE( -fn f() { +fn f() => { b = 3; } )SOURCE", 1, ".tmp_source.zig:3:5: error: use of undeclared identifier 'b'"); add_compile_fail_case("const is a statement, not an expression", R"SOURCE( -fn f() { +fn f() => { (const a = 0); } )SOURCE", 1, ".tmp_source.zig:3:6: error: invalid token: 'const'"); add_compile_fail_case("array access errors", R"SOURCE( -fn f() { +fn f() => { var bad : bool; i[i] = i[i]; bad[bad] = bad[bad]; @@ -1268,19 +1268,19 @@ fn f() { ".tmp_source.zig:5:20: error: expected type 'usize', got 'bool'"); add_compile_fail_case("variadic functions only allowed in extern", R"SOURCE( -fn f(...) {} +fn f(...) => {} )SOURCE", 1, ".tmp_source.zig:2:1: error: variadic arguments only allowed in extern functions"); add_compile_fail_case("write to const global variable", R"SOURCE( const x : i32 = 99; -fn f() { +fn f() => { x = 1; } )SOURCE", 1, ".tmp_source.zig:4:5: error: cannot assign to constant"); add_compile_fail_case("missing else clause", R"SOURCE( -fn f() { +fn f() => { const x : i32 = if (true) { 1 }; const y = if (true) { 1 as i32 }; } @@ -1299,7 +1299,7 @@ struct C { a : A, } add_compile_fail_case("invalid struct field", R"SOURCE( struct A { x : i32, } -fn f() { +fn f() => { var a : A; a.foo = 1; const y = a.bar; @@ -1315,7 +1315,7 @@ struct A { y : i32, } add_compile_fail_case("byvalue struct on exported functions", R"SOURCE( struct A { x : i32, } -export fn f(a : A) {} +export fn f(a : A) => {} )SOURCE", 1, ".tmp_source.zig:3:13: error: byvalue struct parameters not yet supported on exported functions"); add_compile_fail_case("duplicate field in struct value expression", R"SOURCE( @@ -1324,7 +1324,7 @@ struct A { y : i32, z : i32, } -fn f() { +fn f() => { const a = A { .z = 1, .y = 2, @@ -1340,13 +1340,13 @@ struct A { y : i32, z : i32, } -fn f() { +fn f() => { const a = A { .z = 4, .y = 2, }; } - )SOURCE", 1, ".tmp_source.zig:8:15: error: missing field: 'x'"); + )SOURCE", 1, ".tmp_source.zig:8:17: error: missing field: 'x'"); add_compile_fail_case("invalid field in struct value expression", R"SOURCE( struct A { @@ -1354,7 +1354,7 @@ struct A { y : i32, z : i32, } -fn f() { +fn f() => { const a = A { .z = 4, .y = 2, @@ -1364,37 +1364,37 @@ fn f() { )SOURCE", 1, ".tmp_source.zig:11:9: error: no member named 'foo' in 'A'"); add_compile_fail_case("invalid break expression", R"SOURCE( -fn f() { +fn f() => { break; } )SOURCE", 1, ".tmp_source.zig:3:5: error: 'break' expression outside loop"); add_compile_fail_case("invalid continue expression", R"SOURCE( -fn f() { +fn f() => { continue; } )SOURCE", 1, ".tmp_source.zig:3:5: error: 'continue' expression outside loop"); add_compile_fail_case("invalid maybe type", R"SOURCE( -fn f() { +fn f() => { if (const x ?= true) { } } )SOURCE", 1, ".tmp_source.zig:3:20: error: expected maybe type"); add_compile_fail_case("cast unreachable", R"SOURCE( -fn f() -> i32 { +fn f() i32 => { (return 1) as i32 } )SOURCE", 1, ".tmp_source.zig:3:16: error: invalid cast from type 'unreachable' to 'i32'"); - add_compile_fail_case("invalid compiler fn", R"SOURCE( -fn f() -> #bogus(foo) { + add_compile_fail_case("invalid builtin fn", R"SOURCE( +fn f() @bogus(foo) => { } - )SOURCE", 1, ".tmp_source.zig:2:11: error: invalid compiler function: 'bogus'"); + )SOURCE", 1, ".tmp_source.zig:2:8: error: invalid builtin function: 'bogus'"); add_compile_fail_case("top level decl dependency loop", R"SOURCE( -const a : #typeof(b) = 0; -const b : #typeof(a) = 0; +const a : @typeof(b) = 0; +const b : @typeof(a) = 0; )SOURCE", 1, ".tmp_source.zig:3:19: error: use of undeclared identifier 'a'"); }