@@ 0,0 1,862 @@
+#include <assert.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "ast.h"
+#include "expr.h"
+#include "identifier.h"
+#include "scope.h"
+#include "template.h"
+#include "typedef.h"
+#include "types.h"
+#include "util.h"
+
+void
+emit_ast_template_args(const struct ast_template_argument *args,
+ const struct template *tmpl,
+ struct scope *scope,
+ FILE *out,
+ templates tmpls)
+{
+ xfprintf(out, "<");
+ for (size_t i = 0; args && args->expr; args = args->next) {
+ if ((tmpl->type_params[i / 64] >> (i % 64) & 1) == 1) {
+ emit_ast_type(args->type, scope, out, tmpls);
+ } else {
+ emit_ast_expr(args->expr, scope, out, tmpls);
+ }
+ if (args->variadic) {
+ xfprintf(out, "...");
+ }
+ if (args->next) {
+ xfprintf(out, ", ");
+ }
+ if (i < tmpl->nparams) {
+ i++;
+ }
+ }
+ xfprintf(out, ">");
+}
+
+void
+emit_ast_template_params(const struct ast_template_parameters *params,
+ struct scope *scope,
+ FILE *out,
+ templates tmpls)
+{
+ xfprintf(out, "<");
+ for (const struct ast_template_parameters *param = params;
+ param; param = param->next) {
+ xfprintf(out, "%s: ", param->name);
+ if (param->type) {
+ emit_ast_type(param->type, scope, out, tmpls);
+ if (param->init) {
+ xfprintf(out, " = ");
+ emit_ast_expr(param->init, scope, out, tmpls);
+ }
+ } else {
+ xfprintf(out, "class");
+ if (param->default_type) {
+ xfprintf(out, " = ");
+ emit_ast_type(param->default_type,
+ scope, out, tmpls);
+ }
+ }
+ if (param->is_pack) {
+ xfprintf(out, "...");
+ }
+ if (param->next) {
+ xfprintf(out, ", ");
+ }
+ }
+ xfprintf(out, ">");
+}
+
+static void
+emit_ast_identifier(const struct identifier *ident,
+ const struct ast_template_argument *tmpl_args,
+ struct scope *scope,
+ FILE *out,
+ templates tmpls)
+{
+ struct identifier adjusted = *ident;
+ adjust_reserved_ident(&adjusted);
+ if (scope) {
+ const struct scope_object *obj = scope_lookup(scope, &adjusted);
+ if (obj) {
+ adjusted = obj->ident; // get fully qualified name
+ adjust_reserved_ident(&adjusted);
+ }
+ }
+ char *identstr = identifier_unparse(&adjusted);
+ xfprintf(out, "%s", identstr);
+ free(identstr);
+ if (tmpl_args) {
+ const struct template *tmpl = get_template(ident, tmpls);
+ assert(tmpl);
+ emit_ast_template_args(tmpl_args, tmpl, scope, out, tmpls);
+ }
+}
+
+static void
+emit_ast_struct(const struct ast_type *type,
+ struct scope *scope,
+ FILE *out,
+ templates tmpls)
+{
+ xfprintf(out, "%s %s{ ",
+ type->storage == STORAGE_STRUCT ? "struct" : "union",
+ type->struct_union.packed ? "@packed " : "");
+ for (const struct ast_struct_union_field *f = &type->struct_union.fields;
+ f; f = f->next) {
+ if (f->offset) {
+ xfprintf(out, "@offset(");
+ emit_ast_expr(f->offset, scope, out, tmpls);
+ xfprintf(out, ") ");
+ }
+ if (f->name) {
+ xfprintf(out, "%s: ", f->name);
+ }
+ emit_ast_type(f->type, scope, out, tmpls);
+ xfprintf(out, ", ");
+ }
+ xfprintf(out, "}");
+}
+
+void
+emit_ast_prototype(const struct ast_function_type *func,
+ struct scope *scope,
+ FILE *out,
+ templates tmpls)
+{
+ xfprintf(out, "(");
+ for (const struct ast_function_parameters *param = func->params;
+ param; param = param->next) {
+ if (param->name) {
+ xfprintf(out, "%s: ", param->name);
+ assert(param->type);
+ }
+ if (param->type) {
+ emit_ast_type(param->type, scope, out, tmpls);
+ }
+ if (param->variadic) {
+ xfprintf(out, "...");
+ }
+ if (param->next) {
+ xfprintf(out, ", ");
+ }
+ }
+ xfprintf(out, ") ");
+ emit_ast_type(func->result, scope, out, tmpls);
+}
+
+void
+emit_ast_type(const struct ast_type *type,
+ struct scope *scope,
+ FILE *out,
+ templates tmpls)
+{
+ if (type->flags & TYPE_CONST) {
+ xfprintf(out, "const ");
+ }
+ if (type->flags & TYPE_ERROR) {
+ xfprintf(out, "!");
+ }
+
+ switch (type->storage) {
+ case STORAGE_BOOL:
+ case STORAGE_F32:
+ case STORAGE_F64:
+ case STORAGE_I16:
+ case STORAGE_I32:
+ case STORAGE_I64:
+ case STORAGE_I8:
+ case STORAGE_INT:
+ case STORAGE_NEVER:
+ case STORAGE_NULL:
+ case STORAGE_OPAQUE:
+ case STORAGE_RUNE:
+ case STORAGE_SIZE:
+ case STORAGE_STRING:
+ case STORAGE_U16:
+ case STORAGE_U32:
+ case STORAGE_U64:
+ case STORAGE_U8:
+ case STORAGE_UINT:
+ case STORAGE_UINTPTR:
+ case STORAGE_VALIST:
+ case STORAGE_VOID:
+ xfprintf(out, "%s", type_storage_unparse(type->storage));
+ break;
+ case STORAGE_ALIAS:
+ if (type->unwrap) {
+ xfprintf(out, "...");
+ }
+ emit_ast_identifier(&type->alias, type->tmpl_args,
+ scope, out, tmpls);
+ break;
+ case STORAGE_ARRAY:
+ if (type->array.length) {
+ xfprintf(out, "[");
+ emit_ast_expr(type->array.length, scope, out, tmpls);
+ xfprintf(out, "]");
+ } else if (type->array.contextual) {
+ xfprintf(out, "[_]");
+ } else {
+ xfprintf(out, "[*]");
+ }
+ emit_ast_type(type->array.members, scope, out, tmpls);
+ break;
+ case STORAGE_FUNCTION:
+ xfprintf(out, "fn");
+ emit_ast_prototype(&type->func, scope, out, tmpls);
+ break;
+ case STORAGE_POINTER:
+ if (type->pointer.flags & PTR_NULLABLE) {
+ xfprintf(out, "nullable ");
+ }
+ xfprintf(out, "*");
+ emit_ast_type(type->pointer.referent, scope, out, tmpls);
+ break;
+ case STORAGE_SLICE:
+ xfprintf(out, "[]");
+ emit_ast_type(type->slice.members, scope, out, tmpls);
+ break;
+ case STORAGE_STRUCT:
+ case STORAGE_UNION:
+ emit_ast_struct(type, scope, out, tmpls);
+ break;
+ case STORAGE_TAGGED:
+ xfprintf(out, "(");
+ for (const struct ast_tagged_union_type *tu = &type->tagged;
+ tu; tu = tu->next) {
+ emit_ast_type(tu->type, scope, out, tmpls);
+ if (tu->variadic) {
+ xfprintf(out, "...");
+ }
+ if (tu->next || (tu->variadic && tu == &type->tagged)) {
+ xfprintf(out, " | ");
+ }
+ }
+ xfprintf(out, ")");
+ break;
+ case STORAGE_TUPLE:
+ xfprintf(out, "(");
+ for (const struct ast_tagged_union_type *tuple = &type->tagged;
+ tuple; tuple = tuple->next) {
+ emit_ast_type(tuple->type, scope, out, tmpls);
+ if (tuple->variadic) {
+ xfprintf(out, "...");
+ }
+ if (tuple->next) {
+ xfprintf(out, ", ");
+ }
+ }
+ xfprintf(out, ")");
+ break;
+ case STORAGE_ENUM:
+ case STORAGE_ERROR:
+ case STORAGE_FCONST:
+ case STORAGE_ICONST:
+ case STORAGE_RCONST:
+ assert(0); // unreachable
+ }
+}
+
+static const char *
+binop_unparse(enum binarithm_operator op)
+{
+ switch (op) {
+ case BIN_BAND:
+ return "&";
+ case BIN_BOR:
+ return "|";
+ case BIN_BXOR:
+ return "^";
+ case BIN_DIV:
+ return "/";
+ case BIN_GREATER:
+ return ">";
+ case BIN_GREATEREQ:
+ return ">=";
+ case BIN_LAND:
+ return "&&";
+ case BIN_LEQUAL:
+ return "==";
+ case BIN_LESS:
+ return "<";
+ case BIN_LESSEQ:
+ return "<=";
+ case BIN_LOR:
+ return "||";
+ case BIN_LSHIFT:
+ return "<<";
+ case BIN_LXOR:
+ return "^^";
+ case BIN_MINUS:
+ return "-";
+ case BIN_MODULO:
+ return "%";
+ case BIN_NEQUAL:
+ return "!=";
+ case BIN_PLUS:
+ return "+";
+ case BIN_RSHIFT:
+ return ">>";
+ case BIN_TIMES:
+ return "*";
+ }
+
+ assert(0); // unreachable
+}
+
+static const char *
+unop_unparse(enum unarithm_operator op)
+{
+ switch (op) {
+ case UN_ADDRESS:
+ return "&";
+ case UN_BNOT:
+ return "~";
+ case UN_DEREF:
+ return "*";
+ case UN_LNOT:
+ return "!";
+ case UN_MINUS:
+ return "-";
+ }
+
+ assert(0); // unreachable
+}
+
+static void
+emit_ast_binding(const struct ast_expression_binding *binding,
+ struct scope *scope,
+ FILE *out,
+ templates tmpls)
+{
+ if (binding->unpack) {
+ xfprintf(out, "(");
+ for (const struct ast_binding_unpack *u = binding->unpack;
+ u; u = u->next) {
+ xfprintf(out, "%s", u->name);
+ if (u->next) {
+ xfprintf(out, ", ");
+ }
+ }
+ xfprintf(out, ")");
+ } else {
+ xfprintf(out, "%s", binding->name);
+ }
+ if (binding->type) {
+ xfprintf(out, ": ");
+ emit_ast_type(binding->type, scope, out, tmpls);
+ }
+ xfprintf(out, " = ");
+ emit_ast_expr(binding->initializer, scope, out, tmpls);
+}
+
+static void
+emit_ast_expr_list(const struct ast_expression_list *exprs,
+ struct scope *scope,
+ FILE *out,
+ templates tmpls)
+{
+ while (exprs) {
+ emit_ast_expr(exprs->expr, scope, out, tmpls);
+ xfprintf(out, "; ");
+ exprs = exprs->next;
+ }
+}
+
+void
+emit_ast_expr(const struct ast_expression *expr,
+ struct scope *scope,
+ FILE *out,
+ templates tmpls)
+{
+ switch (expr->type) {
+ case EXPR_ACCESS:
+ switch (expr->access.type) {
+ case ACCESS_IDENTIFIER:
+ emit_ast_identifier(&expr->access.ident,
+ expr->access.tmpl_args, scope, out, tmpls);
+ break;
+ case ACCESS_INDEX:
+ emit_ast_expr(expr->access.array, scope, out, tmpls);
+ xfprintf(out, "[");
+ emit_ast_expr(expr->access.index, scope, out, tmpls);
+ xfprintf(out, "]");
+ break;
+ case ACCESS_FIELD:
+ emit_ast_expr(expr->access._struct, scope, out, tmpls);
+ xfprintf(out, ".%s", expr->access.field);
+ break;
+ case ACCESS_TUPLE:
+ emit_ast_expr(expr->access.tuple, scope, out, tmpls);
+ xfprintf(out, ".");
+ emit_ast_expr(expr->access.value, scope, out, tmpls);
+ break;
+ }
+ break;
+ case EXPR_ALLOC:
+ xfprintf(out, "alloc(");
+ emit_ast_expr(expr->alloc.init, scope, out, tmpls);
+ switch (expr->alloc.kind) {
+ case ALLOC_OBJECT:
+ xfprintf(out, ")");
+ break;
+ case ALLOC_WITH_CAP:
+ case ALLOC_WITH_LEN:
+ xfprintf(out, ", ");
+ emit_ast_expr(expr->alloc.cap, scope, out, tmpls);
+ xfprintf(out, ")");
+ break;
+ case ALLOC_COPY:
+ xfprintf(out, "...)");
+ break;
+ }
+ break;
+ case EXPR_APPEND:
+ case EXPR_INSERT:
+ xfprintf(out, "%s%s(", expr->append.is_static ? "static " : "",
+ expr->type == EXPR_APPEND ? "append" : "insert");
+ emit_ast_expr(expr->append.object, scope, out, tmpls);
+ xfprintf(out, ", ");
+ emit_ast_expr(expr->append.value, scope, out, tmpls);
+ if (expr->append.is_multi) {
+ xfprintf(out, "...");
+ }
+ if (expr->append.length) {
+ xfprintf(out, ", ");
+ emit_ast_expr(expr->append.length, scope, out, tmpls);
+ }
+ xfprintf(out, ")");
+ break;
+ case EXPR_ASSERT:
+ if (expr->assert.is_static) {
+ xfprintf(out, "static ");
+ }
+ if (expr->assert.cond) {
+ xfprintf(out, "assert(");
+ emit_ast_expr(expr->assert.cond, scope, out, tmpls);
+ if (expr->assert.message) {
+ xfprintf(out, ",");
+ }
+ } else {
+ xfprintf(out, "abort(");
+ }
+ if (expr->assert.message) {
+ emit_ast_expr(expr->assert.message, scope, out, tmpls);
+ }
+ xfprintf(out, ")");
+ break;
+ case EXPR_ASSIGN:
+ xfprintf(out, "(");
+ emit_ast_expr(expr->assign.object, scope, out, tmpls);
+ xfprintf(out, " ");
+ if (expr->assign.op != BIN_LEQUAL) {
+ xfprintf(out, "%s", binop_unparse(expr->assign.op));
+ }
+ xfprintf(out, "= ");
+ emit_ast_expr(expr->assign.value, scope, out, tmpls);
+ xfprintf(out, ")");
+ break;
+ case EXPR_BINARITHM:
+ xfprintf(out, "(");
+ if (expr->binarithm.lvalue) {
+ xfprintf(out, "(");
+ emit_ast_expr(expr->binarithm.lvalue,
+ scope, out, tmpls);
+ xfprintf(out, ")");
+ if (expr->binarithm.rvalue && expr->binarithm.fold) {
+ xfprintf(out, " %s ...",
+ binop_unparse(expr->binarithm.op));
+ }
+ } else {
+ xfprintf(out, "...");
+ }
+ xfprintf(out, " %s ", binop_unparse(expr->binarithm.op));
+ if (expr->binarithm.rvalue) {
+ xfprintf(out, "(");
+ emit_ast_expr(expr->binarithm.rvalue,
+ scope, out, tmpls);
+ xfprintf(out, ")");
+ } else {
+ xfprintf(out, "...");
+ }
+ xfprintf(out, ")");
+ break;
+ case EXPR_BINDING:
+ case EXPR_DEFINE:
+ xfprintf(out, "%s%s ",
+ expr->binding.is_static ? "static " : "",
+ expr->type == EXPR_BINDING ? "let" : "def");
+ for (const struct ast_expression_binding *binding = &expr->binding;
+ binding; binding = binding->next) {
+ emit_ast_binding(binding, scope, out, tmpls);
+ if (binding->next) {
+ xfprintf(out, ", ");
+ }
+ }
+ break;
+ case EXPR_BREAK:
+ case EXPR_CONTINUE:
+ xfprintf(out, expr->type == EXPR_BREAK ? "break" : "continue");
+ if (expr->control.label) {
+ xfprintf(out, " :%s", expr->control.label);
+ }
+ break;
+ case EXPR_CALL:
+ emit_ast_expr(expr->call.lvalue, scope, out, tmpls);
+ xfprintf(out, "(");
+ for (const struct ast_call_argument *arg = expr->call.args;
+ arg; arg = arg->next) {
+ emit_ast_expr(arg->value, scope, out, tmpls);
+ if (arg->variadic) {
+ xfprintf(out, "...");
+ }
+ if (arg->next) {
+ xfprintf(out, ", ");
+ }
+ }
+ xfprintf(out, ")");
+ break;
+ case EXPR_CAST:
+ xfprintf(out, "(");
+ emit_ast_expr(expr->cast.value, scope, out, tmpls);
+ switch (expr->cast.kind) {
+ case C_CAST:
+ xfprintf(out, ": ");
+ break;
+ case C_ASSERTION:
+ xfprintf(out, "as ");
+ break;
+ case C_TEST:
+ xfprintf(out, "is ");
+ break;
+ }
+ emit_ast_type(expr->cast.type, scope, out, tmpls);
+ xfprintf(out, ")");
+ break;
+ case EXPR_COMPOUND:
+ if (expr->compound.label) {
+ xfprintf(out, ":%s ", expr->compound.label);
+ }
+ xfprintf(out, "{ ");
+ emit_ast_expr_list(&expr->compound.list, scope, out, tmpls);
+ xfprintf(out, "}");
+ break;
+ case EXPR_DEFER:
+ xfprintf(out, "defer ");
+ emit_ast_expr(expr->defer.deferred, scope, out, tmpls);
+ break;
+ case EXPR_DELETE:
+ xfprintf(out, "%sdelete(",
+ expr->delete.is_static ? "static " : "");
+ emit_ast_expr(expr->delete.expr, scope, out, tmpls);
+ xfprintf(out, ")");
+ break;
+ case EXPR_FOR:
+ xfprintf(out, "for ");
+ if (expr->_for.label) {
+ xfprintf(out, ":%s ", expr->_for.label);
+ }
+ xfprintf(out, "(");
+ if (expr->_for.bindings) {
+ emit_ast_expr(expr->_for.bindings, scope, out, tmpls);
+ xfprintf(out, "; ");
+ }
+ emit_ast_expr(expr->_for.cond, scope, out, tmpls);
+ if (expr->_for.afterthought) {
+ xfprintf(out, "; ");
+ emit_ast_expr(expr->_for.afterthought,
+ scope, out, tmpls);
+ }
+ xfprintf(out, ") ");
+ emit_ast_expr(expr->_for.body, scope, out, tmpls);
+ break;
+ case EXPR_FREE:
+ xfprintf(out, "free(");
+ emit_ast_expr(expr->free.expr, scope, out, tmpls);
+ xfprintf(out, ")");
+ break;
+ case EXPR_IF:
+ xfprintf(out, "if (");
+ emit_ast_expr(expr->_if.cond, scope, out, tmpls);
+ xfprintf(out, ") ");
+ emit_ast_expr(expr->_if.true_branch, scope, out, tmpls);
+ if (expr->_if.false_branch) {
+ xfprintf(out, " else ");
+ emit_ast_expr(expr->_if.false_branch,
+ scope, out, tmpls);
+ }
+ break;
+ case EXPR_MEASURE:
+ switch (expr->measure.op) {
+ case M_ALIGN:
+ xfprintf(out, "align(");
+ emit_ast_type(expr->measure.type, scope, out, tmpls);
+ break;
+ case M_LEN:
+ xfprintf(out, "len(");
+ emit_ast_expr(expr->measure.value, scope, out, tmpls);
+ break;
+ case M_SIZE:
+ xfprintf(out, "size(");
+ emit_ast_type(expr->measure.type, scope, out, tmpls);
+ break;
+ case M_SIZEPACK:
+ xfprintf(out, "size...(");
+ emit_ast_identifier(&expr->measure.ident,
+ NULL, scope, out, tmpls);
+ break;
+ case M_OFFSET:
+ xfprintf(out, "offset(");
+ emit_ast_expr(expr->measure.value, scope, out, tmpls);
+ break;
+ }
+ xfprintf(out, ")");
+ break;
+ case EXPR_LITERAL:
+ switch (expr->literal.storage) {
+ case STORAGE_BOOL:
+ xfprintf(out, "%s",
+ expr->literal.bval ? "true" : "false");
+ break;
+ case STORAGE_F32:
+ case STORAGE_F64:
+ case STORAGE_FCONST:
+ xfprintf(out, "%a%s", expr->literal.fval,
+ storage_to_suffix(expr->literal.storage));
+ break;
+ case STORAGE_I16:
+ case STORAGE_I32:
+ case STORAGE_I64:
+ case STORAGE_I8:
+ case STORAGE_ICONST:
+ case STORAGE_INT:
+ xfprintf(out, "%" PRIi64 "%s", expr->literal.ival,
+ storage_to_suffix(expr->literal.storage));
+ break;
+ case STORAGE_NULL:
+ xfprintf(out, "null");
+ break;
+ case STORAGE_SIZE:
+ case STORAGE_U16:
+ case STORAGE_U32:
+ case STORAGE_U64:
+ case STORAGE_U8:
+ case STORAGE_UINT:
+ xfprintf(out, "%" PRIu64 "%s", expr->literal.uval,
+ storage_to_suffix(expr->literal.storage));
+ break;
+ case STORAGE_VOID:
+ xfprintf(out, "void");
+ break;
+ case STORAGE_RCONST:
+ case STORAGE_RUNE:
+ xfprintf(out, "\'\\U%08" PRIx32 "\'",
+ expr->literal.rune);
+ break;
+ case STORAGE_STRING:
+ xfprintf(out, "\"");
+ for (size_t i = 0; i < expr->literal.string.len; i++) {
+ char c = expr->literal.string.value[i];
+ if (isalnum((unsigned char)c)) {
+ xfprintf(out, "%c", c);
+ } else {
+ xfprintf(out, "\\x%02X", c);
+ }
+ }
+ xfprintf(out, "\"");
+ break;
+ case STORAGE_ARRAY:
+ xfprintf(out, "[");
+ for (const struct ast_array_literal *item = expr->literal.array;
+ item; item = item->next) {
+ emit_ast_expr(item->value, scope, out, tmpls);
+ if (item->expand) {
+ xfprintf(out, "...");
+ }
+ if (item->next) {
+ xfprintf(out, ", ");
+ }
+ }
+ xfprintf(out, "]");
+ break;
+ case STORAGE_ALIAS:
+ case STORAGE_ENUM:
+ case STORAGE_ERROR:
+ case STORAGE_FUNCTION:
+ case STORAGE_NEVER:
+ case STORAGE_OPAQUE:
+ case STORAGE_POINTER:
+ case STORAGE_SLICE:
+ case STORAGE_STRUCT:
+ case STORAGE_TAGGED:
+ case STORAGE_TUPLE:
+ case STORAGE_UINTPTR:
+ case STORAGE_UNION:
+ case STORAGE_VALIST:
+ assert(0); // unreachable
+ }
+ break;
+ case EXPR_MATCH:
+ xfprintf(out, "match ");
+ if (expr->match.label) {
+ xfprintf(out, ":%s ", expr->match.label);
+ }
+ xfprintf(out, "(");
+ emit_ast_expr(expr->match.value, scope, out, tmpls);
+ xfprintf(out, ") { ");
+ for (const struct ast_match_case *c = expr->match.cases;
+ c; c = c->next) {
+ xfprintf(out, "case");
+ if (c->name) {
+ xfprintf(out, " let %s", c->name);
+ if (c->type) {
+ xfprintf(out, ":");
+ }
+ }
+ if (c->type) {
+ xfprintf(out, " ");
+ emit_ast_type(c->type, scope, out, tmpls);
+ }
+ xfprintf(out, " => ");
+ emit_ast_expr_list(&c->exprs, scope, out, tmpls);
+ }
+ xfprintf(out, "}");
+ break;
+ case EXPR_PROPAGATE:
+ xfprintf(out, "(");
+ emit_ast_expr(expr->propagate.value, scope, out, tmpls);
+ xfprintf(out, ")%c", expr->propagate.abort ? '!' : '?');
+ break;
+ case EXPR_RETURN:
+ xfprintf(out, "return");
+ if (expr->_return.value) {
+ xfprintf(out, " ");
+ emit_ast_expr(expr->_return.value, scope, out, tmpls);
+ }
+ break;
+ case EXPR_SLICE:
+ emit_ast_expr(expr->slice.object, scope, out, tmpls);
+ xfprintf(out, "[");
+ if (expr->slice.start) {
+ emit_ast_expr(expr->slice.start, scope, out, tmpls);
+ }
+ xfprintf(out, "..");
+ if (expr->slice.end) {
+ emit_ast_expr(expr->slice.end, scope, out, tmpls);
+ }
+ xfprintf(out, "]");
+ break;
+ case EXPR_STRUCT:
+ if (expr->_struct.type.name) {
+ emit_ast_identifier(&expr->_struct.type,
+ expr->_struct.tmpl_args, scope, out, tmpls);
+ xfprintf(out, " { ");
+ } else {
+ xfprintf(out, "struct { ");
+ }
+ for (const struct ast_field_value *f = expr->_struct.fields;
+ f; f = f->next) {
+ if (f->name) {
+ xfprintf(out, "%s", f->name);
+ if (f->type) {
+ xfprintf(out, ": ");
+ }
+ }
+ if (f->type) {
+ emit_ast_type(f->type, scope, out, tmpls);
+ }
+ if (f->initializer) {
+ xfprintf(out, " = ");
+ emit_ast_expr(f->initializer,
+ scope, out, tmpls);
+ }
+ xfprintf(out, ", ");
+ }
+ if (expr->_struct.autofill) {
+ xfprintf(out, "... ");
+ }
+ xfprintf(out, "}");
+ break;
+ case EXPR_SWITCH:
+ xfprintf(out, "switch ");
+ if (expr->_switch.label) {
+ xfprintf(out, ":%s ", expr->_switch.label);
+ }
+ xfprintf(out, "(");
+ emit_ast_expr(expr->_switch.value, scope, out, tmpls);
+ xfprintf(out, ") { ");
+ for (const struct ast_switch_case *c = expr->_switch.cases;
+ c; c = c->next) {
+ xfprintf(out, "case");
+ for (const struct ast_case_option *opt = c->options;
+ opt; opt = opt->next) {
+ xfprintf(out, " ");
+ emit_ast_expr(opt->value, scope, out, tmpls);
+ if (opt->variadic) {
+ xfprintf(out, "...");
+ }
+ if (opt->next) {
+ xfprintf(out, ",");
+ }
+ }
+ xfprintf(out, " => ");
+ emit_ast_expr_list(&c->exprs, scope, out, tmpls);
+ }
+ xfprintf(out, "}");
+ break;
+ case EXPR_TUPLE:
+ xfprintf(out, "(");
+ for (const struct ast_expression_tuple *tuple = &expr->tuple;
+ tuple; tuple = tuple->next) {
+ emit_ast_expr(tuple->expr, scope, out, tmpls);
+ if (tuple->variadic) {
+ xfprintf(out, "...");
+ }
+ if (tuple->next) {
+ xfprintf(out, ", ");
+ }
+ }
+ xfprintf(out, ")");
+ break;
+ case EXPR_UNARITHM:
+ xfprintf(out, "(");
+ xfprintf(out, "%s", unop_unparse(expr->unarithm.op));
+ emit_ast_expr(expr->unarithm.operand, scope, out, tmpls);
+ xfprintf(out, ")");
+ break;
+ case EXPR_VAARG:
+ xfprintf(out, "vaarg(");
+ emit_ast_expr(expr->vaarg.ap, scope, out, tmpls);
+ xfprintf(out, ")");
+ break;
+ case EXPR_VAEND:
+ xfprintf(out, "vaarg(");
+ emit_ast_expr(expr->vaarg.ap, scope, out, tmpls);
+ xfprintf(out, ")");
+ break;
+ case EXPR_VASTART:
+ xfprintf(out, "vastart()");
+ break;
+ case EXPR_YIELD:
+ xfprintf(out, "yield");
+ if (expr->control.label) {
+ xfprintf(out, " :%s", expr->control.label);
+ if (expr->control.value) {
+ xfprintf(out, ",");
+ }
+ }
+ if (expr->control.value) {
+ xfprintf(out, " ");
+ emit_ast_expr(expr->control.value, scope, out, tmpls);
+ }
+ break;
+ }
+}
@@ 1,6 1,7 @@
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
+#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ 74,10 75,16 @@ handle_errors(struct errors *errors)
{
struct errors *error = errors;
while (error) {
- xfprintf(stderr, "%s:%d:%d: error: %s\n", sources[error->loc.file],
- error->loc.lineno, error->loc.colno, error->msg);
- errline(error->loc);
- free(error->msg);
+ xfprintf(stderr, "%s:%d:%d: ", sources[error->loc.file],
+ error->loc.lineno, error->loc.colno);
+ if (error->msg) {
+ xfprintf(stderr, "error: %s\n", error->msg);
+ errline(error->loc, 31);
+ free(error->msg);
+ } else {
+ xfprintf(stderr, "note: specialized from here\n");
+ errline(error->loc, 33);
+ }
struct errors *next = error->next;
free(error);
error = next;
@@ 112,6 119,14 @@ verror(struct context *ctx, const struct location loc,
next->loc = loc;
next->msg = msg;
ctx->next = &next->next;
+
+ for (const struct error_context *errctx = ctx->errctx;
+ errctx; errctx = errctx->next) {
+ struct errors *next = *ctx->next =
+ xcalloc(1, sizeof(struct errors));
+ next->loc = errctx->loc;
+ ctx->next = &next->next;
+ }
}
void
@@ 139,6 154,23 @@ error_norec(struct context *ctx, struct location loc, const char *fmt, ...)
abort();
}
+void
+push_error_context(struct context *ctx, struct location loc)
+{
+ struct error_context *errctx = xcalloc(1, sizeof(struct error_context));
+ errctx->loc = loc;
+ errctx->next = ctx->errctx;
+ ctx->errctx = errctx;
+}
+
+void
+pop_error_context(struct context *ctx)
+{
+ struct error_context *errctx = ctx->errctx;
+ ctx->errctx = ctx->errctx->next;
+ free(errctx);
+}
+
static struct expression *
lower_implicit_cast(struct context *ctx,
const struct type *to, struct expression *expr)
@@ 154,19 186,1257 @@ lower_implicit_cast(struct context *ctx,
expr = lower_implicit_cast(ctx, interim, expr);
}
}
-
- struct expression *cast = xcalloc(1, sizeof(struct expression));
- cast->type = EXPR_CAST;
- cast->result = cast->cast.secondary = to;
- cast->cast.kind = C_CAST;
- cast->cast.value = expr;
- cast->cast.lowered = true;
- return cast;
+
+ struct expression *cast = xcalloc(1, sizeof(struct expression));
+ cast->type = EXPR_CAST;
+ cast->result = cast->cast.secondary = to;
+ cast->cast.kind = C_CAST;
+ cast->cast.value = expr;
+ cast->cast.lowered = true;
+ return cast;
+}
+
+static void
+resolve_unresolved(struct context *ctx)
+{
+ while (ctx->unresolved) {
+ struct ast_types *unresolved = ctx->unresolved;
+ ctx->unresolved = unresolved->next;
+ type_store_lookup_atype(ctx, unresolved->type);
+ free(unresolved);
+ }
+}
+
+static void
+reset_pack_positions(struct scope *scope)
+{
+ for (struct scope_object *obj = scope->objects; obj; obj = obj->lnext) {
+ obj->pack_pos = 0;
+ }
+}
+
+static void
+deduce(struct context *ctx,
+ struct scope *scope,
+ const struct ast_template_parameters *tmpl_params,
+ char *name,
+ const struct type *type,
+ struct expression *value)
+{
+ assert(!type != !value);
+ struct identifier ident = {0};
+ bool is_pack = false;
+ for (const struct ast_template_parameters *p = tmpl_params;
+ p; p = p->next) {
+ if (!p->name) {
+ continue;
+ }
+ if (strcmp(name, p->name) == 0) {
+ if (!p->type == !value) {
+ ident.name = name;
+ is_pack = p->is_pack;
+ }
+ break;
+ }
+ }
+ if (!ident.name) {
+ return;
+ }
+ adjust_reserved_ident(&ident);
+
+ struct scope_object *obj = scope_lookup_noparent(scope, &ident);
+ if (!obj) {
+ if (is_pack && type) {
+ obj = scope_insert(scope, O_PACK, &ident,
+ &ident, NULL, NULL);
+ obj->pack_types = xcalloc(1, sizeof(struct types));
+ obj->pack_types->type = lower_flexible(ctx, type, NULL);
+ obj->pack_pos = 1;
+ } else if (is_pack && !type) {
+ obj = scope_insert(scope, O_PACK, &ident,
+ &ident, NULL, NULL);
+ obj->pack_values = xcalloc(1, sizeof(struct expressions));
+ obj->pack_values->expr = value;
+ obj->pack_pos = 1;
+ } else {
+ scope_insert(scope, type ? O_TYPE : O_CONST,
+ &ident, &ident, type, value);
+ }
+ } else if (type) {
+ switch (obj->otype) {
+ case O_PACK:
+ assert(!obj->type);
+ // XXX: algorithm will be quadratic because of linked
+ // list lol
+ struct types **next = &obj->pack_types;
+ for (size_t i = 0; *next && i < obj->pack_pos; i++) {
+ next = &(*next)->next;
+ }
+ if (!*next) {
+ *next = xcalloc(1, sizeof(struct types));
+ (*next)->type = lower_flexible(ctx, type, NULL);
+ }
+ obj->pack_pos++;
+ break;
+ case O_TYPE:
+ if (type_is_flexible(obj->type)) {
+ const struct type *promoted =
+ promote_flexible(ctx, obj->type, type);
+ if (promoted) {
+ obj->type = promoted;
+ }
+ }
+ break;
+ default:
+ assert(0); // unreachable
+ }
+ }
+}
+
+static void
+deduce_type(struct context *ctx,
+ struct scope *scope,
+ const struct ast_template_parameters *tmpl_params,
+ const struct ast_type *param_type,
+ const struct type *arg_type)
+{
+ switch (param_type->storage) {
+ case STORAGE_ALIAS:
+ // TODO: deduce template template parameters?
+ if (param_type->tmpl_args == NULL && !param_type->unwrap
+ && param_type->alias.ns == NULL) {
+ deduce(ctx, scope, tmpl_params, param_type->alias.name,
+ arg_type, NULL);
+ }
+ break;
+ case STORAGE_ARRAY:
+ if (arg_type->storage != STORAGE_ARRAY) {
+ break;
+ }
+ deduce_type(ctx, scope, tmpl_params, param_type->array.members,
+ arg_type->array.members);
+
+ // deduce expr from array length
+ // (deduce_expr isn't used since the length is stored as size_t,
+ // not struct expression or anything like that, so there's not
+ // really a point. it doesn't really matter i guess idk)
+ if (arg_type->array.length == SIZE_UNDEFINED
+ || param_type->array.length == NULL) {
+ break;
+ }
+ const struct ast_expression *len = param_type->array.length;
+ if (len->type != EXPR_ACCESS
+ || len->access.type != ACCESS_IDENTIFIER
+ || len->access.ident.ns != NULL
+ || len->access.tmpl_args != NULL) {
+ break;
+ }
+
+ struct expression *expr = xcalloc(1, sizeof(struct expression));
+ expr->type = EXPR_LITERAL;
+ expr->loc = param_type->loc;
+ expr->result = &builtin_type_size;
+ expr->literal.uval = arg_type->array.length;
+ deduce(ctx, scope, tmpl_params, len->access.ident.name,
+ NULL, expr);
+ break;
+ case STORAGE_FUNCTION:
+ reset_pack_positions(scope);
+ if (arg_type->storage != STORAGE_FUNCTION) {
+ break;
+ }
+ deduce_type(ctx, scope, tmpl_params, param_type->func.result,
+ arg_type->func.result);
+
+ const struct ast_function_parameters *aparam =
+ param_type->func.params;
+ for (const struct type_func_param *param = arg_type->func.params;
+ param && aparam && aparam->type; param = param->next) {
+ const struct type *type;
+ if (arg_type->func.variadism == VARIADISM_HARE
+ && aparam->variadic && param->next == NULL) {
+ assert(param->type->storage == STORAGE_SLICE);
+ type = param->type->array.members;
+ } else {
+ type = param->type;
+ }
+
+ deduce_type(ctx, scope, tmpl_params,
+ aparam->type, type);
+ if (!aparam->variadic) {
+ aparam = aparam->next;
+ }
+ }
+ break;
+ case STORAGE_POINTER:
+ if (arg_type->storage == STORAGE_POINTER) {
+ deduce_type(ctx, scope, tmpl_params,
+ param_type->pointer.referent,
+ arg_type->pointer.referent);
+ }
+ break;
+ case STORAGE_SLICE:
+ switch (arg_type->storage) {
+ case STORAGE_POINTER:
+ arg_type = arg_type->pointer.referent;
+ if (arg_type->storage != STORAGE_ARRAY) {
+ return;
+ }
+ break;
+ case STORAGE_ARRAY:
+ case STORAGE_SLICE:
+ break;
+ default:
+ return;
+ }
+
+ deduce_type(ctx, scope, tmpl_params, param_type->slice.members,
+ arg_type->array.members);
+ break;
+ case STORAGE_STRUCT:
+ case STORAGE_TAGGED:
+ case STORAGE_TUPLE:
+ case STORAGE_UNION:
+ break; // TODO?
+ default:
+ break;
+ }
+}
+
+static void
+deduce_expr(struct context *ctx,
+ struct scope *scope,
+ const struct ast_template_parameters *tmpl_params,
+ const struct ast_expression *param_expr,
+ struct expression *arg_expr)
+{
+ if (param_expr->type == EXPR_ACCESS
+ && param_expr->access.type == ACCESS_IDENTIFIER
+ && param_expr->access.ident.ns == NULL
+ && param_expr->access.tmpl_args == NULL) {
+ deduce(ctx, scope, tmpl_params, param_expr->access.ident.name,
+ NULL, arg_expr);
+ }
+}
+
+union pack {
+ struct types **types;
+ struct expressions **values;
+};
+
+// returns true on success, false on error
+static bool
+init_tmpl_arg_explicit(struct context *ctx,
+ struct identifier *ident,
+ const struct ast_template_parameters *p,
+ const struct ast_template_argument *arg,
+ union pack *pack_next,
+ int idx)
+{
+ if (p->type != NULL) {
+ const struct type *type = type_store_lookup_atype(ctx, p->type);
+ struct expression *expr = xcalloc(1, sizeof(struct expression));
+ check_expression(ctx, arg->expr, expr, type);
+
+ if (!type_is_assignable(ctx, type, expr->result)) {
+ char *argtypename = gen_typename(expr->result);
+ char *paramtypename = gen_typename(type);
+ error(ctx, expr->loc, NULL,
+ "Argument type %s is not assignable to parameter type %s",
+ argtypename, paramtypename);
+ free(argtypename);
+ free(paramtypename);
+ return false;
+ }
+ expr = lower_implicit_cast(ctx, type, expr);
+
+ struct expression *evaled = xcalloc(1,
+ sizeof(struct expression));
+ bool success = eval_expr(ctx, expr, evaled);
+ free(expr);
+ if (!success) {
+ return false;
+ }
+ evaled->tmpl_param_idx = idx;
+ if (pack_next->values == NULL) {
+ adjust_reserved_ident(ident);
+ scope_insert(ctx->scope, O_CONST, ident,
+ ident, NULL, evaled);
+ } else {
+ // argument in parameter pack
+ *pack_next->values = xcalloc(1,
+ sizeof(struct expressions));
+ (*pack_next->values)->expr = evaled;
+ pack_next->values = &(*pack_next->values)->next;
+ }
+ } else {
+ struct type *type = xcalloc(1, sizeof(struct type));
+ *type = *type_store_lookup_atype(ctx, arg->type);
+ type->tmpl_param_idx = idx;
+ if (pack_next->types == NULL) {
+ adjust_reserved_ident(ident);
+ scope_insert(ctx->scope, O_TYPE,
+ ident, ident, type, NULL);
+ } else {
+ // argument in parameter pack
+ *pack_next->types = xcalloc(1, sizeof(struct types));
+ (*pack_next->types)->type = type;
+ pack_next->types = &(*pack_next->types)->next;
+ }
+ }
+
+ return true;
+}
+
+// returns true on success, false on error
+static bool
+check_explicit_tmpl_arg(struct context *ctx,
+ struct scope *scope,
+ struct scope *imports,
+ const struct ast_template_parameters **params,
+ const struct ast_template_argument **args,
+ union pack *pack_next,
+ int *idx)
+{
+ struct scope *old_subunit = ctx->unit->parent;
+ struct scope *old_scope = ctx->scope;
+
+ const struct ast_template_parameters *p = *params;
+ const struct ast_template_argument *arg = *args;
+ struct identifier ident;
+ tmpl_param_scope_ident(&ident, p->name, *idx);
+ ident.name = xstrdup(ident.name); // extend lifetime
+
+ if (p->is_pack && pack_next->values == NULL) {
+ // start of parameter pack
+ struct scope_object *new = scope_insert(scope,
+ O_PACK, &ident, &ident, NULL, NULL);
+ if (p->type != NULL) {
+ ctx->unit->parent = imports;
+ ctx->scope = scope;
+ new->type = type_store_lookup_atype(ctx, p->type);
+ ctx->scope = old_scope;
+ ctx->unit->parent = old_subunit;
+ };
+ // `struct types` and `struct expressions` have the same
+ // representation, so this works for both expression packs and
+ // type packs
+ pack_next->values = &new->pack_values;
+ }
+
+ ctx->unit->parent = imports;
+ ctx->scope = scope;
+ bool res = init_tmpl_arg_explicit(ctx, &ident, p, arg, pack_next, *idx);
+ ctx->scope = old_scope;
+ ctx->unit->parent = old_subunit;
+ free(ident.name);
+ if (!res) {
+ return false;
+ }
+
+ if (pack_next->values == NULL || arg->next == NULL) {
+ *params = p->next;
+ ++*idx;
+ }
+ *args = arg->next;
+ return true;
+}
+
+static void
+deduce_tmpl_args_from_func_params(struct context *ctx,
+ struct scope *scope,
+ const struct ast_template_parameters *p,
+ const struct ast_function_parameters *fp,
+ const struct types *params)
+{
+ reset_pack_positions(scope);
+ while (fp != NULL && params != NULL) {
+ // no need to do anything different if fp->variadic is true: if
+ // anything can be deduced then it's guaranteed to be pack
+ // expansion; not c-style variadism. so just iterate over the
+ // args and add them to the pack (handled in deduce)
+ deduce_type(ctx, scope, p, fp->type, params->type);
+ if (!fp->variadic) {
+ fp = fp->next;
+ }
+ params = params->next;
+ }
+}
+
+static void
+deduce_tmpl_args_from_ast_func_params(struct context *ctx,
+ struct scope *scope,
+ const struct ast_template_parameters *p,
+ const struct ast_function_parameters *fp,
+ const struct ast_function_parameters *params)
+{
+ reset_pack_positions(scope);
+
+ size_t fp_count = 0, params_count = 0, params_max = 0;
+ struct scope *old_scope = ctx->scope;
+ struct scope *exp_scope = ctx->scope;
+ scope_push(&exp_scope, SCOPE_DEFINES);
+
+ while (fp != NULL && params != NULL) {
+ if (fp->variadic && fp_count == 0) {
+ ctx->scope = scope;
+ size_t n = type_pack_expand(ctx, NULL, fp->type, -1);
+ ctx->scope = old_scope;
+ if (n == 0) {
+ fp = fp->next;
+ continue;
+ } else if (n != (size_t)-1) {
+ fp_count = n;
+ }
+ }
+ if (params->variadic && params_count == 0) {
+ size_t n = type_pack_expand(ctx, NULL, params->type, -1);
+ if (n == 0) {
+ params = params->next;
+ continue;
+ } else if (n != (size_t)-1) {
+ params_count = params_max = n;
+ }
+ }
+
+ if (params_count > 0) {
+ type_pack_expand(ctx, exp_scope, params->type,
+ params_max - params_count);
+ ctx->scope = exp_scope;
+ }
+ const struct type *type =
+ type_store_lookup_atype(ctx, params->type);
+ ctx->scope = old_scope;
+ deduce_type(ctx, scope, p, fp->type, type);
+
+ fp_count--;
+ if (fp_count == 0 || fp_count == (size_t)-1) {
+ fp_count = 0;
+ fp = fp->next;
+ }
+
+ params_count--;
+ if (params_count == 0 || params_count == (size_t)-1) {
+ params_count = 0;
+ params = params->next;
+ }
+ }
+}
+
+static bool
+tmpl_prototypes_compat(struct context *ctx,
+ struct scope *scope,
+ struct scope *sp_scope,
+ struct scope *imports,
+ struct scope *sp_imports,
+ const struct ast_function_parameters *fp,
+ const struct ast_function_parameters *params)
+{
+ // TODO: would be nice if some of this stuff errored out instead of just
+ // saying they're incompatible (like, if the number of parameters in
+ // each prototype is different, and stuff like that)
+
+ size_t fp_count = 0, params_count = 0, fp_max = 0, params_max = 0;
+ struct scope *exp_scope = scope;
+ scope_push(&exp_scope, SCOPE_DEFINES);
+ struct scope *exp_sp_scope = sp_scope;
+ scope_push(&exp_sp_scope, SCOPE_DEFINES);
+
+ while (fp != NULL && params != NULL) {
+ if (fp->variadic && fp_count == 0) {
+ ctx->unit->parent = imports;
+ ctx->scope = scope;
+ size_t n = type_pack_expand(ctx, NULL, fp->type, -1);
+ if (n == 0) {
+ fp = fp->next;
+ continue;
+ } else if (n != (size_t)-1) {
+ fp_count = fp_max = n;
+ }
+ }
+ if (params->variadic && params_count == 0) {
+ ctx->unit->parent = sp_imports;
+ ctx->scope = sp_scope;
+ size_t n = type_pack_expand(ctx, NULL, params->type, -1);
+ if (n == 0) {
+ params = params->next;
+ continue;
+ } else if (n != (size_t)-1) {
+ params_count = params_max = n;
+ }
+ }
+
+ if (fp->variadic != params->variadic
+ && ((!fp_count && !params_count)
+ || !fp->variadic == !params_count)) {
+ return false;
+ }
+
+ ctx->unit->parent = imports;
+ ctx->scope = scope;
+ if (fp_count > 0) {
+ type_pack_expand(ctx, exp_scope, fp->type,
+ fp_max - fp_count);
+ ctx->scope = exp_scope;
+ }
+ const struct type *unspec =
+ type_store_lookup_atype(ctx, fp->type);
+
+ ctx->unit->parent = sp_imports;
+ ctx->scope = sp_scope;
+ if (params_count > 0) {
+ type_pack_expand(ctx, exp_sp_scope, params->type,
+ params_max - params_count);
+ ctx->scope = exp_sp_scope;
+ }
+ const struct type *spec =
+ type_store_lookup_atype(ctx, params->type);
+
+ if (unspec->id != spec->id) {
+ return false;
+ }
+
+ fp_count--;
+ if (fp_count == 0 || fp_count == (size_t)-1) {
+ fp_count = 0;
+ fp = fp->next;
+ }
+
+ params_count--;
+ if (params_count == 0 || params_count == (size_t)-1) {
+ params_count = 0;
+ params = params->next;
+ }
+ }
+
+ return fp == NULL && params == NULL;
+}
+
+// returns true on success, false on error
+// XXX: it might maybe be possible to deduplicate some of this stuff from
+// init_tmpl_arg_explicit
+static bool
+init_tmpl_arg_default(struct context *ctx,
+ const struct identifier *ident,
+ const struct ast_template_parameters *p,
+ int idx)
+{
+ if (p->type != NULL) {
+ const struct type *type = type_store_lookup_atype(ctx, p->type);
+ struct expression *expr = xcalloc(1, sizeof(struct expression));
+ check_expression(ctx, p->init, expr, type);
+
+ if (!type_is_assignable(ctx, type, expr->result)) {
+ char *argtypename = gen_typename(expr->result);
+ char *paramtypename = gen_typename(type);
+ error(ctx, expr->loc, NULL,
+ "Argument type %s is not assignable to parameter type %s",
+ argtypename, paramtypename);
+ free(argtypename);
+ free(paramtypename);
+ return false;
+ }
+ expr = lower_implicit_cast(ctx, type, expr);
+
+ struct expression *evaled = xcalloc(1,
+ sizeof(struct expression));
+ bool success = eval_expr(ctx, expr, evaled);
+ free(expr);
+ if (!success) {
+ return false;
+ }
+ evaled->tmpl_param_idx = idx;
+ scope_insert(ctx->scope, O_CONST, ident, ident, NULL, evaled);
+ } else {
+ struct type *type = xcalloc(1, sizeof(struct type));
+ *type = *type_store_lookup_atype(ctx, p->default_type);
+
+ type->tmpl_param_idx = idx;
+ scope_insert(ctx->scope, O_TYPE, ident, ident, type, NULL);
+ }
+
+ return true;
+}
+
+// returns true on success, false on error
+static bool
+check_deduced_tmpl_arg(struct context *ctx,
+ struct scope *scope,
+ struct scope *imports,
+ const struct ast_template_parameters *p,
+ int idx)
+{
+ struct scope *old_subunit = ctx->unit->parent;
+ struct scope *old_scope = ctx->scope;
+
+ struct identifier ident;
+ tmpl_param_scope_ident(&ident, p->name, idx);
+ ident.name = xstrdup(ident.name); // extend lifetime
+
+ struct scope_object *obj = scope_lookup_noparent(scope, &ident);
+ if (obj != NULL) {
+ // parameter was deduced
+ switch (obj->otype) {
+ case O_CONST:
+ assert(obj->value->tmpl_param_idx == 0);
+ obj->value->tmpl_param_idx = idx;
+ break;
+ case O_TYPE:;
+ struct type *type = xcalloc(1, sizeof(struct type));
+ *type = *obj->type;
+ type->tmpl_param_idx = idx;
+ obj->type = lower_flexible(ctx, type, NULL);
+ break;
+ default:
+ assert(obj->otype == O_PACK);
+ break;
+ }
+
+ free(ident.name);
+ return true;
+ }
+
+ if (p->is_pack) {
+ // insert empty pack into scope
+ ctx->unit->parent = imports;
+ ctx->scope = scope;
+ const struct type *type = NULL;
+ if (p->type != NULL) {
+ type = type_store_lookup_atype(ctx, p->type);
+ }
+ scope_insert(scope, O_PACK, &ident, &ident, type, NULL);
+
+ ctx->scope = old_scope;
+ ctx->unit->parent = old_subunit;
+ free(ident.name);
+ return true;
+ }
+ if (p->init == NULL) {
+ error(ctx, p->loc, NULL, "Argument can't be deduced");
+ free(ident.name);
+ return false;
+ }
+
+ // initialize to default argument
+ ctx->unit->parent = imports;
+ ctx->scope = scope;
+ bool res = init_tmpl_arg_default(ctx, &ident, p, idx);
+ ctx->scope = old_scope;
+ ctx->unit->parent = old_subunit;
+ free(ident.name);
+ return res;
+}
+
+static void
+push_dummy_pack_scope(struct scope **scope,
+ const struct ast_template_parameters *params)
+{
+ scope_push(scope, SCOPE_DEFINES);
+ for (const struct scope_object *obj = (*scope)->parent->objects;
+ obj; obj = obj->lnext) {
+ if (obj->otype == O_PACK) {
+ int idx = 1;
+ const struct ast_template_parameters *p;
+ for (p = params; p; p = p->next, idx++) {
+ if (strcmp(obj->name.name, p->name) == 0) {
+ break;
+ }
+ }
+ assert(p != NULL);
+
+ // int is just a dummy type. it just needs to be a type
+ // with nonzero definite size
+ struct type *type = xcalloc(1, sizeof(struct type));
+ *type = builtin_type_int;
+ type->tmpl_param_idx = idx;
+ scope_insert(*scope, O_TYPE, &obj->ident,
+ &obj->name, type, NULL);
+ }
+ }
+}
+
+static struct type_func get_prototype(struct context *ctx,
+ const struct ast_function_type *atype);
+
+// XXX: this duplicates some logic from type_store; it's dumb. it's necessary
+// because function types are treated differently, but everything else is the
+// same
+static const struct type *
+get_prototype_param(struct context *ctx, const struct ast_type *atype)
+{
+ const struct type *ts_type;
+ if ((atype->flags & TYPE_ERROR) == 0) {
+ ts_type = builtin_type_for_storage(atype->storage,
+ (atype->flags & TYPE_CONST) != 0);
+ if (ts_type != NULL) {
+ return ts_type;
+ }
+ }
+ ts_type = type_store_lookup_atype(ctx, atype);
+ // XXX: this is leaky, but so is the rest of harec so it doesn't really matter
+ struct type *new_type = xcalloc(1, sizeof(struct type));
+ *new_type = *ts_type;
+
+ switch (atype->storage) {
+ case STORAGE_ARRAY:
+ new_type->array.members = get_prototype_param(ctx,
+ atype->array.members);
+ break;
+ case STORAGE_FUNCTION:
+ new_type->func = get_prototype(ctx, &atype->func);
+ break;
+ case STORAGE_POINTER:
+ new_type->pointer.referent = get_prototype_param(ctx,
+ atype->pointer.referent);
+ break;
+ default:
+ free(new_type);
+ return ts_type;
+ }
+
+ return new_type;
+}
+
+static struct type_func
+get_prototype(struct context *ctx, const struct ast_function_type *atype)
+{
+ struct type_func prototype = {
+ .result = type_store_lookup_atype(ctx, atype->result),
+ };
+
+ struct type_func_param **next = &prototype.params;
+ for (const struct ast_function_parameters *p = atype->params;
+ p; p = p->next) {
+ if (p->variadic && p->type == NULL) {
+ prototype.variadism = VARIADISM_C;
+ assert(p->next == NULL);
+ break;
+ }
+
+ *next = xcalloc(1, sizeof(struct type_func_param));
+ (*next)->type = get_prototype_param(ctx, p->type);
+ if (p->variadic) {
+ (*next)->type = type_store_lookup_with_flags(
+ ctx, (*next)->type, TYPE_EXPAND);
+ }
+
+ next = &(*next)->next;
+ }
+
+ return prototype;
+}
+
+static void
+deduce_specialized_param(struct context *ctx,
+ struct scope *scope,
+ const struct ast_template_parameters *params,
+ const struct ast_template_parameters *p,
+ const struct ast_template_argument **arg,
+ int idx)
+{
+ struct identifier ident;
+ tmpl_param_scope_ident(&ident, p->name, idx);
+ struct scope_object *obj = scope_lookup_noparent(scope, &ident);
+ assert(obj != NULL);
+
+ switch (obj->otype) {
+ case O_CONST:
+ assert(p->type != NULL);
+ deduce_expr(ctx, ctx->scope, params, (*arg)->expr, obj->value);
+ deduce_type(ctx, ctx->scope, params, p->type, obj->value->result);
+ *arg = (*arg)->next;
+ break;
+ case O_PACK:
+ // `struct types` and `struct expressions` have the same
+ // representation, so this works for both expression packs and
+ // type packs
+ for (const struct expressions *pack = obj->pack_values;
+ pack != NULL && *arg != NULL;
+ pack = pack->next, *arg = (*arg)->next) {
+ if (obj->type != NULL) {
+ deduce_expr(ctx, ctx->scope, params,
+ (*arg)->expr, pack->expr);
+ deduce_type(ctx, ctx->scope, params,
+ p->type, pack->expr->result);
+ } else {
+ deduce_type(ctx, ctx->scope, params, (*arg)->type,
+ ((const struct types *)pack)->type);
+ }
+ }
+ *arg = NULL;
+ break;
+ case O_TYPE:
+ assert(p->type == NULL);
+ deduce_type(ctx, ctx->scope, params, (*arg)->type, obj->type);
+ *arg = (*arg)->next;
+ break;
+ default:
+ assert(0); // unreachable
+ }
+}
+
+// returns true on success, false on error
+static bool
+match_specialization_arg_expr(struct context *ctx,
+ const struct ast_expression *arg_expr,
+ const struct expression *obj_value)
+{
+ struct expression *expr = xcalloc(1, sizeof(struct expression));
+ check_expression(ctx, arg_expr, expr, obj_value->result);
+ if (!type_is_assignable(ctx, obj_value->result, expr->result)) {
+ // XXX: can this even happen in a valid program? like, is this
+ // branch reachable? and if so, should this error out or just
+ // return?
+ return false;
+ }
+ expr = lower_implicit_cast(ctx, obj_value->result, expr);
+
+ struct expression *evaled = xcalloc(1, sizeof(struct expression));
+ bool success = eval_expr(ctx, expr, evaled);
+ if (!success) {
+ // XXX: should this properly error out?
+ return false;
+ }
+
+ return literal_eq(ctx, evaled, obj_value);
+}
+
+// returns true on success, false on error
+static bool
+match_specialization_arg_type(struct context *ctx,
+ const struct ast_type *arg_type,
+ const struct type *obj_type)
+{
+ const struct type *type = type_store_lookup_atype(ctx, arg_type);
+ return type->id == obj_type->id;
+}
+
+// returns true on success, false on error
+static bool
+match_specialization_arg(struct context *ctx,
+ struct scope *scope,
+ const struct ast_template_parameters *p,
+ const struct ast_template_argument *arg,
+ int idx)
+{
+ struct identifier ident;
+ tmpl_param_scope_ident(&ident, p->name, idx);
+
+ struct scope_object *obj = scope_lookup_noparent(scope, &ident);
+ assert(obj != NULL);
+
+ switch (obj->otype) {
+ case O_CONST:
+ return match_specialization_arg_expr(ctx, arg->expr, obj->value);
+ case O_PACK:
+ // `struct types` and `struct expressions` have the same
+ // representation, so this works for both expression packs and
+ // type packs
+ for (const struct expressions *pack = obj->pack_values;
+ pack != NULL && arg != NULL;
+ pack = pack->next, arg = arg->next) {
+ if (obj->type != NULL) {
+ if (!match_specialization_arg_expr(ctx,
+ arg->expr, pack->expr)) {
+ return false;
+ }
+ } else {
+ const struct type *type =
+ ((const struct types *)pack)->type;
+ if (!match_specialization_arg_type(ctx,
+ arg->type, type)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ case O_TYPE:
+ return match_specialization_arg_type(ctx, arg->type, obj->type);
+ default:
+ assert(0); // unreachable
+ }
+}
+
+static int
+match_specialization(struct context *ctx,
+ struct scope *scope,
+ struct scope *sp_scope,
+ struct scope *imports,
+ const struct template_specialization *sp,
+ const struct ast_template_parameters *tmpl_params,
+ const struct ast_function_parameters *fn_params)
+{
+ struct scope *old_subunit = ctx->unit->parent;
+ struct scope *old_scope = ctx->scope;
+
+ ctx->unit->parent = sp->imports;
+ ctx->scope = sp_scope;
+
+ reset_pack_positions(scope);
+ const struct ast_template_parameters *p = tmpl_params;
+ const struct ast_template_argument *arg = sp->args;
+ int idx = 1;
+ while (arg != NULL && arg->expr != NULL) {
+ assert(p != NULL);
+ deduce_specialized_param(ctx, scope, sp->params, p, &arg, idx);
+ p = p->next;
+ idx++;
+ }
+
+ if (fn_params != NULL) {
+ deduce_tmpl_args_from_ast_func_params(ctx, scope, tmpl_params,
+ fn_params, sp->type->func.params);
+ }
+
+ int ret = 0;
+ for (p = sp->params; p; p = p->next) {
+ struct identifier ident = {
+ .name = p->name,
+ };
+ struct scope_object *obj =
+ scope_lookup_noparent(sp_scope, &ident);
+ if (obj == NULL) {
+ return -1;
+ }
+ // TODO: not entirely sure if the logic for ret is correct. i
+ // assume it probably isn't tbh
+ ret++;
+ }
+
+ if (fn_params != NULL) {
+ bool compat = tmpl_prototypes_compat(ctx, scope, sp_scope,
+ imports, sp->imports, fn_params, sp->type->func.params);
+ if (!compat) {
+ ctx->scope = old_scope;
+ ctx->unit->parent = old_subunit;
+ return -1;
+ }
+ }
+
+ ctx->unit->parent = sp->imports;
+ ctx->scope = sp_scope;
+
+ arg = sp->args;
+ for (p = tmpl_params, idx = 1; p; p = p->next, idx++) {
+ if (arg == NULL || arg->expr == NULL) {
+ // if it's in the scope, it was deduced, so things are
+ // fine i think
+ struct identifier ident;
+ tmpl_param_scope_ident(&ident, p->name, idx);
+ struct scope_object *obj =
+ scope_lookup_noparent(scope, &ident);
+ if (obj == NULL) {
+ // XXX: should this properly error out?
+ ret = -1;
+ break;
+ }
+ continue;
+ }
+
+ bool match = match_specialization_arg(ctx, scope, p, arg, idx);
+ if (!match) {
+ ret = -1;
+ break;
+ }
+ if (p->is_pack) {
+ // parameter pack was already fully matched in
+ // match_specialization_arg
+ break;
+ }
+ arg = arg->next;
+ }
+
+ ctx->scope = old_scope;
+ ctx->unit->parent = old_subunit;
+ return ret;
+}
+
+static struct scope_object *
+check_specialization(struct context *ctx,
+ struct scope *scope,
+ struct scope *imports,
+ struct scope *unit,
+ const struct identifier *ident,
+ enum ast_decl_type decl_type,
+ struct location loc,
+ struct ast_type *type,
+ struct ast_expression *expr,
+ bool threadlocal)
+{
+ struct scope *old_unit = ctx->unit;
+ ctx->unit = ctx->defines->parent = unit;
+ // wrap_resolver sets the scope to ctx->defines, so we change
+ // ctx->defines to include the template parameters
+ struct scope *old_defines = ctx->defines;
+ ctx->defines = scope;
+
+ struct incomplete_declaration *idecl = xcalloc(1,
+ sizeof(struct incomplete_declaration));
+ idecl->imports = imports;
+ idecl->type = IDECL_DECL;
+ idecl->decl.decl_type = decl_type;
+ idecl->decl.loc = loc;
+
+ switch (decl_type) {
+ case ADECL_GLOBAL:
+ idecl->decl.global.symbol = ident->name;
+ idecl->decl.global.threadlocal = threadlocal;
+ // fallthrough
+ case ADECL_CONST:
+ idecl->decl.global.ident = *ident;
+ idecl->decl.global.type = type;
+ idecl->decl.global.init = expr;
+ break;
+ case ADECL_FUNC:
+ idecl->decl.function.symbol = ident->name;
+ idecl->decl.function.ident = *ident;
+ idecl->decl.function.prototype = type->func;
+ idecl->decl.function.body = expr;
+ break;
+ case ADECL_TYPE:
+ idecl->decl.type.ident = *ident;
+ idecl->decl.type.type = type;
+ break;
+ case ADECL_ASSERT:
+ assert(0); // unreachable
+ }
+
+ scope_object_init(&idecl->obj, O_SCAN, ident, ident, NULL, NULL);
+ scope_insert_from_object(ctx->unit, &idecl->obj);
+ wrap_resolver(ctx, &idecl->obj, resolve_decl);
+ ctx->defines = old_defines;
+ ctx->unit = ctx->defines->parent = old_unit;
+ if (ctx->unit != unit) {
+ scope_insert_from_object(ctx->unit, &idecl->obj);
+ }
+ return &idecl->obj;
+}
+
+struct scope_object *
+specialize(struct context *ctx,
+ const struct scope_object *obj,
+ const struct ast_template_argument *tmpl_args,
+ const struct types *fn_params)
+{
+ struct scope *old_subunit = ctx->unit->parent;
+ struct scope *old_scope = ctx->scope;
+ struct scope *scope = ctx->defines;
+ scope_push(&scope, SCOPE_DEFINES);
+
+ int idx = 1;
+ assert(obj->otype == O_TEMPLATE);
+ const struct ast_template_parameters *p = obj->template->params;
+ union pack pack_next = {0};
+ while (p != NULL && tmpl_args != NULL && tmpl_args->expr != NULL) {
+ bool res = check_explicit_tmpl_arg(ctx, scope,
+ obj->template->imports, &p,
+ &tmpl_args, &pack_next, &idx);
+ if (!res) {
+ return NULL;
+ }
+ }
+
+ if (tmpl_args != NULL && tmpl_args->expr != NULL) {
+ error(ctx, tmpl_args->expr->loc, NULL,
+ "Too many arguments for template instantiation");
+ return NULL;
+ }
+
+ // TODO: also take type hint into account maybe
+ if (fn_params != NULL && obj->template->decl_type == ADECL_FUNC) {
+ deduce_tmpl_args_from_func_params(ctx, scope,
+ obj->template->params, obj->template->type->func.params,
+ fn_params);
+ }
+
+ while (p != NULL) {
+ bool success = check_deduced_tmpl_arg(ctx, scope,
+ obj->template->imports, p, idx);
+ if (!success) {
+ return NULL;
+ }
+ p = p->next;
+ idx++;
+ }
+
+ // the prototype is used in the mangled name of function templates
+ struct type_func prototype;
+ if (obj->template->decl_type == ADECL_FUNC) {
+ ctx->unit->parent = obj->template->imports;
+ ctx->scope = scope;
+ push_dummy_pack_scope(&ctx->scope, obj->template->params);
+ prototype = get_prototype(ctx, &obj->template->type->func);
+ }
+ ctx->unit->parent = obj->template->imports;
+ ctx->scope = scope;
+ resolve_unresolved(ctx);
+ ctx->scope = old_scope;
+ ctx->unit->parent = old_subunit;
+
+ struct identifier ident = {0};
+ ident.name = mangle(&obj->ident, scope, obj->template->params,
+ obj->template->decl_type == ADECL_FUNC ? &prototype : NULL);
+ struct scope_object *old = scope_lookup(ctx->scope, &ident);
+ if (old != NULL) {
+ // template was already specialized; return old specialization
+ free(ident.name);
+ return old;
+ }
+
+ const struct template_specialization *best_sp = NULL;
+ struct scope *best_sp_scope = NULL;
+ int best_n = -1, count = 0;
+ for (const struct template_specialization *sp =
+ obj->template->specializations; sp; sp = sp->next) {
+ struct scope *sp_scope = ctx->defines;
+ scope_push(&sp_scope, SCOPE_DEFINES);
+
+ const struct ast_function_parameters *fn_params = NULL;
+ if (obj->template->decl_type == ADECL_FUNC) {
+ fn_params = obj->template->type->func.params;
+ }
+
+ int n = match_specialization(ctx, scope, sp_scope,
+ obj->template->imports, sp, obj->template->params,
+ fn_params);
+ if (n == -1) {
+ continue;
+ }
+ if (n == best_n) {
+ count++;
+ }
+ if (n < best_n || best_n == -1) {
+ best_sp = sp;
+ best_sp_scope = sp_scope;
+ best_n = n;
+ count = 1;
+ }
+ }
+ if (count > 1) {
+ // XXX: the location for this error is kinda meh i think
+ error(ctx, best_sp->loc, NULL,
+ "Multiple specializations could be used (ambiguous)");
+ free(ident.name);
+ return NULL;
+ }
+
+ struct scope_object *ret;
+ // XXX: this would be a lot nicer if, idk, template_decl used a
+ // template_specialization field or something
+ if (best_sp != NULL) {
+ ret = check_specialization(ctx, best_sp_scope, best_sp->imports,
+ obj->template->unit, &ident, obj->template->decl_type,
+ best_sp->loc, best_sp->type, best_sp->expr,
+ best_sp->threadlocal);
+ } else {
+ ret = check_specialization(ctx, scope, obj->template->imports,
+ obj->template->unit, &ident, obj->template->decl_type,
+ obj->template->loc, obj->template->type,
+ obj->template->expr, obj->template->threadlocal);
+ }
+ free(ident.name);
+ ctx->decls->decl.exported = obj->template->exported;
+ return ret;
+}
+
+static struct types *
+types_from_call_args(struct context *ctx, const struct call_argument *args)
+{
+ struct types *ret = NULL, **next = &ret;
+ while (args != NULL) {
+ struct types *cur = *next = xcalloc(1, sizeof(struct types));
+ next = &cur->next;
+ cur->type = args->value->result;
+ args = args->next;
+ }
+ return ret;
+}
+
+static struct types *
+types_from_ast_func_params(struct context *ctx,
+ const struct ast_function_parameters *params)
+{
+ struct types *ret = NULL, **next = &ret;
+ while (params != NULL) {
+ // this function is called when instantiating full
+ // specializations. since there's no template parameters, a
+ // variadic parameter must actually be variadism; not a
+ // parameter pack
+ if (params->type == NULL) {
+ assert(params->variadic);
+ break;
+ }
+ struct types *cur = *next = xcalloc(1, sizeof(struct types));
+ next = &cur->next;
+ cur->type = type_store_lookup_atype(ctx, params->type);
+ if (params->variadic) {
+ cur->type = type_store_lookup_slice(ctx,
+ params->loc, cur->type);
+ }
+ params = params->next;
+ }
+ return ret;
+}
+
+static void
+check_access_identifier(struct context *ctx,
+ const struct ast_expression *aexpr,
+ struct expression *expr,
+ struct call_argument *fn_args)
+{
+ struct identifier ident = aexpr->access.ident;
+ adjust_reserved_ident(&ident);
+ struct scope_object *obj = scope_lookup(ctx->scope, &ident);
+ if (!obj) {
+ char buf[IDENT_BUFSIZ];
+ identifier_unparse_static(&aexpr->access.ident, buf);
+ error(ctx, aexpr->loc, expr, "Unknown object '%s'", buf);
+ return;
+ }
+ wrap_resolver(ctx, obj, resolve_decl);
+ if (obj->otype == O_TEMPLATE) {
+ push_error_context(ctx, aexpr->loc);
+ struct types *fn_params = types_from_call_args(ctx, fn_args);
+ obj = specialize(ctx, obj, aexpr->access.tmpl_args, fn_params);
+ pop_error_context(ctx);
+ if (!obj) {
+ mkerror(aexpr->loc, expr);
+ return;
+ }
+ }
+
+ switch (obj->otype) {
+ case O_CONST:
+ // Lower flexible types
+ *expr = *obj->value;
+ flexible_reset_refs(expr->result);
+ break;
+ case O_BIND:
+ case O_DECL:
+ expr->result = obj->type;
+ expr->access.object = obj;
+ break;
+ case O_PACK:
+ error(ctx, aexpr->loc, expr, "Parameter pack isn't expanded");
+ return;
+ case O_TYPE:
+ if (type_dealias(ctx, obj->type)->storage != STORAGE_VOID) {
+ char *typename = gen_typename(obj->type);
+ error(ctx, aexpr->loc, expr,
+ "Cannot use non-void type %s as literal",
+ typename);
+ free(typename);
+ return;
+ }
+ expr->type = EXPR_LITERAL;
+ expr->result = obj->type;
+ break;
+ case O_SCAN:
+ case O_TEMPLATE:
+ assert(0); // handled above
+ }
}
-static void resolve_decl(struct context *ctx,
- struct incomplete_declaration *idecl);
-
static void
check_expr_access(struct context *ctx,
const struct ast_expression *aexpr,
@@ 176,45 1446,9 @@ check_expr_access(struct context *ctx,
expr->type = EXPR_ACCESS;
expr->access.type = aexpr->access.type;
- struct scope_object *obj = NULL;
switch (expr->access.type) {
case ACCESS_IDENTIFIER:
- obj = scope_lookup(ctx->scope, &aexpr->access.ident);
- if (!obj) {
- char buf[IDENT_BUFSIZ];
- identifier_unparse_static(&aexpr->access.ident, buf);
- error(ctx, aexpr->loc, expr,
- "Unknown object '%s'", buf);
- return;
- }
- wrap_resolver(ctx, obj, resolve_decl);
-
- switch (obj->otype) {
- case O_CONST:
- // Lower flexible types
- *expr = *obj->value;
- flexible_reset_refs(expr->result);
- break;
- case O_BIND:
- case O_DECL:
- expr->result = obj->type;
- expr->access.object = obj;
- break;
- case O_TYPE:
- if (type_dealias(ctx, obj->type)->storage != STORAGE_VOID) {
- char *ident = identifier_unparse(&obj->type->alias.ident);
- error(ctx, aexpr->loc, expr,
- "Cannot use non-void type alias '%s' as literal",
- ident);
- free(ident);
- return;
- }
- expr->type = EXPR_LITERAL;
- expr->result = obj->type;
- break;
- case O_SCAN:
- assert(0); // handled above
- }
+ check_access_identifier(ctx, aexpr, expr, NULL);
break;
case ACCESS_INDEX:
expr->access.array = xcalloc(1, sizeof(struct expression));
@@ 896,7 2130,7 @@ type_promote(struct context *ctx, const struct type *a, const struct type *b)
const struct type *da = type_store_lookup_with_flags(ctx, a, 0);
const struct type *db = type_store_lookup_with_flags(ctx, b, 0);
- if (da == db) {
+ if (da->id == db->id) {
const struct type *base = type_store_lookup_with_flags(ctx, a,
a->flags | b->flags);
assert(base == a || base == b);
@@ 910,7 2144,7 @@ type_promote(struct context *ctx, const struct type *a, const struct type *b)
da = type_dealias(ctx, da);
db = type_dealias(ctx, db);
- if (da == db) {
+ if (da->id == db->id) {
return a->storage == STORAGE_ALIAS ? a : b;
}
@@ 1082,40 2316,600 @@ type_has_default(struct context *ctx, const struct type *type)
if (obj->otype == O_SCAN) {
wrap_resolver(ctx, obj, resolve_enum_field);
}
- assert(obj->otype == O_CONST);
- if (obj->value->literal.uval == 0) {
- return true;
+ assert(obj->otype == O_CONST);
+ if (obj->value->literal.uval == 0) {
+ return true;
+ }
+ }
+ return false;
+ case STORAGE_POINTER:
+ return type->pointer.flags & PTR_NULLABLE;
+ case STORAGE_STRUCT:
+ case STORAGE_UNION:
+ for (struct struct_field *sf = type->struct_union.fields;
+ sf != NULL; sf = sf->next) {
+ if (!type_has_default(ctx, sf->type)) {
+ return false;
+ }
+ }
+ return true;
+ case STORAGE_TUPLE:
+ for (const struct type_tuple *t = &type->tuple;
+ t != NULL; t = t->next) {
+ if (!type_has_default(ctx, t->type)) {
+ return false;
+ }
+ }
+ return true;
+ case STORAGE_ALIAS:
+ return type_has_default(ctx, type_dealias(ctx, type));
+ case STORAGE_FCONST:
+ case STORAGE_ICONST:
+ case STORAGE_NULL:
+ case STORAGE_RCONST:
+ abort(); // unreachable
+ }
+ abort(); // Unreachable
+}
+
+// returns false if the lengths are incompatible, true on success
+static bool
+pack_expand_update_len(size_t *len, size_t tmplen)
+{
+ if (*len == (size_t)-1) {
+ *len = tmplen;
+ }
+ return *len == tmplen || tmplen == (size_t)-1;
+}
+
+static size_t expr_pack_expand(struct context *ctx, struct scope *scope,
+ const struct ast_expression *aexpr, size_t n);
+
+static size_t
+ident_pack_expand(struct context *ctx,
+ struct scope *scope,
+ const struct identifier *ident,
+ const struct ast_template_argument *tmpl_args,
+ size_t n)
+{
+ const struct scope_object *obj = scope_lookup(ctx->scope, ident);
+ if (obj == NULL) {
+ return -1;
+ }
+ size_t len = -1, tmplen;
+
+ switch (obj->otype) {
+ case O_PACK:
+ break;
+ case O_TEMPLATE:;
+ const struct ast_template_parameters *param =
+ obj->template->params;
+ if (param == NULL) {
+ return -1;
+ }
+ for (const struct ast_template_argument *arg = tmpl_args;
+ arg; arg = arg->next) {
+ if (param->type == NULL) {
+ tmplen = type_pack_expand(ctx, scope, arg->type, n);
+ } else {
+ tmplen = expr_pack_expand(ctx, scope, arg->expr, n);
+ }
+ if (!pack_expand_update_len(&len, tmplen)) {
+ return -1;
+ }
+ if (param->next != NULL) {
+ param = param->next;
+ }
+ }
+ return len;
+ case O_TYPE:
+ if (ctx->scope->class == SCOPE_DEFINES) {
+ // dummy scope; pack may be shadowed
+ obj = scope_lookup(ctx->scope->parent, ident);
+ if (obj != NULL && obj->otype == O_PACK) {
+ break;
+ }
+ }
+ return -1;
+ default:
+ return -1;
+ }
+
+ len = 0;
+ // `struct types` and `struct expressions` have the same representation,
+ // so this works for both expression packs and type packs
+ for (const struct expressions *expr = obj->pack_values;
+ expr; expr = expr->next) {
+ if (len++ != n) {
+ continue;
+ }
+ // put item in scope, overwriting any previous item if one
+ // exists (from a previous call to pack_expand)
+ struct scope_object *new = scope_lookup(scope, &obj->ident);
+ if (new == obj) {
+ new = xcalloc(1, sizeof(struct scope_object));
+ // dummy values, will be overwritten below
+ scope_object_init(new, O_PACK, &obj->ident,
+ &obj->name, NULL, NULL);
+ scope_insert_from_object(scope, new);
+ }
+ new->otype = obj->type != NULL ? O_CONST : O_TYPE;
+ // new->type and new->value are a union
+ new->value = expr->expr;
+ }
+
+ if (len <= n && n != (size_t)-1) {
+ return -1;
+ }
+ return len;
+}
+
+// returns -1 if there's no packs to expand or if an error occurred (e.g.
+// incompatible pack lengths), otherwise returns the length of the packs. on
+// success, the identifiers referring to the packs are inserted into 'scope' but
+// modified to instead refer to the item at index 'n', so the type can be
+// checked for each pack item. if n is -1, the length is returned and the scope
+// isn't modified (so it may be NULL in this case)
+size_t
+type_pack_expand(struct context *ctx,
+ struct scope *scope,
+ const struct ast_type *atype,
+ size_t n)
+{
+ size_t len, tmplen;
+ switch (atype->storage) {
+ case STORAGE_ALIAS:
+ len = ident_pack_expand(ctx, scope, &atype->alias,
+ atype->tmpl_args, n);
+ break;
+ case STORAGE_ARRAY:
+ case STORAGE_SLICE:
+ len = type_pack_expand(ctx, scope, atype->array.members, n);
+ if (atype->array.length != NULL) {
+ tmplen = expr_pack_expand(ctx, scope, atype->array.length, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ break;
+ case STORAGE_FUNCTION:
+ len = type_pack_expand(ctx, scope, atype->func.result, n);
+ for (const struct ast_function_parameters *p = atype->func.params;
+ p; p = p->next) {
+ if (!p->variadic) {
+ tmplen = type_pack_expand(ctx, scope, p->type, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ }
+ break;
+ case STORAGE_POINTER:
+ len = type_pack_expand(ctx, scope, atype->pointer.referent, n);
+ break;
+ case STORAGE_STRUCT:
+ case STORAGE_UNION:
+ len = -1;
+ for (const struct ast_struct_union_field *f = &atype->struct_union.fields;
+ f; f = f->next) {
+ if (f->offset != NULL) {
+ tmplen = expr_pack_expand(ctx, scope, f->offset, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ tmplen = type_pack_expand(ctx, scope, f->type, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ break;
+ case STORAGE_TAGGED:
+ len = -1;
+ for (const struct ast_tagged_union_type *tu = &atype->tagged;
+ tu; tu = tu->next) {
+ if (tu->variadic) {
+ continue;
+ };
+ tmplen = type_pack_expand(ctx, scope, tu->type, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ break;
+ case STORAGE_TUPLE:
+ len = -1;
+ for (const struct ast_tuple_type *t = &atype->tuple;
+ t; t = t->next) {
+ if (t->variadic) {
+ continue;
+ };
+ tmplen = type_pack_expand(ctx, scope, t->type, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ break;
+ case STORAGE_NULL:
+ case STORAGE_ENUM:
+ case STORAGE_FCONST:
+ case STORAGE_ICONST:
+ case STORAGE_RCONST:
+ case STORAGE_ERROR:
+ assert(0); // unreachable
+ default:
+ return -1;
+ }
+
+ return len;
+}
+
+// returns -1 if there's no packs to expand or if an error occurred (e.g.
+// incompatible pack lengths), otherwise returns the length of the packs. on
+// success, the identifiers referring to the packs are inserted into 'scope' but
+// modified to instead refer to the item at index 'n', so the expression can be
+// checked for each pack item. if n is -1, the length is returned and the scope
+// isn't modified (so it may be NULL in this case)
+static size_t
+expr_pack_expand(struct context *ctx,
+ struct scope *scope,
+ const struct ast_expression *aexpr,
+ size_t n)
+{
+ size_t len, tmplen;
+ switch (aexpr->type) {
+ case EXPR_ACCESS:
+ switch (aexpr->access.type) {
+ case ACCESS_IDENTIFIER:
+ len = ident_pack_expand(ctx, scope, &aexpr->access.ident,
+ aexpr->access.tmpl_args, n);
+ break;
+ case ACCESS_INDEX:
+ len = expr_pack_expand(ctx, scope, aexpr->access.array, n);
+ tmplen = expr_pack_expand(ctx, scope, aexpr->access.index, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ break;
+ case ACCESS_FIELD:
+ len = expr_pack_expand(ctx, scope, aexpr->access._struct, n);
+ break;
+ case ACCESS_TUPLE:
+ len = expr_pack_expand(ctx, scope, aexpr->access.tuple, n);
+ break;
+ }
+ break;
+ case EXPR_ALLOC:
+ len = expr_pack_expand(ctx, scope, aexpr->alloc.init, n);
+ tmplen = expr_pack_expand(ctx, scope, aexpr->alloc.cap, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ break;
+ case EXPR_APPEND:
+ len = expr_pack_expand(ctx, scope, aexpr->append.object, n);
+ tmplen = expr_pack_expand(ctx, scope, aexpr->append.value, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ if (aexpr->append.length != NULL) {
+ tmplen = expr_pack_expand(ctx, scope, aexpr->append.length, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ break;
+ case EXPR_ASSERT:
+ case EXPR_INSERT:
+ if (aexpr->assert.cond == NULL) return -1;
+ len = expr_pack_expand(ctx, scope, aexpr->assert.cond, n);
+ if (aexpr->assert.message != NULL) {
+ tmplen = expr_pack_expand(ctx, scope, aexpr->assert.message, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ break;
+ case EXPR_ASSIGN:
+ len = expr_pack_expand(ctx, scope, aexpr->assign.object, n);
+ tmplen = expr_pack_expand(ctx, scope, aexpr->assign.value, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ break;
+ case EXPR_BINARITHM:
+ if (aexpr->binarithm.fold) return -1;
+ len = expr_pack_expand(ctx, scope, aexpr->binarithm.lvalue, n);
+ tmplen = expr_pack_expand(ctx, scope, aexpr->binarithm.rvalue, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ break;
+ case EXPR_CALL:
+ len = expr_pack_expand(ctx, scope, aexpr->call.lvalue, n);
+ for (const struct ast_call_argument *arg = aexpr->call.args;
+ arg; arg = arg->next) {
+ if (arg->variadic) {
+ continue;
+ }
+ tmplen = expr_pack_expand(ctx, scope, arg->value, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ break;
+ case EXPR_CAST:
+ len = expr_pack_expand(ctx, scope, aexpr->cast.value, n);
+ tmplen = type_pack_expand(ctx, scope, aexpr->cast.type, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ break;
+ case EXPR_COMPOUND:
+ len = -1;
+ for (const struct ast_expression_list *expr = &aexpr->compound.list;
+ expr; expr = expr->next) {
+ tmplen = expr_pack_expand(ctx, scope, expr->expr, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ break;
+ case EXPR_DELETE:
+ len = expr_pack_expand(ctx, scope, aexpr->delete.expr, n);
+ break;
+ case EXPR_FOR:
+ len = expr_pack_expand(ctx, scope, aexpr->_for.cond, n);
+ if (aexpr->_for.bindings != NULL) {
+ tmplen = expr_pack_expand(ctx, scope, aexpr->_for.bindings, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ if (aexpr->_for.afterthought != NULL) {
+ tmplen = expr_pack_expand(ctx, scope, aexpr->_for.afterthought, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ tmplen = expr_pack_expand(ctx, scope, aexpr->_for.body, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ break;
+ case EXPR_FREE:
+ len = expr_pack_expand(ctx, scope, aexpr->free.expr, n);
+ break;
+ case EXPR_IF:
+ len = expr_pack_expand(ctx, scope, aexpr->_if.cond, n);
+ tmplen = expr_pack_expand(ctx, scope, aexpr->_if.true_branch, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ if (aexpr->_if.false_branch != NULL) {
+ tmplen = expr_pack_expand(ctx, scope, aexpr->_if.false_branch, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ break;
+ case EXPR_LITERAL:
+ if (aexpr->literal.storage != STORAGE_ARRAY) return -1;
+ len = -1;
+ for (const struct ast_array_literal *item = aexpr->literal.array;
+ item; item = item->next) {
+ if (item->expand) {
+ continue;
+ }
+ tmplen = expr_pack_expand(ctx, scope, item->value, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ break;
+ case EXPR_MATCH:
+ len = expr_pack_expand(ctx, scope, aexpr->match.value, n);
+ for (const struct ast_match_case *c = aexpr->match.cases;
+ c; c = c->next) {
+ if (c->type != NULL) {
+ tmplen = type_pack_expand(ctx, scope, c->type, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ for (const struct ast_expression_list *expr = &c->exprs;
+ expr; expr = expr->next) {
+ tmplen = expr_pack_expand(ctx, scope, expr->expr, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ }
+ break;
+ case EXPR_MEASURE:
+ switch (aexpr->measure.op) {
+ case M_ALIGN:
+ case M_SIZE:
+ len = type_pack_expand(ctx, scope, aexpr->measure.type, n);
+ break;
+ case M_LEN:
+ case M_OFFSET:
+ len = expr_pack_expand(ctx, scope, aexpr->measure.value, n);
+ break;
+ case M_SIZEPACK:
+ return -1;
+ }
+ break;
+ case EXPR_PROPAGATE:
+ len = expr_pack_expand(ctx, scope, aexpr->propagate.value, n);
+ break;
+ case EXPR_RETURN:
+ if (aexpr->_return.value == NULL) return -1;
+ len = expr_pack_expand(ctx, scope, aexpr->_return.value, n);
+ break;
+ case EXPR_SLICE:
+ len = expr_pack_expand(ctx, scope, aexpr->slice.object, n);
+ if (aexpr->slice.start != NULL) {
+ tmplen = expr_pack_expand(ctx, scope, aexpr->slice.start, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ if (aexpr->slice.end != NULL) {
+ tmplen = expr_pack_expand(ctx, scope, aexpr->slice.end, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ break;
+ case EXPR_STRUCT:
+ len = -1;
+ if (aexpr->_struct.type.name != NULL) {
+ tmplen = ident_pack_expand(ctx, scope, &aexpr->_struct.type,
+ aexpr->_struct.tmpl_args, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ for (const struct ast_field_value *f = aexpr->_struct.fields;
+ f; f = f->next) {
+ if (f->type != NULL) {
+ tmplen = type_pack_expand(ctx, scope, f->type, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ tmplen = expr_pack_expand(ctx, scope, f->initializer, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ break;
+ case EXPR_SWITCH:
+ len = expr_pack_expand(ctx, scope, aexpr->_switch.value, n);
+ for (const struct ast_switch_case *c = aexpr->_switch.cases;
+ c; c = c->next) {
+ for (const struct ast_case_option *opt = c->options;
+ opt; opt = opt->next) {
+ tmplen = expr_pack_expand(ctx, scope, opt->value, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ for (const struct ast_expression_list *expr = &c->exprs;
+ expr; expr = expr->next) {
+ tmplen = expr_pack_expand(ctx, scope, expr->expr, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ }
+ break;
+ case EXPR_TUPLE:
+ len = -1;
+ for (const struct ast_expression_tuple *item = &aexpr->tuple;
+ item; item = item->next) {
+ tmplen = expr_pack_expand(ctx, scope, item->expr, n);
+ if (!pack_expand_update_len(&len, tmplen)) return -1;
+ }
+ break;
+ case EXPR_UNARITHM:
+ len = expr_pack_expand(ctx, scope, aexpr->unarithm.operand, n);
+ break;
+ case EXPR_VAARG:
+ case EXPR_VAEND:
+ len = expr_pack_expand(ctx, scope, aexpr->vaarg.ap, n);
+ break;
+ case EXPR_YIELD:
+ if (aexpr->control.value == NULL) return -1;
+ len = expr_pack_expand(ctx, scope, aexpr->control.value, n);
+ break;
+ case EXPR_BREAK:
+ case EXPR_CONTINUE:
+ case EXPR_VASTART:
+ return -1;
+ case EXPR_BINDING:
+ case EXPR_DEFER:
+ case EXPR_DEFINE:
+ assert(0); // unreachable: disallowed by syntax
+ }
+
+ return len;
+}
+
+static void
+check_expr_fold(struct context *ctx,
+ const struct ast_expression *aexpr,
+ struct expression *expr,
+ const struct type *hint)
+{
+ struct ast_expression *pack = NULL;
+ enum { UNKNOWN, LEFT, RIGHT } kind = UNKNOWN;
+ size_t len = -1;
+
+ if (aexpr->binarithm.lvalue != NULL) {
+ size_t n = expr_pack_expand(ctx, NULL,
+ aexpr->binarithm.lvalue, -1);
+ if (n == (size_t)-1) {
+ check_expression(ctx, aexpr->binarithm.lvalue,
+ expr, NULL);
+ kind = LEFT;
+ } else {
+ pack = aexpr->binarithm.lvalue;
+ kind = RIGHT;
+ len = n;
+ }
+ }
+ if (aexpr->binarithm.rvalue != NULL) {
+ size_t n = expr_pack_expand(ctx, NULL,
+ aexpr->binarithm.rvalue, -1);
+ if (n == (size_t)-1) {
+ if (len == (size_t)-1) {
+ error(ctx, aexpr->loc, expr,
+ "Binary fold operand should contain unexpanded parameter packs, but it doesn't");
+ return;
+ }
+ assert(pack != NULL);
+ check_expression(ctx, aexpr->binarithm.rvalue,
+ expr, NULL);
+ assert(kind != LEFT);
+ kind = RIGHT;
+ } else {
+ if (len != (size_t)-1) {
+ error(ctx, aexpr->loc, expr,
+ "Binary fold operand shouldn't contain any unexpanded parameter packs, but it does");
+ return;
}
+ assert(pack == NULL);
+ pack = aexpr->binarithm.rvalue;
+ assert(kind != RIGHT);
+ kind = LEFT;
+ len = n;
}
- return false;
- case STORAGE_POINTER:
- return type->pointer.flags & PTR_NULLABLE;
- case STORAGE_STRUCT:
- case STORAGE_UNION:
- for (struct struct_field *sf = type->struct_union.fields;
- sf != NULL; sf = sf->next) {
- if (!type_has_default(ctx, sf->type)) {
- return false;
+ }
+
+ if (pack == NULL) {
+ error(ctx, aexpr->loc, expr,
+ "Fold expression must operate on unexpanded parameter packs");
+ return;
+ }
+ assert(len != (size_t)-1);
+ assert(kind != UNKNOWN);
+
+ struct scope *scope = ctx->scope;
+ scope_push(&scope, SCOPE_DEFINES);
+
+ size_t i = 0;
+ if (aexpr->binarithm.lvalue == NULL || aexpr->binarithm.rvalue == NULL) {
+ if (len == 0) {
+ expr->type = EXPR_LITERAL;
+ expr->result = &builtin_type_bool;
+
+ switch (aexpr->binarithm.op) {
+ case BIN_LAND:
+ expr->literal.bval = true;
+ break;
+ case BIN_LOR:
+ expr->literal.bval = false;
+ break;
+ default:
+ error(ctx, aexpr->loc, expr,
+ "Folding on zero-length parameter packs is only allowed with && and || operators");
}
+ return;
}
- return true;
- case STORAGE_TUPLE:
- for (const struct type_tuple *t = &type->tuple;
- t != NULL; t = t->next) {
- if (!type_has_default(ctx, t->type)) {
- return false;
- }
+
+ expr_pack_expand(ctx, scope, pack, kind == LEFT ? 0 : len - 1);
+ ctx->scope = scope;
+ check_expression(ctx, pack, expr, NULL);
+ scope_pop(&ctx->scope);
+ i = 1;
+ }
+
+ for (; i < len; i++) {
+ size_t n = kind == LEFT ? i : len - i - 1;
+ expr_pack_expand(ctx, scope, pack, n);
+
+ struct expression *operand = xcalloc(1,
+ sizeof(struct expression));
+ ctx->scope = scope;
+ check_expression(ctx, pack, operand, NULL);
+ scope_pop(&ctx->scope);
+
+ if (operand->result->storage == STORAGE_ERROR) {
+ mkerror(aexpr->loc, expr);
+ return;
+ }
+
+ struct expression *old = xcalloc(1, sizeof(struct expression));
+ *old = *expr;
+ struct expression *lvalue = kind == LEFT ? old : operand;
+ struct expression *rvalue = kind == LEFT ? operand : old;
+
+ expr->loc = aexpr->loc;
+ expr->type = EXPR_BINARITHM;
+ expr->binarithm.op = aexpr->binarithm.op;
+
+ expr->result = type_promote(ctx, lvalue->result, rvalue->result);
+ if (expr->result == NULL) {
+ char *ltypename = gen_typename(lvalue->result);
+ char *rtypename = gen_typename(rvalue->result);
+ error(ctx, aexpr->loc, expr,
+ "Cannot promote lvalue %s and rvalue %s",
+ ltypename, rtypename);
+ free(ltypename);
+ free(rtypename);
+ return;
+ }
+
+ expr->binarithm.lvalue = lower_implicit_cast(ctx,
+ expr->result, lvalue);
+ expr->binarithm.rvalue = lower_implicit_cast(ctx,
+ expr->result, rvalue);
+
+ check_binarithm_op(ctx, expr, expr->binarithm.op);
+ if (expr->result->storage == STORAGE_ERROR) {
+ return;
}
- return true;
- case STORAGE_ALIAS:
- return type_has_default(ctx, type_dealias(ctx, type));
- case STORAGE_FCONST:
- case STORAGE_ICONST:
- case STORAGE_NULL:
- case STORAGE_RCONST:
- abort(); // unreachable
}
- abort(); // Unreachable
}
static void
@@ 1124,6 2918,11 @@ check_expr_binarithm(struct context *ctx,
struct expression *expr,
const struct type *hint)
{
+ if (aexpr->binarithm.fold) {
+ check_expr_fold(ctx, aexpr, expr, hint);
+ return;
+ }
+
expr->type = EXPR_BINARITHM;
expr->binarithm.op = aexpr->binarithm.op;
@@ 1217,6 3016,7 @@ check_binding_unpack(struct context *ctx,
struct identifier ident = {
.name = cur->name,
};
+ adjust_reserved_ident(&ident);
if (abinding->is_static) {
struct identifier gen = {0};
@@ 1331,6 3131,7 @@ check_expr_binding(struct context *ctx,
type = &builtin_type_error;
}
binding->initializer = value;
+ adjust_reserved_ident(&ident);
binding->object = scope_insert(ctx->scope,
O_CONST, &ident, &ident, NULL, value);
goto done;
@@ 1346,6 3147,7 @@ check_expr_binding(struct context *ctx,
binding->object = scope_insert(ctx->scope,
O_DECL, &gen, &ident, type, NULL);
} else {
+ adjust_reserved_ident(&ident);
binding->object = scope_insert(ctx->scope,
O_BIND, &ident, &ident, type, NULL);
}
@@ 1444,32 3246,45 @@ check_expr_call(struct context *ctx,
{
expr->type = EXPR_CALL;
+ struct scope_object *obj = NULL;
+ if (aexpr->call.lvalue->type == EXPR_ACCESS
+ && aexpr->call.lvalue->access.type == ACCESS_IDENTIFIER) {
+ struct identifier ident = aexpr->call.lvalue->access.ident;
+ adjust_reserved_ident(&ident);
+ obj = scope_lookup(ctx->scope, &ident);
+ wrap_resolver(ctx, obj, resolve_decl);
+ }
+
struct expression *lvalue = xcalloc(1, sizeof(struct expression));
- check_expression(ctx, aexpr->call.lvalue, lvalue, NULL);
- expr->call.lvalue = lvalue;
+ const struct type *fntype = NULL;
+ if (!obj || obj->otype != O_TEMPLATE) {
+ check_expression(ctx, aexpr->call.lvalue, lvalue, NULL);
- const struct type *fntype = type_dereference(ctx, lvalue->result);
- if (!fntype) {
- error(ctx, aexpr->loc, expr,
- "Cannot dereference nullable pointer type for function call");
- return;
- }
- fntype = type_dealias(ctx, fntype);
- if (fntype->storage == STORAGE_ERROR) {
- mkerror(aexpr->loc, expr);
- return;
- };
- if (fntype->storage != STORAGE_FUNCTION) {
- error(ctx, aexpr->loc, expr,
- "Cannot call non-function type");
- return;
+ fntype = type_dereference(ctx, lvalue->result);
+ if (!fntype) {
+ error(ctx, aexpr->loc, expr,
+ "Cannot dereference nullable pointer type for function call");
+ return;
+ }
+ fntype = type_dealias(ctx, fntype);
+ if (fntype->storage == STORAGE_ERROR) {
+ mkerror(aexpr->loc, expr);
+ return;
+ };
+ if (fntype->storage != STORAGE_FUNCTION) {
+ error(ctx, aexpr->loc, expr,
+ "Cannot call non-function type");
+ return;
+ }
}
- expr->result = fntype->func.result;
struct call_argument *arg, **next = &expr->call.args;
struct ast_call_argument *aarg = aexpr->call.args;
- struct type_func_param *param = fntype->func.params;
- while ((param || fntype->func.variadism == VARIADISM_C) && aarg) {
+ struct type_func_param *param = fntype ? fntype->func.params : NULL;
+ size_t exp_idx = -1, exp_len = -1;
+ struct scope *exp_scope = ctx->scope;
+ scope_push(&exp_scope, SCOPE_DEFINES);
+ while ((param || !fntype || fntype->func.variadism == VARIADISM_C) && aarg) {
arg = *next = xcalloc(1, sizeof(struct call_argument));
arg->value = xcalloc(1, sizeof(struct expression));
@@ 1477,6 3292,7 @@ check_expr_call(struct context *ctx,
&& fntype->func.variadism == VARIADISM_HARE
&& !aarg->variadic) {
if (param->type->storage == STORAGE_ERROR) {
+ mkerror(aexpr->loc, expr);
return;
};
lower_vaargs(ctx, aarg, arg->value,
@@ 1487,11 3303,33 @@ check_expr_call(struct context *ctx,
break;
}
+ // TODO: type hints aren't used at all for function templates,
+ // even when they could (and should) be
const struct type *ptype = NULL;
if (param) {
ptype = param->type;
}
+ if (exp_idx == (size_t)-1 && aarg->variadic && (aarg->next || !fntype
+ || fntype->func.variadism != VARIADISM_HARE)) {
+ exp_len = expr_pack_expand(ctx, NULL, aarg->value, -1);
+ if (exp_len == (size_t)-1) {
+ error(ctx, aarg->value->loc, expr,
+ "No parameter packs to expand");
+ return;
+ }
+ exp_idx = 0;
+ }
+ if (exp_idx != (size_t)-1) {
+ expr_pack_expand(ctx, exp_scope, aarg->value, exp_idx);
+ exp_idx++;
+ if (exp_idx == exp_len) {
+ exp_idx = -1;
+ exp_len = -1;
+ }
+ }
+ ctx->scope = exp_scope;
check_expression(ctx, aarg->value, arg->value, ptype);
+ ctx->scope = ctx->scope->parent;
if (param) {
if (!type_is_assignable(ctx, ptype, arg->value->result)) {
@@ 1514,11 3352,67 @@ check_expr_call(struct context *ctx,
}
}
+ if (!fntype) {
+ check_access_identifier(ctx, aexpr->call.lvalue,
+ lvalue, expr->call.args);
+ if (lvalue->result->storage == STORAGE_ERROR) {
+ mkerror(aexpr->loc, expr);
+ return;
+ }
+ fntype = lvalue->result;
+ if (fntype->storage != STORAGE_FUNCTION) {
+ error(ctx, aexpr->loc, expr,
+ "Cannot call non-function type");
+ return;
+ }
+
+ arg = expr->call.args;
+ aarg = aexpr->call.args;
+ param = fntype->func.params;
+ while (param && aarg) {
+ assert(arg);
+
+ if (!param->next && fntype->func.variadism == VARIADISM_HARE
+ && !aarg->variadic) {
+ if (param->type->storage == STORAGE_ERROR) {
+ mkerror(aexpr->loc, expr);
+ return;
+ }
+ lower_vaargs(ctx, aarg, arg->value,
+ param->type->array.members);
+ arg->value = lower_implicit_cast(ctx,
+ param->type, arg->value);
+ param = NULL;
+ aarg = NULL;
+ break;
+ }
+
+ if (!type_is_assignable(ctx, param->type,
+ arg->value->result)) {
+ char *argtypename = gen_typename(arg->value->result);
+ char *paramtypename = gen_typename(param->type);
+ error(ctx, aarg->value->loc, expr,
+ "Argument type %s is not assignable to parameter type %s",
+ argtypename, paramtypename);
+ free(argtypename);
+ free(paramtypename);
+ return;
+ }
+ arg->value = lower_implicit_cast(ctx,
+ param->type, arg->value);
+
+ aarg = aarg->next;
+ arg = arg->next;
+ param = param->next;
+ }
+ }
+
if (param && !param->next && fntype->func.variadism == VARIADISM_HARE) {
// No variadic arguments, lower to empty slice
arg = *next = xcalloc(1, sizeof(struct call_argument));
arg->value = xcalloc(1, sizeof(struct expression));
if (param->type->storage == STORAGE_ERROR) {
+ mkerror(aexpr->loc, expr);
return;
};
lower_vaargs(ctx, NULL, arg->value,
@@ 1538,6 3432,8 @@ check_expr_call(struct context *ctx,
return;
}
+ expr->call.lvalue = lvalue;
+ expr->result = fntype->func.result;
}
static void
@@ 1625,16 3521,59 @@ check_expr_cast(struct context *ctx,
expr->result = aexpr->cast.kind == C_TEST? &builtin_type_bool : secondary;
}
+// XXX: this function could probably maybe be simplified; it's kinda funny rn
+static void
+check_array_member(struct context *ctx,
+ const struct ast_expression *aexpr,
+ struct array_literal ***next,
+ const struct type **type,
+ struct array_literal *const *array,
+ const struct type *hint,
+ size_t *len)
+{
+ struct expression *value = xcalloc(1, sizeof(struct expression));
+ check_expression(ctx, aexpr, value, *type);
+ struct array_literal *cur = **next =
+ xcalloc(1, sizeof(struct array_literal));
+ cur->value = value;
+
+ if (!*type) {
+ *type = value->result;
+ } else {
+ if (!hint) {
+ // The promote_flexible in check_expression_literal
+ // might've caused the type to change out from under our
+ // feet
+ *type = (*array)->value->result;
+ }
+ if (!type_is_assignable(ctx, *type, value->result)) {
+ char *typename1 = gen_typename(*type);
+ char *typename2 = gen_typename(value->result);
+ error(ctx, aexpr->loc, NULL,
+ "Array members must be of a uniform type, previously seen %s, but now see %s",
+ typename1, typename2);
+ free(typename1);
+ free(typename2);
+ *len = -1;
+ return;
+ }
+ if (!hint) {
+ // Ditto
+ *type = (*array)->value->result;
+ }
+ cur->value = lower_implicit_cast(ctx, *type, cur->value);
+ }
+
+ *next = &cur->next;
+ ++*len;
+}
+
static void
check_expr_array(struct context *ctx,
const struct ast_expression *aexpr,
struct expression *expr,
const struct type *hint)
{
- size_t len = 0;
- bool expand = false;
- struct ast_array_literal *item = aexpr->literal.array;
- struct array_literal *cur, **next = &expr->literal.array;
const struct type *type = NULL;
if (hint) {
hint = type_dealias(ctx, hint);
@@ 1666,46 3605,49 @@ check_expr_array(struct context *ctx,
}
}
- while (item) {
- struct expression *value = xcalloc(1, sizeof(struct expression));
- check_expression(ctx, item->value, value, type);
- cur = *next = xcalloc(1, sizeof(struct array_literal));
- cur->value = value;
+ size_t len = 0;
+ bool expand = false;
+ struct array_literal **next = &expr->literal.array;
+ for (const struct ast_array_literal *item = aexpr->literal.array;
+ item; item = item->next) {
+ size_t n = -1;
+ if (item->expand) {
+ n = expr_pack_expand(ctx, NULL, item->value, -1);
+ }
+ if (item->expand && n != (size_t)-1) {
+ struct scope *scope = ctx->scope;
+ scope_push(&scope, SCOPE_DEFINES);
+
+ for (size_t i = 0; i < n; i++) {
+ expr_pack_expand(ctx, scope, item->value, i);
+ ctx->scope = scope;
+ check_array_member(ctx, item->value, &next, &type,
+ &expr->literal.array, hint, &len);
+ if (len == (size_t)-1) {
+ mkerror(item->value->loc, expr);
+ return;
+ }
+ scope_pop(&ctx->scope);
+ }
- if (!type) {
- type = value->result;
+ continue;
} else {
- if (!hint) {
- // The promote_flexible in
- // check_expression_literal might've caused the
- // type to change out from under our feet
- type = expr->literal.array->value->result;
+ if (item->expand && n == (size_t)-1) {
+ if (item->next) {
+ error(ctx, item->value->loc, expr,
+ "No parameter packs to expand");
+ return;
+ }
+ expand = true;
}
- if (!type_is_assignable(ctx, type, value->result)) {
- char *typename1 = gen_typename(type);
- char *typename2 = gen_typename(value->result);
- error(ctx, item->value->loc, expr,
- "Array members must be of a uniform type, previously seen %s, but now see %s",
- typename1, typename2);
- free(typename1);
- free(typename2);
+
+ check_array_member(ctx, item->value, &next, &type,
+ &expr->literal.array, hint, &len);
+ if (len == (size_t)-1) {
+ mkerror(item->value->loc, expr);
return;
}
- if (!hint) {
- // Ditto
- type = expr->literal.array->value->result;
- }
- cur->value = lower_implicit_cast(ctx, type, cur->value);
}
-
- if (item->expand) {
- expand = true;
- assert(!item->next);
- }
-
- item = item->next;
- next = &cur->next;
- ++len;
}
if (type == NULL) {
@@ 2261,6 4203,7 @@ check_expr_match(struct context *ctx,
struct identifier ident = {
.name = acase->name,
};
+ adjust_reserved_ident(&ident);
struct scope *scope = scope_push(
&ctx->scope, SCOPE_MATCH);
_case->object = scope_insert(scope, O_BIND,
@@ 2343,6 4286,16 @@ check_expr_measure(struct context *ctx,
case M_ALIGN:
case M_SIZE:
break;
+ case M_SIZEPACK:
+ expr->type = EXPR_LITERAL;
+ expr->literal.uval = ident_pack_expand(ctx, NULL,
+ &aexpr->measure.ident, NULL, -1);
+ if (expr->literal.uval == (uint64_t)-1) {
+ error(ctx, aexpr->loc, expr,
+ "size... argument must be a parameter pack");
+ return;
+ }
+ return;
case M_LEN:
expr->type = EXPR_LEN;
expr->len.value = xcalloc(1, sizeof(struct expression));
@@ 2808,15 4761,25 @@ check_expr_struct(struct context *ctx,
const struct type *stype = NULL;
if (aexpr->_struct.type.name) {
- struct scope_object *obj = scope_lookup(ctx->scope,
- &aexpr->_struct.type);
+ struct identifier ident = aexpr->_struct.type;
+ adjust_reserved_ident(&ident);
+ struct scope_object *obj = scope_lookup(ctx->scope, &ident);
// resolve the unknown type
- wrap_resolver(ctx, obj, resolve_type);
+ wrap_resolver(ctx, obj, resolve_decl);
if (!obj) {
error(ctx, aexpr->loc, expr,
"Unknown type alias");
return;
}
+ if (obj->otype == O_TEMPLATE) {
+ push_error_context(ctx, aexpr->loc);
+ obj = specialize(ctx, obj, aexpr->_struct.tmpl_args, NULL);
+ pop_error_context(ctx);
+ if (!obj) {
+ mkerror(aexpr->loc, expr);
+ return;
+ }
+ }
if (obj->otype != O_TYPE) {
error(ctx, aexpr->loc, expr,
@@ 3047,6 5010,35 @@ num_cases(struct context *ctx, const struct type *type)
}
static void
+check_case_option(struct context *ctx,
+ const struct ast_expression *aexpr,
+ struct expression *expr,
+ struct case_option ***next,
+ const struct type *hint,
+ size_t *nmemb)
+{
+ struct case_option *opt = **next =
+ xcalloc(1, sizeof(struct case_option));
+ opt->value = xcalloc(1, sizeof(struct expression));
+ *next = &opt->next;
+ ++*nmemb;
+
+ struct expression *value = xcalloc(1, sizeof(struct expression));
+ check_expression(ctx, aexpr, value, hint);
+ if (!type_is_assignable(ctx, hint, value->result)) {
+ error(ctx, aexpr->loc, expr, "Invalid type for switch case");
+ return;
+ }
+ value = lower_implicit_cast(ctx, hint, value);
+
+ if (!eval_expr(ctx, value, opt->value)) {
+ error(ctx, aexpr->loc, expr,
+ "Unable to evaluate case at compile time");
+ return;
+ }
+}
+
+static void
check_expr_switch(struct context *ctx,
const struct ast_expression *aexpr,
struct expression *expr,
@@ 3091,32 5083,44 @@ check_expr_switch(struct context *ctx,
has_default_case = true;
}
- struct case_option *opt, **next_opt = &_case->options;
+ struct case_option **next_opt = &_case->options;
for (const struct ast_case_option *aopt = acase->options;
aopt; aopt = aopt->next) {
- opt = *next_opt = xcalloc(1, sizeof(struct case_option));
- struct expression *value =
- xcalloc(1, sizeof(struct expression));
- struct expression *evaled =
- xcalloc(1, sizeof(struct expression));
+ if (!aopt->variadic) {
+ check_case_option(ctx, aopt->value,
+ expr, &next_opt, type, &n);
+ continue;
+ }
- check_expression(ctx, aopt->value, value, type);
- if (!type_is_assignable(ctx, type, value->result)) {
+ size_t len = expr_pack_expand(ctx,
+ NULL, aopt->value, -1);
+ if (len == (size_t)-1) {
error(ctx, aopt->value->loc, expr,
- "Invalid type for switch case");
- return;
+ "No parameter packs to expand");
+ continue;
}
- value = lower_implicit_cast(ctx, type, value);
- if (!eval_expr(ctx, value, evaled)) {
- error(ctx, aopt->value->loc, expr,
- "Unable to evaluate case at compile time");
- return;
+ struct scope *scope = ctx->scope;
+ scope_push(&scope, SCOPE_DEFINES);
+
+ for (size_t i = 0; i < len; i++) {
+ expr_pack_expand(ctx, scope, aopt->value, i);
+ ctx->scope = scope;
+ check_case_option(ctx, aopt->value,
+ expr, &next_opt, type, &n);
+ scope_pop(&ctx->scope);
}
+ }
- opt->value = evaled;
- next_opt = &opt->next;
- n++;
+ // check if error occurred in check_switch_case_options
+ if (expr->type == EXPR_LITERAL) {
+ assert(expr->result->storage == STORAGE_ERROR);
+ return;
+ }
+
+ if (acase->options != NULL && _case->options == NULL) {
+ error(ctx, acase->exprs.expr->loc, _case->value,
+ "Non-default case has no options");
}
// Lower to compound
@@ 3145,33 5149,25 @@ check_expr_switch(struct context *ctx,
struct expression *_case;
struct location loc;
};
- struct located_case *cases_array = xcalloc(n, sizeof(struct located_case));
+ struct expression **cases_array = xcalloc(n, sizeof(struct expression *));
size_t i = 0;
- for (acase = aexpr->_switch.cases, _case = expr->_switch.cases;
- _case; acase = acase->next, _case = _case->next) {
- assert(acase);
- const struct ast_case_option *aopt;
- const struct case_option *opt;
- for (aopt = acase->options, opt = _case->options;
- opt; aopt = aopt->next, opt = opt->next) {
- assert(aopt);
+ for (_case = expr->_switch.cases; _case; _case = _case->next) {
+ for (const struct case_option *opt = _case->options;
+ opt; opt = opt->next) {
assert(i < n);
- cases_array[i]._case = opt->value;
- cases_array[i].loc = aopt->value->loc;
+ cases_array[i] = opt->value;
i++;
}
- assert(!aopt);
}
- assert(!acase);
assert(i == n);
- qsort(cases_array, n, sizeof(struct located_case), &casecmp);
+ qsort(cases_array, n, sizeof(struct expression *), &casecmp);
bool has_duplicate = false;
for (size_t i = 1; i < n; i++) {
- if (cases_array[i]._case->result->storage == STORAGE_ERROR) {
+ if (cases_array[i]->result->storage == STORAGE_ERROR) {
break;
}
- const struct expression_literal *a = &cases_array[i - 1]._case->literal;
- const struct expression_literal *b = &cases_array[i]._case->literal;
+ const struct expression_literal *a = &cases_array[i - 1]->literal;
+ const struct expression_literal *b = &cases_array[i]->literal;
bool equal;
if (type_is_integer(ctx, value->result)) {
equal = a->uval == b->uval;
@@ 3188,7 5184,7 @@ check_expr_switch(struct context *ctx,
equal = a->rune == b->rune;
}
if (equal) {
- error(ctx, cases_array[i].loc, cases_array[i]._case,
+ error(ctx, cases_array[i]->loc, cases_array[i],
"Duplicate switch case");
has_duplicate = true;
}
@@ 3238,6 5234,32 @@ check_expr_switch(struct context *ctx,
}
static void
+check_tuple_member(struct context *ctx,
+ const struct ast_expression *aexpr,
+ struct expression_tuple **tuple,
+ const struct type_tuple **hint,
+ struct type_tuple **result,
+ size_t *nmemb)
+{
+ if (*nmemb > 0) {
+ (*result)->next = xcalloc(1, sizeof(struct type_tuple));
+ *result = (*result)->next;
+ (*tuple)->next = xcalloc(1, sizeof(struct expression_tuple));
+ *tuple = (*tuple)->next;
+ }
+ ++*nmemb;
+
+ (*tuple)->value = xcalloc(1, sizeof(struct expression));
+ check_expression(ctx, aexpr, (*tuple)->value,
+ *hint ? (*hint)->type : NULL);
+ (*result)->type = (*tuple)->value->result;
+
+ if (*hint) {
+ *hint = (*hint)->next;
+ }
+}
+
+static void
check_expr_tuple(struct context *ctx,
const struct ast_expression *aexpr,
struct expression *expr,
@@ 3253,25 5275,41 @@ check_expr_tuple(struct context *ctx,
struct type_tuple result = {0};
struct type_tuple *rtype = &result;
+ size_t nmemb = 0;
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->variadic) {
+ size_t n = expr_pack_expand(ctx,
+ NULL, atuple->expr, -1);
+ if (n == (size_t)-1) {
+ error(ctx, atuple->expr->loc, expr,
+ "No parameter packs to expand");
+ return;
+ }
- 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;
- }
+ struct scope *scope = ctx->scope;
+ scope_push(&scope, SCOPE_DEFINES);
- if (ttuple) {
- ttuple = ttuple->next;
+ for (size_t i = 0; i < n; i++) {
+ expr_pack_expand(ctx, scope, atuple->expr, i);
+ ctx->scope = scope;
+ check_tuple_member(ctx, atuple->expr, &tuple,
+ &ttuple, &rtype, &nmemb);
+ scope_pop(&ctx->scope);
+ }
+ } else {
+ check_tuple_member(ctx, atuple->expr, &tuple,
+ &ttuple, &rtype, &nmemb);
}
}
+ if (nmemb < 2) {
+ error(ctx, aexpr->loc, expr,
+ "Tuple must contain at least two members");
+ return;
+ }
+
if (hint && type_dealias(ctx, hint)->storage == STORAGE_TUPLE) {
expr->result = hint;
} else if (hint && type_dealias(ctx, hint)->storage == STORAGE_TAGGED) {
@@ 3314,21 5352,19 @@ check_expr_tuple(struct context *ctx,
ttuple = &type_dealias(ctx, expr->result)->tuple;
struct expression_tuple *etuple = &expr->tuple;
- const struct ast_expression_tuple *atuple = &aexpr->tuple;
while (etuple) {
if (!ttuple) {
- error(ctx, atuple->expr->loc, expr,
+ error(ctx, etuple->value->loc, expr,
"Too many values for tuple type");
return;
}
if (!type_is_assignable(ctx, ttuple->type, etuple->value->result)) {
- error(ctx, atuple->expr->loc, expr,
+ error(ctx, etuple->value->loc, expr,
"Value is not assignable to tuple value type");
return;
}
etuple->value = lower_implicit_cast(ctx, ttuple->type, etuple->value);
etuple = etuple->next;
- atuple = atuple->next;
ttuple = ttuple->next;
}
if (ttuple) {
@@ 3615,17 5651,6 @@ append_decl(struct context *ctx, struct declaration *decl)
ctx->decls = decls;
}
-static void
-resolve_unresolved(struct context *ctx)
-{
- while (ctx->unresolved) {
- struct ast_types *unresolved = ctx->unresolved;
- ctx->unresolved = unresolved->next;
- type_store_lookup_atype(ctx, unresolved->type);
- free(unresolved);
- }
-}
-
void
check_function(struct context *ctx,
const struct scope_object *obj,
@@ 3662,24 5687,55 @@ check_function(struct context *ctx,
}
decl->func.scope = scope_push(&ctx->scope, SCOPE_FUNC);
- struct ast_function_parameters *params = afndecl->prototype.params;
- while (params) {
- if (!params->name) {
- error(ctx, params->loc, NULL,
+ const struct ast_function_parameters *aparams = afndecl->prototype.params;
+ const struct type_func_param *params = ctx->fntype->func.params;
+ while (aparams) {
+ if (!aparams->type) {
+ assert(aparams->variadic);
+ assert(!params);
+ assert(ctx->fntype->func.variadism == VARIADISM_C);
+ break;
+ }
+ if (!aparams->name) {
+ error(ctx, aparams->loc, NULL,
"Function parameters must be named");
return;
}
struct identifier ident = {
- .name = params->name,
+ .name = aparams->name,
};
+
+ if (aparams->variadic && (aparams->next
+ || ctx->fntype->func.variadism != VARIADISM_HARE)) {
+ adjust_reserved_ident(&ident);
+ struct scope_object *obj = scope_insert(decl->func.scope,
+ O_PACK, &ident, &ident, NULL, NULL);
+ struct types **next = &obj->pack_types;
+ while (params) {
+ *next = xcalloc(1, sizeof(struct types));
+ (*next)->type = params->type;
+ next = &(*next)->next;
+ params = params->next;
+ }
+ break;
+ }
+
+ assert(params);
+ // i'm pretty sure the lookup here is still necessary even
+ // though we already have the type, because the scoping rules
+ // are different for function implementation declarations than
+ // for function prototype declarations or function types
const struct type *type = type_store_lookup_atype(
- ctx, params->type);
- if (obj->type->func.variadism == VARIADISM_HARE
- && !params->next) {
- type = type_store_lookup_slice(ctx, params->loc, type);
+ ctx, aparams->type);
+ if (aparams->variadic) {
+ assert(!aparams->next);
+ assert(ctx->fntype->func.variadism == VARIADISM_HARE);
+ type = type_store_lookup_slice(ctx, aparams->loc, type);
}
+ adjust_reserved_ident(&ident);
scope_insert(decl->func.scope, O_BIND,
&ident, &ident, type, NULL);
+ aparams = aparams->next;
params = params->next;
}
@@ 3737,29 5793,74 @@ end:
if ((adecl->function.flags & FN_TEST) && !ctx->is_test) {
return;
}
- append_decl(ctx, decl);
+ append_decl(ctx, decl);
+}
+
+static struct incomplete_declaration *
+incomplete_declaration_create(struct context *ctx, struct location loc,
+ struct scope *scope, const struct identifier *ident,
+ const struct identifier *name, bool no_adjust_symbol)
+{
+ struct scope *subunit = ctx->unit->parent;
+ ctx->unit->parent = NULL;
+ struct identifier adj_name = *name;
+ struct identifier adj_ident = *ident;
+ if (!no_adjust_symbol) {
+ adjust_reserved_ident(&adj_name);
+ // adjust_reserved_ident's buffer is statically allocated, so
+ // temporarily duplicate adj_name.name before overwriting the
+ // buffer (identifiers will be duplicated in scope_object_init)
+ adj_name.name = xstrdup(adj_name.name);
+ adjust_reserved_ident(&adj_ident);
+ }
+ struct incomplete_declaration *idecl =
+ (struct incomplete_declaration *)scope_lookup(scope, &adj_name);
+ ctx->unit->parent = subunit;
+
+ if (idecl) {
+ error_norec(ctx, loc, "Duplicate global identifier '%s'",
+ identifier_unparse(ident));
+ }
+ idecl = xcalloc(1, sizeof(struct incomplete_declaration));
+
+ scope_object_init((struct scope_object *)idecl, O_SCAN,
+ &adj_ident, &adj_name, NULL, NULL);
+ if (!no_adjust_symbol) {
+ free(adj_name.name);
+ }
+ scope_insert_from_object(scope, (struct scope_object *)idecl);
+ return idecl;
}
static struct incomplete_declaration *
-incomplete_declaration_create(struct context *ctx, struct location loc,
- struct scope *scope, const struct identifier *ident,
- const struct identifier *name)
+incomplete_template_create(struct context *ctx,
+ struct location loc,
+ const struct identifier *ident)
{
struct scope *subunit = ctx->unit->parent;
ctx->unit->parent = NULL;
+ struct identifier adjusted = *ident;
+ adjust_reserved_ident(&adjusted);
struct incomplete_declaration *idecl =
- (struct incomplete_declaration *)scope_lookup(scope, name);
+ (struct incomplete_declaration *)scope_lookup(ctx->scope, &adjusted);
ctx->unit->parent = subunit;
if (idecl) {
- error_norec(ctx, loc, "Duplicate global identifier '%s'",
- identifier_unparse(ident));
+ if (idecl->type == IDECL_ENUM_FLD
+ || (!idecl->next && !idecl->decl.template)) {
+ error_norec(ctx, loc,
+ "Incompatible duplicate global identifier: '%s'",
+ identifier_unparse(ident));
+ }
+ return idecl;
}
- idecl = xcalloc(1, sizeof(struct incomplete_declaration));
+ idecl = xcalloc(1, sizeof(struct incomplete_declaration));
scope_object_init((struct scope_object *)idecl, O_SCAN,
- ident, name, NULL, NULL);
- scope_insert_from_object(scope, (struct scope_object *)idecl);
+ &adjusted, &adjusted, NULL, NULL);
+ scope_insert_from_object(ctx->scope, (struct scope_object *)idecl);
+
+ idecl->type = IDECL_DECL;
return idecl;
}
@@ 3792,7 5893,7 @@ scan_enum_field(struct context *ctx, struct scope *imports,
};
struct incomplete_declaration *fld =
incomplete_declaration_create(ctx, f->loc, enum_scope,
- &name, &localname);
+ &name, &localname, false);
fld->type = IDECL_ENUM_FLD;
fld->imports = imports;
fld->obj.type = etype,
@@ 3857,7 5958,7 @@ scan_types(struct context *ctx, struct scope *imp, const struct ast_decl *decl)
check_hosted_main(ctx, decl->loc, NULL, with_ns, NULL);
struct incomplete_declaration *idecl =
incomplete_declaration_create(ctx, decl->loc, ctx->scope,
- &with_ns, &t->ident);
+ &with_ns, &t->ident, false);
idecl->decl = (struct ast_decl){
.decl_type = ADECL_TYPE,
.loc = decl->loc,
@@ 4085,6 6186,13 @@ end:
});
}
+static bool
+ident_is_reserved(const struct identifier *ident)
+{
+ return strncmp(ident->name, "_Z", 2) == 0
+ && ident->name[strlen(ident->name) - 1] != '$';
+}
+
void
resolve_function(struct context *ctx, struct incomplete_declaration *idecl)
{
@@ 4102,6 6210,17 @@ resolve_function(struct context *ctx, struct incomplete_declaration *idecl)
idecl->obj.otype = O_DECL;
idecl->obj.type = fntype;
+
+ // function templates need to be checked here for scoping reasons
+ // functions which aren't templates are checked later, to allow for what
+ // would otherwise be circular dependencies in the function body
+ // XXX: circular dependencies aren't allowed in function bodies. this is
+ // kinda a lazy way to get things working because the scoping stuff is
+ // complicated as hell and i coded everything in a very unmaintainable
+ // janky way
+ if (ident_is_reserved(&idecl->obj.ident)) {
+ check_function(ctx, &idecl->obj, &idecl->decl);
+ }
}
void
@@ 4197,6 6316,337 @@ end:
}
static void
+check_explicit_tmpl_instantiation(struct context *ctx,
+ const struct scope_object *obj,
+ const struct ast_decl *adecl)
+{
+ const struct type *type;
+ switch (adecl->decl_type) {
+ case ADECL_GLOBAL:
+ if (adecl->global.init != NULL) {
+ error(ctx, adecl->loc, NULL,
+ "Explicit instantiation can't be initialized");
+ return;
+ }
+ assert(adecl->global.type != NULL);
+ type = type_store_lookup_atype(ctx, adecl->global.type);
+ break;
+ case ADECL_FUNC:
+ if (adecl->function.body != NULL) {
+ error(ctx, adecl->loc, NULL,
+ "Explicit instantiation can't be implemented");
+ return;
+ }
+ const struct ast_type fn_atype = {
+ .storage = STORAGE_FUNCTION,
+ .flags = 0,
+ .func = adecl->function.prototype,
+ };
+ type = type_store_lookup_atype(ctx, &fn_atype);
+ break;
+ default:
+ assert(0); // unreachable
+ }
+ if (type->id != obj->type->id) {
+ error(ctx, adecl->loc, NULL,
+ "Explicit instantiation's type is incompatible with template declaration");
+ return;
+ }
+}
+
+static void
+instantiate_extern_template(struct context *ctx,
+ struct incomplete_declaration *idecl)
+{
+ // remove this incomplete_declaration from the scope, since it doesn't
+ // actually declare anything
+ uint32_t hash = fnv1a_s(FNV1A_INIT, idecl->obj.name.name);
+ struct scope_object **bucket =
+ &ctx->unit->buckets[hash % SCOPE_BUCKETS];
+ if (*bucket == &idecl->obj) {
+ *bucket = idecl->obj.mnext;
+ } else {
+ for (;;) {
+ assert(*bucket != NULL);
+ if ((*bucket)->mnext == &idecl->obj) {
+ (*bucket)->mnext = idecl->obj.mnext;
+ break;
+ }
+ bucket = &(*bucket)->mnext;
+ }
+ }
+
+ assert(idecl->next != NULL);
+ const struct identifier *ident;
+ switch (idecl->next->decl.decl_type) {
+ case ADECL_GLOBAL:
+ ident = &idecl->next->decl.global.ident;
+ break;
+ case ADECL_FUNC:
+ ident = &idecl->next->decl.function.ident;
+ break;
+ case ADECL_CONST:
+ case ADECL_TYPE:
+ error(ctx, idecl->next->decl.loc, NULL,
+ "Explicit instantiation can't be used for %s template",
+ idecl->next->decl.decl_type == ADECL_CONST ? "constant" : "type");
+ return;
+ case ADECL_ASSERT:
+ assert(0); // unreachable
+ }
+
+ struct scope_object *obj = scope_lookup(idecl->next->imports, ident);
+ if (obj == NULL) {
+ char *identstr = identifier_unparse(ident);
+ error(ctx, idecl->decl.loc, NULL, "Unknown template '%s'",
+ identstr);
+ free(identstr);
+ return;
+ }
+ if (obj->otype != O_TEMPLATE) {
+ char *identstr = identifier_unparse(ident);
+ error(ctx, idecl->decl.loc, NULL,
+ "Identifier '%s' does not refer to a template",
+ identstr);
+ free(identstr);
+ return;
+ }
+
+ for (const struct incomplete_specialization *isp = idecl->next;
+ isp; isp = isp->next) {
+ if (isp->decl.template) {
+ error_norec(ctx, isp->decl.loc,
+ "Can't declare explicit specialization of imported template");
+ continue;
+ }
+ if (!isp->decl.exported) {
+ error(ctx, isp->decl.loc, NULL,
+ "Template instantiation should be exported");
+ }
+ if (idecl->decl.decl_type != isp->decl.decl_type) {
+ error_norec(ctx, isp->decl.loc,
+ "Explicit template instantiation is incompatible with template declaration");
+ }
+
+ struct types *fn_params = NULL;
+ if (isp->decl.decl_type == ADECL_FUNC) {
+ fn_params = types_from_ast_func_params(ctx,
+ isp->decl.function.prototype.params);
+ }
+ push_error_context(ctx, isp->decl.loc);
+ const struct scope_object *new = specialize(ctx,
+ obj, isp->decl.specialization, fn_params);
+ pop_error_context(ctx);
+ if (new != NULL) {
+ check_explicit_tmpl_instantiation(ctx, new, &isp->decl);
+ }
+ }
+}
+
+static void
+resolve_template_specialization(struct context *ctx,
+ const struct ast_decl *decl,
+ struct identifier *ident_out,
+ struct ast_type **type_out,
+ struct ast_expression **expr_out,
+ bool *threadlocal_out)
+{
+ switch (decl->decl_type) {
+ case ADECL_CONST:
+ case ADECL_GLOBAL:
+ if (decl->global.symbol != NULL) {
+ error(ctx, decl->loc, NULL,
+ "Can't use @symbol on template declaration");
+ }
+ if (decl->global.threadlocal) {
+ *threadlocal_out = true;
+ }
+ if (ident_out != NULL) {
+ mkident(ctx, ident_out, &decl->global.ident, NULL);
+ }
+ *type_out = decl->global.type;
+ *expr_out = decl->global.init;
+ break;
+ case ADECL_FUNC:
+ if (decl->function.symbol != NULL) {
+ error(ctx, decl->loc, NULL,
+ "Can't use @symbol on template declaration");
+ }
+ if (decl->function.flags != 0) {
+ error(ctx, decl->loc, NULL,
+ "Can't use @init, @fini, or @test on template declaration");
+ }
+ if (ident_out != NULL) {
+ mkident(ctx, ident_out, &decl->function.ident, NULL);
+ }
+ *type_out = xcalloc(1, sizeof(struct ast_type));
+ (*type_out)->storage = STORAGE_FUNCTION;
+ (*type_out)->func = decl->function.prototype;
+ *expr_out = decl->function.body;
+ break;
+ case ADECL_TYPE:
+ if (decl->type.type->storage == STORAGE_ENUM) {
+ error_norec(ctx, decl->loc,
+ "Type template declaration can't declare enum");
+ }
+ if (ident_out != NULL) {
+ mkident(ctx, ident_out, &decl->type.ident, NULL);
+ }
+ *type_out = decl->type.type;
+ break;
+ case ADECL_ASSERT:
+ assert(0); // unreachable
+ }
+}
+
+static void
+resolve_template(struct context *ctx, struct incomplete_declaration *idecl)
+{
+ if (idecl->decl.decl_type != ADECL_FUNC
+ && idecl->decl.tmpl_params != NULL) {
+ for (const struct ast_template_parameters *p = idecl->decl.tmpl_params;
+ p->next != NULL; p = p->next) {
+ assert(!p->is_pack || p->init == NULL);
+ // XXX: these errors are probably recoverable
+ if (p->is_pack) {
+ error_norec(ctx, p->next->loc,
+ "Parameter pack can't be followed by additional parameters in non-function template");
+ } else if (p->init != NULL && p->next->init == NULL
+ && !p->next->is_pack) {
+ error_norec(ctx, p->next->loc,
+ "Non-pack parameter without default %s follows parameter with default %s in non-function template",
+ p->next->type != NULL ? "value" : "type",
+ p->type != NULL ? "value" : "type");
+ }
+ }
+ }
+
+ struct declaration decl = {0};
+ decl.decl_type = DECL_TEMPLATE;
+ decl.file = idecl->decl.loc.file;
+ decl.exported = idecl->decl.exported;
+ decl.template.imports = idecl->imports;
+ decl.template.unit = ctx->unit;
+ decl.template.params = idecl->decl.tmpl_params;
+ decl.template.decl_type = idecl->decl.decl_type;
+ decl.template.exported = idecl->decl.exported;
+ decl.template.loc = idecl->decl.loc;
+
+ // reseolve unspecialized declaration
+ resolve_template_specialization(ctx, &idecl->decl, &decl.ident,
+ &decl.template.type, &decl.template.expr,
+ &decl.template.threadlocal);
+ struct identifier adjusted = decl.ident;
+ adjust_reserved_ident(&adjusted);
+ identifier_dup(&idecl->obj.ident, &adjusted);
+
+ // resolve explicit specializations, and check explicit instantiations
+ struct template_specialization **next = &decl.template.specializations;
+ for (const struct incomplete_specialization *isp = idecl->next;
+ isp; isp = isp->next) {
+ if (idecl->decl.exported != isp->decl.exported) {
+ error(ctx, isp->decl.loc, NULL,
+ "Template %s should%s be exported",
+ isp->decl.template ? "instantiation" : "specialization",
+ idecl->decl.exported ? "" : "n't");
+ }
+ if (!isp->decl.template) { // explicit instantiation
+ if (idecl->decl.decl_type != isp->decl.decl_type) {
+ error_norec(ctx, isp->decl.loc,
+ "Explicit template instantiation is incompatible with template declaration");
+ }
+
+ switch (isp->decl.decl_type) {
+ case ADECL_GLOBAL:
+ if (isp->decl.global.init != NULL) {
+ error(ctx, isp->decl.loc, NULL,
+ "Explicit instantiation can't be initialized");
+ }
+ break;
+ case ADECL_FUNC:
+ if (isp->decl.function.body != NULL) {
+ error(ctx, isp->decl.loc, NULL,
+ "Explicit instantiation can't be implemented");
+ }
+ break;
+ case ADECL_CONST:
+ case ADECL_TYPE:
+ error(ctx, isp->decl.loc, NULL,
+ "Explicit instantiation can't be used for %s template",
+ isp->decl.decl_type == ADECL_CONST ? "constant" : "type");
+ break;
+ case ADECL_ASSERT:
+ assert(0); // unreachable
+ }
+
+ continue; // instantiated later below
+ } else if (idecl->decl.decl_type != isp->decl.decl_type) {
+ error_norec(ctx, isp->decl.loc,
+ "Specialized template declaration is incompatible with unspecialized template declaration");
+ }
+
+ // TODO: disallow default arguments in explicit specializations
+ *next = xcalloc(1, sizeof(struct template_specialization));
+ (*next)->imports = isp->imports;
+ (*next)->params = isp->decl.tmpl_params;
+ (*next)->args = isp->decl.specialization;
+ (*next)->loc = isp->decl.loc;
+
+ resolve_template_specialization(ctx, &isp->decl,
+ NULL, &(*next)->type, &(*next)->expr,
+ &(*next)->threadlocal);
+
+ next = &(*next)->next;
+ }
+
+ append_decl(ctx, &decl);
+ idecl->obj.otype = O_TEMPLATE;
+ idecl->obj.template = &ctx->decls->decl.template;
+
+ // instantiate full specializations and explicit instantiations
+ const struct template_specialization *sp = decl.template.specializations;
+ for (const struct incomplete_specialization *isp = idecl->next;
+ isp; isp = isp->next) {
+ assert(!isp->decl.template || sp != NULL);
+ const struct ast_template_argument *args =
+ isp->decl.specialization;
+ if (isp->decl.template && sp->params == NULL) {
+ struct types *fn_params = NULL;
+ if (isp->decl.decl_type == ADECL_FUNC) {
+ assert(sp->type->storage == STORAGE_FUNCTION);
+ fn_params = types_from_ast_func_params(
+ ctx, sp->type->func.params);
+ }
+ push_error_context(ctx, isp->decl.loc);
+ specialize(ctx, &idecl->obj, args, fn_params);
+ pop_error_context(ctx);
+ sp = sp->next;
+ } else if (isp->decl.template) {
+ sp = sp->next;
+ } else if (isp->decl.decl_type == ADECL_CONST
+ || isp->decl.decl_type == ADECL_TYPE) {
+ error(ctx, isp->decl.loc, NULL,
+ "Explicit instantiation can't be used for %s template",
+ isp->decl.decl_type == ADECL_CONST ? "constant" : "type");
+ } else {
+ struct types *fn_params = NULL;
+ if (isp->decl.decl_type == ADECL_FUNC) {
+ fn_params = types_from_ast_func_params(ctx,
+ isp->decl.function.prototype.params);
+ }
+ push_error_context(ctx, isp->decl.loc);
+ const struct scope_object *new =
+ specialize(ctx, &idecl->obj, args, fn_params);
+ pop_error_context(ctx);
+ if (new != NULL) {
+ check_explicit_tmpl_instantiation(
+ ctx, new, &isp->decl);
+ }
+ }
+ }
+}
+
+static void
resolve_enum_field(struct context *ctx, struct incomplete_declaration *idecl)
{
assert(idecl->type == IDECL_ENUM_FLD);
@@ 4284,13 6734,14 @@ lookup_enum_type(struct context *ctx, const struct scope_object *obj)
}
if (idecl->decl.type.type->storage == STORAGE_ENUM) {
- assert(false);
+ assert(idecl->decl.template);
+ return NULL; // let this error out later
} else if (idecl->decl.type.type->storage == STORAGE_ALIAS) {
ctx->scope->parent = idecl->imports;
- const struct identifier *alias =
- &idecl->decl.type.type->alias;
+ struct identifier alias = idecl->decl.type.type->alias;
+ adjust_reserved_ident(&alias);
const struct scope_object *new = scope_lookup(ctx->scope,
- alias);
+ &alias);
if (new) {
idecl->in_progress = true;
enum_type = lookup_enum_type(ctx, new);
@@ 4317,54 6768,6 @@ lookup_enum_type(struct context *ctx, const struct scope_object *obj)
return enum_type;
}
-static void
-scan_enum_field_aliases(struct context *ctx, struct scope_object *obj)
-{
- const struct type *enum_type = lookup_enum_type(ctx, obj);
-
- if (!enum_type) {
- return;
- }
-
- // orig->type is (perhaps transitively) an alias of a resolved enum
- // type, which means its dependency graph is a linear chain of
- // resolved types ending with that enum, so we can immediately resolve it
- wrap_resolver(ctx, obj, resolve_type);
-
- for (const struct scope_object *val = enum_type->_enum.values->objects;
- val; val = val->lnext) {
- struct identifier name = {
- .name = val->name.name,
- .ns = (struct identifier *)&obj->name,
- };
- struct identifier ident = {
- .name = val->name.name,
- .ns = (struct identifier *)&obj->ident,
- };
- struct ast_enum_field *afield =
- xcalloc(1, sizeof(struct ast_enum_field));
- *afield = (struct ast_enum_field){
- .loc = (struct location){0}, // XXX: what to put here?
- .name = xstrdup(val->name.name),
- };
-
- struct incomplete_enum_field *field =
- xcalloc(1, sizeof(struct incomplete_enum_field));
- struct incomplete_declaration *idecl =
- (struct incomplete_declaration *)val;
- *field = (struct incomplete_enum_field){
- .field = afield,
- .enum_scope = idecl->field->enum_scope,
- };
-
- idecl = incomplete_declaration_create(ctx, (struct location){0},
- ctx->scope, &ident, &name);
- idecl->type = IDECL_ENUM_FLD;
- idecl->obj.type = obj->type;
- idecl->field = field;
- };
-}
-
void
resolve_dimensions(struct context *ctx, struct incomplete_declaration *idecl)
{
@@ 4390,7 6793,7 @@ resolve_dimensions(struct context *ctx, struct incomplete_declaration *idecl)
};
}
-void
+static void
resolve_type(struct context *ctx, struct incomplete_declaration *idecl)
{
struct location loc;
@@ 4454,6 6857,54 @@ resolve_type(struct context *ctx, struct incomplete_declaration *idecl)
});
}
+static void
+scan_enum_field_aliases(struct context *ctx, struct scope_object *obj)
+{
+ const struct type *enum_type = lookup_enum_type(ctx, obj);
+
+ if (!enum_type) {
+ return;
+ }
+
+ // orig->type is (perhaps transitively) an alias of a resolved enum
+ // type, which means its dependency graph is a linear chain of
+ // resolved types ending with that enum, so we can immediately resolve it
+ wrap_resolver(ctx, obj, resolve_type);
+
+ for (const struct scope_object *val = enum_type->_enum.values->objects;
+ val; val = val->lnext) {
+ struct identifier name = {
+ .name = val->name.name,
+ .ns = (struct identifier *)&obj->name,
+ };
+ struct identifier ident = {
+ .name = val->name.name,
+ .ns = (struct identifier *)&obj->ident,
+ };
+ struct ast_enum_field *afield =
+ xcalloc(1, sizeof(struct ast_enum_field));
+ *afield = (struct ast_enum_field){
+ .loc = (struct location){0}, // XXX: what to put here?
+ .name = xstrdup(val->name.name),
+ };
+
+ struct incomplete_enum_field *field =
+ xcalloc(1, sizeof(struct incomplete_enum_field));
+ struct incomplete_declaration *idecl =
+ (struct incomplete_declaration *)val;
+ *field = (struct incomplete_enum_field){
+ .field = afield,
+ .enum_scope = idecl->field->enum_scope,
+ };
+
+ idecl = incomplete_declaration_create(ctx, (struct location){0},
+ ctx->scope, &ident, &name, true);
+ idecl->type = IDECL_ENUM_FLD;
+ idecl->obj.type = obj->type;
+ idecl->field = field;
+ };
+}
+
static struct incomplete_declaration *
scan_const(struct context *ctx, struct scope *imports, bool exported,
struct location loc, const struct ast_global_decl *decl)
@@ 4463,7 6914,7 @@ scan_const(struct context *ctx, struct scope *imports, bool exported,
check_hosted_main(ctx, loc, NULL, with_ns, NULL);
struct incomplete_declaration *idecl =
incomplete_declaration_create(ctx, loc,
- ctx->scope, &with_ns, &decl->ident);
+ ctx->scope, &with_ns, &decl->ident, false);
idecl->type = IDECL_DECL;
idecl->decl = (struct ast_decl){
.decl_type = ADECL_CONST,
@@ 4476,10 6927,55 @@ scan_const(struct context *ctx, struct scope *imports, bool exported,
}
static void
+scan_template(struct context *ctx, struct scope *imports, const struct ast_decl *decl)
+{
+ const struct identifier *ident;
+ switch (decl->decl_type) {
+ case ADECL_CONST:
+ case ADECL_GLOBAL:
+ ident = &decl->global.ident;
+ break;
+ case ADECL_FUNC:
+ ident = &decl->function.ident;
+ break;
+ case ADECL_TYPE:
+ ident = &decl->type.ident;
+ break;
+ case ADECL_ASSERT:
+ assert(0); // Invariant
+ }
+
+ struct incomplete_declaration *idecl =
+ incomplete_template_create(ctx, decl->loc, ident);
+ if (idecl->has_unspecialized && !decl->specialization) {
+ error_norec(ctx, decl->loc,
+ "Duplicate unspecialized template declaration: '%s'",
+ identifier_unparse(ident));
+ }
+
+ if (decl->specialization) {
+ struct incomplete_specialization *sp =
+ xcalloc(1, sizeof(struct incomplete_specialization));
+ sp->decl = *decl;
+ sp->imports = imports;
+ sp->next = idecl->next;
+ idecl->next = sp;
+ } else {
+ idecl->imports = imports;
+ idecl->has_unspecialized = true;
+ idecl->decl = *decl;
+ }
+}
+
+static void
scan_decl(struct context *ctx, struct scope *imports, const struct ast_decl *decl)
{
struct incomplete_declaration *idecl = {0};
struct identifier ident = {0};
+ if (decl->template || decl->specialization) {
+ scan_template(ctx, imports, decl);
+ return;
+ };
switch (decl->decl_type) {
case ADECL_CONST:
for (const struct ast_global_decl *g = &decl->constant;
@@ 4493,7 6989,8 @@ scan_decl(struct context *ctx, struct scope *imports, const struct ast_decl *dec
mkident(ctx, &ident, &g->ident, g->symbol);
check_hosted_main(ctx, decl->loc, NULL, ident, g->symbol);
idecl = incomplete_declaration_create(ctx, decl->loc,
- ctx->scope, &ident, &g->ident);
+ ctx->scope, &ident, &g->ident,
+ g->symbol != NULL);
idecl->type = IDECL_DECL;
idecl->decl = (struct ast_decl){
.decl_type = ADECL_GLOBAL,
@@ 4526,7 7023,7 @@ scan_decl(struct context *ctx, struct scope *imports, const struct ast_decl *dec
name = &func->ident;
}
idecl = incomplete_declaration_create(ctx, decl->loc,
- ctx->scope, &ident, name);
+ ctx->scope, &ident, name, func->symbol != NULL);
check_hosted_main(ctx, decl->loc, decl, ident, func->symbol);
idecl->type = IDECL_DECL;
idecl->decl = (struct ast_decl){
@@ 4547,7 7044,7 @@ scan_decl(struct context *ctx, struct scope *imports, const struct ast_decl *dec
snprintf(ident.name, n + 1, "static assert %" PRIu64, num);
++num;
idecl = incomplete_declaration_create(ctx, decl->loc,
- ctx->scope, &ident, &ident);
+ ctx->scope, &ident, &ident, true);
idecl->type = IDECL_DECL;
idecl->decl = (struct ast_decl){
.decl_type = ADECL_ASSERT,
@@ 4560,7 7057,7 @@ scan_decl(struct context *ctx, struct scope *imports, const struct ast_decl *dec
}
}
-static void
+void
resolve_decl(struct context *ctx, struct incomplete_declaration *idecl)
{
switch (idecl->type) {
@@ 4570,6 7067,18 @@ resolve_decl(struct context *ctx, struct incomplete_declaration *idecl)
case IDECL_DECL:
break;
}
+ // specialized template declarations will fail to parse if no
+ // unspecialized declaration exists
+ if (idecl->next && !idecl->has_unspeciali