diff --git a/doc/langref.md b/doc/langref.md index 78904d8325..e49dd66ff6 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -17,9 +17,9 @@ GlobalVarDecl = VariableDeclaration ";" VariableDeclaration = option("comptime") ("var" | "const") Symbol option(":" TypeExpr) "=" Expression -StructMember = (StructField | FnDef | GlobalVarDecl) +ContainerMember = (ContainerField | FnDef | GlobalVarDecl) -StructField = Symbol option(":" Expression) ",") +ContainerField = Symbol option(":" Expression) ",") UseDecl = "use" Expression ";" @@ -155,7 +155,7 @@ GroupedExpression = "(" Expression ")" KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "type" | "this" -ContainerDecl = option("extern") ("struct" | "enum" | "union") "{" many(StructMember) "}" +ContainerDecl = option("extern" | "packed") ("struct" | "enum" | "union") "{" many(ContainerMember) "}" ``` diff --git a/doc/vim/syntax/zig.vim b/doc/vim/syntax/zig.vim index ccae0a6f14..aa97b7b460 100644 --- a/doc/vim/syntax/zig.vim +++ b/doc/vim/syntax/zig.vim @@ -8,15 +8,15 @@ if exists("b:current_syntax") endif let b:current_syntax = "zig" -syn keyword zigStorage const var extern export pub noalias inline comptime nakedcc coldcc +syn keyword zigStorage const var extern packed export pub noalias inline comptime nakedcc coldcc volatile syn keyword zigStructure struct enum union -syn keyword zigStatement goto break return continue asm defer +syn keyword zigStatement goto break return continue asm defer unreachable syn keyword zigConditional if else switch try syn keyword zigRepeat while for syn keyword zigConstant null undefined zeroes this syn keyword zigKeyword fn use -syn keyword zigType bool f32 f64 void unreachable type error +syn keyword zigType bool f32 f64 void Unreachable type error syn keyword zigType i8 u8 i16 u16 i32 u32 i64 u64 isize usize syn keyword zigType c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong c_long_double diff --git a/src/all_types.hpp b/src/all_types.hpp index 7eaca8c45a..4b971ea552 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -621,11 +621,17 @@ enum ContainerKind { ContainerKindUnion, }; +enum ContainerLayout { + ContainerLayoutAuto, + ContainerLayoutExtern, + ContainerLayoutPacked, +}; + struct AstNodeContainerDecl { ContainerKind kind; ZigList fields; ZigList decls; - bool is_extern; + ContainerLayout layout; }; struct AstNodeStructField { @@ -820,7 +826,7 @@ struct TypeStructField { }; struct TypeTableEntryStruct { AstNode *decl_node; - bool is_extern; + ContainerLayout layout; bool is_packed; uint32_t src_field_count; uint32_t gen_field_count; @@ -850,7 +856,7 @@ struct TypeTableEntryError { struct TypeTableEntryEnum { AstNode *decl_node; - bool is_extern; + ContainerLayout layout; uint32_t src_field_count; uint32_t gen_field_count; TypeEnumField *fields; @@ -877,7 +883,7 @@ struct TypeTableEntryEnumTag { struct TypeTableEntryUnion { AstNode *decl_node; - bool is_extern; + ContainerLayout layout; uint32_t src_field_count; uint32_t gen_field_count; TypeStructField *fields; diff --git a/src/analyze.cpp b/src/analyze.cpp index 506d5e1db3..280fc94214 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -823,22 +823,24 @@ static TypeTableEntryId container_to_type(ContainerKind kind) { zig_unreachable(); } -TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind, AstNode *decl_node, const char *name, bool is_extern) { +TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind, + AstNode *decl_node, const char *name, ContainerLayout layout) +{ TypeTableEntryId type_id = container_to_type(kind); TypeTableEntry *entry = new_container_type_entry(type_id, decl_node, scope); switch (kind) { case ContainerKindStruct: entry->data.structure.decl_node = decl_node; - entry->data.structure.is_extern = is_extern; + entry->data.structure.layout = layout; break; case ContainerKindEnum: entry->data.enumeration.decl_node = decl_node; - entry->data.enumeration.is_extern = is_extern; + entry->data.enumeration.layout = layout; break; case ContainerKindUnion: entry->data.unionation.decl_node = decl_node; - entry->data.unionation.is_extern = is_extern; + entry->data.unionation.layout = layout; break; } @@ -1328,7 +1330,8 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) { } assert(struct_type->di_type); - LLVMStructSetBody(struct_type->type_ref, element_types, gen_field_count, false); + bool packed = (struct_type->data.structure.layout == ContainerLayoutPacked); + LLVMStructSetBody(struct_type->type_ref, element_types, gen_field_count, packed); assert(LLVMStoreSizeOfType(g->target_data_ref, struct_type->type_ref) > 0); ZigLLVMDIType **di_element_types = allocate(gen_field_count); diff --git a/src/analyze.hpp b/src/analyze.hpp index 7c83030c51..fdbc6e74c8 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -26,7 +26,8 @@ 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_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_const); -TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind, AstNode *decl_node, const char *name, bool is_extern); +TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind, + AstNode *decl_node, const char *name, ContainerLayout layout); TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x); TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type); TypeTableEntry *get_bound_fn_type(CodeGen *g, FnTableEntry *fn_entry); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 4689f723ee..1c82ef972e 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -104,6 +104,15 @@ static const char *defer_string(ReturnKind kind) { zig_unreachable(); } +static const char *layout_string(ContainerLayout layout) { + switch (layout) { + case ContainerLayoutAuto: return ""; + case ContainerLayoutExtern: return "extern "; + case ContainerLayoutPacked: return "packed "; + } + zig_unreachable(); +} + static const char *extern_string(bool is_extern) { return is_extern ? "extern " : ""; } @@ -970,8 +979,8 @@ static void ast_render_tld_var(AstRender *ar, Buf *name, TldVar *tld_var) { { TypeTableEntry *type_entry = var->value.data.x_type; if (type_entry->id == TypeTableEntryIdStruct) { - const char *extern_str = extern_string(type_entry->data.structure.is_extern); - fprintf(ar->f, "%sstruct {\n", extern_str); + const char *layout_str = layout_string(type_entry->data.structure.layout); + fprintf(ar->f, "%sstruct {\n", layout_str); if (type_entry->data.structure.complete) { for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { TypeStructField *field = &type_entry->data.structure.fields[i]; @@ -982,8 +991,8 @@ static void ast_render_tld_var(AstRender *ar, Buf *name, TldVar *tld_var) { } fprintf(ar->f, "}"); } else if (type_entry->id == TypeTableEntryIdEnum) { - const char *extern_str = extern_string(type_entry->data.enumeration.is_extern); - fprintf(ar->f, "%senum {\n", extern_str); + const char *layout_str = layout_string(type_entry->data.enumeration.layout); + fprintf(ar->f, "%senum {\n", layout_str); if (type_entry->data.enumeration.complete) { for (size_t i = 0; i < type_entry->data.enumeration.src_field_count; i += 1) { TypeEnumField *field = &type_entry->data.enumeration.fields[i]; diff --git a/src/ir.cpp b/src/ir.cpp index bf85a20f7a..573e4cefad 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5283,8 +5283,9 @@ static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope, TldContainer *tld_container = allocate(1); init_tld(&tld_container->base, TldIdContainer, name, visib_mod, node, parent_scope); - TypeTableEntry *container_type = get_partial_container_type(irb->codegen, parent_scope, kind, node, buf_ptr(name), - node->data.container_decl.is_extern); + ContainerLayout layout = node->data.container_decl.layout; + TypeTableEntry *container_type = get_partial_container_type(irb->codegen, parent_scope, + kind, node, buf_ptr(name), layout); ScopeDecls *child_scope = get_container_scope(container_type); tld_container->type_entry = container_type; diff --git a/src/parseh.cpp b/src/parseh.cpp index 94f430cdfa..9a83e0d6b1 100644 --- a/src/parseh.cpp +++ b/src/parseh.cpp @@ -720,7 +720,7 @@ static TypeTableEntry *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) const EnumDecl *enum_def = enum_decl->getDefinition(); if (!enum_def) { TypeTableEntry *enum_type = get_partial_container_type(c->codegen, &c->import->decls_scope->base, - ContainerKindEnum, c->source_node, buf_ptr(full_type_name), true); + ContainerKindEnum, c->source_node, buf_ptr(full_type_name), ContainerLayoutExtern); enum_type->data.enumeration.zero_bits_known = true; c->enum_type_table.put(bare_name, enum_type); c->decl_table.put(enum_decl, enum_type); @@ -745,7 +745,7 @@ static TypeTableEntry *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) if (pure_enum) { TypeTableEntry *enum_type = get_partial_container_type(c->codegen, &c->import->decls_scope->base, - ContainerKindEnum, c->source_node, buf_ptr(full_type_name), true); + ContainerKindEnum, c->source_node, buf_ptr(full_type_name), ContainerLayoutExtern); c->enum_type_table.put(bare_name, enum_type); c->decl_table.put(enum_decl, enum_type); @@ -885,7 +885,7 @@ static TypeTableEntry *resolve_record_decl(Context *c, const RecordDecl *record_ TypeTableEntry *struct_type = get_partial_container_type(c->codegen, &c->import->decls_scope->base, - ContainerKindStruct, c->source_node, buf_ptr(full_type_name), true); + ContainerKindStruct, c->source_node, buf_ptr(full_type_name), ContainerLayoutExtern); struct_type->data.structure.zero_bits_known = true; c->struct_type_table.put(bare_name, struct_type); diff --git a/src/parser.cpp b/src/parser.cpp index 54081d91a7..7b7126ca40 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2275,21 +2275,24 @@ static AstNode *ast_parse_use(ParseContext *pc, size_t *token_index, VisibMod vi } /* -ContainerDecl = option("extern") ("struct" | "enum" | "union") "{" many(StructMember) "}" -StructMember = (StructField | FnDef | GlobalVarDecl) -StructField = Symbol option(":" Expression) ",") +ContainerDecl = option("extern" | "packed") ("struct" | "enum" | "union") "{" many(ContainerMember) "}" +ContainerMember = (ContainerField | FnDef | GlobalVarDecl) +ContainerField = Symbol option(":" Expression) ",") */ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, bool mandatory) { Token *first_token = &pc->tokens->at(*token_index); Token *container_kind_token; - bool is_extern; + ContainerLayout layout; if (first_token->id == TokenIdKeywordExtern) { container_kind_token = &pc->tokens->at(*token_index + 1); - is_extern = true; + layout = ContainerLayoutExtern; + } else if (first_token->id == TokenIdKeywordPacked) { + container_kind_token = &pc->tokens->at(*token_index + 1); + layout = ContainerLayoutPacked; } else { container_kind_token = first_token; - is_extern = false; + layout = ContainerLayoutAuto; } ContainerKind kind; @@ -2304,10 +2307,10 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, } else { return nullptr; } - *token_index += is_extern ? 2 : 1; + *token_index += (layout == ContainerLayoutAuto) ? 1 : 2; AstNode *node = ast_create_node(pc, NodeTypeContainerDecl, first_token); - node->data.container_decl.is_extern = is_extern; + node->data.container_decl.layout = layout; node->data.container_decl.kind = kind; ast_eat_token(pc, token_index, TokenIdLBrace); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 769e8c9d8d..6adb7dd863 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -128,6 +128,7 @@ static const struct ZigKeyword zig_keywords[] = { {"nakedcc", TokenIdKeywordNakedCC}, {"noalias", TokenIdKeywordNoAlias}, {"null", TokenIdKeywordNull}, + {"packed", TokenIdKeywordPacked}, {"pub", TokenIdKeywordPub}, {"return", TokenIdKeywordReturn}, {"struct", TokenIdKeywordStruct}, @@ -1502,6 +1503,7 @@ const char * token_name(TokenId id) { case TokenIdKeywordNakedCC: return "nakedcc"; case TokenIdKeywordNoAlias: return "noalias"; case TokenIdKeywordNull: return "null"; + case TokenIdKeywordPacked: return "packed"; case TokenIdKeywordPub: return "pub"; case TokenIdKeywordReturn: return "return"; case TokenIdKeywordStruct: return "struct"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index d004ad4241..a5cbe4cdea 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -12,109 +12,110 @@ #include "bignum.hpp" enum TokenId { - TokenIdEof, - TokenIdSymbol, - TokenIdKeywordFn, - TokenIdKeywordReturn, - TokenIdKeywordVar, - TokenIdKeywordConst, - TokenIdKeywordExtern, - TokenIdKeywordPub, - TokenIdKeywordUse, - TokenIdKeywordExport, - TokenIdKeywordTrue, - TokenIdKeywordFalse, - TokenIdKeywordIf, - TokenIdKeywordTry, - TokenIdKeywordElse, - TokenIdKeywordGoto, - TokenIdKeywordAsm, - TokenIdKeywordVolatile, - TokenIdKeywordStruct, - TokenIdKeywordEnum, - TokenIdKeywordUnion, - TokenIdKeywordWhile, - TokenIdKeywordFor, - TokenIdKeywordContinue, - TokenIdKeywordBreak, - TokenIdKeywordNull, - TokenIdKeywordNoAlias, - TokenIdKeywordSwitch, - TokenIdKeywordUndefined, - TokenIdKeywordError, - TokenIdKeywordType, - TokenIdKeywordInline, - TokenIdKeywordCompTime, - TokenIdKeywordDefer, - TokenIdKeywordThis, - TokenIdKeywordColdCC, - TokenIdKeywordNakedCC, - TokenIdLParen, - TokenIdRParen, - TokenIdComma, - TokenIdStar, - TokenIdStarStar, - TokenIdLBrace, - TokenIdRBrace, - TokenIdLBracket, - TokenIdRBracket, - TokenIdStringLiteral, - TokenIdCharLiteral, - TokenIdSemicolon, - TokenIdNumberLiteral, - TokenIdPlus, - TokenIdPlusPlus, - TokenIdColon, - TokenIdArrow, - TokenIdFatArrow, - TokenIdDash, - TokenIdNumberSign, - TokenIdBoolOr, - TokenIdBoolAnd, - TokenIdBinOr, TokenIdAmpersand, + TokenIdArrow, + TokenIdAtSign, + TokenIdBang, + TokenIdBinOr, TokenIdBinXor, - TokenIdEq, - TokenIdTimesEq, - TokenIdTimesPercent, - TokenIdTimesPercentEq, - TokenIdDivEq, - TokenIdModEq, - TokenIdPlusEq, - TokenIdPlusPercent, - TokenIdPlusPercentEq, - TokenIdMinusEq, - TokenIdMinusPercent, - TokenIdMinusPercentEq, + TokenIdBitAndEq, + TokenIdBitOrEq, + TokenIdBitShiftLeft, TokenIdBitShiftLeftEq, TokenIdBitShiftLeftPercent, TokenIdBitShiftLeftPercentEq, + TokenIdBitShiftRight, TokenIdBitShiftRightEq, - TokenIdBitAndEq, TokenIdBitXorEq, - TokenIdBitOrEq, + TokenIdBoolAnd, TokenIdBoolAndEq, + TokenIdBoolOr, TokenIdBoolOrEq, + TokenIdCharLiteral, TokenIdCmpEq, - TokenIdBang, - TokenIdTilde, - TokenIdCmpNotEq, - TokenIdCmpLessThan, + TokenIdCmpGreaterOrEq, TokenIdCmpGreaterThan, TokenIdCmpLessOrEq, - TokenIdCmpGreaterOrEq, - TokenIdBitShiftLeft, - TokenIdBitShiftRight, - TokenIdSlash, - TokenIdPercent, - TokenIdPercentPercent, + TokenIdCmpLessThan, + TokenIdCmpNotEq, + TokenIdColon, + TokenIdComma, + TokenIdDash, + TokenIdDivEq, TokenIdDot, - TokenIdEllipsis, - TokenIdMaybe, TokenIdDoubleQuestion, + TokenIdEllipsis, + TokenIdEof, + TokenIdEq, + TokenIdFatArrow, + TokenIdKeywordAsm, + TokenIdKeywordBreak, + TokenIdKeywordColdCC, + TokenIdKeywordCompTime, + TokenIdKeywordConst, + TokenIdKeywordContinue, + TokenIdKeywordDefer, + TokenIdKeywordElse, + TokenIdKeywordEnum, + TokenIdKeywordError, + TokenIdKeywordExport, + TokenIdKeywordExtern, + TokenIdKeywordFalse, + TokenIdKeywordFn, + TokenIdKeywordFor, + TokenIdKeywordGoto, + TokenIdKeywordIf, + TokenIdKeywordInline, + TokenIdKeywordNakedCC, + TokenIdKeywordNoAlias, + TokenIdKeywordNull, + TokenIdKeywordPacked, + TokenIdKeywordPub, + TokenIdKeywordReturn, + TokenIdKeywordStruct, + TokenIdKeywordSwitch, + TokenIdKeywordThis, + TokenIdKeywordTrue, + TokenIdKeywordTry, + TokenIdKeywordType, + TokenIdKeywordUndefined, + TokenIdKeywordUnion, + TokenIdKeywordUse, + TokenIdKeywordVar, + TokenIdKeywordVolatile, + TokenIdKeywordWhile, + TokenIdLBrace, + TokenIdLBracket, + TokenIdLParen, + TokenIdMaybe, TokenIdMaybeAssign, - TokenIdAtSign, + TokenIdMinusEq, + TokenIdMinusPercent, + TokenIdMinusPercentEq, + TokenIdModEq, + TokenIdNumberLiteral, + TokenIdNumberSign, + TokenIdPercent, TokenIdPercentDot, + TokenIdPercentPercent, + TokenIdPlus, + TokenIdPlusEq, + TokenIdPlusPercent, + TokenIdPlusPercentEq, + TokenIdPlusPlus, + TokenIdRBrace, + TokenIdRBracket, + TokenIdRParen, + TokenIdSemicolon, + TokenIdSlash, + TokenIdStar, + TokenIdStarStar, + TokenIdStringLiteral, + TokenIdSymbol, + TokenIdTilde, + TokenIdTimesEq, + TokenIdTimesPercent, + TokenIdTimesPercentEq, }; struct TokenNumLit { diff --git a/test/cases/struct.zig b/test/cases/struct.zig index 930aa79db1..09312990a2 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -208,3 +208,20 @@ fn passSliceOfEmptyStructToFn() { fn testPassSliceOfEmptyStructToFn(slice: []EmptyStruct2) -> usize { slice.len } + +const APackedStruct = packed struct { + x: u8, + y: u8, +}; + +fn packedStruct() { + @setFnTest(this); + + var foo = APackedStruct { + .x = 1, + .y = 2, + }; + foo.y += 1; + const four = foo.x + foo.y; + assert(four == 4); +}