commit 5f52622a1a740a98de74cb540503a5171ae1fd03
parent 345e507e27c555a52925e6e3a9bc99ff8c5ae1b3
Author: Drew DeVault <sir@cmpwn.com>
Date: Wed, 10 Feb 2021 12:36:04 -0500
Implement tuples
Diffstat:
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