Commit 71d335e5cc

Andrew Kelley <superjoe30@gmail.com>
2017-02-03 17:39:24
implement packed structs
closes #183
1 parent cd7713b
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
 
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) "}"
 
 ```
 
src/all_types.hpp
@@ -621,11 +621,17 @@ enum ContainerKind {
     ContainerKindUnion,
 };
 
+enum ContainerLayout {
+    ContainerLayoutAuto,
+    ContainerLayoutExtern,
+    ContainerLayoutPacked,
+};
+
 struct AstNodeContainerDecl {
     ContainerKind kind;
     ZigList<AstNode *> fields;
     ZigList<AstNode *> 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;
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<ZigLLVMDIType*>(gen_field_count);
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);
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];
src/ir.cpp
@@ -5283,8 +5283,9 @@ static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope,
     TldContainer *tld_container = allocate<TldContainer>(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;
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);
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);
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";
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,
+    TokenIdAmpersand,
     TokenIdArrow,
-    TokenIdFatArrow,
-    TokenIdDash,
-    TokenIdNumberSign,
-    TokenIdBoolOr,
-    TokenIdBoolAnd,
+    TokenIdAtSign,
+    TokenIdBang,
     TokenIdBinOr,
-    TokenIdAmpersand,
     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,
+    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,
-    TokenIdDoubleQuestion,
     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 {
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);
+}