diff --git a/doc/langref.md b/doc/langref.md index 3bcf50b6d..4b2c176f4 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -5,15 +5,17 @@ ``` Root = many(TopLevelDecl) "EOF" -TopLevelDecl = many(Directive) option(VisibleMod) (FnDef | ExternDecl | RootExportDecl | Import | ContainerDecl | GlobalVarDecl | ErrorValueDecl | CImportDecl) +TopLevelDecl = many(Directive) option(VisibleMod) (FnDef | ExternDecl | RootExportDecl | Import | ContainerDecl | GlobalVarDecl | ErrorValueDecl | CImportDecl | TypeDecl) CImportDecl = "c_import" Block +TypeDecl = "type" "Symbol" "=" TypeExpr ";" + ErrorValueDecl = "error" "Symbol" ";" GlobalVarDecl = VariableDeclaration ";" -VariableDeclaration = ("var" | "const") "Symbol" option(":" PrefixOpExpression) "=" Expression +VariableDeclaration = ("var" | "const") "Symbol" option(":" TypeExpr) "=" Expression ContainerDecl = ("struct" | "enum") "Symbol" "{" many(StructMember) "}" @@ -27,7 +29,7 @@ RootExportDecl = "export" "Symbol" "String" ";" ExternDecl = "extern" (FnProto | VariableDeclaration) ";" -FnProto = "fn" option("Symbol") ParamDeclList option("->" PrefixOpExpression) +FnProto = "fn" option("Symbol") ParamDeclList option("->" TypeExpr) Directive = "#" "Symbol" "(" "String" ")" @@ -37,7 +39,7 @@ FnDef = FnProto Block ParamDeclList = "(" list(ParamDecl, ",") ")" -ParamDecl = option("noalias") option("Symbol" ":") PrefixOpExpression | "..." +ParamDecl = option("noalias") option("Symbol" ":") TypeExpr | "..." Block = "{" list(option(Statement), ";") "}" @@ -47,6 +49,8 @@ Label = "Symbol" ":" Expression = BlockExpression | NonBlockExpression +TypeExpr = PrefixOpExpression + NonBlockExpression = ReturnExpression | AssignmentExpression AsmExpression = "asm" option("volatile") "(" "String" option(AsmOutput) ")" @@ -55,7 +59,7 @@ AsmOutput = ":" list(AsmOutputItem, ",") option(AsmInput) AsmInput = ":" list(AsmInputItem, ",") option(AsmClobbers) -AsmOutputItem = "[" "Symbol" "]" "String" "(" ("Symbol" | "->" PrefixOpExpression) ")" +AsmOutputItem = "[" "Symbol" "]" "String" "(" ("Symbol" | "->" TypeExpr) ")" AsmInputItem = "[" "Symbol" "]" "String" "(" Expression ")" @@ -91,7 +95,7 @@ IfExpression = IfVarExpression | IfBoolExpression IfBoolExpression = "if" "(" Expression ")" Expression option(Else) -IfVarExpression = "if" "(" ("const" | "var") "Symbol" option(":" PrefixOpExpression) "?=" Expression ")" Expression Option(Else) +IfVarExpression = "if" "(" ("const" | "var") "Symbol" option(":" TypeExpr) "?=" Expression ")" Expression Option(Else) Else = "else" Expression @@ -117,7 +121,7 @@ AdditionOperator = "+" | "-" | "++" MultiplyExpression = CurlySuffixExpression MultiplyOperator MultiplyExpression | CurlySuffixExpression -CurlySuffixExpression = PrefixOpExpression option(ContainerInitExpression) +CurlySuffixExpression = TypeExpr option(ContainerInitExpression) MultiplyOperator = "*" | "/" | "%" @@ -143,13 +147,13 @@ PrefixOp = "!" | "-" | "~" | "*" | ("&" option("const")) | "?" | "%" | "%%" PrimaryExpression = "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." "Symbol") -ArrayType = "[" option(Expression) "]" option("const") PrefixOpExpression +ArrayType = "[" option(Expression) "]" option("const") TypeExpr GotoExpression = "goto" "Symbol" GroupedExpression = "(" Expression ")" -KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" +KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "type" ``` ## Operator Precedence diff --git a/src/all_types.hpp b/src/all_types.hpp index 6983a6388..1c432b42c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -124,6 +124,7 @@ enum NodeType { NodeTypeDirective, NodeTypeReturnExpr, NodeTypeVariableDeclaration, + NodeTypeTypeDecl, NodeTypeErrorValueDecl, NodeTypeBinOpExpr, NodeTypeUnwrapErrorExpr, @@ -159,6 +160,7 @@ enum NodeType { NodeTypeStructValueField, NodeTypeArrayType, NodeTypeErrorType, + NodeTypeTypeLiteral, }; struct AstNodeRoot { @@ -212,9 +214,6 @@ struct AstNodeParamDecl { // populated by semantic analyzer VariableTableEntry *variable; - bool is_byval; - int src_index; - int gen_index; }; struct AstNodeBlock { @@ -256,6 +255,19 @@ struct AstNodeVariableDeclaration { VariableTableEntry *variable; }; +struct AstNodeTypeDecl { + VisibMod visib_mod; + ZigList *directives; + Buf symbol; + AstNode *child_type; + + // populated by semantic analyzer + TopLevelDecl top_level_decl; + // if this is set, don't process the node; we've already done so + // and here is the type (with id TypeTableEntryIdTypeDecl) + TypeTableEntry *override_type; +}; + struct AstNodeErrorValueDecl { Buf name; VisibMod visib_mod; @@ -684,6 +696,11 @@ struct AstNodeErrorType { Expr resolved_expr; }; +struct AstNodeTypeLiteral { + // populated by semantic analyzer + Expr resolved_expr; +}; + struct AstNode { enum NodeType type; int line; @@ -704,6 +721,7 @@ struct AstNode { AstNodeBlock block; AstNodeReturnExpr return_expr; AstNodeVariableDeclaration variable_declaration; + AstNodeTypeDecl type_decl; AstNodeErrorValueDecl error_value_decl; AstNodeBinOpExpr bin_op_expr; AstNodeUnwrapErrorExpr unwrap_err_expr; @@ -740,6 +758,7 @@ struct AstNode { AstNodeContinueExpr continue_expr; AstNodeArrayType array_type; AstNodeErrorType error_type; + AstNodeTypeLiteral type_literal; } data; }; @@ -755,6 +774,24 @@ struct AsmToken { int end; }; +struct FnTypeParamInfo { + bool is_noalias; + TypeTableEntry *type; +}; + +struct FnTypeId { + TypeTableEntry *return_type; + FnTypeParamInfo *param_info; + int param_count; + bool is_var_args; + bool is_naked; + bool is_extern; +}; + +uint32_t fn_type_id_hash(FnTypeId); +bool fn_type_id_eql(FnTypeId a, FnTypeId b); + + struct TypeTableEntryPointer { TypeTableEntry *child_type; bool is_const; @@ -820,17 +857,25 @@ struct TypeTableEntryEnum { bool complete; }; +struct FnGenParamInfo { + int src_index; + int gen_index; + bool is_byval; +}; + struct TypeTableEntryFn { - TypeTableEntry *src_return_type; + FnTypeId fn_type_id; TypeTableEntry *gen_return_type; - TypeTableEntry **param_types; - int src_param_count; - LLVMTypeRef raw_type_ref; - bool is_var_args; int gen_param_count; + FnGenParamInfo *gen_param_info; + + LLVMTypeRef raw_type_ref; LLVMCallConv calling_convention; - bool is_extern; - bool is_naked; +}; + +struct TypeTableEntryTypeDecl { + TypeTableEntry *child_type; + TypeTableEntry *canonical_type; }; enum TypeTableEntryId { @@ -852,6 +897,7 @@ enum TypeTableEntryId { TypeTableEntryIdPureError, TypeTableEntryIdEnum, TypeTableEntryIdFn, + TypeTableEntryIdTypeDecl, }; struct TypeTableEntry { @@ -873,6 +919,7 @@ struct TypeTableEntry { TypeTableEntryError error; TypeTableEntryEnum enumeration; TypeTableEntryFn fn; + TypeTableEntryTypeDecl type_decl; } data; // use these fields to make sure we don't duplicate type table entries for the same type @@ -900,7 +947,6 @@ struct ImportTableEntry { // reminder: hash tables must be initialized before use HashMap fn_table; - HashMap fn_type_table; }; struct LabelTableEntry { @@ -969,12 +1015,14 @@ struct CodeGen { HashMap builtin_fn_table; HashMap primitive_type_table; HashMap unresolved_top_level_decls; + HashMap fn_type_table; uint32_t next_unresolved_index; struct { TypeTableEntry *entry_bool; TypeTableEntry *entry_int[2][4]; // [signed,unsigned][8,16,32,64] + TypeTableEntry *entry_c_int[8]; TypeTableEntry *entry_u8; TypeTableEntry *entry_u16; TypeTableEntry *entry_u32; @@ -1082,12 +1130,16 @@ struct BlockContext { Buf *c_import_buf; }; -struct ParseH { - ZigList errors; - ZigList fn_list; - ZigList struct_list; - ZigList var_list; - ZigList incomplete_struct_list; +enum CIntType { + CIntTypeShort, + CIntTypeUShort, + CIntTypeInt, + CIntTypeUInt, + CIntTypeLong, + CIntTypeULong, + CIntTypeLongLong, + CIntTypeULongLong, }; + #endif diff --git a/src/analyze.cpp b/src/analyze.cpp index 27e6a1a38..fcb5f5d0e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -56,6 +56,7 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeDirective: case NodeTypeReturnExpr: case NodeTypeVariableDeclaration: + case NodeTypeTypeDecl: case NodeTypeErrorValueDecl: case NodeTypeNumberLiteral: case NodeTypeStringLiteral: @@ -83,6 +84,7 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeSwitchProng: case NodeTypeArrayType: case NodeTypeErrorType: + case NodeTypeTypeLiteral: case NodeTypeContainerInitExpr: return node; } @@ -123,6 +125,7 @@ TypeTableEntry *new_type_table_entry(TypeTableEntryId id) { case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdUndefLit: + case TypeTableEntryIdTypeDecl: // nothing to init break; case TypeTableEntryIdStruct: @@ -149,7 +152,7 @@ static int bits_needed_for_unsigned(uint64_t x) { } } -static TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) { +TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) { return get_int_type(g, false, bits_needed_for_unsigned(x)); } @@ -165,12 +168,15 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool buf_resize(&entry->name, 0); buf_appendf(&entry->name, "&%s%s", const_str, buf_ptr(&child_type->name)); + TypeTableEntry *canon_child_type = get_underlying_type(child_type); + assert(canon_child_type->id != TypeTableEntryIdInvalid); + bool zero_bits; - if (child_type->size_in_bits == 0) { - if (child_type->id == TypeTableEntryIdStruct) { - zero_bits = child_type->data.structure.complete; - } else if (child_type->id == TypeTableEntryIdEnum) { - zero_bits = child_type->data.enumeration.complete; + if (canon_child_type->size_in_bits == 0) { + if (canon_child_type->id == TypeTableEntryIdStruct) { + zero_bits = canon_child_type->data.structure.complete; + } else if (canon_child_type->id == TypeTableEntryIdEnum) { + zero_bits = canon_child_type->data.enumeration.complete; } else { zero_bits = true; } @@ -196,7 +202,7 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool } } -static TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { +TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { if (child_type->maybe_parent) { TypeTableEntry *entry = child_type->maybe_parent; return entry; @@ -317,8 +323,7 @@ static TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) { } } -static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size) -{ +TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size) { auto existing_entry = child_type->arrays_by_size.maybe_get(array_size); if (existing_entry) { TypeTableEntry *entry = existing_entry->value; @@ -417,6 +422,177 @@ static TypeTableEntry *get_unknown_size_array_type(CodeGen *g, TypeTableEntry *c } } +TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry *child_type) { + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdTypeDecl); + + buf_init_from_str(&entry->name, name); + + entry->type_ref = child_type->type_ref; + entry->type_ref = child_type->type_ref; + entry->di_type = child_type->di_type; + entry->size_in_bits = child_type->size_in_bits; + entry->align_in_bits = child_type->align_in_bits; + + entry->data.type_decl.child_type = child_type; + + if (child_type->id == TypeTableEntryIdTypeDecl) { + entry->data.type_decl.canonical_type = child_type->data.type_decl.canonical_type; + } else { + entry->data.type_decl.canonical_type = child_type; + } + + return entry; +} + +// accepts ownership of fn_type_id memory +TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId fn_type_id) { + auto table_entry = g->fn_type_table.maybe_get(fn_type_id); + if (table_entry) { + return table_entry->value; + } + + TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn); + fn_type->data.fn.fn_type_id = fn_type_id; + fn_type->data.fn.calling_convention = fn_type_id.is_extern ? LLVMCCallConv : LLVMFastCallConv; + + fn_type->size_in_bits = g->pointer_size_bytes * 8; + fn_type->align_in_bits = g->pointer_size_bytes * 8; + + // populate the name of the type + buf_resize(&fn_type->name, 0); + const char *extern_str = fn_type_id.is_extern ? "extern " : ""; + const char *naked_str = fn_type_id.is_naked ? "naked " : ""; + buf_appendf(&fn_type->name, "%s%sfn(", extern_str, naked_str); + for (int i = 0; i < fn_type_id.param_count; i += 1) { + FnTypeParamInfo *param_info = &fn_type_id.param_info[i]; + + TypeTableEntry *param_type = param_info->type; + const char *comma = (i == 0) ? "" : ", "; + const char *noalias_str = param_info->is_noalias ? "noalias " : ""; + buf_appendf(&fn_type->name, "%s%s%s", comma, noalias_str, buf_ptr(¶m_type->name)); + } + + if (fn_type_id.is_var_args) { + const char *comma = (fn_type_id.param_count == 0) ? "" : ", "; + buf_appendf(&fn_type->name, "%s...", comma); + } + buf_appendf(&fn_type->name, ")"); + if (fn_type_id.return_type->id != TypeTableEntryIdVoid) { + buf_appendf(&fn_type->name, " -> %s", buf_ptr(&fn_type_id.return_type->name)); + } + + + // next, loop over the parameters again and compute debug information + // and codegen information + bool first_arg_return = handle_is_ptr(fn_type_id.return_type); + // +1 for maybe making the first argument the return value + LLVMTypeRef *gen_param_types = allocate(1 + fn_type_id.param_count); + // +1 because 0 is the return type and +1 for maybe making first arg ret val + LLVMZigDIType **param_di_types = allocate(2 + fn_type_id.param_count); + param_di_types[0] = fn_type_id.return_type->di_type; + int gen_param_index = 0; + TypeTableEntry *gen_return_type; + if (first_arg_return) { + TypeTableEntry *gen_type = get_pointer_to_type(g, fn_type_id.return_type, false); + gen_param_types[gen_param_index] = gen_type->type_ref; + gen_param_index += 1; + // after the gen_param_index += 1 because 0 is the return type + param_di_types[gen_param_index] = gen_type->di_type; + gen_return_type = g->builtin_types.entry_void; + } else if (fn_type_id.return_type->size_in_bits == 0) { + gen_return_type = g->builtin_types.entry_void; + } else { + gen_return_type = fn_type_id.return_type; + } + fn_type->data.fn.gen_return_type = gen_return_type; + + fn_type->data.fn.gen_param_info = allocate(fn_type_id.param_count); + for (int i = 0; i < fn_type_id.param_count; i += 1) { + FnTypeParamInfo *src_param_info = &fn_type->data.fn.fn_type_id.param_info[i]; + TypeTableEntry *type_entry = src_param_info->type; + FnGenParamInfo *gen_param_info = &fn_type->data.fn.gen_param_info[i]; + + gen_param_info->src_index = i; + gen_param_info->gen_index = -1; + + if (type_entry->size_in_bits > 0) { + TypeTableEntry *gen_type; + if (handle_is_ptr(type_entry)) { + gen_type = get_pointer_to_type(g, type_entry, true); + gen_param_info->is_byval = true; + } else { + gen_type = type_entry; + } + gen_param_types[gen_param_index] = gen_type->type_ref; + gen_param_info->gen_index = gen_param_index; + + gen_param_index += 1; + + // after the gen_param_index += 1 because 0 is the return type + param_di_types[gen_param_index] = gen_type->di_type; + } + } + + fn_type->data.fn.gen_param_count = gen_param_index; + + fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref, + gen_param_types, gen_param_index, fn_type_id.is_var_args); + fn_type->type_ref = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0); + LLVMZigDIFile *di_file = nullptr; // TODO if we get a crash maybe this is the culprit + fn_type->di_type = LLVMZigCreateSubroutineType(g->dbuilder, di_file, + param_di_types, gen_param_index + 1, 0); + + g->fn_type_table.put(fn_type_id, fn_type); + + return fn_type; +} + +static TypeTableEntryId container_to_type(ContainerKind kind) { + switch (kind) { + case ContainerKindStruct: + return TypeTableEntryIdStruct; + case ContainerKindEnum: + return TypeTableEntryIdEnum; + } + zig_unreachable(); +} + +TypeTableEntry *get_partial_container_type(CodeGen *g, ImportTableEntry *import, + ContainerKind kind, AstNode *decl_node, const char *name) +{ + TypeTableEntryId type_id = container_to_type(kind); + TypeTableEntry *entry = new_type_table_entry(type_id); + + switch (kind) { + case ContainerKindStruct: + entry->data.structure.decl_node = decl_node; + break; + case ContainerKindEnum: + entry->data.enumeration.decl_node = decl_node; + break; + } + + unsigned line = decl_node ? decl_node->line : 0; + + entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), name); + entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder, + LLVMZigTag_DW_structure_type(), name, + LLVMZigFileToScope(import->di_file), import->di_file, line + 1); + + buf_init_from_str(&entry->name, name); + + return entry; +} + + +TypeTableEntry *get_underlying_type(TypeTableEntry *type_entry) { + if (type_entry->id == TypeTableEntryIdTypeDecl) { + return type_entry->data.type_decl.canonical_type; + } else { + return type_entry; + } +} + // If the node does not have a constant expression value with a metatype, generates an error // and returns invalid type. Otherwise, returns the type of the constant expression value. // Must be called after analyze_expression on the same node. @@ -463,129 +639,64 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor return g->builtin_types.entry_invalid; } - TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn); - fn_type->data.fn.is_extern = fn_proto->is_extern || (fn_proto->visib_mod == VisibModExport); - fn_type->data.fn.is_naked = is_naked; - fn_type->data.fn.calling_convention = fn_proto->is_extern ? LLVMCCallConv : LLVMFastCallConv; + FnTypeId fn_type_id; + fn_type_id.is_extern = fn_proto->is_extern || (fn_proto->visib_mod == VisibModExport); + fn_type_id.is_naked = is_naked; + fn_type_id.param_count = node->data.fn_proto.params.length; + fn_type_id.param_info = allocate(fn_type_id.param_count); + fn_type_id.is_var_args = fn_proto->is_var_args; + fn_type_id.return_type = analyze_type_expr(g, import, import->block_context, node->data.fn_proto.return_type); - int src_param_count = node->data.fn_proto.params.length; - fn_type->size_in_bits = g->pointer_size_bytes * 8; - fn_type->align_in_bits = g->pointer_size_bytes * 8; - fn_type->data.fn.src_param_count = src_param_count; - fn_type->data.fn.param_types = allocate(src_param_count); + if (fn_type_id.return_type->id == TypeTableEntryIdInvalid) { + fn_proto->skip = true; + } - // first, analyze the parameters and return type in order they appear in - // source code in order for error messages to be in the best order. - buf_resize(&fn_type->name, 0); - const char *extern_str = fn_type->data.fn.is_extern ? "extern " : ""; - const char *naked_str = fn_type->data.fn.is_naked ? "naked " : ""; - buf_appendf(&fn_type->name, "%s%sfn(", extern_str, naked_str); - for (int i = 0; i < src_param_count; i += 1) { + for (int i = 0; i < fn_type_id.param_count; i += 1) { AstNode *child = node->data.fn_proto.params.at(i); assert(child->type == NodeTypeParamDecl); TypeTableEntry *type_entry = analyze_type_expr(g, import, import->block_context, child->data.param_decl.type); - fn_type->data.fn.param_types[i] = type_entry; - - const char *comma = (i == 0) ? "" : ", "; - buf_appendf(&fn_type->name, "%s%s", comma, buf_ptr(&type_entry->name)); - } - - TypeTableEntry *return_type = analyze_type_expr(g, import, import->block_context, - node->data.fn_proto.return_type); - fn_type->data.fn.src_return_type = return_type; - if (return_type->id == TypeTableEntryIdInvalid) { - fn_proto->skip = true; - } - fn_type->data.fn.is_var_args = fn_proto->is_var_args; - if (fn_proto->is_var_args) { - const char *comma = (src_param_count == 0) ? "" : ", "; - buf_appendf(&fn_type->name, "%s...", comma); - } - buf_appendf(&fn_type->name, ")"); - if (return_type->id != TypeTableEntryIdVoid) { - buf_appendf(&fn_type->name, " -> %s", buf_ptr(&return_type->name)); - } - - - // next, loop over the parameters again and compute debug information - // and codegen information - bool first_arg_return = !fn_proto->skip && handle_is_ptr(return_type); - // +1 for maybe making the first argument the return value - LLVMTypeRef *gen_param_types = allocate(1 + src_param_count); - // +1 because 0 is the return type and +1 for maybe making first arg ret val - LLVMZigDIType **param_di_types = allocate(2 + src_param_count); - param_di_types[0] = return_type->di_type; - int gen_param_index = 0; - TypeTableEntry *gen_return_type; - if (first_arg_return) { - TypeTableEntry *gen_type = get_pointer_to_type(g, return_type, false); - gen_param_types[gen_param_index] = gen_type->type_ref; - gen_param_index += 1; - // after the gen_param_index += 1 because 0 is the return type - param_di_types[gen_param_index] = gen_type->di_type; - gen_return_type = g->builtin_types.entry_void; - } else if (return_type->size_in_bits == 0) { - gen_return_type = g->builtin_types.entry_void; - } else { - gen_return_type = return_type; - } - fn_type->data.fn.gen_return_type = gen_return_type; - for (int i = 0; i < src_param_count; i += 1) { - AstNode *child = node->data.fn_proto.params.at(i); - assert(child->type == NodeTypeParamDecl); - TypeTableEntry *type_entry = fn_type->data.fn.param_types[i]; - - if (type_entry->id == TypeTableEntryIdUnreachable) { - add_node_error(g, child->data.param_decl.type, - buf_sprintf("parameter of type 'unreachable' not allowed")); - fn_proto->skip = true; - } else if (type_entry->id == TypeTableEntryIdInvalid) { + switch (type_entry->id) { + case TypeTableEntryIdInvalid: + fn_proto->skip = true; + break; + case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdUndefLit: + case TypeTableEntryIdMetaType: + case TypeTableEntryIdUnreachable: + fn_proto->skip = true; + add_node_error(g, child->data.param_decl.type, + buf_sprintf("parameter of type '%s' not allowed'", buf_ptr(&type_entry->name))); + break; + case TypeTableEntryIdVoid: + case TypeTableEntryIdBool: + case TypeTableEntryIdInt: + case TypeTableEntryIdFloat: + case TypeTableEntryIdPointer: + case TypeTableEntryIdArray: + case TypeTableEntryIdStruct: + case TypeTableEntryIdMaybe: + case TypeTableEntryIdErrorUnion: + case TypeTableEntryIdPureError: + case TypeTableEntryIdEnum: + case TypeTableEntryIdFn: + case TypeTableEntryIdTypeDecl: + break; + } + if (type_entry->id == TypeTableEntryIdInvalid) { fn_proto->skip = true; } - - child->data.param_decl.src_index = i; - child->data.param_decl.gen_index = -1; - - if (!fn_proto->skip && type_entry->size_in_bits > 0) { - - TypeTableEntry *gen_type; - if (handle_is_ptr(type_entry)) { - gen_type = get_pointer_to_type(g, type_entry, true); - child->data.param_decl.is_byval = true; - } else { - gen_type = type_entry; - } - gen_param_types[gen_param_index] = gen_type->type_ref; - child->data.param_decl.gen_index = gen_param_index; - - gen_param_index += 1; - - // after the gen_param_index += 1 because 0 is the return type - param_di_types[gen_param_index] = gen_type->di_type; - } + FnTypeParamInfo *param_info = &fn_type_id.param_info[i]; + param_info->type = type_entry; + param_info->is_noalias = child->data.param_decl.is_noalias; } - fn_type->data.fn.gen_param_count = gen_param_index; - if (fn_proto->skip) { return g->builtin_types.entry_invalid; } - auto table_entry = import->fn_type_table.maybe_get(&fn_type->name); - if (table_entry) { - return table_entry->value; - } else { - fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref, - gen_param_types, gen_param_index, fn_type->data.fn.is_var_args); - fn_type->type_ref = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0); - fn_type->di_type = LLVMZigCreateSubroutineType(g->dbuilder, import->di_file, - param_di_types, gen_param_index + 1, 0); - - import->fn_type_table.put(&fn_type->name, fn_type); - - return fn_type; - } + return get_fn_type(g, fn_type_id); } @@ -640,14 +751,14 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t if (fn_table_entry->is_inline) { LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMAlwaysInlineAttribute); } - if (fn_type->data.fn.is_naked) { + if (fn_type->data.fn.fn_type_id.is_naked) { LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNakedAttribute); } LLVMSetLinkage(fn_table_entry->fn_value, fn_table_entry->internal_linkage ? LLVMInternalLinkage : LLVMExternalLinkage); - if (fn_type->data.fn.src_return_type->id == TypeTableEntryIdUnreachable) { + if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdUnreachable) { LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoReturnAttribute); } LLVMSetFunctionCallConv(fn_table_entry->fn_value, fn_type->data.fn.calling_convention); @@ -691,6 +802,7 @@ static void preview_function_labels(CodeGen *g, AstNode *node, FnTableEntry *fn_ } static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *enum_type) { + // if you change this logic you likely must also change similar logic in parseh.cpp assert(enum_type->id == TypeTableEntryIdEnum); AstNode *decl_node = enum_type->data.enumeration.decl_node; @@ -853,6 +965,8 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt } static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *struct_type) { + // if you change the logic of this function likely you must make a similar change in + // parseh.cpp assert(struct_type->id == TypeTableEntryIdStruct); AstNode *decl_node = struct_type->data.structure.decl_node; @@ -1104,15 +1218,12 @@ static void resolve_c_import_decl(CodeGen *g, ImportTableEntry *parent_import, A ImportTableEntry *child_import = allocate(1); child_import->fn_table.init(32); - child_import->fn_type_table.init(32); child_import->c_import_node = node; ZigList errors = {0}; int err; - if ((err = parse_h_buf(child_import, &errors, child_context->c_import_buf, g->clang_argv, g->clang_argv_len, - buf_ptr(g->libc_include_path), false, &g->next_node_index))) - { + if ((err = parse_h_buf(child_import, &errors, child_context->c_import_buf, g, node))) { zig_panic("unable to parse h file: %s\n", err_str(err)); } @@ -1175,6 +1286,27 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode VariableTableEntry *var = analyze_variable_declaration(g, import, import->block_context, nullptr, node); g->global_vars.append(var); + break; + } + case NodeTypeTypeDecl: + { + AstNode *type_node = node->data.type_decl.child_type; + Buf *decl_name = &node->data.type_decl.symbol; + + TypeTableEntry *typedecl_type; + if (node->data.type_decl.override_type) { + typedecl_type = node->data.type_decl.override_type; + } else { + TypeTableEntry *child_type = analyze_type_expr(g, import, import->block_context, type_node); + if (child_type->id == TypeTableEntryIdInvalid) { + typedecl_type = child_type; + } else { + typedecl_type = get_typedecl_type(g, buf_ptr(decl_name), child_type); + } + } + + import->block_context->type_table.put(decl_name, typedecl_type); + break; } case NodeTypeErrorValueDecl: @@ -1224,6 +1356,7 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeContainerInitExpr: case NodeTypeArrayType: case NodeTypeErrorType: + case NodeTypeTypeLiteral: zig_unreachable(); } @@ -1278,8 +1411,10 @@ static bool type_has_codegen_value(TypeTableEntryId id) { case TypeTableEntryIdEnum: case TypeTableEntryIdFn: return true; + + case TypeTableEntryIdTypeDecl: + zig_unreachable(); } - zig_unreachable(); } static void add_global_const_expr(CodeGen *g, Expr *expr) { @@ -1376,25 +1511,30 @@ static bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTable if (expected_type->id == TypeTableEntryIdFn && actual_type->id == TypeTableEntryIdFn) { - if (expected_type->data.fn.is_extern != actual_type->data.fn.is_extern) { + if (expected_type->data.fn.fn_type_id.is_extern != actual_type->data.fn.fn_type_id.is_extern) { return false; } - if (expected_type->data.fn.is_naked != actual_type->data.fn.is_naked) { + if (expected_type->data.fn.fn_type_id.is_naked != actual_type->data.fn.fn_type_id.is_naked) { return false; } - if (!types_match_const_cast_only(expected_type->data.fn.src_return_type, - actual_type->data.fn.src_return_type)) + if (!types_match_const_cast_only(expected_type->data.fn.fn_type_id.return_type, + actual_type->data.fn.fn_type_id.return_type)) { return false; } - if (expected_type->data.fn.src_param_count != actual_type->data.fn.src_param_count) { + if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) { return false; } - for (int i = 0; i < expected_type->data.fn.src_param_count; i += 1) { + for (int i = 0; i < expected_type->data.fn.fn_type_id.param_count; i += 1) { // note it's reversed for parameters - if (types_match_const_cast_only(actual_type->data.fn.param_types[i], - expected_type->data.fn.param_types[i])) - { + FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i]; + FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i]; + + if (!types_match_const_cast_only(actual_param_info->type, expected_param_info->type)) { + return false; + } + + if (expected_param_info->is_noalias != actual_param_info->is_noalias) { return false; } } @@ -3610,6 +3750,7 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdFn: + case TypeTableEntryIdTypeDecl: return resolve_expr_const_val_as_type(g, node, type_entry); } } @@ -3664,14 +3805,14 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, assert(node->type == NodeTypeFnCallExpr); // count parameters - int src_param_count = fn_type->data.fn.src_param_count; + int src_param_count = fn_type->data.fn.fn_type_id.param_count; int actual_param_count = node->data.fn_call_expr.params.length; if (struct_type) { actual_param_count += 1; } - if (fn_type->data.fn.is_var_args) { + if (fn_type->data.fn.fn_type_id.is_var_args) { if (actual_param_count < src_param_count) { add_node_error(g, node, buf_sprintf("expected at least %d arguments, got %d", src_param_count, actual_param_count)); @@ -3689,12 +3830,12 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, TypeTableEntry *expected_param_type = nullptr; int fn_proto_i = i + (struct_type ? 1 : 0); if (fn_proto_i < src_param_count) { - expected_param_type = fn_type->data.fn.param_types[fn_proto_i]; + expected_param_type = fn_type->data.fn.fn_type_id.param_info[fn_proto_i].type; } analyze_expression(g, import, context, expected_param_type, child); } - TypeTableEntry *return_type = fn_type->data.fn.src_return_type; + TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type; if (return_type->id == TypeTableEntryIdInvalid) { return return_type; @@ -4304,6 +4445,9 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeErrorType: return_type = resolve_expr_const_val_as_type(g, node, g->builtin_types.entry_pure_error); break; + case NodeTypeTypeLiteral: + return_type = resolve_expr_const_val_as_type(g, node, g->builtin_types.entry_type); + break; case NodeTypeSwitchExpr: return_type = analyze_switch_expr(g, import, context, expected_type, node); break; @@ -4322,6 +4466,7 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeStructField: case NodeTypeStructValueField: case NodeTypeErrorValueDecl: + case NodeTypeTypeDecl: zig_unreachable(); } assert(return_type); @@ -4354,8 +4499,9 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo BlockContext *context = node->data.fn_def.block_context; + FnTableEntry *fn_table_entry = fn_proto_node->data.fn_proto.fn_table_entry; + TypeTableEntry *fn_type = fn_table_entry->type_entry; AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto; - bool is_exported = (fn_proto->visib_mod == VisibModExport); for (int i = 0; i < fn_proto->params.length; i += 1) { AstNode *param_decl_node = fn_proto->params.at(i); assert(param_decl_node->type == NodeTypeParamDecl); @@ -4369,9 +4515,9 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo buf_sprintf("noalias on non-pointer parameter")); } - if (is_exported && type->id == TypeTableEntryIdStruct) { + if (fn_type->data.fn.fn_type_id.is_extern && type->id == TypeTableEntryIdStruct) { add_node_error(g, param_decl_node, - buf_sprintf("byvalue struct parameters not yet supported on exported functions")); + buf_sprintf("byvalue struct parameters not yet supported on extern functions")); } if (buf_len(¶m_decl->name) == 0) { @@ -4382,16 +4528,15 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo var->src_arg_index = i; param_decl_node->data.param_decl.variable = var; - var->gen_arg_index = param_decl_node->data.param_decl.gen_index; + var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index; } - TypeTableEntry *expected_type = unwrapped_node_type(fn_proto->return_type); + TypeTableEntry *expected_type = fn_type->data.fn.fn_type_id.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; { - FnTableEntry *fn_table_entry = fn_proto_node->data.fn_proto.fn_table_entry; auto it = fn_table_entry->label_table.entry_iterator(); for (;;) { auto *entry = it.next(); @@ -4427,6 +4572,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeVariableDeclaration: case NodeTypeErrorValueDecl: case NodeTypeFnProto: + case NodeTypeTypeDecl: // already took care of these break; case NodeTypeDirective: @@ -4466,6 +4612,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeContainerInitExpr: case NodeTypeArrayType: case NodeTypeErrorType: + case NodeTypeTypeLiteral: zig_unreachable(); } } @@ -4485,10 +4632,14 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeContinue: case NodeTypeErrorValueDecl: case NodeTypeErrorType: + case NodeTypeTypeLiteral: // no dependencies on other top level declarations break; case NodeTypeSymbol: { + if (node->data.symbol_expr.override_type_entry) { + break; + } Buf *name = &node->data.symbol_expr.symbol; auto table_entry = g->primitive_type_table.maybe_get(name); if (!table_entry) { @@ -4627,6 +4778,9 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeParamDecl: collect_expr_decl_deps(g, import, node->data.param_decl.type, decl_node); break; + case NodeTypeTypeDecl: + collect_expr_decl_deps(g, import, node->data.type_decl.child_type, decl_node); + break; case NodeTypeVariableDeclaration: case NodeTypeRootExportDecl: case NodeTypeFnDef: @@ -4642,16 +4796,6 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode } } -static TypeTableEntryId container_to_type(ContainerKind kind) { - switch (kind) { - case ContainerKindStruct: - return TypeTableEntryIdStruct; - case ContainerKindEnum: - return TypeTableEntryIdEnum; - } - zig_unreachable(); -} - static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *node) { switch (node->type) { case NodeTypeRoot: @@ -4669,28 +4813,16 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast } if (table_entry) { node->data.struct_decl.type_entry = table_entry->value; - add_node_error(g, node, - buf_sprintf("redefinition of '%s'", buf_ptr(name))); + add_node_error(g, node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); } else { - TypeTableEntryId type_id = container_to_type(node->data.struct_decl.kind); - TypeTableEntry *entry = new_type_table_entry(type_id); - switch (node->data.struct_decl.kind) { - case ContainerKindStruct: - entry->data.structure.decl_node = node; - break; - case ContainerKindEnum: - entry->data.enumeration.decl_node = node; - break; + TypeTableEntry *entry; + if (node->data.struct_decl.type_entry) { + entry = node->data.struct_decl.type_entry; + } else { + entry = get_partial_container_type(g, import, + node->data.struct_decl.kind, node, buf_ptr(name)); } - entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(name)); - entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder, - LLVMZigTag_DW_structure_type(), buf_ptr(name), - LLVMZigFileToScope(import->di_file), import->di_file, node->line + 1); - - 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 import->block_context->type_table.put(&entry->name, entry); node->data.struct_decl.type_entry = entry; @@ -4761,6 +4893,23 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast } break; } + case NodeTypeTypeDecl: + { + // determine which other top level declarations this variable declaration depends on. + TopLevelDecl *decl_node = &node->data.type_decl.top_level_decl; + decl_node->deps.init(1); + collect_expr_decl_deps(g, import, node, decl_node); + + Buf *name = &node->data.type_decl.symbol; + decl_node->name = name; + decl_node->import = import; + if (decl_node->deps.size() > 0) { + g->unresolved_top_level_decls.put(name, node); + } else { + resolve_top_level_decl(g, import, node); + } + break; + } case NodeTypeFnProto: { // if the name is missing, we immediately announce an error @@ -4848,6 +4997,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast case NodeTypeStructValueField: case NodeTypeArrayType: case NodeTypeErrorType: + case NodeTypeTypeLiteral: zig_unreachable(); } } @@ -5063,6 +5213,8 @@ Expr *get_resolved_expr(AstNode *node) { return &node->data.array_type.resolved_expr; case NodeTypeErrorType: return &node->data.error_type.resolved_expr; + case NodeTypeTypeLiteral: + return &node->data.type_literal.resolved_expr; case NodeTypeSwitchExpr: return &node->data.switch_expr.resolved_expr; case NodeTypeFnProto: @@ -5081,6 +5233,7 @@ Expr *get_resolved_expr(AstNode *node) { case NodeTypeStructField: case NodeTypeStructValueField: case NodeTypeErrorValueDecl: + case NodeTypeTypeDecl: zig_unreachable(); } zig_unreachable(); @@ -5098,6 +5251,8 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { return &node->data.error_value_decl.top_level_decl; case NodeTypeCImport: return &node->data.c_import.top_level_decl; + case NodeTypeTypeDecl: + return &node->data.type_decl.top_level_decl; case NodeTypeNumberLiteral: case NodeTypeReturnExpr: case NodeTypeBinOpExpr: @@ -5138,6 +5293,7 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { case NodeTypeStructValueField: case NodeTypeArrayType: case NodeTypeErrorType: + case NodeTypeTypeLiteral: zig_unreachable(); } zig_unreachable(); @@ -5178,6 +5334,14 @@ TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits) { return *get_int_type_ptr(g, is_signed, size_in_bits); } +TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type) { + return &g->builtin_types.entry_c_int[c_int_type]; +} + +TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type) { + return *get_c_int_type_ptr(g, c_int_type); +} + bool handle_is_ptr(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: @@ -5204,6 +5368,8 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { return type_entry->data.enumeration.gen_field_count != 0; case TypeTableEntryIdMaybe: return type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer; + case TypeTableEntryIdTypeDecl: + return handle_is_ptr(type_entry->data.type_decl.canonical_type); } zig_unreachable(); } @@ -5226,3 +5392,48 @@ void find_libc_path(CodeGen *g) { } } +static uint32_t hash_ptr(void *ptr) { + uint64_t x = (uint64_t)(uintptr_t)(ptr); + uint32_t a = x >> 32; + uint32_t b = x & 0xffffffff; + return a ^ b; +} + +uint32_t fn_type_id_hash(FnTypeId id) { + uint32_t result = 0; + result += id.is_extern ? 3349388391 : 0; + result += id.is_naked ? 608688877 : 0; + result += id.is_var_args ? 1931444534 : 0; + result += hash_ptr(id.return_type); + result += id.param_count; + for (int i = 0; i < id.param_count; i += 1) { + FnTypeParamInfo *info = &id.param_info[i]; + result += info->is_noalias ? 892356923 : 0; + result += hash_ptr(info->type); + } + return result; +} + +bool fn_type_id_eql(FnTypeId a, FnTypeId b) { + if (a.is_extern != b.is_extern || + a.is_naked != b.is_naked || + a.return_type != b.return_type || + a.is_var_args != b.is_var_args || + a.param_count != b.param_count) + { + return false; + } + for (int i = 0; i < a.param_count; i += 1) { + FnTypeParamInfo *a_param_info = &a.param_info[i]; + FnTypeParamInfo *b_param_info = &b.param_info[i]; + + if (a_param_info->type != b_param_info->type) { + return false; + } + + if (a_param_info->is_noalias != b_param_info->is_noalias) { + return false; + } + } + return true; +} diff --git a/src/analyze.hpp b/src/analyze.hpp index 00b9c0d24..377989439 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -22,7 +22,18 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node); bool is_node_void_expr(AstNode *node); TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, int size_in_bits); TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits); +TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type); +TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type); +TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry *child_type); +TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId fn_type_id); +TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type); +TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size); +TypeTableEntry *get_partial_container_type(CodeGen *g, ImportTableEntry *import, + ContainerKind kind, AstNode *decl_node, const char *name); +TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x); bool handle_is_ptr(TypeTableEntry *type_entry); void find_libc_path(CodeGen *g); +TypeTableEntry *get_underlying_type(TypeTableEntry *type_entry); + #endif diff --git a/src/ast_render.cpp b/src/ast_render.cpp index efe144ebc..cebacaa0f 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -119,6 +119,8 @@ static const char *node_type_str(NodeType node_type) { return "ReturnExpr"; case NodeTypeVariableDeclaration: return "VariableDeclaration"; + case NodeTypeTypeDecl: + return "TypeDecl"; case NodeTypeErrorValueDecl: return "ErrorValueDecl"; case NodeTypeNumberLiteral: @@ -179,6 +181,8 @@ static const char *node_type_str(NodeType node_type) { return "ArrayType"; case NodeTypeErrorType: return "ErrorType"; + case NodeTypeTypeLiteral: + return "TypeLiteral"; } } @@ -260,6 +264,13 @@ void ast_print(FILE *f, AstNode *node, int indent) { ast_print(f, node->data.variable_declaration.expr, indent + 2); break; } + case NodeTypeTypeDecl: + { + Buf *name_buf = &node->data.type_decl.symbol; + fprintf(f, "%s '%s'\n", node_type_str(node->type), buf_ptr(name_buf)); + ast_print(f, node->data.type_decl.child_type, indent + 2); + break; + } case NodeTypeErrorValueDecl: { Buf *name_buf = &node->data.error_value_decl.name; @@ -478,6 +489,9 @@ void ast_print(FILE *f, AstNode *node, int indent) { case NodeTypeErrorType: fprintf(f, "%s\n", node_type_str(node->type)); break; + case NodeTypeTypeLiteral: + fprintf(f, "%s\n", node_type_str(node->type)); + break; } } @@ -494,7 +508,14 @@ static void print_indent(AstRender *ar) { } static bool is_node_void(AstNode *node) { - return node->type == NodeTypeSymbol && buf_eql_str(&node->data.symbol_expr.symbol, "void"); + if (node->type == NodeTypeSymbol) { + if (node->data.symbol_expr.override_type_entry) { + return node->data.symbol_expr.override_type_entry->id == TypeTableEntryIdVoid; + } else if (buf_eql_str(&node->data.symbol_expr.symbol, "void")) { + return true; + } + } + return false; } static bool is_printable(uint8_t c) { @@ -515,6 +536,7 @@ static void render_node(AstRender *ar, AstNode *node) { if (child->type == NodeTypeImport || child->type == NodeTypeVariableDeclaration || + child->type == NodeTypeTypeDecl || child->type == NodeTypeErrorValueDecl || child->type == NodeTypeFnProto) { @@ -588,6 +610,14 @@ static void render_node(AstRender *ar, AstNode *node) { } break; } + case NodeTypeTypeDecl: + { + const char *pub_str = visib_mod_string(node->data.type_decl.visib_mod); + const char *var_name = buf_ptr(&node->data.type_decl.symbol); + fprintf(ar->f, "%stype %s = ", pub_str, var_name); + render_node(ar, node->data.type_decl.child_type); + break; + } case NodeTypeErrorValueDecl: zig_panic("TODO"); case NodeTypeBinOpExpr: @@ -617,7 +647,14 @@ static void render_node(AstRender *ar, AstNode *node) { break; } case NodeTypeSymbol: - fprintf(ar->f, "%s", buf_ptr(&node->data.symbol_expr.symbol)); + { + TypeTableEntry *override_type = node->data.symbol_expr.override_type_entry; + if (override_type) { + fprintf(ar->f, "%s", buf_ptr(&override_type->name)); + } else { + fprintf(ar->f, "%s", buf_ptr(&node->data.symbol_expr.symbol)); + } + } break; case NodeTypePrefixOpExpr: { @@ -719,7 +756,11 @@ static void render_node(AstRender *ar, AstNode *node) { break; } case NodeTypeErrorType: - zig_panic("TODO"); + fprintf(ar->f, "error"); + break; + case NodeTypeTypeLiteral: + fprintf(ar->f, "type"); + break; } } diff --git a/src/codegen.cpp b/src/codegen.cpp index 97b03d14d..f3c0b72c2 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -13,11 +13,13 @@ #include "error.hpp" #include "analyze.hpp" #include "errmsg.hpp" +#include "parseh.hpp" #include "ast_render.hpp" #include #include + CodeGen *codegen_create(Buf *root_source_dir) { CodeGen *g = allocate(1); g->link_table.init(32); @@ -25,6 +27,7 @@ CodeGen *codegen_create(Buf *root_source_dir) { g->builtin_fn_table.init(32); g->primitive_type_table.init(32); g->unresolved_top_level_decls.init(32); + g->fn_type_table.init(32); g->build_type = CodeGenBuildTypeDebug; g->root_source_dir = root_source_dir; g->next_error_index = 1; @@ -530,12 +533,12 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { fn_type = get_expr_type(fn_ref_expr); } - TypeTableEntry *src_return_type = fn_type->data.fn.src_return_type; + TypeTableEntry *src_return_type = fn_type->data.fn.fn_type_id.return_type; int fn_call_param_count = node->data.fn_call_expr.params.length; bool first_arg_ret = handle_is_ptr(src_return_type); int actual_param_count = fn_call_param_count + (struct_type ? 1 : 0) + (first_arg_ret ? 1 : 0); - bool is_var_args = fn_type->data.fn.is_var_args; + bool is_var_args = fn_type->data.fn.fn_type_id.is_var_args; // don't really include void values LLVMValueRef *gen_param_values = allocate(actual_param_count); @@ -1460,7 +1463,7 @@ static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) { } static LLVMValueRef gen_return(CodeGen *g, AstNode *source_node, LLVMValueRef value) { - TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.src_return_type; + TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type; if (handle_is_ptr(return_type)) { assert(g->cur_ret_ptr); gen_assign_raw(g, source_node, BinOpTypeAssign, g->cur_ret_ptr, value, return_type, return_type); @@ -1503,7 +1506,7 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) { LLVMBuildCondBr(g->builder, cond_val, continue_block, return_block); LLVMPositionBuilderAtEnd(g->builder, return_block); - TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.src_return_type; + TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type; if (return_type->id == TypeTableEntryIdPureError) { gen_return(g, node, err_val); } else if (return_type->id == TypeTableEntryIdErrorUnion) { @@ -2296,6 +2299,9 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { case NodeTypeCharLiteral: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: + case NodeTypeErrorType: + case NodeTypeTypeLiteral: + case NodeTypeArrayType: // caught by constant expression eval codegen zig_unreachable(); case NodeTypeRoot: @@ -2310,11 +2316,10 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { case NodeTypeStructDecl: case NodeTypeStructField: case NodeTypeStructValueField: - case NodeTypeArrayType: - case NodeTypeErrorType: case NodeTypeSwitchProng: case NodeTypeSwitchRange: case NodeTypeErrorValueDecl: + case NodeTypeTypeDecl: zig_unreachable(); } zig_unreachable(); @@ -2341,6 +2346,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE } switch (type_entry->id) { + case TypeTableEntryIdTypeDecl: + return gen_const_val(g, type_entry->data.type_decl.canonical_type, const_val); case TypeTableEntryIdInt: return LLVMConstInt(type_entry->type_ref, bignum_to_twos_complement(&const_val->data.x_bignum), false); case TypeTableEntryIdPureError: @@ -2557,7 +2564,9 @@ static void do_code_gen(CodeGen *g) { assert(proto_node->type == NodeTypeFnProto); AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; - if (handle_is_ptr(fn_table_entry->type_entry->data.fn.src_return_type)) { + TypeTableEntry *fn_type = fn_table_entry->type_entry; + + if (handle_is_ptr(fn_type->data.fn.fn_type_id.return_type)) { LLVMValueRef first_arg = LLVMGetParam(fn_table_entry->fn_value, 0); LLVMAddAttribute(first_arg, LLVMStructRetAttribute); } @@ -2567,7 +2576,9 @@ static void do_code_gen(CodeGen *g) { AstNode *param_node = fn_proto->params.at(param_decl_i); assert(param_node->type == NodeTypeParamDecl); - int gen_index = param_node->data.param_decl.gen_index; + FnGenParamInfo *info = &fn_type->data.fn.gen_param_info[param_decl_i]; + int gen_index = info->gen_index; + bool is_byval = info->is_byval; if (gen_index < 0) { continue; @@ -2587,7 +2598,7 @@ static void do_code_gen(CodeGen *g) { // when https://github.com/andrewrk/zig/issues/82 is fixed, add // non null attribute here } - if (param_node->data.param_decl.is_byval) { + if (is_byval) { LLVMAddAttribute(argument_val, LLVMByValAttribute); } } @@ -2601,7 +2612,7 @@ static void do_code_gen(CodeGen *g) { AstNode *fn_def_node = fn_table_entry->fn_def_node; LLVMValueRef fn = fn_table_entry->fn_value; g->cur_fn = fn_table_entry; - if (handle_is_ptr(fn_table_entry->type_entry->data.fn.src_return_type)) { + if (handle_is_ptr(fn_table_entry->type_entry->data.fn.fn_type_id.return_type)) { g->cur_ret_ptr = LLVMGetParam(fn, 0); } else { g->cur_ret_ptr = nullptr; @@ -2685,7 +2696,9 @@ static void do_code_gen(CodeGen *g) { AstNode *param_decl = fn_proto->params.at(param_i); assert(param_decl->type == NodeTypeParamDecl); - if (param_decl->data.param_decl.gen_index < 0) { + FnGenParamInfo *info = &fn_table_entry->type_entry->data.fn.gen_param_info[param_i]; + + if (info->gen_index < 0) { continue; } @@ -2724,17 +2737,6 @@ static const int int_sizes_in_bits[] = { 64, }; -enum CIntType { - CIntTypeShort, - CIntTypeUShort, - CIntTypeInt, - CIntTypeUInt, - CIntTypeLong, - CIntTypeULong, - CIntTypeLongLong, - CIntTypeULongLong, -}; - struct CIntTypeInfo { CIntType id; const char *name; @@ -2840,6 +2842,8 @@ static void define_builtin_types(CodeGen *g) { is_signed ? LLVMZigEncoding_DW_ATE_signed() : LLVMZigEncoding_DW_ATE_unsigned()); entry->data.integral.is_signed = is_signed; g->primitive_type_table.put(&entry->name, entry); + + get_c_int_type_ptr(g, info->id)[0] = entry; } { @@ -3093,6 +3097,42 @@ static void init(CodeGen *g, Buf *source_path) { } +void codegen_parseh(CodeGen *g, Buf *src_dirname, Buf *src_basename, Buf *source_code) { + find_libc_path(g); + Buf *full_path = buf_alloc(); + os_path_join(src_dirname, src_basename, full_path); + + ImportTableEntry *import = allocate(1); + import->source_code = source_code; + import->path = full_path; + import->fn_table.init(32); + g->root_import = import; + + init(g, full_path); + + import->di_file = LLVMZigCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname)); + + ZigList errors = {0}; + int err = parse_h_buf(import, &errors, source_code, g, nullptr); + if (err) { + fprintf(stderr, "unable to parse .h file: %s\n", err_str(err)); + exit(1); + } + + if (errors.length > 0) { + for (int i = 0; i < errors.length; i += 1) { + ErrorMsg *err_msg = errors.at(i); + print_err_msg(err_msg, g->err_color); + } + exit(1); + } +} + +void codegen_render_ast(CodeGen *g, FILE *f, int indent_size) { + ast_render(stdout, g->root_import->root, 4); +} + + static int parse_version_string(Buf *buf, int *major, int *minor, int *patch) { char *dot1 = strstr(buf_ptr(buf), "."); if (!dot1) @@ -3156,7 +3196,6 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path, import_entry->line_offsets = tokenization.line_offsets; import_entry->path = full_path; import_entry->fn_table.init(32); - import_entry->fn_type_table.init(32); import_entry->root = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color, &g->next_node_index); diff --git a/src/codegen.hpp b/src/codegen.hpp index a8044a739..c8ae8c93b 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -11,6 +11,8 @@ #include "parser.hpp" #include "errmsg.hpp" +#include + CodeGen *codegen_create(Buf *root_source_dir); void codegen_set_clang_argv(CodeGen *codegen, const char **args, int len); @@ -27,4 +29,7 @@ void codegen_add_root_code(CodeGen *g, Buf *source_dir, Buf *source_basename, Bu void codegen_link(CodeGen *g, const char *out_file); +void codegen_parseh(CodeGen *g, Buf *src_dirname, Buf *src_basename, Buf *source_code); +void codegen_render_ast(CodeGen *g, FILE *f, int indent_size); + #endif diff --git a/src/main.cpp b/src/main.cpp index 9248954e8..bc112c564 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,8 +10,6 @@ #include "codegen.hpp" #include "os.hpp" #include "error.hpp" -#include "parseh.hpp" -#include "ast_render.hpp" #include @@ -38,40 +36,41 @@ static int usage(const char *arg0) { return EXIT_FAILURE; } -static int version(const char *arg0, int argc, char **argv) { - printf("%s\n", ZIG_VERSION_STRING); - return EXIT_SUCCESS; -} - -struct Build { - const char *in_file; - const char *out_file; - bool release; - bool strip; - bool is_static; - OutType out_type; - const char *out_name; - bool verbose; - ErrColor color; - const char *libc_path; - ZigList clang_argv; +enum Cmd { + CmdInvalid, + CmdBuild, + CmdVersion, + CmdParseH, }; -static int build(const char *arg0, int argc, char **argv) { +int main(int argc, char **argv) { + char *arg0 = argv[0]; + Cmd cmd = CmdInvalid; + const char *in_file = nullptr; + const char *out_file = nullptr; + bool release = false; + bool strip = false; + bool is_static = false; + OutType out_type = OutTypeUnknown; + const char *out_name = nullptr; + bool verbose = false; + ErrColor color = ErrColorAuto; + const char *libc_path = nullptr; + ZigList clang_argv = {0}; int err; - Build b = {0}; - for (int i = 0; i < argc; i += 1) { + for (int i = 1; i < argc; i += 1) { char *arg = argv[i]; + if (arg[0] == '-') { if (strcmp(arg, "--release") == 0) { - b.release = true; + release = true; } else if (strcmp(arg, "--strip") == 0) { - b.strip = true; + strip = true; } else if (strcmp(arg, "--static") == 0) { - b.is_static = true; + is_static = true; } else if (strcmp(arg, "--verbose") == 0) { - b.verbose = true; + verbose = true; } else if (i + 1 >= argc) { return usage(arg0); } else { @@ -79,190 +78,128 @@ static int build(const char *arg0, int argc, char **argv) { if (i >= argc) { return usage(arg0); } else if (strcmp(arg, "--output") == 0) { - b.out_file = argv[i]; + out_file = argv[i]; } else if (strcmp(arg, "--export") == 0) { if (strcmp(argv[i], "exe") == 0) { - b.out_type = OutTypeExe; + out_type = OutTypeExe; } else if (strcmp(argv[i], "lib") == 0) { - b.out_type = OutTypeLib; + out_type = OutTypeLib; } else if (strcmp(argv[i], "obj") == 0) { - b.out_type = OutTypeObj; + out_type = OutTypeObj; } else { return usage(arg0); } } else if (strcmp(arg, "--color") == 0) { if (strcmp(argv[i], "auto") == 0) { - b.color = ErrColorAuto; + color = ErrColorAuto; } else if (strcmp(argv[i], "on") == 0) { - b.color = ErrColorOn; + color = ErrColorOn; } else if (strcmp(argv[i], "off") == 0) { - b.color = ErrColorOff; + color = ErrColorOff; } else { return usage(arg0); } } else if (strcmp(arg, "--name") == 0) { - b.out_name = argv[i]; + out_name = argv[i]; } else if (strcmp(arg, "--libc-path") == 0) { - b.libc_path = argv[i]; + libc_path = argv[i]; } else if (strcmp(arg, "-isystem") == 0) { - b.clang_argv.append("-isystem"); - b.clang_argv.append(argv[i]); + clang_argv.append("-isystem"); + clang_argv.append(argv[i]); } else if (strcmp(arg, "-dirafter") == 0) { - b.clang_argv.append("-dirafter"); - b.clang_argv.append(argv[i]); + clang_argv.append("-dirafter"); + clang_argv.append(argv[i]); } else { return usage(arg0); } } - } else if (!b.in_file) { - b.in_file = arg; - } else { - return usage(arg0); - } - } - - if (!b.in_file) - return usage(arg0); - - Buf in_file_buf = BUF_INIT; - buf_init_from_str(&in_file_buf, b.in_file); - - Buf root_source_dir = BUF_INIT; - Buf root_source_code = BUF_INIT; - Buf root_source_name = BUF_INIT; - if (buf_eql_str(&in_file_buf, "-")) { - os_get_cwd(&root_source_dir); - if ((err = os_fetch_file(stdin, &root_source_code))) { - fprintf(stderr, "unable to read stdin: %s\n", err_str(err)); - return 1; - } - buf_init_from_str(&root_source_name, ""); - } else { - os_path_split(&in_file_buf, &root_source_dir, &root_source_name); - if ((err = os_fetch_file_path(buf_create_from_str(b.in_file), &root_source_code))) { - fprintf(stderr, "unable to open '%s': %s\n", b.in_file, err_str(err)); - return 1; - } - } - - CodeGen *g = codegen_create(&root_source_dir); - codegen_set_build_type(g, b.release ? CodeGenBuildTypeRelease : CodeGenBuildTypeDebug); - codegen_set_clang_argv(g, b.clang_argv.items, b.clang_argv.length); - codegen_set_strip(g, b.strip); - codegen_set_is_static(g, b.is_static); - if (b.out_type != OutTypeUnknown) - codegen_set_out_type(g, b.out_type); - if (b.out_name) - codegen_set_out_name(g, buf_create_from_str(b.out_name)); - if (b.libc_path) - codegen_set_libc_path(g, buf_create_from_str(b.libc_path)); - codegen_set_verbose(g, b.verbose); - codegen_set_errmsg_color(g, b.color); - codegen_add_root_code(g, &root_source_dir, &root_source_name, &root_source_code); - codegen_link(g, b.out_file); - - return 0; -} - -static int parseh(const char *arg0, int argc, char **argv) { - char *in_file = nullptr; - ZigList clang_argv = {0}; - ErrColor color = ErrColorAuto; - bool warnings_on = false; - for (int i = 0; i < argc; i += 1) { - char *arg = argv[i]; - if (arg[0] == '-') { - if (arg[1] == 'I') { - clang_argv.append(arg); - } else if (strcmp(arg, "-isystem") == 0) { - if (i + 1 >= argc) { - return usage(arg0); - } - i += 1; - clang_argv.append("-isystem"); - clang_argv.append(argv[i]); - } else if (strcmp(arg, "--color") == 0) { - if (i + 1 >= argc) { - return usage(arg0); - } - i += 1; - if (strcmp(argv[i], "auto") == 0) { - color = ErrColorAuto; - } else if (strcmp(argv[i], "on") == 0) { - color = ErrColorOn; - } else if (strcmp(argv[i], "off") == 0) { - color = ErrColorOff; - } else { - return usage(arg0); - } - } else if (strcmp(arg, "--c-import-warnings") == 0) { - warnings_on = true; - } else { - fprintf(stderr, "unrecognized argument: %s", arg); - return usage(arg0); - } - } else if (!in_file) { - in_file = arg; - } else { - return usage(arg0); - } - } - if (!in_file) { - fprintf(stderr, "missing target argument"); - return usage(arg0); - } - - clang_argv.append(in_file); - - Buf *libc_include_path = buf_alloc(); - os_path_join(buf_create_from_str(ZIG_LIBC_DIR), buf_create_from_str("include"), libc_include_path); - clang_argv.append("-isystem"); - clang_argv.append(buf_ptr(libc_include_path)); - - ImportTableEntry import = {0}; - ZigList errors = {0}; - uint32_t next_node_index = 0; - int err = parse_h_file(&import, &errors, &clang_argv, warnings_on, &next_node_index); - - if (err) { - fprintf(stderr, "unable to parse .h file: %s\n", err_str(err)); - return EXIT_FAILURE; - } - - if (errors.length > 0) { - for (int i = 0; i < errors.length; i += 1) { - ErrorMsg *err_msg = errors.at(i); - print_err_msg(err_msg, color); - } - return EXIT_FAILURE; - } - - ast_render(stdout, import.root, 4); - - return 0; -} - -int main(int argc, char **argv) { - char *arg0 = argv[0]; - int (*cmd)(const char *, int, char **) = nullptr; - for (int i = 1; i < argc; i += 1) { - char *arg = argv[i]; - if (arg[0] == '-' && arg[1] == '-') { - return usage(arg0); - } else { + } else if (cmd == CmdInvalid) { if (strcmp(arg, "build") == 0) { - cmd = build; + cmd = CmdBuild; } else if (strcmp(arg, "version") == 0) { - cmd = version; + cmd = CmdVersion; } else if (strcmp(arg, "parseh") == 0) { - cmd = parseh; + cmd = CmdParseH; } else { fprintf(stderr, "Unrecognized command: %s\n", arg); return usage(arg0); } - return cmd(arg0, argc - i - 1, &argv[i + 1]); + } else { + switch (cmd) { + case CmdBuild: + case CmdParseH: + if (!in_file) { + in_file = arg; + } else { + return usage(arg0); + } + break; + case CmdVersion: + return usage(arg0); + case CmdInvalid: + zig_unreachable(); + } } } - return usage(arg0); + switch (cmd) { + case CmdBuild: + case CmdParseH: + { + if (!in_file) + return usage(arg0); + + Buf in_file_buf = BUF_INIT; + buf_init_from_str(&in_file_buf, in_file); + + Buf root_source_dir = BUF_INIT; + Buf root_source_code = BUF_INIT; + Buf root_source_name = BUF_INIT; + if (buf_eql_str(&in_file_buf, "-")) { + os_get_cwd(&root_source_dir); + if ((err = os_fetch_file(stdin, &root_source_code))) { + fprintf(stderr, "unable to read stdin: %s\n", err_str(err)); + return 1; + } + buf_init_from_str(&root_source_name, ""); + } else { + os_path_split(&in_file_buf, &root_source_dir, &root_source_name); + if ((err = os_fetch_file_path(buf_create_from_str(in_file), &root_source_code))) { + fprintf(stderr, "unable to open '%s': %s\n", in_file, err_str(err)); + return 1; + } + } + + CodeGen *g = codegen_create(&root_source_dir); + codegen_set_build_type(g, release ? CodeGenBuildTypeRelease : CodeGenBuildTypeDebug); + codegen_set_clang_argv(g, clang_argv.items, clang_argv.length); + codegen_set_strip(g, strip); + codegen_set_is_static(g, is_static); + if (out_type != OutTypeUnknown) + codegen_set_out_type(g, out_type); + if (out_name) + codegen_set_out_name(g, buf_create_from_str(out_name)); + if (libc_path) + codegen_set_libc_path(g, buf_create_from_str(libc_path)); + codegen_set_verbose(g, verbose); + codegen_set_errmsg_color(g, color); + + if (cmd == CmdBuild) { + codegen_add_root_code(g, &root_source_dir, &root_source_name, &root_source_code); + codegen_link(g, out_file); + return EXIT_SUCCESS; + } else if (cmd == CmdParseH) { + codegen_parseh(g, &root_source_dir, &root_source_name, &root_source_code); + codegen_render_ast(g, stdout, 4); + return EXIT_SUCCESS; + } else { + zig_unreachable(); + } + } + case CmdVersion: + printf("%s\n", ZIG_VERSION_STRING); + return EXIT_SUCCESS; + case CmdInvalid: + return usage(arg0); + } } diff --git a/src/parseh.cpp b/src/parseh.cpp index 0d62ba352..add4b7a70 100644 --- a/src/parseh.cpp +++ b/src/parseh.cpp @@ -12,6 +12,7 @@ #include "parser.hpp" #include "all_types.hpp" #include "tokenizer.hpp" +#include "analyze.hpp" #include #include @@ -30,22 +31,27 @@ struct Context { ZigList *errors; bool warnings_on; VisibMod visib_mod; - bool have_c_void_decl_node; + TypeTableEntry *c_void_type; AstNode *root; - HashMap root_name_table; - HashMap struct_type_table; - HashMap enum_type_table; + HashMap global_type_table; + HashMap global_value_table; + HashMap struct_type_table; + HashMap enum_type_table; HashMap fn_table; HashMap macro_table; SourceManager *source_manager; ZigList aliases; ZigList macro_symbols; - uint32_t *next_node_index; + AstNode *source_node; + + CodeGen *codegen; }; -static AstNode *make_qual_type_node(Context *c, QualType qt, const Decl *decl); -static AstNode *make_qual_type_node_with_table(Context *c, QualType qt, const Decl *decl, - HashMap *type_table); +static TypeTableEntry *resolve_qual_type_with_table(Context *c, QualType qt, const Decl *decl, + HashMap *type_table); + +static TypeTableEntry *resolve_qual_type(Context *c, QualType qt, const Decl *decl); + __attribute__ ((format (printf, 3, 4))) static void emit_warning(Context *c, const Decl *decl, const char *format, ...) { @@ -77,8 +83,8 @@ static AstNode *create_node(Context *c, NodeType type) { AstNode *node = allocate(1); node->type = type; node->owner = c->import; - node->create_index = *c->next_node_index; - *c->next_node_index += 1; + node->create_index = c->codegen->next_node_index; + c->codegen->next_node_index += 1; return node; } @@ -178,61 +184,51 @@ static AstNode *create_num_lit_signed(Context *c, int64_t x) { return create_prefix_node(c, PrefixOpNegation, num_lit_node); } -static AstNode *create_array_type_node(Context *c, AstNode *child_type_node, uint64_t size, bool is_const) { - AstNode *node = create_node(c, NodeTypeArrayType); - node->data.array_type.size = create_num_lit_unsigned(c, size); - node->data.array_type.child_type = child_type_node; - node->data.array_type.is_const = is_const; +static AstNode *create_type_decl_node(Context *c, const char *name, AstNode *child_type_node) { + AstNode *node = create_node(c, NodeTypeTypeDecl); + buf_init_from_str(&node->data.type_decl.symbol, name); + node->data.type_decl.visib_mod = c->visib_mod; + node->data.type_decl.directives = create_empty_directives(c); + node->data.type_decl.child_type = child_type_node; normalize_parent_ptrs(node); return node; } +static AstNode *make_type_node(Context *c, TypeTableEntry *type_entry) { + AstNode *node = create_node(c, NodeTypeSymbol); + node->data.symbol_expr.override_type_entry = type_entry; + return node; +} + static const char *decl_name(const Decl *decl) { const NamedDecl *named_decl = static_cast(decl); return (const char *)named_decl->getName().bytes_begin(); } +static AstNode *add_typedef_node(Context *c, TypeTableEntry *type_decl) { + assert(type_decl); -static AstNode *add_typedef_node(Context *c, Buf *new_name, AstNode *target_node) { - if (!target_node) { - return nullptr; - } - AstNode *node = create_var_decl_node(c, buf_ptr(new_name), target_node); + AstNode *node = create_type_decl_node(c, buf_ptr(&type_decl->name), + make_type_node(c, type_decl->data.type_decl.child_type)); + node->data.type_decl.override_type = type_decl; - c->root_name_table.put(new_name, true); + c->global_type_table.put(&type_decl->name, type_decl); c->root->data.root.top_level_decls.append(node); return node; } -static AstNode *convert_to_c_void(Context *c, AstNode *type_node) { - if (type_node->type == NodeTypeSymbol && - buf_eql_str(&type_node->data.symbol_expr.symbol, "void")) - { - if (!c->have_c_void_decl_node) { - add_typedef_node(c, buf_create_from_str("c_void"), create_symbol_node(c, "u8")); - c->have_c_void_decl_node = true; - } - return create_symbol_node(c, "c_void"); - } else { - return type_node; +static TypeTableEntry *get_c_void_type(Context *c) { + if (!c->c_void_type) { + c->c_void_type = get_typedecl_type(c->codegen, "c_void", c->codegen->builtin_types.entry_u8); + add_typedef_node(c, c->c_void_type); } + + return c->c_void_type; } -static AstNode *pointer_to_type(Context *c, AstNode *type_node, bool is_const) { - assert(type_node); - PrefixOp op = is_const ? PrefixOpConstAddressOf : PrefixOpAddressOf; - AstNode *child_node = create_prefix_node(c, op, convert_to_c_void(c, type_node)); - return create_prefix_node(c, PrefixOpMaybe, child_node); -} - -static bool type_is_int(AstNode *type_node) { - // TODO recurse through the type table - return true; -} - -static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl, - HashMap *type_table) +static TypeTableEntry *resolve_type_with_table(Context *c, const Type *ty, const Decl *decl, + HashMap *type_table) { switch (ty->getTypeClass()) { case Type::Builtin: @@ -240,35 +236,35 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl, const BuiltinType *builtin_ty = static_cast(ty); switch (builtin_ty->getKind()) { case BuiltinType::Void: - return create_symbol_node(c, "void"); + return c->codegen->builtin_types.entry_void; case BuiltinType::Bool: - return create_symbol_node(c, "bool"); + return c->codegen->builtin_types.entry_bool; case BuiltinType::Char_U: case BuiltinType::UChar: case BuiltinType::Char_S: - return create_symbol_node(c, "u8"); + return c->codegen->builtin_types.entry_u8; case BuiltinType::SChar: - return create_symbol_node(c, "i8"); + return c->codegen->builtin_types.entry_i8; case BuiltinType::UShort: - return create_symbol_node(c, "c_ushort"); + return get_c_int_type(c->codegen, CIntTypeUShort); case BuiltinType::UInt: - return create_symbol_node(c, "c_uint"); + return get_c_int_type(c->codegen, CIntTypeUInt); case BuiltinType::ULong: - return create_symbol_node(c, "c_ulong"); + return get_c_int_type(c->codegen, CIntTypeULong); case BuiltinType::ULongLong: - return create_symbol_node(c, "c_ulonglong"); + return get_c_int_type(c->codegen, CIntTypeULongLong); case BuiltinType::Short: - return create_symbol_node(c, "c_short"); + return get_c_int_type(c->codegen, CIntTypeShort); case BuiltinType::Int: - return create_symbol_node(c, "c_int"); + return get_c_int_type(c->codegen, CIntTypeInt); case BuiltinType::Long: - return create_symbol_node(c, "c_long"); + return get_c_int_type(c->codegen, CIntTypeLong); case BuiltinType::LongLong: - return create_symbol_node(c, "c_longlong"); + return get_c_int_type(c->codegen, CIntTypeLongLong); case BuiltinType::Float: - return create_symbol_node(c, "f32"); + return c->codegen->builtin_types.entry_f32; case BuiltinType::Double: - return create_symbol_node(c, "f64"); + return c->codegen->builtin_types.entry_f64; case BuiltinType::LongDouble: case BuiltinType::WChar_U: case BuiltinType::Char16: @@ -297,7 +293,7 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl, case BuiltinType::BuiltinFn: case BuiltinType::ARCUnbridgedCast: emit_warning(c, decl, "missed a builtin type"); - return nullptr; + return c->codegen->builtin_types.entry_invalid; } break; } @@ -305,17 +301,26 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl, { const PointerType *pointer_ty = static_cast(ty); QualType child_qt = pointer_ty->getPointeeType(); - AstNode *type_node = make_qual_type_node(c, child_qt, decl); - if (!type_node) { - return nullptr; + TypeTableEntry *child_type = resolve_qual_type(c, child_qt, decl); + if (get_underlying_type(child_type)->id == TypeTableEntryIdInvalid) { + emit_warning(c, decl, "pointer to unresolved type"); + return c->codegen->builtin_types.entry_invalid; } + if (child_qt.getTypePtr()->getTypeClass() == Type::Paren) { const ParenType *paren_type = static_cast(child_qt.getTypePtr()); if (paren_type->getInnerType()->getTypeClass() == Type::FunctionProto) { - return create_prefix_node(c, PrefixOpMaybe, type_node); + return get_maybe_type(c->codegen, child_type); } } - return pointer_to_type(c, type_node, child_qt.isConstQualified()); + bool is_const = child_qt.isConstQualified(); + + if (child_type->id == TypeTableEntryIdVoid) { + child_type = get_c_void_type(c); + } + + TypeTableEntry *non_null_pointer_type = get_pointer_to_type(c->codegen, child_type, is_const); + return get_maybe_type(c->codegen, non_null_pointer_type); } case Type::Typedef: { @@ -323,32 +328,28 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl, const TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); Buf *type_name = buf_create_from_str(decl_name(typedef_decl)); if (buf_eql_str(type_name, "uint8_t")) { - return create_symbol_node(c, "u8"); + return c->codegen->builtin_types.entry_u8; } else if (buf_eql_str(type_name, "int8_t")) { - return create_symbol_node(c, "i8"); + return c->codegen->builtin_types.entry_i8; } else if (buf_eql_str(type_name, "uint16_t")) { - return create_symbol_node(c, "u16"); + return c->codegen->builtin_types.entry_u16; } else if (buf_eql_str(type_name, "int16_t")) { - return create_symbol_node(c, "i16"); + return c->codegen->builtin_types.entry_i16; } else if (buf_eql_str(type_name, "uint32_t")) { - return create_symbol_node(c, "u32"); + return c->codegen->builtin_types.entry_u32; } else if (buf_eql_str(type_name, "int32_t")) { - return create_symbol_node(c, "i32"); + return c->codegen->builtin_types.entry_i32; } else if (buf_eql_str(type_name, "uint64_t")) { - return create_symbol_node(c, "u64"); + return c->codegen->builtin_types.entry_u64; } else if (buf_eql_str(type_name, "int64_t")) { - return create_symbol_node(c, "i64"); + return c->codegen->builtin_types.entry_i64; } else if (buf_eql_str(type_name, "intptr_t")) { - return create_symbol_node(c, "isize"); + return c->codegen->builtin_types.entry_isize; } else if (buf_eql_str(type_name, "uintptr_t")) { - return create_symbol_node(c, "usize"); + return c->codegen->builtin_types.entry_usize; } else { auto entry = type_table->maybe_get(type_name); - if (entry) { - return create_symbol_node(c, buf_ptr(type_name)); - } else { - return nullptr; - } + return entry ? entry->value : c->codegen->builtin_types.entry_invalid; } } case Type::Elaborated: @@ -356,10 +357,10 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl, const ElaboratedType *elaborated_ty = static_cast(ty); switch (elaborated_ty->getKeyword()) { case ETK_Struct: - return make_qual_type_node_with_table(c, elaborated_ty->getNamedType(), + return resolve_qual_type_with_table(c, elaborated_ty->getNamedType(), decl, &c->struct_type_table); case ETK_Enum: - return make_qual_type_node_with_table(c, elaborated_ty->getNamedType(), + return resolve_qual_type_with_table(c, elaborated_ty->getNamedType(), decl, &c->enum_type_table); case ETK_Interface: case ETK_Union: @@ -367,35 +368,63 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl, case ETK_Typename: case ETK_None: emit_warning(c, decl, "unsupported elaborated type"); - return nullptr; + return c->codegen->builtin_types.entry_invalid; } } case Type::FunctionProto: { const FunctionProtoType *fn_proto_ty = static_cast(ty); - AstNode *node = create_node(c, NodeTypeFnProto); - buf_resize(&node->data.fn_proto.name, 0); - node->data.fn_proto.is_extern = true; - node->data.fn_proto.is_var_args = fn_proto_ty->isVariadic(); - node->data.fn_proto.return_type = make_qual_type_node(c, fn_proto_ty->getReturnType(), decl); - if (!node->data.fn_proto.return_type) { - return nullptr; + switch (fn_proto_ty->getCallConv()) { + case CC_C: // __attribute__((cdecl)) + break; + case CC_X86StdCall: // __attribute__((stdcall)) + case CC_X86FastCall: // __attribute__((fastcall)) + case CC_X86ThisCall: // __attribute__((thiscall)) + case CC_X86VectorCall: // __attribute__((vectorcall)) + case CC_X86Pascal: // __attribute__((pascal)) + case CC_X86_64Win64: // __attribute__((ms_abi)) + case CC_X86_64SysV: // __attribute__((sysv_abi)) + case CC_AAPCS: // __attribute__((pcs("aapcs"))) + case CC_AAPCS_VFP: // __attribute__((pcs("aapcs-vfp"))) + case CC_IntelOclBicc: // __attribute__((intel_ocl_bicc)) + case CC_SpirFunction: // default for OpenCL functions on SPIR target + case CC_SpirKernel: // inferred for OpenCL kernels on SPIR target + emit_warning(c, decl, "function type has non C calling convention"); + return c->codegen->builtin_types.entry_invalid; } - int arg_count = fn_proto_ty->getNumParams(); - for (int i = 0; i < arg_count; i += 1) { - QualType qt = fn_proto_ty->getParamType(i); - bool is_noalias = qt.isRestrictQualified(); - AstNode *type_node = make_qual_type_node(c, qt, decl); - if (!type_node) { - return nullptr; + FnTypeId fn_type_id; + fn_type_id.is_naked = false; + fn_type_id.is_extern = true; + fn_type_id.is_var_args = fn_proto_ty->isVariadic(); + fn_type_id.param_count = fn_proto_ty->getNumParams(); + + + if (fn_proto_ty->getNoReturnAttr()) { + fn_type_id.return_type = c->codegen->builtin_types.entry_unreachable; + } else { + fn_type_id.return_type = resolve_qual_type(c, fn_proto_ty->getReturnType(), decl); + if (fn_type_id.return_type->id == TypeTableEntryIdInvalid) { + return c->codegen->builtin_types.entry_invalid; } - node->data.fn_proto.params.append(create_param_decl_node(c, "", type_node, is_noalias)); } - normalize_parent_ptrs(node); - return node; + fn_type_id.param_info = allocate(fn_type_id.param_count); + for (int i = 0; i < fn_type_id.param_count; i += 1) { + QualType qt = fn_proto_ty->getParamType(i); + TypeTableEntry *param_type = resolve_qual_type(c, qt, decl); + + if (param_type->id == TypeTableEntryIdInvalid) { + return c->codegen->builtin_types.entry_invalid; + } + + FnTypeParamInfo *param_info = &fn_type_id.param_info[i]; + param_info->type = param_type; + param_info->is_noalias = qt.isRestrictQualified(); + } + + return get_fn_type(c->codegen, fn_type_id); } case Type::Record: { @@ -403,20 +432,15 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl, Buf *record_name = buf_create_from_str(decl_name(record_ty->getDecl())); if (buf_len(record_name) == 0) { emit_warning(c, decl, "unhandled anonymous struct"); - return nullptr; - } else if (type_table->maybe_get(record_name)) { - const char *prefix_str; - if (type_table == &c->enum_type_table) { - prefix_str = "enum_"; - } else if (type_table == &c->struct_type_table) { - prefix_str = "struct_"; - } else { - prefix_str = ""; - } - return create_symbol_node(c, buf_ptr(buf_sprintf("%s%s", prefix_str, buf_ptr(record_name)))); - } else { - return nullptr; + return c->codegen->builtin_types.entry_invalid; } + + auto entry = type_table->maybe_get(record_name); + if (!entry) { + return c->codegen->builtin_types.entry_invalid; + } + + return entry->value; } case Type::Enum: { @@ -424,32 +448,32 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl, Buf *record_name = buf_create_from_str(decl_name(enum_ty->getDecl())); if (buf_len(record_name) == 0) { emit_warning(c, decl, "unhandled anonymous enum"); - return nullptr; - } else if (type_table->maybe_get(record_name)) { - const char *prefix_str; - if (type_table == &c->enum_type_table) { - prefix_str = "enum_"; - } else if (type_table == &c->struct_type_table) { - prefix_str = "struct_"; - } else { - prefix_str = ""; - } - return create_symbol_node(c, buf_ptr(buf_sprintf("%s%s", prefix_str, buf_ptr(record_name)))); - } else { - return nullptr; + return c->codegen->builtin_types.entry_invalid; } + + auto entry = type_table->maybe_get(record_name); + if (!entry) { + return c->codegen->builtin_types.entry_invalid; + } + + return entry->value; } case Type::ConstantArray: { const ConstantArrayType *const_arr_ty = static_cast(ty); - AstNode *child_type_node = make_qual_type_node(c, const_arr_ty->getElementType(), decl); + TypeTableEntry *child_type = resolve_qual_type(c, const_arr_ty->getElementType(), decl); uint64_t size = const_arr_ty->getSize().getLimitedValue(); - return create_array_type_node(c, child_type_node, size, false); + return get_array_type(c->codegen, child_type, size); } case Type::Paren: { const ParenType *paren_ty = static_cast(ty); - return make_qual_type_node(c, paren_ty->getInnerType(), decl); + return resolve_qual_type(c, paren_ty->getInnerType(), decl); + } + case Type::Decayed: + { + const DecayedType *decayed_ty = static_cast(ty); + return resolve_qual_type(c, decayed_ty->getOriginalType(), decl); } case Type::BlockPointer: case Type::LValueReference: @@ -464,7 +488,6 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl, case Type::FunctionNoProto: case Type::UnresolvedUsing: case Type::Adjusted: - case Type::Decayed: case Type::TypeOfExpr: case Type::TypeOf: case Type::Decltype: @@ -485,68 +508,68 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl, case Type::ObjCObjectPointer: case Type::Atomic: emit_warning(c, decl, "missed a '%s' type", ty->getTypeClassName()); - return nullptr; + return c->codegen->builtin_types.entry_invalid; } } -static AstNode *make_qual_type_node_with_table(Context *c, QualType qt, const Decl *decl, - HashMap *type_table) +static TypeTableEntry *resolve_qual_type_with_table(Context *c, QualType qt, const Decl *decl, + HashMap *type_table) { - return make_type_node(c, qt.getTypePtr(), decl, type_table); + return resolve_type_with_table(c, qt.getTypePtr(), decl, type_table); } -static AstNode *make_qual_type_node(Context *c, QualType qt, const Decl *decl) { - return make_qual_type_node_with_table(c, qt, decl, &c->root_name_table); +static TypeTableEntry *resolve_qual_type(Context *c, QualType qt, const Decl *decl) { + return resolve_qual_type_with_table(c, qt, decl, &c->global_type_table); } static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) { - AstNode *node = create_node(c, NodeTypeFnProto); - buf_init_from_str(&node->data.fn_proto.name, decl_name(fn_decl)); + Buf fn_name = BUF_INIT; + buf_init_from_str(&fn_name, decl_name(fn_decl)); - auto fn_entry = c->fn_table.maybe_get(&node->data.fn_proto.name); - if (fn_entry) { + if (c->fn_table.maybe_get(&fn_name)) { // we already saw this function return; } - node->data.fn_proto.is_extern = true; + TypeTableEntry *fn_type = resolve_qual_type(c, fn_decl->getType(), fn_decl); + + if (fn_type->id == TypeTableEntryIdInvalid) { + emit_warning(c, fn_decl, "ignoring function '%s' - unable to resolve type", buf_ptr(&fn_name)); + return; + } + assert(fn_type->id == TypeTableEntryIdFn); + + + AstNode *node = create_node(c, NodeTypeFnProto); + buf_init_from_buf(&node->data.fn_proto.name, &fn_name); + + node->data.fn_proto.is_extern = fn_type->data.fn.fn_type_id.is_extern; node->data.fn_proto.visib_mod = c->visib_mod; node->data.fn_proto.directives = create_empty_directives(c); - node->data.fn_proto.is_var_args = fn_decl->isVariadic(); + node->data.fn_proto.is_var_args = fn_type->data.fn.fn_type_id.is_var_args; + node->data.fn_proto.return_type = make_type_node(c, fn_type->data.fn.fn_type_id.return_type); - int arg_count = fn_decl->getNumParams(); + assert(!fn_type->data.fn.fn_type_id.is_naked); + + int arg_count = fn_type->data.fn.fn_type_id.param_count; + Buf name_buf = BUF_INIT; for (int i = 0; i < arg_count; i += 1) { + FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[i]; + AstNode *type_node = make_type_node(c, param_info->type); const ParmVarDecl *param = fn_decl->getParamDecl(i); const char *name = decl_name(param); if (strlen(name) == 0) { - name = buf_ptr(buf_sprintf("arg%d", i)); - } - QualType qt = param->getOriginalType(); - bool is_noalias = qt.isRestrictQualified(); - AstNode *type_node = make_qual_type_node(c, qt, fn_decl); - if (!type_node) { - emit_warning(c, param, "skipping function %s, unresolved param type\n", name); - return; + buf_resize(&name_buf, 0); + buf_appendf(&name_buf, "arg%d", i); + name = buf_ptr(&name_buf); } - node->data.fn_proto.params.append(create_param_decl_node(c, name, type_node, is_noalias)); - } - - if (fn_decl->isNoReturn()) { - node->data.fn_proto.return_type = create_symbol_node(c, "unreachable"); - } else { - node->data.fn_proto.return_type = make_qual_type_node(c, fn_decl->getReturnType(), fn_decl); - } - - if (!node->data.fn_proto.return_type) { - emit_warning(c, fn_decl, "skipping function %s, unresolved return type\n", - buf_ptr(&node->data.fn_proto.name)); - return; + node->data.fn_proto.params.append(create_param_decl_node(c, name, type_node, param_info->is_noalias)); } normalize_parent_ptrs(node); - c->fn_table.put(&node->data.fn_proto.name, true); + c->fn_table.put(buf_create_from_buf(&fn_name), true); c->root->data.root.top_level_decls.append(node); } @@ -573,7 +596,9 @@ static void visit_typedef_decl(Context *c, const TypedefNameDecl *typedef_decl) // use the name of this typedef // TODO - add_typedef_node(c, type_name, make_qual_type_node(c, child_qt, typedef_decl)); + TypeTableEntry *child_type = resolve_qual_type(c, child_qt, typedef_decl); + TypeTableEntry *decl_type = get_typedecl_type(c->codegen, buf_ptr(type_name), child_type); + add_typedef_node(c, decl_type); } static void add_alias(Context *c, const char *new_name, const char *target_name) { @@ -582,7 +607,14 @@ static void add_alias(Context *c, const char *new_name, const char *target_name) } static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) { - Buf *bare_name = buf_create_from_str(decl_name(enum_decl)); + const char *raw_name = decl_name(enum_decl); + // we have no interest in top level anonymous enums since they're + // not exposing anything. + if (raw_name[0] == 0) { + return; + } + + Buf *bare_name = buf_create_from_str(raw_name); Buf *full_type_name = buf_sprintf("enum_%s", buf_ptr(bare_name)); if (c->enum_type_table.maybe_get(bare_name)) { @@ -590,97 +622,145 @@ static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) { return; } - // eagerly put the name in the table, but we need to remember to remove it if it fails - // boy it would be nice to have defer here wouldn't it - c->enum_type_table.put(bare_name, true); - const EnumDecl *enum_def = enum_decl->getDefinition(); if (!enum_def) { + TypeTableEntry *typedecl_type = get_typedecl_type(c->codegen, buf_ptr(full_type_name), + c->codegen->builtin_types.entry_u8); + c->enum_type_table.put(bare_name, typedecl_type); + // this is a type that we can point to but that's it, same as `struct Foo;`. - add_typedef_node(c, full_type_name, create_symbol_node(c, "u8")); + add_typedef_node(c, typedecl_type); add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name)); return; } - AstNode *node = create_node(c, NodeTypeStructDecl); - buf_init_from_buf(&node->data.struct_decl.name, full_type_name); + // count and validate + uint32_t field_count = 0; + for (auto it = enum_def->enumerator_begin(), + it_end = enum_def->enumerator_end(); + it != it_end; ++it, field_count += 1) + { + const EnumConstantDecl *enum_const = *it; + if (enum_const->getInitExpr()) { + emit_warning(c, enum_const, "skipping enum %s - has init expression\n", buf_ptr(bare_name)); + return; + } + } - node->data.struct_decl.kind = ContainerKindEnum; - node->data.struct_decl.visib_mod = VisibModExport; - node->data.struct_decl.directives = create_empty_directives(c); + TypeTableEntry *enum_type = get_partial_container_type(c->codegen, c->import, + ContainerKindEnum, c->source_node, buf_ptr(full_type_name)); + enum_type->data.enumeration.gen_field_count = 0; + enum_type->data.enumeration.complete = true; + + TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(c->codegen, field_count); + enum_type->align_in_bits = tag_type_entry->size_in_bits; + enum_type->size_in_bits = tag_type_entry->size_in_bits; + enum_type->data.enumeration.tag_type = tag_type_entry; + + c->enum_type_table.put(bare_name, enum_type); + // make an alias without the "enum_" prefix. this will get emitted at the + // end if it doesn't conflict with anything else + add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name)); + + enum_type->data.enumeration.field_count = field_count; + enum_type->data.enumeration.fields = allocate(field_count); + LLVMZigDIEnumerator **di_enumerators = allocate(field_count); ZigList var_decls = {0}; - int i = 0; + uint32_t i = 0; for (auto it = enum_def->enumerator_begin(), it_end = enum_def->enumerator_end(); it != it_end; ++it, i += 1) { const EnumConstantDecl *enum_const = *it; - if (enum_const->getInitExpr()) { - c->enum_type_table.remove(bare_name); - emit_warning(c, enum_const, "skipping enum %s - has init expression\n", buf_ptr(bare_name)); - return; - } + Buf *enum_val_name = buf_create_from_str(decl_name(enum_const)); - - Buf field_name = BUF_INIT; - + Buf *field_name; if (buf_starts_with_buf(enum_val_name, bare_name)) { Buf *slice = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name)); if (valid_symbol_starter(buf_ptr(slice)[0])) { - buf_init_from_buf(&field_name, slice); + field_name = slice; } else { - buf_resize(&field_name, 0); - buf_appendf(&field_name, "_%s", buf_ptr(slice)); + field_name = buf_sprintf("_%s", buf_ptr(slice)); } } else { - buf_init_from_buf(&field_name, enum_val_name); + field_name = enum_val_name; } - AstNode *field_node = create_struct_field_node(c, buf_ptr(&field_name), create_symbol_node(c, "void")); - node->data.struct_decl.fields.append(field_node); + TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; + type_enum_field->name = field_name; + type_enum_field->type_entry = c->codegen->builtin_types.entry_void; + type_enum_field->value = i; + + di_enumerators[i] = LLVMZigCreateDebugEnumerator(c->codegen->dbuilder, buf_ptr(type_enum_field->name), i); + // in C each enum value is in the global namespace. so we put them there too. - AstNode *field_access_node = create_field_access_node(c, buf_ptr(full_type_name), buf_ptr(&field_name)); + // at this point we can rely on the enum emitting successfully + AstNode *field_access_node = create_field_access_node(c, buf_ptr(full_type_name), buf_ptr(field_name)); AstNode *var_node = create_var_decl_node(c, buf_ptr(enum_val_name), field_access_node); var_decls.append(var_node); - c->root_name_table.put(enum_val_name, true); + c->global_value_table.put(enum_val_name, enum_type); } - normalize_parent_ptrs(node); - c->root->data.root.top_level_decls.append(node); + // create llvm type for root struct + enum_type->type_ref = tag_type_entry->type_ref; + + // create debug type for tag + unsigned line = c->source_node ? (c->source_node->line + 1) : 0; + LLVMZigDIType *tag_di_type = LLVMZigCreateDebugEnumerationType(c->codegen->dbuilder, + LLVMZigFileToScope(c->import->di_file), buf_ptr(bare_name), + c->import->di_file, line, + tag_type_entry->size_in_bits, tag_type_entry->align_in_bits, di_enumerators, field_count, + tag_type_entry->di_type, ""); + + LLVMZigReplaceTemporary(c->codegen->dbuilder, enum_type->di_type, tag_di_type); + enum_type->di_type = tag_di_type; + + ////////// + + // now create top level decl for the type + AstNode *enum_node = create_node(c, NodeTypeStructDecl); + buf_init_from_buf(&enum_node->data.struct_decl.name, full_type_name); + enum_node->data.struct_decl.kind = ContainerKindEnum; + enum_node->data.struct_decl.visib_mod = VisibModExport; + enum_node->data.struct_decl.directives = create_empty_directives(c); + enum_node->data.struct_decl.type_entry = enum_type; + + for (uint32_t i = 0; i < field_count; i += 1) { + TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; + AstNode *type_node = make_type_node(c, type_enum_field->type_entry); + AstNode *field_node = create_struct_field_node(c, buf_ptr(type_enum_field->name), type_node); + enum_node->data.struct_decl.fields.append(field_node); + } + + normalize_parent_ptrs(enum_node); + c->root->data.root.top_level_decls.append(enum_node); for (int i = 0; i < var_decls.length; i += 1) { AstNode *var_node = var_decls.at(i); c->root->data.root.top_level_decls.append(var_node); } - // make an alias without the "enum_" prefix. this will get emitted at the - // end if it doesn't conflict with anything else - add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name)); } static void visit_record_decl(Context *c, const RecordDecl *record_decl) { const char *raw_name = decl_name(record_decl); + // we have no interest in top level anonymous structs since they're + // not exposing anything. if (record_decl->isAnonymousStructOrUnion() || raw_name[0] == 0) { return; } - Buf *bare_name = buf_create_from_str(raw_name); - if (!record_decl->isStruct()) { - emit_warning(c, record_decl, "skipping record %s, not a struct", buf_ptr(bare_name)); - return; - } - - if (buf_len(bare_name) == 0) { - emit_warning(c, record_decl, "skipping anonymous struct"); + emit_warning(c, record_decl, "skipping record %s, not a struct", raw_name); return; } + Buf *bare_name = buf_create_from_str(raw_name); Buf *full_type_name = buf_sprintf("struct_%s", buf_ptr(bare_name)); if (c->struct_type_table.maybe_get(bare_name)) { @@ -688,55 +768,127 @@ static void visit_record_decl(Context *c, const RecordDecl *record_decl) { return; } - // eagerly put the name in the table, but we need to remember to remove it if it fails - // boy it would be nice to have defer here wouldn't it - c->struct_type_table.put(bare_name, true); - - RecordDecl *record_def = record_decl->getDefinition(); if (!record_def) { + TypeTableEntry *typedecl_type = get_typedecl_type(c->codegen, buf_ptr(full_type_name), + c->codegen->builtin_types.entry_u8); + c->struct_type_table.put(bare_name, typedecl_type); + // this is a type that we can point to but that's it, such as `struct Foo;`. - add_typedef_node(c, full_type_name, create_symbol_node(c, "u8")); + add_typedef_node(c, typedecl_type); add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name)); return; } - AstNode *node = create_node(c, NodeTypeStructDecl); - buf_init_from_buf(&node->data.struct_decl.name, full_type_name); + TypeTableEntry *struct_type = get_partial_container_type(c->codegen, c->import, + ContainerKindStruct, c->source_node, buf_ptr(full_type_name)); - node->data.struct_decl.kind = ContainerKindStruct; - node->data.struct_decl.visib_mod = VisibModExport; - node->data.struct_decl.directives = create_empty_directives(c); + c->struct_type_table.put(bare_name, struct_type); + // make an alias without the "struct_" prefix. this will get emitted at the + // end if it doesn't conflict with anything else + add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name)); + // count fields and validate + uint32_t field_count = 0; for (auto it = record_def->field_begin(), it_end = record_def->field_end(); - it != it_end; ++it) + it != it_end; ++it, field_count += 1) { const FieldDecl *field_decl = *it; if (field_decl->isBitField()) { - c->struct_type_table.remove(bare_name); emit_warning(c, field_decl, "skipping struct %s - has bitfield\n", buf_ptr(bare_name)); return; } + } - AstNode *type_node = make_qual_type_node(c, field_decl->getType(), field_decl); - if (!type_node) { - c->struct_type_table.remove(bare_name); - emit_warning(c, field_decl, "skipping struct %s - unhandled type\n", buf_ptr(bare_name)); + struct_type->data.structure.src_field_count = field_count; + struct_type->data.structure.fields = allocate(field_count); + + // we possibly allocate too much here since gen_field_count can be lower than field_count. + // the only problem is potential wasted space though. + LLVMTypeRef *element_types = allocate(field_count); + LLVMZigDIType **di_element_types = allocate(field_count); + + uint64_t total_size_in_bits = 0; + uint64_t first_field_align_in_bits = 0; + uint64_t offset_in_bits = 0; + + uint32_t i = 0; + unsigned line = c->source_node ? c->source_node->line : 0; + for (auto it = record_def->field_begin(), + it_end = record_def->field_end(); + it != it_end; ++it, i += 1) + { + const FieldDecl *field_decl = *it; + + TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; + type_struct_field->name = buf_create_from_str(decl_name(field_decl)); + type_struct_field->src_index = i; + type_struct_field->gen_index = i; + type_struct_field->type_entry = resolve_qual_type(c, field_decl->getType(), field_decl); + + if (type_struct_field->type_entry->id == TypeTableEntryIdInvalid) { + emit_warning(c, field_decl, "skipping struct %s - unresolved type\n", buf_ptr(bare_name)); return; } - AstNode *field_node = create_struct_field_node(c, decl_name(field_decl), type_node); - node->data.struct_decl.fields.append(field_node); + di_element_types[i] = LLVMZigCreateDebugMemberType(c->codegen->dbuilder, + LLVMZigTypeToScope(struct_type->di_type), buf_ptr(type_struct_field->name), + c->import->di_file, line + 1, + type_struct_field->type_entry->size_in_bits, + type_struct_field->type_entry->align_in_bits, + offset_in_bits, 0, type_struct_field->type_entry->di_type); + + element_types[i] = type_struct_field->type_entry->type_ref; + assert(di_element_types[i]); + assert(element_types[i]); + + total_size_in_bits += type_struct_field->type_entry->size_in_bits; + if (first_field_align_in_bits == 0) { + first_field_align_in_bits = type_struct_field->type_entry->align_in_bits; + } + offset_in_bits += type_struct_field->type_entry->size_in_bits; + + } + struct_type->data.structure.embedded_in_current = false; + + struct_type->data.structure.gen_field_count = field_count; + struct_type->data.structure.complete = true; + + LLVMStructSetBody(struct_type->type_ref, element_types, field_count, false); + + struct_type->align_in_bits = first_field_align_in_bits; + struct_type->size_in_bits = total_size_in_bits; + + LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(c->codegen->dbuilder, + LLVMZigFileToScope(c->import->di_file), + buf_ptr(full_type_name), + c->import->di_file, line + 1, struct_type->size_in_bits, struct_type->align_in_bits, 0, + nullptr, di_element_types, field_count, 0, nullptr, ""); + + LLVMZigReplaceTemporary(c->codegen->dbuilder, struct_type->di_type, replacement_di_type); + struct_type->di_type = replacement_di_type; + + ////// + + // now create a top level decl node for the type + AstNode *struct_node = create_node(c, NodeTypeStructDecl); + buf_init_from_buf(&struct_node->data.struct_decl.name, full_type_name); + struct_node->data.struct_decl.kind = ContainerKindStruct; + struct_node->data.struct_decl.visib_mod = VisibModExport; + struct_node->data.struct_decl.directives = create_empty_directives(c); + struct_node->data.struct_decl.type_entry = struct_type; + + for (uint32_t i = 0; i < field_count; i += 1) { + TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; + AstNode *type_node = make_type_node(c, type_struct_field->type_entry); + AstNode *field_node = create_struct_field_node(c, buf_ptr(type_struct_field->name), type_node); + struct_node->data.struct_decl.fields.append(field_node); } - normalize_parent_ptrs(node); - c->root->data.root.top_level_decls.append(node); - - // make an alias without the "struct_" prefix. this will get emitted at the - // end if it doesn't conflict with anything else - add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name)); + normalize_parent_ptrs(struct_node); + c->root->data.root.top_level_decls.append(struct_node); } static void visit_var_decl(Context *c, const VarDecl *var_decl) { @@ -754,8 +906,8 @@ static void visit_var_decl(Context *c, const VarDecl *var_decl) { } QualType qt = var_decl->getType(); - AstNode *type_node = make_qual_type_node(c, qt, var_decl); - if (!type_node) { + TypeTableEntry *var_type = resolve_qual_type(c, qt, var_decl); + if (var_type->id == TypeTableEntryIdInvalid) { emit_warning(c, var_decl, "ignoring variable '%s' - unresolved type\n", buf_ptr(name)); return; } @@ -778,7 +930,8 @@ static void visit_var_decl(Context *c, const VarDecl *var_decl) { switch (ap_value->getKind()) { case APValue::Int: { - if (!type_is_int(type_node)) { + TypeTableEntry *canon_type = get_underlying_type(var_type); + if (canon_type->id != TypeTableEntryIdInt) { emit_warning(c, var_decl, "ignoring variable '%s' - int initializer for non int type\n", buf_ptr(name)); return; @@ -819,17 +972,19 @@ static void visit_var_decl(Context *c, const VarDecl *var_decl) { return; } + AstNode *type_node = make_type_node(c, var_type); AstNode *var_node = create_typed_var_decl_node(c, true, buf_ptr(name), type_node, init_node); c->root->data.root.top_level_decls.append(var_node); - c->root_name_table.put(name, true); + c->global_value_table.put(name, var_type); return; } if (is_extern) { + AstNode *type_node = make_type_node(c, var_type); AstNode *var_node = create_typed_var_decl_node(c, is_const, buf_ptr(name), type_node, nullptr); var_node->data.variable_declaration.is_extern = true; c->root->data.root.top_level_decls.append(var_node); - c->root_name_table.put(name, true); + c->global_value_table.put(name, var_type); return; } @@ -864,7 +1019,10 @@ static bool decl_visitor(void *context, const Decl *decl) { } static bool name_exists(Context *c, Buf *name) { - if (c->root_name_table.maybe_get(name)) { + if (c->global_type_table.maybe_get(name)) { + return true; + } + if (c->global_value_table.maybe_get(name)) { return true; } if (c->fn_table.maybe_get(name)) { @@ -1001,6 +1159,11 @@ static void process_macro(Context *c, Buf *name, Buf *value) { // maybe it's a symbol if (is_simple_symbol(value)) { + // if it equals itself, ignore. for example, from stdio.h: + // #define stdin stdin + if (buf_eql_buf(name, value)) { + return; + } c->macro_symbols.append({name, value}); } } @@ -1054,46 +1217,43 @@ static void process_preprocessor_entities(Context *c, ASTUnit &unit) { } int parse_h_buf(ImportTableEntry *import, ZigList *errors, Buf *source, - const char **args, int args_len, const char *libc_include_path, bool warnings_on, - uint32_t *next_node_index) + CodeGen *codegen, AstNode *source_node) { int err; Buf tmp_file_path = BUF_INIT; if ((err = os_buf_to_tmp_file(source, buf_create_from_str(".h"), &tmp_file_path))) { return err; } - ZigList clang_argv = {0}; - clang_argv.append(buf_ptr(&tmp_file_path)); - clang_argv.append("-isystem"); - clang_argv.append(libc_include_path); - - for (int i = 0; i < args_len; i += 1) { - clang_argv.append(args[i]); - } - - err = parse_h_file(import, errors, &clang_argv, warnings_on, next_node_index); + err = parse_h_file(import, errors, buf_ptr(&tmp_file_path), codegen, source_node); os_delete_file(&tmp_file_path); return err; } -int parse_h_file(ImportTableEntry *import, ZigList *errors, - ZigList *clang_argv, bool warnings_on, uint32_t *next_node_index) +int parse_h_file(ImportTableEntry *import, ZigList *errors, const char *target_file, + CodeGen *codegen, AstNode *source_node) { Context context = {0}; Context *c = &context; - c->warnings_on = warnings_on; + c->warnings_on = codegen->verbose; c->import = import; c->errors = errors; c->visib_mod = VisibModPub; - c->root_name_table.init(8); + c->global_type_table.init(8); + c->global_value_table.init(8); c->enum_type_table.init(8); c->struct_type_table.init(8); c->fn_table.init(8); c->macro_table.init(8); - c->next_node_index = next_node_index; + c->codegen = codegen; + c->source_node = source_node; + + ZigList clang_argv = {0}; + + clang_argv.append("-x"); + clang_argv.append("c"); char *ZIG_PARSEH_CFLAGS = getenv("ZIG_PARSEH_CFLAGS"); if (ZIG_PARSEH_CFLAGS) { @@ -1103,28 +1263,37 @@ int parse_h_file(ImportTableEntry *import, ZigList *errors, while (space) { if (space - start > 0) { buf_init_from_mem(&tmp_buf, start, space - start); - clang_argv->append(buf_ptr(buf_create_from_buf(&tmp_buf))); + clang_argv.append(buf_ptr(buf_create_from_buf(&tmp_buf))); } start = space + 1; space = strstr(start, " "); } buf_init_from_str(&tmp_buf, start); - clang_argv->append(buf_ptr(buf_create_from_buf(&tmp_buf))); + clang_argv.append(buf_ptr(buf_create_from_buf(&tmp_buf))); } - clang_argv->append("-isystem"); - clang_argv->append(ZIG_HEADERS_DIR); + clang_argv.append("-isystem"); + clang_argv.append(ZIG_HEADERS_DIR); + + clang_argv.append("-isystem"); + clang_argv.append(buf_ptr(codegen->libc_include_path)); + + for (int i = 0; i < codegen->clang_argv_len; i += 1) { + clang_argv.append(codegen->clang_argv[i]); + } // we don't need spell checking and it slows things down - clang_argv->append("-fno-spell-checking"); + clang_argv.append("-fno-spell-checking"); // this gives us access to preprocessing entities, presumably at // the cost of performance - clang_argv->append("-Xclang"); - clang_argv->append("-detailed-preprocessing-record"); + clang_argv.append("-Xclang"); + clang_argv.append("-detailed-preprocessing-record"); - // to make the end argument work - clang_argv->append(nullptr); + clang_argv.append(target_file); + + // to make the [start...end] argument work + clang_argv.append(nullptr); IntrusiveRefCntPtr diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); @@ -1138,7 +1307,7 @@ int parse_h_file(ImportTableEntry *import, ZigList *errors, const char *resources_path = ZIG_HEADERS_DIR; std::unique_ptr err_unit; std::unique_ptr ast_unit(ASTUnit::LoadFromCommandLine( - &clang_argv->at(0), &clang_argv->last(), + &clang_argv.at(0), &clang_argv.last(), pch_container_ops, diags, resources_path, only_local_decls, capture_diagnostics, None, true, false, TU_Complete, false, false, allow_pch_with_compiler_errors, skip_function_bodies, diff --git a/src/parseh.hpp b/src/parseh.hpp index f3a6c8371..3efaafcfa 100644 --- a/src/parseh.hpp +++ b/src/parseh.hpp @@ -11,10 +11,10 @@ #include "all_types.hpp" -int parse_h_file(ImportTableEntry *out_import, ZigList *out_errs, - ZigList *clang_argv, bool warnings_on, uint32_t *next_node_index); -int parse_h_buf(ImportTableEntry *out_import, ZigList *out_errs, - Buf *source, const char **args, int args_len, const char *libc_include_path, - bool warnings_on, uint32_t *next_node_index); +int parse_h_file(ImportTableEntry *import, ZigList *errors, const char *target_file, + CodeGen *codegen, AstNode *source_node); + +int parse_h_buf(ImportTableEntry *import, ZigList *errors, Buf *source, + CodeGen *codegen, AstNode *source_node); #endif diff --git a/src/parser.cpp b/src/parser.cpp index b9755f312..67d2c2131 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -908,7 +908,7 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mand /* PrimaryExpression = "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." "Symbol") -KeywordLiteral : "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" +KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "type" */ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -954,6 +954,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool AstNode *node = ast_create_node(pc, NodeTypeUndefinedLiteral, token); *token_index += 1; return node; + } else if (token->id == TokenIdKeywordType) { + AstNode *node = ast_create_node(pc, NodeTypeTypeLiteral, token); + *token_index += 1; + return node; } else if (token->id == TokenIdKeywordError) { AstNode *node = ast_create_node(pc, NodeTypeErrorType, token); *token_index += 1; @@ -2470,7 +2474,34 @@ static AstNode *ast_parse_error_value_decl(ParseContext *pc, int *token_index, } /* -TopLevelDecl : many(Directive) option(FnVisibleMod) (FnDef | ExternFnProto | RootExportDecl | Import | ContainerDecl | VariableDeclaration | ErrorValueDecl | CImportDecl) +TypeDecl = "type" "Symbol" "=" TypeExpr ";" +*/ +static AstNode *ast_parse_type_decl(ParseContext *pc, int *token_index, + ZigList *directives, VisibMod visib_mod) +{ + Token *first_token = &pc->tokens->at(*token_index); + + if (first_token->id != TokenIdKeywordType) { + return nullptr; + } + *token_index += 1; + + Token *name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); + ast_eat_token(pc, token_index, TokenIdEq); + + AstNode *node = ast_create_node(pc, NodeTypeTypeDecl, first_token); + ast_buf_from_token(pc, name_tok, &node->data.type_decl.symbol); + node->data.type_decl.child_type = ast_parse_prefix_op_expr(pc, token_index, true); + + node->data.type_decl.visib_mod = visib_mod; + node->data.type_decl.directives = directives; + + normalize_parent_ptrs(node); + return node; +} + +/* +TopLevelDecl = many(Directive) option(VisibleMod) (FnDef | ExternDecl | RootExportDecl | Import | ContainerDecl | GlobalVarDecl | ErrorValueDecl | CImportDecl | TypeDecl) */ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigList *top_level_decls) { for (;;) { @@ -2545,6 +2576,12 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis continue; } + AstNode *type_decl_node = ast_parse_type_decl(pc, token_index, directives, visib_mod); + if (type_decl_node) { + top_level_decls->append(type_decl_node); + continue; + } + if (directives->length > 0) { ast_error(pc, directive_token, "invalid directive"); } @@ -2631,9 +2668,16 @@ void normalize_parent_ptrs(AstNode *node) { set_field(&node->data.return_expr.expr); break; case NodeTypeVariableDeclaration: + if (node->data.variable_declaration.directives) { + set_list_fields(node->data.variable_declaration.directives); + } set_field(&node->data.variable_declaration.type); set_field(&node->data.variable_declaration.expr); break; + case NodeTypeTypeDecl: + set_list_fields(node->data.type_decl.directives); + set_field(&node->data.type_decl.child_type); + break; case NodeTypeErrorValueDecl: // none break; @@ -2772,5 +2816,8 @@ void normalize_parent_ptrs(AstNode *node) { case NodeTypeErrorType: // none break; + case NodeTypeTypeLiteral: + // none + break; } } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index d5649b9eb..6d8c55f38 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -101,7 +101,7 @@ const char * zig_keywords[] = { "true", "false", "null", "fn", "return", "var", "const", "extern", "pub", "export", "import", "c_import", "if", "else", "goto", "asm", "volatile", "struct", "enum", "while", "for", "continue", "break", - "null", "noalias", "switch", "undefined", "error" + "null", "noalias", "switch", "undefined", "error", "type" }; bool is_zig_keyword(Buf *buf) { @@ -271,6 +271,8 @@ static void end_token(Tokenize *t) { t->cur_tok->id = TokenIdKeywordUndefined; } else if (mem_eql_str(token_mem, token_len, "error")) { t->cur_tok->id = TokenIdKeywordError; + } else if (mem_eql_str(token_mem, token_len, "type")) { + t->cur_tok->id = TokenIdKeywordType; } t->cur_tok = nullptr; @@ -1084,6 +1086,7 @@ const char * token_name(TokenId id) { case TokenIdKeywordSwitch: return "switch"; case TokenIdKeywordUndefined: return "undefined"; case TokenIdKeywordError: return "error"; + case TokenIdKeywordType: return "type"; case TokenIdLParen: return "("; case TokenIdRParen: return ")"; case TokenIdComma: return ","; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index b1b1319eb..627e04520 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -40,6 +40,7 @@ enum TokenId { TokenIdKeywordSwitch, TokenIdKeywordUndefined, TokenIdKeywordError, + TokenIdKeywordType, TokenIdLParen, TokenIdRParen, TokenIdComma, diff --git a/test/run_tests.cpp b/test/run_tests.cpp index b5afdabfc..a818bbc4f 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -115,7 +115,7 @@ static TestCase *add_parseh_case(const char *case_name, const char *source, int test_case->compiler_args.append("parseh"); test_case->compiler_args.append(tmp_h_path); - test_case->compiler_args.append("--c-import-warnings"); + test_case->compiler_args.append("--verbose"); test_cases.append(test_case); @@ -1689,7 +1689,7 @@ var a : i32 = 2; add_compile_fail_case("byvalue struct on exported functions", R"SOURCE( struct A { x : i32, } export fn f(a : A) {} - )SOURCE", 1, ".tmp_source.zig:3:13: error: byvalue struct parameters not yet supported on exported functions"); + )SOURCE", 1, ".tmp_source.zig:3:13: error: byvalue struct parameters not yet supported on extern functions"); add_compile_fail_case("duplicate field in struct value expression", R"SOURCE( struct A { @@ -1929,7 +1929,7 @@ pub const Foo1 = enum_Foo._1;)OUTPUT", add_parseh_case("restrict -> noalias", R"SOURCE( void foo(void *restrict bar, void *restrict); - )SOURCE", 1, R"OUTPUT(pub const c_void = u8; + )SOURCE", 1, R"OUTPUT(pub type c_void = u8; pub extern fn foo(noalias bar: ?&c_void, noalias arg1: ?&c_void);)OUTPUT"); add_parseh_case("simple struct", R"SOURCE( @@ -1977,14 +1977,14 @@ struct Foo { void (*derp)(struct Foo *foo); }; )SOURCE", 2, R"OUTPUT(export struct struct_Foo { - derp: ?extern fn (?&struct_Foo), + derp: ?extern fn(?&struct_Foo), })OUTPUT", R"OUTPUT(pub const Foo = struct_Foo;)OUTPUT"); add_parseh_case("struct prototype used in func", R"SOURCE( struct Foo; struct Foo *some_func(struct Foo *foo, int x); - )SOURCE", 2, R"OUTPUT(pub const struct_Foo = u8; + )SOURCE", 2, R"OUTPUT(pub type struct_Foo = u8; pub extern fn some_func(foo: ?&struct_Foo, x: c_int) -> ?&struct_Foo;)OUTPUT", R"OUTPUT(pub const Foo = struct_Foo;)OUTPUT");