harec

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 5f52622a1a740a98de74cb540503a5171ae1fd03
parent 345e507e27c555a52925e6e3a9bc99ff8c5ae1b3
Author: Drew DeVault <sir@cmpwn.com>
Date:   Wed, 10 Feb 2021 12:36:04 -0500

Implement tuples

Diffstat:
Minclude/ast.h | 16++++++++++++++++
Minclude/expr.h | 13+++++++++++++
Minclude/type_store.h | 3+++
Minclude/types.h | 10++++++++++
Msrc/check.c | 95++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/dump.c | 8++++++++
Msrc/eval.c | 5+++++
Msrc/gen.c | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/lex.c | 1+
Msrc/parse.c | 124++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/qtype.c | 17+++++++++++++++++
Msrc/type_store.c | 43+++++++++++++++++++++++++++++++++++++++++++
Msrc/typedef.c | 12++++++++++++
Msrc/types.c | 27+++++++++++++++++++++++++++
Atests/21-tuples.ha | 16++++++++++++++++
Mtests/configure | 3++-
16 files changed, 444 insertions(+), 13 deletions(-)

diff --git a/include/ast.h b/include/ast.h @@ -65,6 +65,11 @@ struct ast_tagged_union_type { struct ast_tagged_union_type *next; }; +struct ast_tuple_type { + struct ast_type *type; + struct ast_tuple_type *next; +}; + enum struct_union_member_type { MEMBER_TYPE_FIELD, MEMBER_TYPE_EMBEDDED, @@ -96,6 +101,7 @@ struct ast_type { struct ast_list_type slice; struct ast_struct_union_type struct_union; struct ast_tagged_union_type tagged_union; + struct ast_tuple_type tuple; struct { struct identifier alias; bool unwrap; @@ -115,6 +121,10 @@ struct ast_expression_access { struct ast_expression *_struct; char *field; }; + struct { + struct ast_expression *tuple; + struct ast_expression *value; + }; }; }; @@ -297,6 +307,11 @@ struct ast_expression_struct { struct ast_field_value *fields; }; +struct ast_expression_tuple { + struct ast_expression *expr; + struct ast_expression_tuple *next; +}; + struct ast_expression_unarithm { enum unarithm_operator op; struct ast_expression *operand; @@ -328,6 +343,7 @@ struct ast_expression { struct ast_expression_slice slice; struct ast_expression_struct _struct; struct ast_expression_switch _switch; + struct ast_expression_tuple tuple; struct ast_expression_unarithm unarithm; }; }; diff --git a/include/expr.h b/include/expr.h @@ -31,6 +31,7 @@ enum expr_type { EXPR_SLICE, EXPR_STRUCT, EXPR_SWITCH, + EXPR_TUPLE, EXPR_UNARITHM, }; @@ -38,6 +39,7 @@ enum access_type { ACCESS_IDENTIFIER, ACCESS_INDEX, ACCESS_FIELD, + ACCESS_TUPLE, }; struct expression_access { @@ -52,6 +54,11 @@ struct expression_access { struct expression *_struct; const struct struct_field *field; }; + struct { + struct expression *tuple; + struct expression *value; + const struct type_tuple *tvalue; + }; }; }; @@ -265,6 +272,11 @@ struct expression_struct { bool autofill; }; +struct expression_tuple { + struct expression *value; + struct expression_tuple *next; +}; + enum unarithm_operator { UN_ADDRESS, // & UN_BNOT, // ~ @@ -306,6 +318,7 @@ struct expression { struct expression_switch _switch; struct expression_struct _struct; struct expression_slice slice; + struct expression_tuple tuple; struct expression_unarithm unarithm; }; }; diff --git a/include/type_store.h b/include/type_store.h @@ -43,4 +43,7 @@ const struct type *type_store_lookup_alias(struct type_store *store, const struct type *type_store_lookup_tagged(struct type_store *store, struct type_tagged_union *tags); +const struct type *type_store_lookup_tuple(struct type_store *store, + struct type_tuple *values); + #endif diff --git a/include/types.h b/include/types.h @@ -35,6 +35,7 @@ enum type_storage { TYPE_STORAGE_STRING, TYPE_STORAGE_STRUCT, TYPE_STORAGE_TAGGED, + TYPE_STORAGE_TUPLE, TYPE_STORAGE_UNION, }; @@ -115,6 +116,12 @@ struct type_struct_union { struct struct_field *fields; }; +struct type_tuple { + const struct type *type; + size_t offset; + struct type_tuple *next; +}; + struct type_tagged_union { const struct type *type; struct type_tagged_union *next; @@ -137,6 +144,7 @@ struct type { struct type_pointer pointer; struct type_struct_union struct_union; struct type_tagged_union tagged; + struct type_tuple tuple; }; }; @@ -144,6 +152,8 @@ const struct type *type_dereference(const struct type *type); const struct type *type_dealias(const struct type *type); const struct struct_field *type_get_field( const struct type *type, const char *name); +const struct type_tuple *type_get_value( + const struct type *type, uintmax_t index); const struct type *tagged_select_subtype( const struct type *tagged, const struct type *subtype); diff --git a/src/check.c b/src/check.c @@ -141,12 +141,40 @@ check_expr_access(struct context *ctx, "Cannot dereference nullable pointer for field selection"); expect(&aexpr->access._struct->loc, stype->storage == TYPE_STORAGE_STRUCT || stype->storage == TYPE_STORAGE_UNION, - "Cannot index non-struct, non-union object"); + "Cannot select field from non-struct, non-union object"); expr->access.field = type_get_field(stype, aexpr->access.field); expect(&aexpr->access._struct->loc, expr->access.field, "No such struct field '%s'", aexpr->access.field); expr->result = expr->access.field->type; break; + case ACCESS_TUPLE: + expr->access.tuple = xcalloc(1, sizeof(struct expression)); + expr->access.value = xcalloc(1, sizeof(struct expression)); + check_expression(ctx, aexpr->access.tuple, + expr->access.tuple, NULL); + check_expression(ctx, aexpr->access.value, + expr->access.value, NULL); + assert(expr->access.value->type == EXPR_CONSTANT); + + const struct type *ttype = + type_dereference(expr->access.tuple->result); + expect(&aexpr->access.tuple->loc, ttype, + "Cannot dereference nullable pointer for value selection"); + expect(&aexpr->access.tuple->loc, + ttype->storage == TYPE_STORAGE_TUPLE, + "Cannot select value from non-tuple object"); + expect(&aexpr->access.tuple->loc, + type_is_integer(expr->access.value->result), + "Cannot use non-integer constant to select tuple value"); + + expr->access.tvalue = type_get_value(ttype, + aexpr->access.value->constant.uval); + expect(&aexpr->access.tuple->loc, expr->access.tvalue, + "No such tuple value '%zu'", + aexpr->access.value->constant.uval); + + expr->result = expr->access.tvalue->type; + break; } } @@ -814,6 +842,7 @@ check_expr_constant(struct context *ctx, case TYPE_STORAGE_POINTER: case TYPE_STORAGE_SLICE: case TYPE_STORAGE_TAGGED: + case TYPE_STORAGE_TUPLE: case TYPE_STORAGE_UNION: assert(0); // Invariant } @@ -1495,6 +1524,67 @@ check_expr_switch(struct context *ctx, } static void +check_expr_tuple(struct context *ctx, + const struct ast_expression *aexpr, + struct expression *expr, + const struct type *hint) +{ + trenter(TR_CHECK, "tuple"); + expr->type = EXPR_TUPLE; + + const struct type_tuple *ttuple = NULL; + if (hint && type_dealias(hint)->storage == TYPE_STORAGE_TUPLE) { + ttuple = &type_dealias(hint)->tuple; + } + + struct type_tuple result = {0}; + struct type_tuple *rtype = &result; + + struct expression_tuple *tuple = &expr->tuple; + for (const struct ast_expression_tuple *atuple = &aexpr->tuple; + atuple; atuple = atuple->next) { + tuple->value = xcalloc(1, sizeof(struct expression)); + check_expression(ctx, atuple->expr, tuple->value, + ttuple ? ttuple->type : NULL); + rtype->type = tuple->value->result; + + if (atuple->next) { + rtype->next = xcalloc(1, sizeof(struct type_tuple)); + rtype = rtype->next; + tuple->next = xcalloc(1, sizeof(struct expression_tuple)); + tuple = tuple->next; + } + + if (ttuple) { + ttuple = ttuple->next; + } + } + + if (hint) { + expr->result = hint; + } else { + expr->result = type_store_lookup_tuple(ctx->store, &result); + } + + ttuple = &type_dealias(expr->result)->tuple; + struct expression_tuple *etuple = &expr->tuple; + const struct ast_expression_tuple *atuple = &aexpr->tuple; + while (etuple) { + expect(&atuple->expr->loc, ttuple, + "Too many values for tuple type"); + expect(&atuple->expr->loc, + type_is_assignable(ttuple->type, etuple->value->result), + "Value is not assignable to tuple value type"); + etuple->value = lower_implicit_cast(ttuple->type, etuple->value); + etuple = etuple->next; + atuple = atuple->next; + ttuple = ttuple->next; + } + + trleave(TR_CHECK, NULL); +} + +static void check_expr_unarithm(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, @@ -1628,6 +1718,9 @@ check_expression(struct context *ctx, case EXPR_SWITCH: check_expr_switch(ctx, aexpr, expr, hint); break; + case EXPR_TUPLE: + check_expr_tuple(ctx, aexpr, expr, hint); + break; case EXPR_UNARITHM: check_expr_unarithm(ctx, aexpr, expr, hint); break; diff --git a/src/dump.c b/src/dump.c @@ -103,6 +103,7 @@ dump_const(const struct expression *expr) case TYPE_STORAGE_SLICE: case TYPE_STORAGE_STRING: case TYPE_STORAGE_STRUCT: + case TYPE_STORAGE_TUPLE: case TYPE_STORAGE_UNION: assert(0); // TODO case TYPE_STORAGE_CHAR: @@ -212,6 +213,8 @@ dump_type(const struct type *type) fprintf(stderr, ") "); dump_type(type->func.result); break; + case TYPE_STORAGE_TUPLE: + assert(0); // TODO } } @@ -257,6 +260,8 @@ dump_expr(const struct expression *expr, int depth) assert(0); case ACCESS_FIELD: assert(0); + case ACCESS_TUPLE: + assert(0); } break; case EXPR_ALLOC: @@ -371,6 +376,9 @@ dump_expr(const struct expression *expr, int depth) case EXPR_SWITCH: fprintf(stderr, "switch"); break; + case EXPR_TUPLE: + fprintf(stderr, "tuple"); + break; case EXPR_UNARITHM: fprintf(stderr, "unarithm"); break; diff --git a/src/eval.c b/src/eval.c @@ -55,6 +55,7 @@ itrunc(const struct type *type, uintmax_t val) case TYPE_STORAGE_STRING: case TYPE_STORAGE_STRUCT: case TYPE_STORAGE_TAGGED: + case TYPE_STORAGE_TUPLE: case TYPE_STORAGE_UNION: case TYPE_STORAGE_VOID: assert(0); @@ -258,6 +259,7 @@ eval_const(struct context *ctx, struct expression *in, struct expression *out) case TYPE_STORAGE_STRUCT: case TYPE_STORAGE_UNION: case TYPE_STORAGE_TAGGED: + case TYPE_STORAGE_TUPLE: assert(0); // TODO case TYPE_STORAGE_BOOL: case TYPE_STORAGE_CHAR: @@ -346,6 +348,7 @@ eval_cast(struct context *ctx, struct expression *in, struct expression *out) case TYPE_STORAGE_FUNCTION: case TYPE_STORAGE_STRING: case TYPE_STORAGE_STRUCT: + case TYPE_STORAGE_TUPLE: case TYPE_STORAGE_UNION: assert(0); // Invariant case TYPE_STORAGE_VOID: @@ -422,6 +425,7 @@ constant_default(struct context *ctx, struct expression *v) break; case TYPE_STORAGE_TAGGED: case TYPE_STORAGE_SLICE: + case TYPE_STORAGE_TUPLE: assert(0); // TODO case TYPE_STORAGE_ALIAS: case TYPE_STORAGE_FUNCTION: @@ -514,6 +518,7 @@ eval_expr(struct context *ctx, struct expression *in, struct expression *out) case EXPR_STRUCT: return eval_struct(ctx, in, out); case EXPR_SLICE: + case EXPR_TUPLE: case EXPR_UNARITHM: assert(0); // TODO case EXPR_ALLOC: diff --git a/src/gen.c b/src/gen.c @@ -406,6 +406,35 @@ address_field(struct gen_context *ctx, } static void +address_value(struct gen_context *ctx, + const struct expression *expr, + struct qbe_value *out) +{ + gen_temp(ctx, out, &qbe_long, "object.%d"); // XXX: ARCH + gen_expression(ctx, expr->access.tuple, out); + + const struct type *ttype = type_dealias(expr->access.tuple->result); + if (ttype->storage == TYPE_STORAGE_POINTER) { + // We get one dereference for free for aggregate types + ttype = type_dealias(ttype->pointer.referent); + } + while (ttype->storage == TYPE_STORAGE_POINTER) { + pushi(ctx->current, out, Q_LOADL, out, NULL); + ttype = type_dealias(ttype->pointer.referent); + } + + const struct type_tuple *value = expr->access.tvalue; + + struct qbe_value offset = {0}; + constl(&offset, value->offset); + pushi(ctx->current, out, Q_ADD, out, &offset, NULL); + out->type = qtype_for_type(ctx, value->type, false); + if (!type_is_aggregate(value->type)) { + qval_deref(out); + } +} + +static void address_object(struct gen_context *ctx, const struct expression *expr, struct qbe_value *out) @@ -421,6 +450,9 @@ address_object(struct gen_context *ctx, case ACCESS_FIELD: address_field(ctx, expr, out); break; + case ACCESS_TUPLE: + address_value(ctx, expr, out); + break; } } @@ -1278,6 +1310,7 @@ gen_expr_cast(struct gen_context *ctx, case TYPE_STORAGE_FUNCTION: case TYPE_STORAGE_STRING: case TYPE_STORAGE_STRUCT: + case TYPE_STORAGE_TUPLE: case TYPE_STORAGE_UNION: assert(0); // Invariant case TYPE_STORAGE_VOID: @@ -2140,6 +2173,33 @@ gen_expr_switch(struct gen_context *ctx, } static void +gen_expr_tuple(struct gen_context *ctx, + const struct expression *expr, + const struct qbe_value *out) +{ + // XXX: ARCH + struct qbe_value base = {0}, ptr = {0}, offset = {0}; + gen_temp(ctx, &base, &qbe_long, "base.%d"); + gen_temp(ctx, &ptr, &qbe_long, "ptr.%d"); + pushi(ctx->current, &base, Q_COPY, out, NULL); + + const struct type_tuple *type = &type_dealias(expr->result)->tuple; + const struct expression_tuple *tuple = &expr->tuple; + while (tuple) { + constl(&offset, type->offset); + ptr.type = &qbe_long; + pushi(ctx->current, &ptr, Q_ADD, &base, &offset, NULL); + + ptr.type = qtype_for_type(ctx, type->type, true); + ptr.indirect = !type_is_aggregate(type->type); + gen_expression(ctx, tuple->value, &ptr); + + tuple = tuple->next; + type = type->next; + } +} + +static void gen_expr_address(struct gen_context *ctx, const struct expression *expr, const struct qbe_value *out) @@ -2274,6 +2334,9 @@ gen_expression(struct gen_context *ctx, case EXPR_SWITCH: gen_expr_switch(ctx, expr, out); break; + case EXPR_TUPLE: + gen_expr_tuple(ctx, expr, out); + break; case EXPR_UNARITHM: gen_expr_unarithm(ctx, expr, out); break; @@ -2433,6 +2496,7 @@ gen_data_item(struct gen_context *ctx, struct expression *expr, break; case TYPE_STORAGE_ENUM: case TYPE_STORAGE_TAGGED: + case TYPE_STORAGE_TUPLE: case TYPE_STORAGE_UNION: assert(0); // TODO case TYPE_STORAGE_ALIAS: diff --git a/src/lex.c b/src/lex.c @@ -1056,6 +1056,7 @@ token_str(const struct token *tok) case TYPE_STORAGE_SLICE: case TYPE_STORAGE_STRUCT: case TYPE_STORAGE_TAGGED: + case TYPE_STORAGE_TUPLE: case TYPE_STORAGE_UNION: case TYPE_STORAGE_VOID: assert(0); diff --git a/src/parse.c b/src/parse.c @@ -525,15 +525,14 @@ parse_struct_union_type(struct lexer *lexer) } static struct ast_type * -parse_tagged_union_type(struct lexer *lexer) +parse_tagged_type(struct lexer *lexer, struct ast_type *first) { trenter(TR_PARSE, "tagged union"); - struct ast_type *type = mktype(&lexer->loc); + struct ast_type *type = mktype(&first->loc); type->storage = TYPE_STORAGE_TAGGED; struct ast_tagged_union_type *next = &type->tagged_union; - next->type = parse_type(lexer); + next->type = first; struct token tok = {0}; - want(lexer, T_BOR, &tok); while (tok.token != T_RPAREN) { next->next = xcalloc(sizeof(struct ast_tagged_union_type), 1); next = next->next; @@ -555,6 +554,51 @@ parse_tagged_union_type(struct lexer *lexer) } static struct ast_type * +parse_tuple_type(struct lexer *lexer, struct ast_type *first) +{ + trenter(TR_PARSE, "tuple"); + struct ast_type *type = mktype(&first->loc); + type->storage = TYPE_STORAGE_TUPLE; + struct ast_tuple_type *next = &type->tuple; + next->type = first; + struct token tok = {0}; + while (tok.token != T_RPAREN) { + next->next = xcalloc(sizeof(struct ast_tuple_type), 1); + next = next->next; + next->type = parse_type(lexer); + switch (lex(lexer, &tok)) { + case T_COMMA: + if (lex(lexer, &tok) != T_RPAREN) { + unlex(lexer, &tok); + } + break; + case T_RPAREN: + break; + default: + synassert(false, &tok, T_COMMA, T_RPAREN, T_EOF); + } + } + trleave(TR_PARSE, NULL); + return type; +} + +static struct ast_type * +parse_tagged_or_tuple_type(struct lexer *lexer) +{ + struct ast_type *type = parse_type(lexer); + struct token tok = {0}; + switch (lex(lexer, &tok)) { + case T_BOR: + return parse_tagged_type(lexer, type); + case T_COMMA: + return parse_tuple_type(lexer, type); + default: + synassert(false, &tok, T_BOR, T_COMMA, T_EOF); + } + assert(0); // Unreachable +} + +static struct ast_type * parse_type(struct lexer *lexer) { trenter(TR_PARSE, "type"); @@ -616,7 +660,7 @@ parse_type(struct lexer *lexer) type = parse_struct_union_type(lexer); break; case T_LPAREN: - type = parse_tagged_union_type(lexer); + type = parse_tagged_or_tuple_type(lexer); break; case T_LBRACKET: type = mktype(&lexer->loc); @@ -933,6 +977,44 @@ parse_struct_literal(struct lexer *lexer, struct identifier ident) } static struct ast_expression * +parse_tuple_expression(struct lexer *lexer, struct ast_expression *first) +{ + struct ast_expression *exp = mkexpr(&first->loc); + exp->type = EXPR_TUPLE; + + bool more = true; + struct token tok = {0}; + struct ast_expression_tuple *tuple = &exp->tuple; + tuple->expr = first; + tuple->next = xcalloc(1, sizeof(struct ast_expression_tuple)); + tuple = tuple->next; + + while (more) { + tuple->expr = parse_complex_expression(lexer); + + switch (lex(lexer, &tok)) { + case T_RPAREN: + more = false; + break; + case T_COMMA: + if (lex(lexer, &tok) == T_RPAREN) { + more = false; + } else { + unlex(lexer, &tok); + tuple->next = xcalloc(1, + sizeof(struct ast_expression_tuple)); + tuple = tuple->next; + } + break; + default: + synassert(false, &tok, T_RPAREN, T_COMMA, T_EOF); + } + } + + return exp; +} + +static struct ast_expression * parse_plain_expression(struct lexer *lexer) { trace(TR_PARSE, "plain"); @@ -971,8 +1053,15 @@ parse_plain_expression(struct lexer *lexer) // nested-expression case T_LPAREN: exp = parse_complex_expression(lexer); - want(lexer, T_RPAREN, &tok); - return exp; + switch (lex(lexer, &tok)) { + case T_RPAREN: + return exp; + case T_COMMA: + return parse_tuple_expression(lexer, exp); + default: + synassert(false, &tok, T_RPAREN, T_COMMA, T_EOF); + }; + assert(0); // Unreachable default: synassert(false, &tok, T_LITERAL, T_NAME, T_LBRACKET, T_STRUCT, T_LPAREN, T_EOF); @@ -1280,13 +1369,26 @@ parse_postfix_expression(struct lexer *lexer, struct ast_expression *lvalue) break; case T_DOT: trenter(TR_PARSE, "field-access"); - want(lexer, T_NAME, &tok); struct ast_expression *exp = mkexpr(&lexer->loc); exp->type = EXPR_ACCESS; - exp->access.type = ACCESS_FIELD; - exp->access._struct = lvalue; - exp->access.field = tok.name; + + switch (lex(lexer, &tok)) { + case T_NAME: + exp->access.type = ACCESS_FIELD; + exp->access._struct = lvalue; + exp->access.field = tok.name; + break; + case T_LITERAL: + exp->access.type = ACCESS_TUPLE; + exp->access.tuple = lvalue; + unlex(lexer, &tok); + exp->access.value = parse_constant(lexer); + break; + default: + synassert(false, &tok, T_NAME, T_LITERAL, T_EOF); + } + lvalue = exp; trleave(TR_PARSE, NULL); break; diff --git a/src/qtype.c b/src/qtype.c @@ -46,6 +46,7 @@ qstype_for_type(const struct type *type) case TYPE_STORAGE_STRING: case TYPE_STORAGE_STRUCT: case TYPE_STORAGE_TAGGED: + case TYPE_STORAGE_TUPLE: case TYPE_STORAGE_UNION: case TYPE_STORAGE_FUNCTION: assert(0); // Invariant @@ -85,6 +86,7 @@ qxtype_for_type(const struct type *type) case TYPE_STORAGE_STRING: case TYPE_STORAGE_STRUCT: case TYPE_STORAGE_TAGGED: + case TYPE_STORAGE_TUPLE: case TYPE_STORAGE_UNION: case TYPE_STORAGE_FUNCTION: return qstype_for_type(type); @@ -197,6 +199,19 @@ lookup_aggregate(struct gen_context *ctx, const struct type *type) field->count = 1; } break; + case TYPE_STORAGE_TUPLE: + def->type.align = type->align; + for (const struct type_tuple *tuple = &type->tuple; + tuple; tuple = tuple->next) { + field->type = qtype_for_type(ctx, tuple->type, true); + field->count = 1; + + if (tuple->next) { + field->next = xcalloc(1, sizeof(struct qbe_field)); + field = field->next; + } + } + break; case TYPE_STORAGE_ENUM: case TYPE_STORAGE_ARRAY: case TYPE_STORAGE_ALIAS: @@ -265,6 +280,7 @@ qtype_for_type(struct gen_context *ctx, const struct type *type, bool extended) case TYPE_STORAGE_STRING: case TYPE_STORAGE_STRUCT: case TYPE_STORAGE_TAGGED: + case TYPE_STORAGE_TUPLE: case TYPE_STORAGE_UNION: return lookup_aggregate(ctx, type); case TYPE_STORAGE_FUNCTION: @@ -308,6 +324,7 @@ type_is_aggregate(const struct type *type) case TYPE_STORAGE_STRING: case TYPE_STORAGE_STRUCT: case TYPE_STORAGE_TAGGED: + case TYPE_STORAGE_TUPLE: case TYPE_STORAGE_UNION: case TYPE_STORAGE_FUNCTION: return true; diff --git a/src/type_store.c b/src/type_store.c @@ -77,6 +77,7 @@ builtin_type_for_storage(enum type_storage storage, bool is_const) case TYPE_STORAGE_SLICE: case TYPE_STORAGE_STRUCT: case TYPE_STORAGE_TAGGED: + case TYPE_STORAGE_TUPLE: case TYPE_STORAGE_UNION: case TYPE_STORAGE_ENUM: return NULL; @@ -328,6 +329,29 @@ tagged_init_from_atype(struct type_store *store, tagged_init(type, tu, nmemb); } +static void +tuple_init_from_atype(struct type_store *store, + struct type *type, const struct ast_type *atype) +{ + type->size = 0, type->align = 0; + const struct ast_tuple_type *atuple = &atype->tuple; + struct type_tuple *cur = &type->tuple; + while (atuple) { + cur->type = type_store_lookup_atype(store, atuple->type); + cur->offset = type->size % cur->type->align + type->size; + type->size += type->size % cur->type->align + cur->type->size; + if (type->align < cur->type->align) { + type->align = cur->type->align; + } + + atuple = atuple->next; + if (atuple) { + cur->next = xcalloc(1, sizeof(struct type_tuple)); + cur = cur->next; + } + } +} + static const struct type *type_store_lookup_type(struct type_store *store, const struct type *type); static void @@ -493,6 +517,9 @@ type_init_from_atype(struct type_store *store, case TYPE_STORAGE_TAGGED: tagged_init_from_atype(store, type, atype); break; + case TYPE_STORAGE_TUPLE: + tuple_init_from_atype(store, type, atype); + break; } } @@ -632,3 +659,19 @@ type_store_lookup_tagged(struct type_store *store, tagged_init(&type, tu, nmemb); return type_store_lookup_type(store, &type); } + +const struct type * +type_store_lookup_tuple(struct type_store *store, struct type_tuple *values) +{ + struct type type = { + .storage = TYPE_STORAGE_TUPLE, + .tuple = *values, + }; + for (struct type_tuple *t = values; t; t = t->next) { + if (t->type->align > type.align) { + type.align = t->type->align; + } + type.size = (type.size % t->type->align) + t->type->size; + } + return type_store_lookup_type(store, &type); +} diff --git a/src/typedef.c b/src/typedef.c @@ -90,6 +90,7 @@ emit_const(const struct expression *expr, FILE *out) case TYPE_STORAGE_SLICE: case TYPE_STORAGE_STRING: case TYPE_STORAGE_STRUCT: + case TYPE_STORAGE_TUPLE: case TYPE_STORAGE_UNION: assert(0); // TODO case TYPE_STORAGE_CHAR: @@ -249,6 +250,17 @@ emit_type(const struct type *type, FILE *out) } fprintf(out, "}"); break; + case TYPE_STORAGE_TUPLE: + fprintf(out, "("); + for (const struct type_tuple *tuple = &type->tuple; + tuple; tuple = tuple->next) { + emit_type(tuple->type, out); + if (tuple->next) { + fprintf(out, ", "); + } + } + fprintf(out, ")"); + break; } } diff --git a/src/types.c b/src/types.c @@ -47,6 +47,21 @@ type_get_field(const struct type *type, const char *name) return NULL; } +const struct type_tuple * +type_get_value(const struct type *type, uintmax_t index) +{ + assert(type->storage == TYPE_STORAGE_TUPLE); + const struct type_tuple *tuple = &type->tuple; + while (tuple) { + if (index == 0) { + return tuple; + } + tuple = tuple->next; + --index; + } + return NULL; +} + const char * type_storage_unparse(enum type_storage storage) { @@ -93,6 +108,8 @@ type_storage_unparse(enum type_storage storage) return "struct"; case TYPE_STORAGE_TAGGED: return "tagged union"; + case TYPE_STORAGE_TUPLE: + return "tuple"; case TYPE_STORAGE_U16: return "u16"; case TYPE_STORAGE_U32: @@ -125,6 +142,7 @@ type_is_integer(const struct type *type) case TYPE_STORAGE_STRING: case TYPE_STORAGE_STRUCT: case TYPE_STORAGE_TAGGED: + case TYPE_STORAGE_TUPLE: case TYPE_STORAGE_UNION: case TYPE_STORAGE_BOOL: case TYPE_STORAGE_NULL: @@ -165,6 +183,7 @@ type_is_numeric(const struct type *type) case TYPE_STORAGE_STRING: case TYPE_STORAGE_STRUCT: case TYPE_STORAGE_TAGGED: + case TYPE_STORAGE_TUPLE: case TYPE_STORAGE_UNION: case TYPE_STORAGE_BOOL: case TYPE_STORAGE_CHAR: @@ -213,6 +232,7 @@ type_storage_is_signed(enum type_storage storage) case TYPE_STORAGE_STRING: case TYPE_STORAGE_STRUCT: case TYPE_STORAGE_TAGGED: + case TYPE_STORAGE_TUPLE: case TYPE_STORAGE_UNION: case TYPE_STORAGE_BOOL: case TYPE_STORAGE_CHAR: @@ -328,6 +348,11 @@ type_hash(const struct type *type) hash = fnv1a_u32(hash, type_hash(tu->type)); } break; + case TYPE_STORAGE_TUPLE: + for (const struct type_tuple *tuple = &type->tuple; + tuple; tuple = tuple->next) { + hash = fnv1a_u32(hash, type_hash(tuple->type)); + } } return hash; } @@ -504,6 +529,7 @@ type_is_assignable(const struct type *to, const struct type *from) case TYPE_STORAGE_NULL: case TYPE_STORAGE_RUNE: case TYPE_STORAGE_STRUCT: + case TYPE_STORAGE_TUPLE: case TYPE_STORAGE_UNION: return false; } @@ -593,6 +619,7 @@ type_is_castable(const struct type *to, const struct type *from) case TYPE_STORAGE_BOOL: case TYPE_STORAGE_VOID: case TYPE_STORAGE_FUNCTION: + case TYPE_STORAGE_TUPLE: case TYPE_STORAGE_STRUCT: case TYPE_STORAGE_UNION: case TYPE_STORAGE_STRING: diff --git a/tests/21-tuples.ha b/tests/21-tuples.ha @@ -0,0 +1,16 @@ +fn storage() void = { + let x: (int, size) = (42, 1337z); + assert(size((int, size)) == size(size) * 2z); + let ptr = &x: *struct { i: int, z: size }; + assert(ptr.i == 42 && ptr.z == 1337z); +}; + +fn indexing() void = { + let x: (int, size) = (42, 1337z); + assert(x.0 == 42 && x.1 == 1337z); +}; + +export fn main() void = { + storage(); + indexing(); +}; diff --git a/tests/configure b/tests/configure @@ -23,7 +23,8 @@ tests() { 17-alloc \ 18-match \ 19-append \ - 20-if + 20-if \ + 21-tuples do cat <<EOF tests/$t: libhart.a tests/$t.ha