llm
Download
89 lines 3.8 KiBCModified 2026-08-31 raw
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
// This file defines how to compile variable declarations

#include "../ast.h"
#include "../environment.h"
#include "../stdlib/datatypes.h"
#include "../stdlib/text.h"
#include "../typecheck.h"
#include "../util.h"
#include "compilation.h"

public
Text_t compile_declaration(type_t *t, Text_t name) {
    if (t->tag == FunctionType) {
        DeclareMatch(fn, t, FunctionType);
        Text_t code = Texts(compile_type(fn->ret), " (*", name, ")(");
        for (arg_t *arg = fn->args; arg; arg = arg->next) {
            code = Texts(code, compile_type(arg->type));
            if (arg->next) code = Texts(code, ", ");
        }
        if (!fn->args) code = Texts(code, "void");
        return Texts(code, ")");
    } else if (t->tag != ModuleType) {
        return Texts(compile_type(t), " ", name);
    } else {
        return EMPTY_TEXT;
    }
}

// A declared value has an incomplete type: some part of it is an empty list or
// table literal whose element type can't be inferred. Incomplete types aren't
// necessarily containers, and the culprit may be nested (`@[]`, `{"hi": []}`),
// so drill into the AST to point at the specific literal, falling back to a
// general message at `ast` when we can't attribute it to one.
static _Noreturn void report_incomplete_type(env_t *env, ast_t *ast) {
    switch (ast->tag) {
    case List: {
        DeclareMatch(list, ast, List);
        if (list->items == NULL)
            code_err(ast, "I can't tell what type of items this empty list holds. "
                          "Please give it a type annotation, like: `[Int]`");
        for (ast_list_t *item = list->items; item; item = item->next) {
            if (item->ast->tag == Comprehension) continue; // comprehensions have a concrete element type
            if (is_incomplete_type(get_type(env, item->ast))) report_incomplete_type(env, item->ast);
        }
        break;
    }
    case Table: {
        DeclareMatch(table, ast, Table);
        if (table->entries == NULL)
            code_err(ast, "I can't tell what types of keys and values this empty table holds. "
                          "Please give it a type annotation, like: `{Text:Int}`");
        for (ast_list_t *e = table->entries; e; e = e->next) {
            if (e->ast->tag != TableEntry) continue;
            DeclareMatch(entry, e->ast, TableEntry);
            if (entry->key && is_incomplete_type(get_type(env, entry->key))) report_incomplete_type(env, entry->key);
            if (entry->value && is_incomplete_type(get_type(env, entry->value)))
                report_incomplete_type(env, entry->value);
        }
        break;
    }
    case HeapAllocate: report_incomplete_type(env, Match(ast, HeapAllocate)->value); break;
    case StackReference: report_incomplete_type(env, Match(ast, StackReference)->value); break;
    default: break;
    }
    code_err(ast, "I can't determine the type of this value. Please give it a type annotation.");
}

public
Text_t compile_declared_value(env_t *env, ast_t *declare_ast) {
    DeclareMatch(decl, declare_ast, Declare);
    type_t *t = decl->type ? parse_type_ast(env, decl->type) : get_type(env, decl->value);

    if (t->tag == AbortType || t->tag == VoidType || t->tag == ReturnType)
        code_err(declare_ast, "You can't declare a variable with a ", type_to_text(t), " value");

    if (decl->value && is_incomplete_type(t)) report_incomplete_type(env, decl->value);

    if (decl->value) {
        Text_t val_code = compile_maybe_incref(env, decl->value, t);
        if (t->tag == FunctionType) assert(promote(env, decl->value, &val_code, t, Type(ClosureType, t)));
        return val_code;
    } else {
        Text_t val_code = compile_empty(t);
        if (val_code.length == 0)
            code_err(declare_ast, "This type (", type_to_text(t),
                     ") cannot be uninitialized. You must provide a value.");
        return val_code;
    }
}