began work on multiple return values
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 12 Jan 2021 04:22:12 +0000 (22:22 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 12 Jan 2021 04:22:12 +0000 (22:22 -0600)
bin/onyx
include/onyxastnodes.h
include/onyxtypes.h
onyx.exe
src/onyxastnodes.c
src/onyxchecker.c
src/onyxparser.c
src/onyxsymres.c
src/onyxtypes.c

index 738c0715811fbc631ce44b0777664f69ef1f916a..c9a1216ec9eb9f1ae144f934aacbd32be04a42f4 100755 (executable)
Binary files a/bin/onyx and b/bin/onyx differ
index a2ff911595206f959b075635af5d042bc2eca5c8..83880b4d7b991b96ba68dd4abaaea10b5153ca6f 100644 (file)
@@ -25,6 +25,7 @@ typedef struct AstFileContents AstFileContents;
 typedef struct AstStructLiteral AstStructLiteral;
 typedef struct AstArrayLiteral AstArrayLiteral;
 typedef struct AstRangeLiteral AstRangeLiteral;
+typedef struct AstCompound AstCompound;
 
 typedef struct AstDirectiveSolidify AstDirectiveSolidify;
 
@@ -56,6 +57,7 @@ typedef struct AstEnumType AstEnumType;
 typedef struct AstEnumValue AstEnumValue;
 typedef struct AstTypeAlias AstTypeAlias;
 typedef struct AstTypeRawAlias AstTypeRawAlias;
+typedef struct AstCompoundType AstCompoundType;
 
 typedef struct AstBinding AstBinding;
 typedef struct AstMemRes AstMemRes;
@@ -106,6 +108,8 @@ typedef enum AstKind {
     Ast_Kind_Unary_Op,
     Ast_Kind_Binary_Op,
 
+    Ast_Kind_Compound,
+
     Ast_Kind_Type_Start,
     Ast_Kind_Type,
     Ast_Kind_Basic_Type,
@@ -121,6 +125,7 @@ typedef enum AstKind {
     Ast_Kind_Enum_Type,
     Ast_Kind_Type_Alias,
     Ast_Kind_Type_Raw_Alias,
+    Ast_Kind_Type_Compound,
     Ast_Kind_Type_End,
 
     Ast_Kind_Struct_Member,
@@ -516,6 +521,11 @@ struct AstIntrinsicCall {
 
     VarArgKind va_kind;
 };
+struct AstCompound {
+    AstTyped_base;
+
+    bh_arr(AstTyped *) exprs;
+};
 
 struct AstDirectiveSolidify {
     AstTyped_base;
@@ -668,6 +678,11 @@ struct AstEnumType {
 struct AstEnumValue    { AstTyped_base; AstNumLit* value; };
 struct AstTypeAlias    { AstType_base; AstType* to; };
 struct AstTypeRawAlias { AstType_base; Type* to; };
+struct AstCompoundType {
+    AstType_base;
+
+    bh_arr(AstType *) types;
+};
 
 // Top level nodes
 struct AstBinding       { AstTyped_base; AstNode* node; };
@@ -988,13 +1003,22 @@ AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstP
 
 // NOTE: Useful inlined functions
 static inline b32 is_lval(AstNode* node) {
-    return (node->kind == Ast_Kind_Local)
+    if    ((node->kind == Ast_Kind_Local)
         || (node->kind == Ast_Kind_Param)
         || (node->kind == Ast_Kind_Global)
         || (node->kind == Ast_Kind_Dereference)
         || (node->kind == Ast_Kind_Array_Access)
         || (node->kind == Ast_Kind_Field_Access)
-        || (node->kind == Ast_Kind_Memres);
+        || (node->kind == Ast_Kind_Memres))
+        return 1;
+
+    if (node->kind == Ast_Kind_Compound) {
+        b32 all_lval = 1;
+        bh_arr_each(AstTyped *, expr, ((AstCompound *) node)->exprs) all_lval = all_lval && is_lval((AstNode *) *expr);
+        return all_lval;
+    }
+
+    return 0;
 }
 
 static inline b32 binop_is_assignment(BinaryOp binop) {
index 0d071135340b2e2a0f201120e226f4c1f455f087..6d92d21bfeabcadc787a785dbf0756b25685237b 100644 (file)
@@ -91,6 +91,11 @@ typedef struct StructMember {
         bh_arr(StructMember *) memarr;                            \
         bh_arr(struct AstPolySolution) poly_sln;                  \
     })                                                            \
+    TYPE_KIND(Compound, struct {                                  \
+        u32 count;                                                \
+        u32 size;                                                 \
+        Type* types[];                                            \
+    })                                                            \
     TYPE_KIND(Array, struct { u32 size; u32 count; Type *elem; }) \
     TYPE_KIND(Slice, struct { Type *ptr_to_data; })               \
     TYPE_KIND(DynArray, struct { Type *ptr_to_data; })            \
@@ -101,6 +106,7 @@ typedef struct StructMember {
         Type* backing;                                            \
     })
 
+
 typedef enum TypeKind {
     Type_Kind_Invalid,
 
@@ -135,6 +141,7 @@ extern Type basic_types[];
 
 struct AstType;
 struct AstFunction;
+struct AstCompound;
 
 b32 types_are_compatible(Type* t1, Type* t2);
 u32 type_size_of(Type* type);
@@ -142,6 +149,7 @@ u32 type_alignment_of(Type* type);
 Type* type_build_from_ast(bh_allocator alloc, struct AstType* type_node);
 
 Type* type_build_function_type(bh_allocator alloc, struct AstFunction* func);
+Type* type_build_compound_type(bh_allocator alloc, struct AstCompound* compound);
 
 Type* type_make_pointer(bh_allocator alloc, Type* to);
 Type* type_make_array(bh_allocator alloc, Type* to, u32 count);
index e585b16a24cb9f830e4d12094744a27d37fea8b6..b503b6cb3f305b682f3238e2690669b569634180 100644 (file)
Binary files a/onyx.exe and b/onyx.exe differ
index c90dc2823d92be5868a4ef2deb071d650dd6659a..8c7b15a36a27e235280ea1820072a3a0045e7caf 100644 (file)
@@ -28,6 +28,8 @@ static const char* ast_node_names[] = {
     "UN_OP",
     "BIN_OP",
 
+    "COMPOUND",
+
     "TYPE_START (BAD)",
     "TYPE",
     "BASIC_TYPE",
@@ -43,6 +45,7 @@ static const char* ast_node_names[] = {
     "ENUM TYPE",
     "TYPE_ALIAS",
     "TYPE RAW ALIAS",
+    "COMPOUND TYPE",
     "TYPE_END (BAD)",
 
     "STRUCT MEMBER",
index 49d1ab0f18cd7c429a64e342f129b9232efb4f84..12f47cc2520783b896fc6535e45ff9de267eef32 100644 (file)
@@ -31,6 +31,7 @@ CheckStatus check_unaryop(AstUnaryOp** punop);
 CheckStatus check_struct_literal(AstStructLiteral* sl);
 CheckStatus check_array_literal(AstArrayLiteral* al);
 CheckStatus check_range_literal(AstRangeLiteral** range);
+CheckStatus check_compound(AstCompound* compound);
 CheckStatus check_expression(AstTyped** expr);
 CheckStatus check_address_of(AstAddressOf* aof);
 CheckStatus check_dereference(AstDereference* deref);
@@ -1111,6 +1112,16 @@ CheckStatus check_range_literal(AstRangeLiteral** prange) {
     return Check_Success;
 }
 
+CheckStatus check_compound(AstCompound* compound) {
+    bh_arr_each(AstTyped *, expr, compound->exprs) {
+        CHECK(expression, expr);
+        resolve_expression_type(*expr);        
+    }
+
+    compound->type = type_build_compound_type(semstate.node_allocator, compound);
+    return Check_Success;
+}
+
 CheckStatus check_address_of(AstAddressOf* aof) {
     CHECK(expression, &aof->expr);
 
@@ -1386,6 +1397,10 @@ CheckStatus check_expression(AstTyped** pexpr) {
             *pexpr = (AstTyped *) ((AstDirectiveSolidify *) expr)->resolved_proc;
             break;
 
+        case Ast_Kind_Compound:
+            CHECK(compound, (AstCompound *) expr);
+            break;
+
         case Ast_Kind_StrLit: break;
         case Ast_Kind_File_Contents: break;
         case Ast_Kind_Overloaded_Function: break;
index 48ea0bf9cb3e0e3752cc969fe8edcd7b546f07ec..e8e5a403c6c5711d9beccdaf88432f486134b7af 100644 (file)
@@ -27,7 +27,9 @@ static AstNumLit*     parse_float_literal(OnyxParser* parser);
 static b32            parse_possible_struct_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret);
 static b32            parse_possible_array_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret);
 static AstTyped*      parse_factor(OnyxParser* parser);
-static AstTyped*      parse_expression(OnyxParser* parser);
+static AstTyped*      parse_compound_assignment(OnyxParser* parser, AstTyped* lhs);
+static AstTyped*      parse_compound_expression(OnyxParser* parser, b32 assignment_allowed);
+static AstTyped*      parse_expression(OnyxParser* parser, b32 assignment_allowed);
 static AstIfWhile*    parse_if_stmt(OnyxParser* parser);
 static AstIfWhile*    parse_while_stmt(OnyxParser* parser);
 static AstFor*        parse_for_stmt(OnyxParser* parser);
@@ -193,7 +195,7 @@ static b32 parse_possible_struct_literal(OnyxParser* parser, AstTyped* left, Ast
             name = NULL;
         }
 
-        AstTyped *expr = parse_expression(parser);
+        AstTyped *expr = parse_expression(parser, 0);
 
         if (is_named) {
             AstStructMember* sm = make_node(AstStructMember, Ast_Kind_Struct_Member);
@@ -230,7 +232,7 @@ static b32 parse_possible_array_literal(OnyxParser* parser, AstTyped* left, AstT
     expect_token(parser, '.');
     expect_token(parser, '[');
     while (parser->curr->type != ']') {
-        AstTyped* value = parse_expression(parser);
+        AstTyped* value = parse_expression(parser, 0);
         bh_arr_push(al->values, value);
 
         if (parser->curr->type != ']')
@@ -258,7 +260,7 @@ static AstTyped* parse_factor(OnyxParser* parser) {
     switch ((u16) parser->curr->type) {
         case '(': {
             consume_token(parser);
-            AstTyped* expr = parse_expression(parser);
+            AstTyped* expr = parse_compound_expression(parser, 0);
             expect_token(parser, ')');
             retval = expr;
             break;
@@ -570,7 +572,7 @@ static AstTyped* parse_factor(OnyxParser* parser) {
         switch ((u16) parser->curr->type) {
             case '[': {
                 OnyxToken *open_bracket = expect_token(parser, '[');
-                AstTyped *expr = parse_expression(parser);
+                AstTyped *expr = parse_compound_expression(parser, 0);
 
                 AstKind kind = Ast_Kind_Array_Access;
 
@@ -610,7 +612,7 @@ static AstTyped* parse_factor(OnyxParser* parser) {
 
                     AstArgument* arg = make_node(AstArgument, Ast_Kind_Argument);
                     arg->token = parser->curr;
-                    arg->value = parse_expression(parser);
+                    arg->value = parse_expression(parser, 0);
 
                     if (arg != NULL && arg->kind != Ast_Kind_Error) {
                         bh_arr_push(call_node->arg_arr, arg);
@@ -728,6 +730,47 @@ static BinaryOp binary_op_from_token_type(TokenType t) {
     }
 }
 
+static AstTyped* parse_compound_assignment(OnyxParser* parser, AstTyped* lhs) {
+    if (parser->curr->type != '=') return lhs;
+
+    AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
+    assignment->token = expect_token(parser, '=');
+    assignment->operation = Binary_Op_Assign;
+    assignment->left = lhs;
+    assignment->right = parse_compound_expression(parser, 0);
+
+    return (AstTyped *) assignment;
+}
+
+static AstTyped* parse_compound_expression(OnyxParser* parser, b32 assignment_allowed) {
+    AstTyped* first = parse_expression(parser, assignment_allowed);
+
+    if (parser->curr->type == ',') {
+        AstCompound* compound = make_node(AstCompound, Ast_Kind_Compound);
+        compound->token = parser->curr;
+
+        bh_arr_new(global_heap_allocator, compound->exprs, 2);
+        bh_arr_push(compound->exprs, first);
+
+        while (parser->curr->type == ',') {
+            if (parser->hit_unexpected_token) return (AstTyped *) compound;
+            consume_token(parser);
+
+            AstTyped* expr = parse_expression(parser, 0);
+            bh_arr_push(compound->exprs, expr);
+
+            if (assignment_allowed && parser->curr->type == '=') {
+                return parse_compound_assignment(parser, (AstTyped *) compound);
+            }
+        }
+
+        return (AstTyped *) compound;
+
+    } else {
+        return first;
+    }
+}
+
 // <expr> +  <expr>
 // <expr> -  <expr>
 // <expr> *  <expr>
@@ -747,7 +790,7 @@ static BinaryOp binary_op_from_token_type(TokenType t) {
 // <expr> %= <expr>
 // <factor>
 // With expected precedence rules
-static AstTyped* parse_expression(OnyxParser* parser) {
+static AstTyped* parse_expression(OnyxParser* parser, b32 assignment_allowed) {
     bh_arr(AstBinaryOp*) tree_stack = NULL;
     bh_arr_new(global_heap_allocator, tree_stack, 4);
     bh_arr_set_length(tree_stack, 0);
@@ -764,6 +807,7 @@ static AstTyped* parse_expression(OnyxParser* parser) {
 
         bin_op_kind = binary_op_from_token_type(parser->curr->type);
         if (bin_op_kind == Binary_Op_Count) goto expression_done;
+        if (binop_is_assignment(bin_op_kind) && !assignment_allowed) goto expression_done;
 
         bin_op_tok = parser->curr;
         consume_token(parser);
@@ -824,13 +868,13 @@ static AstIfWhile* parse_if_stmt(OnyxParser* parser) {
         assignment->operation = Binary_Op_Assign;
         assignment->token = expect_token(parser, '=');
         assignment->left = (AstTyped *) if_node->local;
-        assignment->right = parse_expression(parser);
+        assignment->right = parse_expression(parser, 0);
 
         if_node->assignment = assignment;
         expect_token(parser, ';');
     }
 
-    AstTyped* cond = parse_expression(parser);
+    AstTyped* cond = parse_expression(parser, 1);
     AstBlock* true_stmt = parse_block(parser);
 
     if_node->cond = cond;
@@ -843,7 +887,7 @@ static AstIfWhile* parse_if_stmt(OnyxParser* parser) {
         consume_token(parser);
         AstIfWhile* elseif_node = make_node(AstIfWhile, Ast_Kind_If);
 
-        cond = parse_expression(parser);
+        cond = parse_expression(parser, 1);
         true_stmt = parse_block(parser);
 
         elseif_node->cond = cond;
@@ -881,13 +925,13 @@ static AstIfWhile* parse_while_stmt(OnyxParser* parser) {
         assignment->operation = Binary_Op_Assign;
         assignment->token = expect_token(parser, '=');
         assignment->left = (AstTyped *) while_node->local;
-        assignment->right = parse_expression(parser);
+        assignment->right = parse_expression(parser, 0);
 
         while_node->assignment = assignment;
         expect_token(parser, ';');
     }
 
-    while_node->cond = parse_expression(parser);
+    while_node->cond = parse_expression(parser, 1);
     while_node->true_stmt = parse_block(parser);
 
     if (parser->curr->type == Token_Type_Keyword_Else) {
@@ -913,7 +957,7 @@ static AstFor* parse_for_stmt(OnyxParser* parser) {
     for_node->var = var_node;
 
     expect_token(parser, ':');
-    for_node->iter = parse_expression(parser);
+    for_node->iter = parse_expression(parser, 1);
     for_node->stmt = parse_block(parser);
 
     return for_node;
@@ -935,13 +979,13 @@ static AstSwitch* parse_switch_stmt(OnyxParser* parser) {
         assignment->operation = Binary_Op_Assign;
         assignment->token = expect_token(parser, '=');
         assignment->left = (AstTyped *) switch_node->local;
-        assignment->right = parse_expression(parser);
+        assignment->right = parse_expression(parser, 0);
 
         switch_node->assignment = assignment;
         expect_token(parser, ';');
     }
 
-    switch_node->expr = parse_expression(parser);
+    switch_node->expr = parse_expression(parser, 1);
     expect_token(parser, '{');
 
     while (parser->curr->type == Token_Type_Keyword_Case) {
@@ -960,13 +1004,13 @@ static AstSwitch* parse_switch_stmt(OnyxParser* parser) {
             break;
         }
 
-        AstTyped* value = parse_expression(parser);
+        AstTyped* value = parse_expression(parser, 1);
         bh_arr_push(case_values, value);
         while (parser->curr->type == ',') {
             if (parser->hit_unexpected_token) return switch_node;
 
             consume_token(parser);
-            value = parse_expression(parser);
+            value = parse_expression(parser, 1);
             bh_arr_push(case_values, value);
         }
 
@@ -1028,7 +1072,7 @@ static i32 parse_possible_symbol_declaration(OnyxParser* parser, AstNode** ret)
         assignment->token = parser->curr;
         consume_token(parser);
 
-        AstTyped* expr = parse_expression(parser);
+        AstTyped* expr = parse_expression(parser, 1);
         if (expr == NULL) return 1;
         assignment->right = expr;
 
@@ -1048,7 +1092,7 @@ static AstReturn* parse_return_stmt(OnyxParser* parser) {
     AstTyped* expr = NULL;
 
     if (parser->curr->type != ';') {
-        expr = parse_expression(parser);
+        expr = parse_compound_expression(parser, 0);
 
         if (expr == NULL || expr == (AstTyped *) &error_node) {
             return (AstReturn *) &error_node;
@@ -1131,7 +1175,7 @@ static AstNode* parse_use_stmt(OnyxParser* parser) {
     } else {
         AstUse* use_node = make_node(AstUse, Ast_Kind_Use);
         use_node->token = use_token;
-        use_node->expr = parse_expression(parser);
+        use_node->expr = parse_expression(parser, 1);
         return (AstNode *) use_node;
     }
 }
@@ -1177,7 +1221,7 @@ static AstNode* parse_statement(OnyxParser* parser) {
         case Token_Type_Literal_Integer:
         case Token_Type_Literal_Float:
         case Token_Type_Literal_String:
-            retval = (AstNode *) parse_expression(parser);
+            retval = (AstNode *) parse_compound_expression(parser, 1);
             break;
 
         case Token_Type_Keyword_If:
@@ -1438,7 +1482,7 @@ static AstType* parse_type(OnyxParser* parser) {
                         AstType** insertion = (AstType **) &((AstArrayType *) new)->count_expr;
                         parse_polymorphic_variable(parser, &insertion);
                     } else {
-                        ((AstArrayType *) new)->count_expr = parse_expression(parser);
+                        ((AstArrayType *) new)->count_expr = parse_expression(parser, 0);
                     }
                 }
 
@@ -1559,7 +1603,7 @@ static AstType* parse_type(OnyxParser* parser) {
                 case Token_Type_Literal_Float:
                 case Token_Type_Literal_True:
                 case Token_Type_Literal_False:
-                    *next_insertion = (AstType *) parse_expression(parser);
+                    *next_insertion = (AstType *) parse_expression(parser, 0);
                     next_insertion = NULL;
                     break;
                 }
@@ -1676,7 +1720,7 @@ static AstStructType* parse_struct(OnyxParser* parser) {
 
         if (parser->curr->type == '=') {
             consume_token(parser);
-            mem->initial_value = parse_expression(parser);
+            mem->initial_value = parse_expression(parser, 0);
         }
 
         expect_token(parser, ';');
@@ -1767,7 +1811,7 @@ static void parse_function_params(OnyxParser* parser, AstFunction* func) {
         if (parser->curr->type == '=' && curr_param.vararg_kind == VA_Kind_Not_VA) {
             consume_token(parser);
 
-            curr_param.default_value = parse_expression(parser);
+            curr_param.default_value = parse_expression(parser, 0);
         }
 
         bh_arr_push(func->params, curr_param);
@@ -1814,7 +1858,7 @@ static AstFunction* parse_function_definition(OnyxParser* parser) {
         while (parser->curr->type != '}') {
             if (parser->hit_unexpected_token) return (AstFunction *) ofunc;
 
-            AstTyped* o_node = parse_expression(parser);
+            AstTyped* o_node = parse_expression(parser, 0);
 
             bh_arr_push(ofunc->overloads, o_node);
 
@@ -1859,7 +1903,7 @@ static AstFunction* parse_function_definition(OnyxParser* parser) {
                     onyx_report_error(parser->curr->pos, "#add_overload cannot be placed on procedures inside of other scopes");
                 }
 
-                func_def->overloaded_function = (AstNode *) parse_expression(parser);
+                func_def->overloaded_function = (AstNode *) parse_expression(parser, 0);
             }
         }
 
@@ -2046,7 +2090,7 @@ static AstTyped* parse_top_level_expression(OnyxParser* parser) {
         return (AstTyped *) parse_enum_declaration(parser);
     }
     else {
-        return parse_expression(parser);
+        return parse_expression(parser, 1);
     }
 }
 
@@ -2148,14 +2192,14 @@ static AstNode* parse_top_level_statement(OnyxParser* parser) {
 
                 if (parser->curr->type == '=') {
                     consume_token(parser);
-                    memres->initial_value = parse_expression(parser);
+                    memres->initial_value = parse_expression(parser, 1);
 
                 } else {
                     memres->type_node = parse_type(parser);
 
                     if (parser->curr->type == '=') {
                         consume_token(parser);
-                        memres->initial_value = parse_expression(parser);
+                        memres->initial_value = parse_expression(parser, 1);
                     }
                 }
 
index 494c881466bf5ca428cb4e8e6791f72b5e754e92..5cc05d9c6f866e2ec13dce033ae5e8710fa3951e 100644 (file)
@@ -14,6 +14,7 @@ static void symres_call(AstCall* call);
 static void symres_size_of(AstSizeOf* so);
 static void symres_align_of(AstAlignOf* so);
 static void symres_field_access(AstFieldAccess** fa);
+static void symres_compound(AstCompound* compound);
 static void symres_expression(AstTyped** expr);
 static void symres_return(AstReturn* ret);
 static void symres_if(AstIfWhile* ifnode);
@@ -271,6 +272,12 @@ static void symres_field_access(AstFieldAccess** fa) {
     }
 }
 
+static void symres_compound(AstCompound* compound) {
+    bh_arr_each(AstTyped *, expr, compound->exprs) {
+        symres_expression(expr);
+    }
+}
+
 static void symres_pipe(AstBinaryOp** pipe) {
     AstCall* call_node = (AstCall *) (*pipe)->right;
     symres_expression((AstTyped **) &call_node);
@@ -405,6 +412,10 @@ static void symres_expression(AstTyped** expr) {
             symres_directive_solidify((AstDirectiveSolidify **) expr);
             break;
 
+        case Ast_Kind_Compound:
+            symres_compound((AstCompound *) *expr);
+            break;
+
         default: break;
     }
 }
index 6da02121a3ffa8b0b4fd13582289de982fb8ecf7..e824988d3a4efb97e8b3e0966af373e3dc194334 100644 (file)
@@ -231,6 +231,17 @@ b32 types_are_compatible(Type* t1, Type* t2) {
             return types_are_compatible(t1->DynArray.ptr_to_data->Pointer.elem, t2->DynArray.ptr_to_data->Pointer.elem);
         }
 
+        case Type_Kind_Compound: {
+            if (t2->kind != Type_Kind_Compound) return 0;
+            if (t1->Compound.count != t2->Compound.count) return 0;
+
+            fori (i, 0, (i64) t1->Compound.count) {
+                if (!types_are_compatible(t1->Compound.types[i], t2->Compound.types[i])) return 0;
+            }
+
+            return 1;
+        }
+
         default:
             assert(("Invalid type", 0));
             break;
@@ -542,6 +553,23 @@ Type* type_build_function_type(bh_allocator alloc, AstFunction* func) {
     return func_type;
 }
 
+Type* type_build_compound_type(bh_allocator alloc, AstCompound* compound) {
+    i64 expr_count = bh_arr_length(compound->exprs);
+
+    Type* comp_type = bh_alloc(alloc, sizeof(Type) + sizeof(Type *) * expr_count);
+    comp_type->kind = Type_Kind_Compound;
+    comp_type->Compound.size = 0;
+    comp_type->Compound.count = expr_count;
+
+    fori (i, 0, expr_count) {
+        assert(compound->exprs[i]->type != NULL);
+        comp_type->Compound.size += type_size_of(compound->exprs[i]->type);
+        comp_type->Compound.types[i] = compound->exprs[i]->type;
+    }
+
+    return comp_type;
+}
+
 Type* type_make_pointer(bh_allocator alloc, Type* to) {
     Type* ptr_type = bh_alloc_item(alloc, Type);