diff --git a/doc/langref.md b/doc/langref.md index 8ab24122b783b770f6a7566e3bec8aae3f8267b3..37ce3cee52c449256a8978a83cd711fa326f80d3 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -32,7 +32,11 @@ zig | C equivalent | Description ``` Root : many(TopLevelDecl) token(EOF) -TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use +TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use | StructDecl + +StructDecl : many(Directive) token(Struct) token(Symbol) token(LBrace) many(StructField) token(RBrace) + +StructField : token(Symbol) token(Colon) Type token(Comma) Use : many(Directive) token(Use) token(String) token(Semicolon) @@ -126,7 +130,9 @@ CastExpression : PrefixOpExpression token(as) Type | PrefixOpExpression PrefixOpExpression : PrefixOp SuffixOpExpression | SuffixOpExpression -SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression) +SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression) + +FieldAccessExpression : token(Dot) token(Symbol) FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) @@ -146,7 +152,7 @@ KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) ## Operator Precedence ``` -x() x[] +x() x[] x.y !x -x ~x as * / % @@ -165,11 +171,11 @@ as ### Characters and Strings - | Example | Characters | Escapes | Null Terminated -------------------------------------------------------------------------------- - Byte | 'H' | All ASCII | Byte | No - UTF-8 Bytes | "hello" | All Unicode | Byte & Unicode | No - UTF-8 C string | c"hello" | All Unicode | Byte & Unicode | Yes + | Example | Characters | Escapes | Null Term | Type +--------------------------------------------------------------------------------- + Byte | 'H' | All ASCII | Byte | No | u8 + UTF-8 Bytes | "hello" | All Unicode | Byte & Unicode | No | [5; u8] + UTF-8 C string | c"hello" | All Unicode | Byte & Unicode | Yes | *const u8 ### Byte Escapes diff --git a/doc/vim/syntax/zig.vim b/doc/vim/syntax/zig.vim index bc63e343b6b86a6c57e30e2f8ccfc41720c26346..828a0bfe540cd205728cce70f62863746370f808 100644 --- a/doc/vim/syntax/zig.vim +++ b/doc/vim/syntax/zig.vim @@ -9,7 +9,7 @@ endif syn keyword zigKeyword fn return mut const extern unreachable export pub as use while asm syn keyword zigKeyword if else let void goto type enum struct continue break match volatile -syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 +syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 string syn keyword zigConstant null diff --git a/example/hello_world/hello2.zig b/example/hello_world/hello2.zig index dfc9146ef7b8117e1e4625b11c99b7b8edab53cf..37ee9e86c3b1d78e76b24c3a543d7d5828a05def 100644 --- a/example/hello_world/hello2.zig +++ b/example/hello_world/hello2.zig @@ -3,6 +3,6 @@ export executable "hello"; use "std.zig"; export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 { - print_str("Hello, world!\n", 14 as isize); + print_str("Hello, world!\n"); return 0; } diff --git a/example/structs/structs.zig b/example/structs/structs.zig new file mode 100644 index 0000000000000000000000000000000000000000..a5d52b6b17fbecb9438631220ee3d542880ce1f9 --- /dev/null +++ b/example/structs/structs.zig @@ -0,0 +1,27 @@ +export executable "structs"; + +use "std.zig"; + +export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 { + let mut foo : Foo; + + foo.a = foo.a + 1; + + foo.b = foo.a == 1; + + test_foo(foo); + + return 0; +} + +struct Foo { + a : i32, + b : bool, + c : f32, +} + +fn test_foo(foo : Foo) { + if foo.b { + print_str("OK"); + } +} diff --git a/src/analyze.cpp b/src/analyze.cpp index a2b4801144c711a6fafc650614f2dce5c9992833..f8144c70e27673f08580a8a5bed9c9caeb6577d3 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -13,6 +13,11 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node); +static void alloc_codegen_node(AstNode *node) { + assert(!node->codegen_node); + node->codegen_node = allocate(1); +} + static AstNode *first_executing_node(AstNode *node) { switch (node->type) { case NodeTypeFnCallExpr: @@ -44,6 +49,9 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeLabel: case NodeTypeGoto: case NodeTypeAsmExpr: + case NodeTypeFieldAccessExpr: + case NodeTypeStructDecl: + case NodeTypeStructField: return node; } zig_panic("unreachable"); @@ -129,6 +137,7 @@ static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, in entry->di_type = LLVMZigCreateDebugArrayType(g->dbuilder, entry->size_in_bits, entry->align_in_bits, child_type->di_type, array_size); entry->data.array.child_type = child_type; + entry->data.array.len = array_size; g->type_table.put(&entry->name, entry); child_type->arrays_by_size.put(array_size, entry); @@ -143,8 +152,7 @@ static int parse_int(Buf *number) { static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeType); - assert(!node->codegen_node); - node->codegen_node = allocate(1); + alloc_codegen_node(node); TypeNode *type_node = &node->codegen_node->data.type_node; switch (node->data.type.type) { case AstNodeTypeTypePrimitive: @@ -259,8 +267,7 @@ static void preview_function_labels(CodeGen *g, AstNode *node, FnTableEntry *fn_ Buf *name = &label_node->data.label.name; fn_table_entry->label_table.put(name, label_entry); - assert(!label_node->codegen_node); - label_node->codegen_node = allocate(1); + alloc_codegen_node(label_node); label_node->codegen_node->data.label_entry = label_entry; } } @@ -302,8 +309,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, g->fn_table.put(name, fn_table_entry); } - assert(!fn_proto->codegen_node); - fn_proto->codegen_node = allocate(1); + alloc_codegen_node(fn_proto); fn_proto->codegen_node->data.fn_proto_node.fn_table_entry = fn_table_entry; } break; @@ -319,8 +325,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, if (entry) { add_node_error(g, node, buf_sprintf("redefinition of '%s'", buf_ptr(proto_name))); - assert(!node->codegen_node); - node->codegen_node = allocate(1); + alloc_codegen_node(node); node->codegen_node->data.fn_def_node.skip = true; skip = true; } else if (is_pub) { @@ -328,8 +333,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, if (entry) { add_node_error(g, node, buf_sprintf("redefinition of '%s'", buf_ptr(proto_name))); - assert(!node->codegen_node); - node->codegen_node = allocate(1); + alloc_codegen_node(node); node->codegen_node->data.fn_def_node.skip = true; skip = true; } @@ -358,8 +362,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, resolve_function_proto(g, proto_node, fn_table_entry); - assert(!proto_node->codegen_node); - proto_node->codegen_node = allocate(1); + alloc_codegen_node(proto_node); proto_node->codegen_node->data.fn_proto_node.fn_table_entry = fn_table_entry; preview_function_labels(g, node->data.fn_def.body, fn_table_entry); @@ -409,6 +412,31 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, buf_sprintf("root export declaration only valid in root source file")); } break; + case NodeTypeStructDecl: + { + StructDeclNode *struct_codegen = &node->codegen_node->data.struct_decl_node; + TypeTableEntry *type_entry = struct_codegen->type_entry; + + int field_count = node->data.struct_decl.fields.length;; + type_entry->data.structure.field_count = field_count; + type_entry->data.structure.fields = allocate(field_count); + + LLVMTypeRef *element_types = allocate(field_count); + + for (int i = 0; i < field_count; i += 1) { + AstNode *field_node = node->data.struct_decl.fields.at(i); + TypeStructField *type_struct_field = &type_entry->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); + + element_types[i] = type_struct_field->type_entry->type_ref; + } + // TODO align_in_bits and size_in_bits + // TODO set up ditype for the struct + LLVMStructSetBody(type_entry->type_ref, element_types, field_count, false); + + break; + } case NodeTypeUse: // nothing to do here break; @@ -436,6 +464,68 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, case NodeTypeLabel: case NodeTypeGoto: case NodeTypeAsmExpr: + case NodeTypeFieldAccessExpr: + case NodeTypeStructField: + zig_unreachable(); + } +} + +static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) { + switch (node->type) { + case NodeTypeStructDecl: + { + alloc_codegen_node(node); + StructDeclNode *struct_codegen = &node->codegen_node->data.struct_decl_node; + + Buf *name = &node->data.struct_decl.name; + auto table_entry = g->type_table.maybe_get(name); + if (table_entry) { + struct_codegen->type_entry = table_entry->value; + add_node_error(g, node, + buf_sprintf("redefinition of '%s'", buf_ptr(name))); + } else { + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct); + entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(name)); + buf_init_from_buf(&entry->name, name); + // put off adding the debug type until we do the full struct body + // this type is incomplete until we do another pass + g->type_table.put(&entry->name, entry); + struct_codegen->type_entry = entry; + } + break; + } + case NodeTypeExternBlock: + case NodeTypeFnDef: + case NodeTypeRootExportDecl: + case NodeTypeUse: + // nothing to do + break; + case NodeTypeDirective: + case NodeTypeParamDecl: + case NodeTypeFnProto: + case NodeTypeType: + case NodeTypeFnDecl: + case NodeTypeReturnExpr: + case NodeTypeVariableDeclaration: + case NodeTypeRoot: + case NodeTypeBlock: + case NodeTypeBinOpExpr: + case NodeTypeFnCallExpr: + case NodeTypeArrayAccessExpr: + case NodeTypeNumberLiteral: + case NodeTypeStringLiteral: + case NodeTypeUnreachable: + case NodeTypeVoid: + case NodeTypeBoolLiteral: + case NodeTypeSymbol: + case NodeTypeCastExpr: + case NodeTypePrefixOpExpr: + case NodeTypeIfExpr: + case NodeTypeLabel: + case NodeTypeGoto: + case NodeTypeAsmExpr: + case NodeTypeFieldAccessExpr: + case NodeTypeStructField: zig_unreachable(); } } @@ -460,7 +550,9 @@ static FnTableEntry *get_context_fn_entry(BlockContext *context) { return fn_proto_node->codegen_node->data.fn_proto_node.fn_table_entry; } -static void check_type_compatibility(CodeGen *g, AstNode *node, TypeTableEntry *expected_type, TypeTableEntry *actual_type) { +static void check_type_compatibility(CodeGen *g, AstNode *node, + TypeTableEntry *expected_type, TypeTableEntry *actual_type) +{ if (expected_type == nullptr) return; // anything will do if (expected_type == actual_type) @@ -471,7 +563,7 @@ static void check_type_compatibility(CodeGen *g, AstNode *node, TypeTableEntry * return; // sorry toots; gotta run. good luck with that expected type. add_node_error(g, node, - buf_sprintf("type mismatch. expected %s. got %s", + buf_sprintf("expected type '%s', got '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name))); } @@ -507,6 +599,55 @@ LocalVariableTableEntry *find_local_variable(BlockContext *context, Buf *name) { } } +static TypeStructField *get_struct_field(TypeTableEntry *struct_type, Buf *name) { + for (int i = 0; i < struct_type->data.structure.field_count; i += 1) { + TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; + if (buf_eql_buf(type_struct_field->name, name)) { + return type_struct_field; + } + } + return nullptr; +} + +static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, + AstNode *node) +{ + TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, + node->data.field_access_expr.struct_expr); + + TypeTableEntry *return_type; + + if (struct_type->id == TypeTableEntryIdStruct) { + Buf *field_name = &node->data.field_access_expr.field_name; + TypeStructField *type_struct_field = get_struct_field(struct_type, field_name); + if (type_struct_field) { + return_type = type_struct_field->type_entry; + } else { + add_node_error(g, node, + buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&struct_type->name))); + return_type = g->builtin_types.entry_invalid; + } + } else if (struct_type->id == TypeTableEntryIdArray) { + Buf *name = &node->data.field_access_expr.field_name; + if (buf_eql_str(name, "len")) { + return_type = g->builtin_types.entry_usize; + } else { + add_node_error(g, node, + buf_sprintf("no member named '%s' in '%s'", buf_ptr(name), + buf_ptr(&struct_type->name))); + return_type = g->builtin_types.entry_invalid; + } + } else { + if (struct_type->id != TypeTableEntryIdInvalid) { + add_node_error(g, node, + buf_sprintf("type '%s' does not support field access", buf_ptr(&struct_type->name))); + } + return_type = g->builtin_types.entry_invalid; + } + + return return_type; +} + static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { @@ -554,8 +695,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, TypeTableEntry *expected_type, AstNode *node) { TypeTableEntry *return_type = nullptr; - assert(!node->codegen_node); - node->codegen_node = allocate(1); + alloc_codegen_node(node); switch (node->type) { case NodeTypeBlock: { @@ -698,9 +838,11 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, } } else if (lhs_node->type == NodeTypeArrayAccessExpr) { expected_rhs_type = analyze_array_access_expr(g, import, context, lhs_node); + } else if (lhs_node->type == NodeTypeFieldAccessExpr) { + expected_rhs_type = analyze_field_access_expr(g, import, context, lhs_node); } else { add_node_error(g, lhs_node, - buf_sprintf("expected a bare identifier")); + buf_sprintf("assignment target must be variable, field, or array element")); } analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2); return_type = g->builtin_types.entry_void; @@ -835,15 +977,21 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, // for reading array access; assignment handled elsewhere return_type = analyze_array_access_expr(g, import, context, node); break; + case NodeTypeFieldAccessExpr: + return_type = analyze_field_access_expr(g, import, context, node); + break; case NodeTypeNumberLiteral: // TODO: generic literal int type return_type = g->builtin_types.entry_i32; break; case NodeTypeStringLiteral: - return_type = g->builtin_types.entry_string_literal; + if (node->data.string_literal.c) { + return_type = g->builtin_types.entry_c_string_literal; + } else { + return_type = get_array_type(g, g->builtin_types.entry_u8, buf_len(&node->data.string_literal.buf)); + } break; - case NodeTypeUnreachable: return_type = g->builtin_types.entry_unreachable; break; @@ -884,7 +1032,10 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, { return_type = wanted_type; } else { - zig_panic("TODO analyze_expression cast expr"); + add_node_error(g, node, + buf_sprintf("TODO handle cast from '%s' to '%s'", + buf_ptr(&actual_type->name), buf_ptr(&wanted_type->name))); + return_type = g->builtin_types.entry_invalid; } break; } @@ -946,6 +1097,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeFnDef: case NodeTypeUse: case NodeTypeLabel: + case NodeTypeStructDecl: + case NodeTypeStructField: zig_unreachable(); } assert(return_type); @@ -970,8 +1123,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, AstNode *fn_proto_node = node->data.fn_def.fn_proto; assert(fn_proto_node->type == NodeTypeFnProto); - assert(!node->codegen_node); - node->codegen_node = allocate(1); + alloc_codegen_node(node); BlockContext *context = new_block_context(node, nullptr); node->codegen_node->data.fn_def_node.block_context = context; @@ -1044,6 +1196,9 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, buf_sprintf("invalid directive: '%s'", buf_ptr(name))); } break; + case NodeTypeStructDecl: + // nothing to do + break; case NodeTypeDirective: case NodeTypeParamDecl: case NodeTypeFnProto: @@ -1068,6 +1223,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, case NodeTypeLabel: case NodeTypeGoto: case NodeTypeAsmExpr: + case NodeTypeFieldAccessExpr: + case NodeTypeStructField: zig_unreachable(); } } @@ -1082,6 +1239,16 @@ static void find_function_declarations_root(CodeGen *g, ImportTableEntry *import } +static void preview_types_root(CodeGen *g, ImportTableEntry *import, AstNode *node) { + assert(node->type == NodeTypeRoot); + + for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) { + AstNode *child = node->data.root.top_level_decls.at(i); + preview_types(g, import, child); + } + +} + static void analyze_top_level_decls_root(CodeGen *g, ImportTableEntry *import, AstNode *node) { assert(node->type == NodeTypeRoot); @@ -1092,6 +1259,17 @@ static void analyze_top_level_decls_root(CodeGen *g, ImportTableEntry *import, A } void semantic_analyze(CodeGen *g) { + { + auto it = g->import_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + ImportTableEntry *import = entry->value; + preview_types_root(g, import, import->root); + } + } { auto it = g->import_table.entry_iterator(); for (;;) { diff --git a/src/analyze.hpp b/src/analyze.hpp index 2f9bb209d4292704202943de7e215ce7d564ad6d..47411b35ff2c345c97403651c35688c3c90f17a0 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -28,6 +28,18 @@ struct TypeTableEntryInt { struct TypeTableEntryArray { TypeTableEntry *child_type; + uint64_t len; +}; + +struct TypeStructField { + Buf *name; + TypeTableEntry *type_entry; +}; + +struct TypeTableEntryStruct { + bool is_packed; + int field_count; + TypeStructField *fields; }; enum TypeTableEntryId { @@ -39,6 +51,7 @@ enum TypeTableEntryId { TypeTableEntryIdFloat, TypeTableEntryIdPointer, TypeTableEntryIdArray, + TypeTableEntryIdStruct, }; struct TypeTableEntry { @@ -55,6 +68,7 @@ struct TypeTableEntry { TypeTableEntryPointer pointer; TypeTableEntryInt integral; TypeTableEntryArray array; + TypeTableEntryStruct structure; } data; // use these fields to make sure we don't duplicate type table entries for the same type @@ -122,8 +136,10 @@ struct CodeGen { TypeTableEntry *entry_u8; TypeTableEntry *entry_i32; TypeTableEntry *entry_isize; + TypeTableEntry *entry_usize; TypeTableEntry *entry_f32; - TypeTableEntry *entry_string_literal; + TypeTableEntry *entry_c_string_literal; + TypeTableEntry *entry_string; TypeTableEntry *entry_void; TypeTableEntry *entry_unreachable; TypeTableEntry *entry_invalid; @@ -211,6 +227,10 @@ struct BlockNode { BlockContext *block_context; }; +struct StructDeclNode { + TypeTableEntry *type_entry; +}; + struct CodeGenNode { union { TypeNode type_node; // for NodeTypeType @@ -219,6 +239,7 @@ struct CodeGenNode { LabelTableEntry *label_entry; // for NodeTypeGoto and NodeTypeLabel AssignNode assign_node; // for NodeTypeBinOpExpr where op is BinOpTypeAssign BlockNode block_node; // for NodeTypeBlock + StructDeclNode struct_decl_node; // for NodeTypeStructDecl } data; ExprNode expr_node; // for all the expression nodes }; diff --git a/src/codegen.cpp b/src/codegen.cpp index 80c19ed33c3418fc3f557b3dc78d3ac9742c2980..368e85edb1bfcabba349cfb8114bdd1e2c8abcaf 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -106,12 +106,12 @@ static void add_debug_source_node(CodeGen *g, AstNode *node) { g->cur_block_context->di_scope); } -static LLVMValueRef find_or_create_string(CodeGen *g, Buf *str) { +static LLVMValueRef find_or_create_string(CodeGen *g, Buf *str, bool c) { auto entry = g->str_table.maybe_get(str); if (entry) { return entry->value; } - LLVMValueRef text = LLVMConstString(buf_ptr(str), buf_len(str), false); + LLVMValueRef text = LLVMConstString(buf_ptr(str), buf_len(str), !c); LLVMValueRef global_value = LLVMAddGlobal(g->module, LLVMTypeOf(text), ""); LLVMSetLinkage(global_value, LLVMPrivateLinkage); LLVMSetInitializer(global_value, text); @@ -204,6 +204,28 @@ static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node) { return LLVMBuildLoad(g->builder, ptr, ""); } +static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node) { + assert(node->type == NodeTypeFieldAccessExpr); + + TypeTableEntry *struct_type = get_expr_type(node->data.field_access_expr.struct_expr); + LLVMValueRef struct_ptr = gen_expr(g, node->data.field_access_expr.struct_expr); + Buf *name = &node->data.field_access_expr.field_name; + + // TODO add struct support + (void)struct_ptr; + + if (struct_type->id == TypeTableEntryIdArray) { + if (buf_eql_str(name, "len")) { + return LLVMConstInt(g->builtin_types.entry_usize->type_ref, + struct_type->data.array.len, false); + } else { + zig_panic("gen_field_access_expr bad array field"); + } + } else { + zig_panic("gen_field_access_expr bad struct type"); + } +} + static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { assert(node->type == NodeTypePrefixOpExpr); assert(node->data.prefix_op_expr.primary_expr); @@ -785,6 +807,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { return gen_fn_call_expr(g, node); case NodeTypeArrayAccessExpr: return gen_array_access_expr(g, node); + case NodeTypeFieldAccessExpr: + return gen_field_access_expr(g, node); case NodeTypeUnreachable: add_debug_source_node(g, node); return LLVMBuildUnreachable(g->builder); @@ -809,8 +833,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { } case NodeTypeStringLiteral: { - Buf *str = &node->data.string; - LLVMValueRef str_val = find_or_create_string(g, str); + Buf *str = &node->data.string_literal.buf; + LLVMValueRef str_val = find_or_create_string(g, str, node->data.string_literal.c); LLVMValueRef indices[] = { LLVMConstInt(LLVMInt32Type(), 0, false), LLVMConstInt(LLVMInt32Type(), 0, false) @@ -864,6 +888,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { case NodeTypeExternBlock: case NodeTypeDirective: case NodeTypeUse: + case NodeTypeStructDecl: + case NodeTypeStructField: zig_unreachable(); } zig_unreachable(); @@ -1072,7 +1098,7 @@ static void do_code_gen(CodeGen *g) { #endif } -static void define_primitive_types(CodeGen *g) { +static void define_builtin_types(CodeGen *g) { { // if this type is anywhere in the AST, we should never hit codegen. TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInvalid); @@ -1103,7 +1129,7 @@ static void define_primitive_types(CodeGen *g) { g->type_table.put(&entry->name, entry); g->builtin_types.entry_u8 = entry; } - g->builtin_types.entry_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + 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 = LLVMInt32Type(); @@ -1130,6 +1156,19 @@ static void define_primitive_types(CodeGen *g) { g->type_table.put(&entry->name, entry); g->builtin_types.entry_isize = entry; } + { + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); + entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8); + buf_init_from_str(&entry->name, "usize"); + entry->size_in_bits = g->pointer_size_bytes * 8; + entry->align_in_bits = g->pointer_size_bytes * 8; + entry->data.integral.is_signed = false; + entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), + entry->size_in_bits, entry->align_in_bits, + LLVMZigEncoding_DW_ATE_unsigned()); + g->type_table.put(&entry->name, entry); + g->builtin_types.entry_usize = entry; + } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); entry->type_ref = LLVMFloatType(); @@ -1160,6 +1199,43 @@ static void define_primitive_types(CodeGen *g) { g->type_table.put(&entry->name, entry); g->builtin_types.entry_unreachable = entry; } + { + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct); + + TypeTableEntry *const_pointer_to_u8 = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + + unsigned element_count = 2; + LLVMTypeRef element_types[] = { + const_pointer_to_u8->type_ref, + g->builtin_types.entry_usize->type_ref + }; + entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), "string"); + LLVMStructSetBody(entry->type_ref, element_types, element_count, false); + + buf_init_from_str(&entry->name, "string"); + entry->size_in_bits = g->pointer_size_bytes * 2 * 8; + entry->align_in_bits = g->pointer_size_bytes; + entry->data.structure.is_packed = false; + entry->data.structure.field_count = element_count; + entry->data.structure.fields = allocate(element_count); + entry->data.structure.fields[0].name = buf_create_from_str("ptr"); + entry->data.structure.fields[0].type_entry = const_pointer_to_u8; + entry->data.structure.fields[1].name = buf_create_from_str("len"); + entry->data.structure.fields[1].type_entry = g->builtin_types.entry_usize; + + LLVMZigDIType *di_element_types[] = { + const_pointer_to_u8->di_type, + g->builtin_types.entry_usize->di_type + }; + LLVMZigDIScope *compile_unit_scope = LLVMZigCompileUnitToScope(g->compile_unit); + LLVMZigDIFile *difile = nullptr; // TODO make sure this ok + entry->di_type = LLVMZigCreateDebugStructType(g->dbuilder, compile_unit_scope, + "string", difile, 0, entry->size_in_bits, entry->align_in_bits, 0, + nullptr, di_element_types, element_count, 0, nullptr, ""); + + g->type_table.put(&entry->name, entry); + g->builtin_types.entry_string = entry; + } } @@ -1213,8 +1289,6 @@ static void init(CodeGen *g, Buf *source_path) { LLVMZigSetFastMath(g->builder, true); - define_primitive_types(g); - Buf *producer = buf_sprintf("zig %s", ZIG_VERSION_STRING); bool is_optimized = g->build_type == CodeGenBuildTypeRelease; const char *flags = ""; @@ -1224,6 +1298,7 @@ static void init(CodeGen *g, Buf *source_path) { buf_ptr(producer), is_optimized, flags, runtime_version, "", 0, !g->strip_debug_symbols); + define_builtin_types(g); } diff --git a/src/parser.cpp b/src/parser.cpp index 29bc0694299434a62d57258db279de8a8527f359..9497224fbb92c97735a371959b82f0634fe822ba 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -106,6 +106,12 @@ const char *node_type_str(NodeType node_type) { return "Label"; case NodeTypeAsmExpr: return "AsmExpr"; + case NodeTypeFieldAccessExpr: + return "FieldAccessExpr"; + case NodeTypeStructDecl: + return "StructDecl"; + case NodeTypeStructField: + return "StructField"; } zig_unreachable(); } @@ -259,9 +265,12 @@ void ast_print(AstNode *node, int indent) { buf_ptr(&node->data.number)); break; case NodeTypeStringLiteral: - fprintf(stderr, "StringLiteral '%s'\n", - buf_ptr(&node->data.string)); - break; + { + const char *c = node->data.string_literal.c ? "c" : ""; + fprintf(stderr, "StringLiteral %s'%s'\n", c, + buf_ptr(&node->data.string_literal.buf)); + break; + } case NodeTypeUnreachable: fprintf(stderr, "Unreachable\n"); break; @@ -295,6 +304,19 @@ void ast_print(AstNode *node, int indent) { case NodeTypeAsmExpr: fprintf(stderr, "%s\n", node_type_str(node->type)); break; + case NodeTypeFieldAccessExpr: + fprintf(stderr, "%s '%s'\n", node_type_str(node->type), + buf_ptr(&node->data.field_access_expr.field_name)); + ast_print(node->data.field_access_expr.struct_expr, indent + 2); + break; + case NodeTypeStructDecl: + fprintf(stderr, "%s '%s'\n", + node_type_str(node->type), buf_ptr(&node->data.struct_decl.name)); + break; + case NodeTypeStructField: + fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.struct_field.name)); + ast_print(node->data.struct_field.type, indent + 2); + break; } } @@ -475,18 +497,28 @@ static void parse_asm_template(ParseContext *pc, AstNode *node) { } } -static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, ZigList *offset_map) { +static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, bool *out_c_str, + ZigList *offset_map) +{ // skip the double quotes at beginning and end // convert escape sequences + // detect c string literal buf_resize(buf, 0); bool escape = false; - bool first = true; + bool skip_quote; SrcPos pos = {token->start_line, token->start_column}; for (int i = token->start_pos; i < token->end_pos - 1; i += 1) { uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i); - if (first) { - first = false; + if (i == token->start_pos) { + skip_quote = (c == 'c'); + if (out_c_str) { + *out_c_str = skip_quote; + } else if (skip_quote) { + ast_error(pc, token, "C string literal not allowed here"); + } + } else if (skip_quote) { + skip_quote = false; } else { if (escape) { switch (c) { @@ -541,13 +573,20 @@ 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 void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) { if (token->id != token_id) { ast_invalid_token_error(pc, token); } } +static Token *ast_eat_token(ParseContext *pc, int *token_index, TokenId token_id) { + Token *token = &pc->tokens->at(*token_index); + ast_expect_token(pc, token, token_id); + *token_index += 1; + return token; +} + + static AstNode *ast_parse_directive(ParseContext *pc, int token_index, int *new_token_index) { Token *number_sign = &pc->tokens->at(token_index); token_index += 1; @@ -569,7 +608,7 @@ static AstNode *ast_parse_directive(ParseContext *pc, int token_index, int *new_ token_index += 1; ast_expect_token(pc, param_str, TokenIdStringLiteral); - parse_string_literal(pc, param_str, &node->data.directive.param, nullptr); + parse_string_literal(pc, param_str, &node->data.directive.param, nullptr, nullptr); Token *r_paren = &pc->tokens->at(token_index); token_index += 1; @@ -782,7 +821,7 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool return node; } else if (token->id == TokenIdStringLiteral) { AstNode *node = ast_create_node(pc, NodeTypeStringLiteral, token); - parse_string_literal(pc, token, &node->data.string, nullptr); + parse_string_literal(pc, token, &node->data.string_literal.buf, &node->data.string_literal.c, nullptr); *token_index += 1; return node; } else if (token->id == TokenIdKeywordUnreachable) { @@ -832,9 +871,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool } /* -SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression) +SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression) FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) ArrayAccessExpression : token(LBracket) Expression token(RBracket) +FieldAccessExpression : token(Dot) token(Symbol) */ 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); @@ -861,6 +901,16 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, boo *token_index += 1; ast_expect_token(pc, r_bracket, TokenIdRBracket); + return node; + } else if (token->id == TokenIdDot) { + *token_index += 1; + + Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol); + + AstNode *node = ast_create_node(pc, NodeTypeFieldAccessExpr, token); + node->data.field_access_expr.struct_expr = primary_expr; + ast_buf_from_token(pc, name_token, &node->data.field_access_expr.field_name); + return node; } else { return primary_expr; @@ -1397,14 +1447,6 @@ static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mand return node; } -static Token *ast_eat_token(ParseContext *pc, int *token_index, TokenId token_id) { - Token *token = &pc->tokens->at(*token_index); - ast_expect_token(pc, token, token_id); - *token_index += 1; - return token; -} - - /* AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) Expression token(RParen) */ @@ -1421,7 +1463,7 @@ static void ast_parse_asm_input_item(ParseContext *pc, int *token_index, AstNode 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); + parse_string_literal(pc, constraint, &asm_input->constraint, nullptr, nullptr); asm_input->expr = expr_node; node->data.asm_expr.input_list.append(asm_input); } @@ -1442,7 +1484,7 @@ static void ast_parse_asm_output_item(ParseContext *pc, int *token_index, AstNod AsmOutput *asm_output = allocate(1); ast_buf_from_token(pc, alias, &asm_output->asm_symbolic_name); - parse_string_literal(pc, constraint, &asm_output->constraint, nullptr); + parse_string_literal(pc, constraint, &asm_output->constraint, nullptr, nullptr); ast_buf_from_token(pc, out_symbol, &asm_output->variable_name); node->data.asm_expr.output_list.append(asm_output); } @@ -1464,7 +1506,7 @@ static void ast_parse_asm_clobbers(ParseContext *pc, int *token_index, AstNode * *token_index += 1; Buf *clobber_buf = buf_alloc(); - parse_string_literal(pc, string_tok, clobber_buf, nullptr); + 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); @@ -1565,7 +1607,7 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mand ast_expect_token(pc, template_tok, TokenIdStringLiteral); *token_index += 1; - parse_string_literal(pc, template_tok, &node->data.asm_expr.asm_template, + parse_string_literal(pc, template_tok, &node->data.asm_expr.asm_template, nullptr, &node->data.asm_expr.offset_map); parse_asm_template(pc, node); @@ -1877,7 +1919,7 @@ static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index, b *token_index += 1; ast_expect_token(pc, export_name, TokenIdStringLiteral); - parse_string_literal(pc, export_name, &node->data.root_export_decl.name, nullptr); + parse_string_literal(pc, export_name, &node->data.root_export_decl.name, nullptr, nullptr); Token *semicolon = &pc->tokens->at(*token_index); *token_index += 1; @@ -1889,9 +1931,7 @@ static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index, b /* Use : many(Directive) token(Use) token(String) token(Semicolon) */ -static AstNode *ast_parse_use(ParseContext *pc, int *token_index, bool mandatory) { - assert(mandatory == false); - +static AstNode *ast_parse_use(ParseContext *pc, int *token_index) { Token *use_kw = &pc->tokens->at(*token_index); if (use_kw->id != TokenIdKeywordUse) return nullptr; @@ -1907,7 +1947,7 @@ static AstNode *ast_parse_use(ParseContext *pc, int *token_index, bool mandatory AstNode *node = ast_create_node(pc, NodeTypeUse, use_kw); - parse_string_literal(pc, use_name, &node->data.use.path, nullptr); + parse_string_literal(pc, use_name, &node->data.use.path, nullptr, nullptr); node->data.use.directives = pc->directive_list; pc->directive_list = nullptr; @@ -1916,7 +1956,57 @@ static AstNode *ast_parse_use(ParseContext *pc, int *token_index, bool mandatory } /* -TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use +StructDecl : many(Directive) token(Struct) token(Symbol) token(LBrace) many(StructField) token(RBrace) +StructField : token(Symbol) token(Colon) Type token(Comma) +*/ +static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) { + Token *struct_kw = &pc->tokens->at(*token_index); + if (struct_kw->id != TokenIdKeywordStruct) + return nullptr; + *token_index += 1; + + Token *struct_name = &pc->tokens->at(*token_index); + *token_index += 1; + ast_expect_token(pc, struct_name, TokenIdSymbol); + + AstNode *node = ast_create_node(pc, NodeTypeStructDecl, struct_kw); + ast_buf_from_token(pc, struct_name, &node->data.struct_decl.name); + + ast_eat_token(pc, token_index, TokenIdLBrace); + + for (;;) { + Token *token = &pc->tokens->at(*token_index); + + if (token->id == TokenIdRBrace) { + *token_index += 1; + break; + } else if (token->id == TokenIdSymbol) { + AstNode *field_node = ast_create_node(pc, NodeTypeStructField, token); + *token_index += 1; + + ast_buf_from_token(pc, token, &field_node->data.struct_field.name); + + ast_eat_token(pc, token_index, TokenIdColon); + + field_node->data.struct_field.type = ast_parse_type(pc, *token_index, token_index); + + ast_eat_token(pc, token_index, TokenIdComma); + + node->data.struct_decl.fields.append(field_node); + } else { + ast_invalid_token_error(pc, token); + } + } + + + node->data.struct_decl.directives = pc->directive_list; + pc->directive_list = nullptr; + + return node; +} + +/* +TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use | StructDecl */ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigList *top_level_decls) { for (;;) { @@ -1943,12 +2033,18 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis continue; } - AstNode *use_node = ast_parse_use(pc, token_index, false); + AstNode *use_node = ast_parse_use(pc, token_index); if (use_node) { top_level_decls->append(use_node); continue; } + AstNode *struct_node = ast_parse_struct_decl(pc, token_index); + if (struct_node) { + top_level_decls->append(struct_node); + continue; + } + if (pc->directive_list->length > 0) { ast_error(pc, directive_token, "invalid directive"); } diff --git a/src/parser.hpp b/src/parser.hpp index 373de3178ae79bec18e12ce5ced90a3b319bda3a..7d3b42d9e54e617e6deeec9a0c2e7e42672e115d 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -40,6 +40,7 @@ enum NodeType { NodeTypePrefixOpExpr, NodeTypeFnCallExpr, NodeTypeArrayAccessExpr, + NodeTypeFieldAccessExpr, NodeTypeUse, NodeTypeVoid, NodeTypeBoolLiteral, @@ -47,6 +48,8 @@ enum NodeType { NodeTypeLabel, NodeTypeGoto, NodeTypeAsmExpr, + NodeTypeStructDecl, + NodeTypeStructField, }; struct AstNodeRoot { @@ -152,6 +155,11 @@ struct AstNodeArrayAccessExpr { AstNode *subscript; }; +struct AstNodeFieldAccessExpr { + AstNode *struct_expr; + Buf field_name; +}; + struct AstNodeExternBlock { ZigList *directives; ZigList fn_decls; @@ -231,6 +239,22 @@ struct AstNodeAsmExpr { ZigList clobber_list; }; +struct AstNodeStructDecl { + Buf name; + ZigList fields; + ZigList *directives; +}; + +struct AstNodeStructField { + Buf name; + AstNode *type; +}; + +struct AstNodeStringLiteral { + Buf buf; + bool c; +}; + struct AstNode { enum NodeType type; int line; @@ -260,8 +284,11 @@ struct AstNode { AstNodeLabel label; AstNodeGoto go_to; AstNodeAsmExpr asm_expr; + AstNodeFieldAccessExpr field_access_expr; + AstNodeStructDecl struct_decl; + AstNodeStructField struct_field; + AstNodeStringLiteral string_literal; Buf number; - Buf string; Buf symbol; bool bool_literal; } data; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 8e6d6c3082551b9c0889fa44ffd982e4689dd4ec..d1e672d2f18d5ffc9b2e32e98a0d7bddc370937b 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -28,10 +28,9 @@ case '8': \ case '9' -#define ALPHA \ +#define ALPHA_EXCEPT_C \ 'a': \ case 'b': \ - case 'c': \ case 'd': \ case 'e': \ case 'f': \ @@ -82,6 +81,10 @@ case 'Y': \ case 'Z' +#define ALPHA \ + ALPHA_EXCEPT_C: \ + case 'c' + #define SYMBOL_CHAR \ ALPHA: \ case DIGIT: \ @@ -90,6 +93,7 @@ enum TokenizeState { TokenizeStateStart, TokenizeStateSymbol, + TokenizeStateSymbolFirst, TokenizeStateNumber, TokenizeStateString, TokenizeStateSawDash, @@ -201,6 +205,8 @@ static void end_token(Tokenize *t) { t->cur_tok->id = TokenIdKeywordVolatile; } else if (mem_eql_str(token_mem, token_len, "asm")) { t->cur_tok->id = TokenIdKeywordAsm; + } else if (mem_eql_str(token_mem, token_len, "struct")) { + t->cur_tok->id = TokenIdKeywordStruct; } t->cur_tok = nullptr; @@ -224,7 +230,11 @@ void tokenize(Buf *buf, Tokenization *out) { switch (c) { case WHITESPACE: break; - case ALPHA: + case 'c': + t.state = TokenizeStateSymbolFirst; + begin_token(&t, TokenIdSymbol); + break; + case ALPHA_EXCEPT_C: case '_': t.state = TokenizeStateSymbol; begin_token(&t, TokenIdSymbol); @@ -526,6 +536,22 @@ void tokenize(Buf *buf, Tokenization *out) { break; } break; + case TokenizeStateSymbolFirst: + switch (c) { + case '"': + t.cur_tok->id = TokenIdStringLiteral; + t.state = TokenizeStateString; + break; + case SYMBOL_CHAR: + t.state = TokenizeStateSymbol; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; case TokenizeStateSymbol: switch (c) { case SYMBOL_CHAR: @@ -589,6 +615,7 @@ void tokenize(Buf *buf, Tokenization *out) { tokenize_error(&t, "unterminated string"); break; case TokenizeStateSymbol: + case TokenizeStateSymbolFirst: case TokenizeStateNumber: case TokenizeStateSawDash: case TokenizeStatePipe: @@ -643,6 +670,7 @@ static const char * token_name(Token *token) { case TokenIdKeywordGoto: return "Goto"; case TokenIdKeywordVolatile: return "Volatile"; case TokenIdKeywordAsm: return "Asm"; + case TokenIdKeywordStruct: return "Struct"; case TokenIdLParen: return "LParen"; case TokenIdRParen: return "RParen"; case TokenIdComma: return "Comma"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index e025f5e827391bb385749f9169da0d4e95e3addf..950c286ea25e72504cd910278ffc11d12b05e5d5 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -32,6 +32,7 @@ enum TokenId { TokenIdKeywordGoto, TokenIdKeywordAsm, TokenIdKeywordVolatile, + TokenIdKeywordStruct, TokenIdLParen, TokenIdRParen, TokenIdComma, diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 4cbb19360d04398e461af55b8ee6312db31138e3..e8d49058cf0504a3162b86d8a58aa946217f1a69 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -161,6 +161,31 @@ LLVMZigDIType *LLVMZigCreateDebugArrayType(LLVMZigDIBuilder *dibuilder, uint64_t return reinterpret_cast(di_type); } + +LLVMZigDIType *LLVMZigCreateDebugStructType(LLVMZigDIBuilder *dibuilder, LLVMZigDIScope *scope, + const char *name, LLVMZigDIFile *file, unsigned line_number, uint64_t size_in_bits, + uint64_t align_in_bits, unsigned flags, LLVMZigDIType *derived_from, + LLVMZigDIType **types_array, int types_array_len, unsigned run_time_lang, LLVMZigDIType *vtable_holder, + const char *unique_id) +{ + SmallVector fields; + for (int i = 0; i < types_array_len; i += 1) { + DIType *ditype = reinterpret_cast(types_array[i]); + fields.push_back(ditype); + } + DIType *di_type = reinterpret_cast(dibuilder)->createStructType( + reinterpret_cast(scope), + name, + reinterpret_cast(file), + line_number, size_in_bits, align_in_bits, flags, + reinterpret_cast(derived_from), + reinterpret_cast(dibuilder)->getOrCreateArray(fields), + run_time_lang, + reinterpret_cast(vtable_holder), + unique_id); + return reinterpret_cast(di_type); +} + LLVMZigDISubroutineType *LLVMZigCreateSubroutineType(LLVMZigDIBuilder *dibuilder_wrapped, LLVMZigDIFile *file, LLVMZigDIType **types_array, int types_array_len, unsigned flags) { diff --git a/src/zig_llvm.hpp b/src/zig_llvm.hpp index d6cdadc9597e4616f53e52ac1aa37e80e3e47edb..8cc1bb5d7bfc55d7c5344bd637b167c7a7a0fcb8 100644 --- a/src/zig_llvm.hpp +++ b/src/zig_llvm.hpp @@ -49,6 +49,11 @@ LLVMZigDIType *LLVMZigCreateDebugArrayType(LLVMZigDIBuilder *dibuilder, uint64_t size_in_bits, uint64_t align_in_bits, LLVMZigDIType *elem_type, int elem_count); +LLVMZigDIType *LLVMZigCreateDebugStructType(LLVMZigDIBuilder *dibuilder, LLVMZigDIScope *scope, + const char *name, LLVMZigDIFile *file, unsigned line_number, uint64_t size_in_bits, + uint64_t align_in_bits, unsigned flags, LLVMZigDIType *derived_from, + LLVMZigDIType **types_array, int types_array_len, unsigned run_time_lang, LLVMZigDIType *vtable_holder, + const char *unique_id); LLVMZigDISubroutineType *LLVMZigCreateSubroutineType(LLVMZigDIBuilder *dibuilder_wrapped, LLVMZigDIFile *file, LLVMZigDIType **types_array, int types_array_len, unsigned flags); diff --git a/std/std.zig b/std/std.zig index 4e523e001523ae5b54a575944a101f9183d6247b..a05a21650f778682266ffbde93e0d5cea1a404e3 100644 --- a/std/std.zig +++ b/std/std.zig @@ -17,8 +17,11 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize { // TODO zig strings instead of C strings // TODO handle buffering and flushing // TODO non-i32 integer literals so we can remove the casts -pub fn print_str(str : *const u8, len: isize) { +// TODO constants for SYS_write and stdout_fileno +//pub fn print_str(str : string) -> isize { +pub fn print_str(str : *const u8, len: isize) -> isize { let SYS_write = 1; let stdout_fileno = 1; - syscall3(SYS_write as isize, stdout_fileno as isize, str as isize, len); + //return syscall3(SYS_write as isize, stdout_fileno as isize, str.ptr as isize, str.len as isize); + return syscall3(SYS_write as isize, stdout_fileno as isize, str as isize, len); } diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 60c85e7d07e34d911fad87cb7eec3c4ec82946ca..7dee8c745ef56063f9a2c1d5a2a96310c86ea970 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -104,7 +104,7 @@ static void add_compiling_test_cases(void) { } export fn _start() -> unreachable { - puts("Hello, world!"); + puts(c"Hello, world!"); exit(0); } )SOURCE", "Hello, world!\n"); @@ -126,7 +126,7 @@ static void add_compiling_test_cases(void) { } fn this_is_a_function() -> unreachable { - puts("OK"); + puts(c"OK"); exit(0); } )SOURCE", "OK\n"); @@ -146,7 +146,7 @@ static void add_compiling_test_cases(void) { /// this is a documentation comment /// doc comment line 2 export fn _start() -> unreachable { - puts(/* mid-line comment /* nested */ */ "OK"); + puts(/* mid-line comment /* nested */ */ c"OK"); exit(0); } )SOURCE", "OK\n"); @@ -180,7 +180,7 @@ static void add_compiling_test_cases(void) { // purposefully conflicting function with main source file // but it's private so it should be OK fn private_function() { - puts("OK"); + puts(c"OK"); } pub fn print_text() { @@ -198,17 +198,17 @@ static void add_compiling_test_cases(void) { export fn _start() -> unreachable { if 1 != 0 { - puts("1 is true"); + puts(c"1 is true"); } else { - puts("1 is false"); + puts(c"1 is false"); } if 0 != 0 { - puts("0 is true"); + puts(c"0 is true"); } else if 1 - 1 != 0 { - puts("1 - 1 is true"); + puts(c"1 - 1 is true"); } if !(0 != 0) { - puts("!0 is true"); + puts(c"!0 is true"); } exit(0); } @@ -227,7 +227,7 @@ static void add_compiling_test_cases(void) { export fn _start() -> unreachable { if add(22, 11) == 33 { - puts("pass"); + puts(c"pass"); } exit(0); } @@ -244,7 +244,7 @@ static void add_compiling_test_cases(void) { if a == 0 { goto done; } - puts("loop"); + puts(c"loop"); loop(a - 1); done: @@ -268,7 +268,7 @@ export fn _start() -> unreachable { let a : i32 = 1; let b = 2; if (a + b == 3) { - puts("OK"); + puts(c"OK"); } exit(0); } @@ -282,10 +282,10 @@ extern { } export fn _start() -> unreachable { - if (true) { puts("OK 1"); } - if (false) { puts("BAD 1"); } - if (!true) { puts("BAD 2"); } - if (!false) { puts("OK 2"); } + if (true) { puts(c"OK 1"); } + if (false) { puts(c"BAD 1"); } + if (!true) { puts(c"BAD 2"); } + if (!false) { puts(c"OK 2"); } exit(0); } )SOURCE", "OK 1\nOK 2\n"); @@ -300,14 +300,14 @@ extern { export fn _start() -> unreachable { if (true) { let no_conflict = 5; - if (no_conflict == 5) { puts("OK 1"); } + if (no_conflict == 5) { puts(c"OK 1"); } } let c = { let no_conflict = 10; no_conflict }; - if (c == 10) { puts("OK 2"); } + if (c == 10) { puts(c"OK 2"); } exit(0); } )SOURCE", "OK 1\nOK 2\n"); @@ -327,7 +327,7 @@ export fn _start() -> unreachable { fn void_fun(a : i32, b : void, c : i32) { let v = b; let vv : void = if (a == 1) {v} else {}; - if (a + c == 3) { puts("OK"); } + if (a + c == 3) { puts(c"OK"); } return vv; } )SOURCE", "OK\n"); @@ -341,14 +341,14 @@ extern { export fn _start() -> unreachable { let mut zero : i32; - if (zero == 0) { puts("zero"); } + if (zero == 0) { puts(c"zero"); } let mut i = 0; loop_start: if i == 3 { goto done; } - puts("loop"); + puts(c"loop"); i = i + 1; goto loop_start; done: @@ -391,7 +391,7 @@ loop_2_start: loop_2_end: if accumulator == 15 { - puts("OK"); + puts(c"OK"); } exit(0); @@ -403,7 +403,7 @@ loop_2_end: use "std.zig"; export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 { - print_str("Hello, world!\n", 14 as isize); + print_str(c"Hello, world!\n", 14 as isize); return 0; } )SOURCE", "Hello, world!\n"); @@ -430,11 +430,11 @@ fn a() {} add_compile_fail_case("unreachable with return", R"SOURCE( fn a() -> unreachable {return;} - )SOURCE", 1, ".tmp_source.zig:2:24: error: type mismatch. expected unreachable. got void"); + )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 {} - )SOURCE", 1, ".tmp_source.zig:2:15: error: type mismatch. expected i32. got void"); + )SOURCE", 1, ".tmp_source.zig:2:15: error: expected type 'i32', got 'void'"); add_compile_fail_case("undefined function call", R"SOURCE( fn a() { @@ -514,16 +514,16 @@ fn f(a : i32) { add_compile_fail_case("variable has wrong type", R"SOURCE( fn f() -> i32 { - let a = "a"; + let a = c"a"; a } - )SOURCE", 1, ".tmp_source.zig:2:15: error: type mismatch. expected i32. got *const u8"); + )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() { if (0) {} } - )SOURCE", 1, ".tmp_source.zig:3:9: error: type mismatch. expected bool. got i32"); + )SOURCE", 1, ".tmp_source.zig:3:9: error: expected type 'bool', got 'i32'"); add_compile_fail_case("assign unreachable", R"SOURCE( fn f() { @@ -551,11 +551,11 @@ a_label: } )SOURCE", 1, ".tmp_source.zig:3:1: error: label 'a_label' defined but not used"); - add_compile_fail_case("expected bare identifier", R"SOURCE( + add_compile_fail_case("bad assignment target", R"SOURCE( fn f() { 3 = 3; } - )SOURCE", 1, ".tmp_source.zig:3:5: error: expected a bare identifier"); + )SOURCE", 1, ".tmp_source.zig:3:5: error: assignment target must be variable, field, or array element"); add_compile_fail_case("assign to constant variable", R"SOURCE( fn f() {