mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
492 lines
13 KiB
C
492 lines
13 KiB
C
// Copyright (c) 2020 Christoffer Lerno. All rights reserved.
|
|
// Use of this source code is governed by a LGPLv3.0
|
|
// a copy of which can be found in the LICENSE file.
|
|
|
|
#include "sema_internal.h"
|
|
|
|
void parent_path(StringSlice *slice)
|
|
{
|
|
for (int i = (int)slice->len - 1; i >= 0; i--)
|
|
{
|
|
if (slice->ptr[i] == ':')
|
|
{
|
|
slice->len = i - 1;
|
|
return;
|
|
}
|
|
}
|
|
slice->len = 0;
|
|
}
|
|
|
|
void sema_analyse_pass_top(Module *module)
|
|
{
|
|
Module *parent = module;
|
|
while (parent->parent_module) parent = parent->parent_module;
|
|
module->top_module = parent;
|
|
}
|
|
|
|
void sema_analyse_pass_module_hierarchy(Module *module)
|
|
{
|
|
const char *name = module->name->module;
|
|
StringSlice slice = slice_from_string(name);
|
|
// foo::bar::baz -> foo::bar
|
|
parent_path(&slice);
|
|
// foo -> return, no parent
|
|
if (!slice.len) return;
|
|
|
|
|
|
unsigned module_count = vec_size(global_context.module_list);
|
|
for (int i = 0; i < module_count; i++)
|
|
{
|
|
Module *checked = global_context.module_list[i];
|
|
Path *checked_name = checked->name;
|
|
if (checked_name->len != slice.len) continue;
|
|
// Found the parent! We're done, we add this parent
|
|
// and this as a child.
|
|
if (memcmp(checked_name->module, slice.ptr, slice.len) == 0)
|
|
{
|
|
module->parent_module = checked;
|
|
vec_add(checked->sub_modules, module);
|
|
return;
|
|
}
|
|
}
|
|
// No match, so we create a synthetic module.
|
|
Path *path = path_create_from_string(slice.ptr, slice.len, module->name->span);
|
|
DEBUG_LOG("Creating parent module for %s: %s", module->name->module, path->module);
|
|
Module *parent_module = compiler_find_or_create_module(path, NULL);
|
|
module->parent_module = parent_module;
|
|
vec_add(parent_module->sub_modules, module);
|
|
sema_analyze_stage(parent_module, ANALYSIS_MODULE_HIERARCHY);
|
|
}
|
|
|
|
|
|
void sema_analysis_pass_process_imports(Module *module)
|
|
{
|
|
DEBUG_LOG("Pass: Importing dependencies for files in module '%s'.", module->name->module);
|
|
|
|
unsigned import_count = 0;
|
|
VECEACH(module->units, index)
|
|
{
|
|
// 1. Loop through each context in the module.
|
|
CompilationUnit *unit = module->units[index];
|
|
DEBUG_LOG("Checking imports for %s.", unit->file->name);
|
|
|
|
// 2. Loop through imports
|
|
unsigned imports = vec_size(unit->imports);
|
|
|
|
for (unsigned i = 0; i < imports; i++)
|
|
{
|
|
// 3. Begin analysis
|
|
Decl *import = unit->imports[i];
|
|
assert(import->resolve_status == RESOLVE_NOT_DONE);
|
|
import->resolve_status = RESOLVE_RUNNING;
|
|
|
|
// 4. Find the module.
|
|
Path *path = import->import.path;
|
|
Module *import_module = global_context_find_module(path->module);
|
|
|
|
// 5. Do we find it?
|
|
if (!import_module)
|
|
{
|
|
SEMA_ERROR(import, "No module named '%s' could be found, did you type the name right?", path->module);
|
|
decl_poison(import);
|
|
continue;
|
|
}
|
|
|
|
// 6. Importing itself is not allowed.
|
|
if (import_module == module)
|
|
{
|
|
SEMA_ERROR(import, "Importing the current module is not allowed, you need to remove it.");
|
|
decl_poison(import);
|
|
continue;
|
|
}
|
|
|
|
// 7. Assign the module.
|
|
DEBUG_LOG("* Import of %s.", path->module);
|
|
import->import.module = import_module;
|
|
}
|
|
import_count += imports;
|
|
}
|
|
(void)import_count; // workaround for clang 13.0
|
|
DEBUG_LOG("Pass finished processing %d import(s) with %d error(s).", import_count, global_context.errors_found);
|
|
}
|
|
|
|
void sema_analysis_pass_register_globals(Module *module)
|
|
{
|
|
DEBUG_LOG("Pass: Register globals for module '%s'.", module->name->module);
|
|
|
|
VECEACH(module->units, index)
|
|
{
|
|
CompilationUnit *unit = module->units[index];
|
|
unit->module = module;
|
|
DEBUG_LOG("Processing %s.", unit->file->name);
|
|
Decl **decls = unit->global_decls;
|
|
VECEACH(decls, i)
|
|
{
|
|
unit_register_global_decl(unit, decls[i]);
|
|
}
|
|
vec_resize(unit->global_decls, 0);
|
|
}
|
|
|
|
DEBUG_LOG("Pass finished with %d error(s).", global_context.errors_found);
|
|
}
|
|
|
|
static inline void sema_append_decls(CompilationUnit *unit, Decl **decls)
|
|
{
|
|
VECEACH(decls, i)
|
|
{
|
|
unit_register_global_decl(unit, decls[i]);
|
|
}
|
|
}
|
|
|
|
static inline bool sema_analyse_top_level_if(SemaContext *context, Decl *ct_if)
|
|
{
|
|
int res = sema_check_comp_time_bool(context, ct_if->ct_if_decl.expr);
|
|
if (res == -1) return false;
|
|
if (res)
|
|
{
|
|
// Append declarations
|
|
sema_append_decls(context->unit, ct_if->ct_if_decl.then);
|
|
return true;
|
|
}
|
|
|
|
// False, so check elifs
|
|
Decl *ct_elif = ct_if->ct_if_decl.elif;
|
|
while (ct_elif)
|
|
{
|
|
if (ct_elif->decl_kind == DECL_CT_ELIF)
|
|
{
|
|
res = sema_check_comp_time_bool(context, ct_elif->ct_elif_decl.expr);
|
|
if (res == -1) return false;
|
|
if (res)
|
|
{
|
|
sema_append_decls(context->unit, ct_elif->ct_elif_decl.then);
|
|
return true;
|
|
}
|
|
ct_elif = ct_elif->ct_elif_decl.elif;
|
|
}
|
|
else
|
|
{
|
|
assert(ct_elif->decl_kind == DECL_CT_ELSE);
|
|
sema_append_decls(context->unit, ct_elif->ct_else_decl);
|
|
return true;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
static inline bool sema_analyse_top_level_switch(SemaContext *context, Decl *ct_switch)
|
|
{
|
|
Expr *cond = ct_switch->ct_switch_decl.expr;
|
|
if (cond && !sema_analyse_ct_expr(context, cond)) return false;
|
|
Type *type = cond ? cond->type : type_bool;
|
|
bool is_type = type == type_typeid;
|
|
ExprConst *switch_expr_const = cond ? &cond->const_expr : NULL;
|
|
Decl **cases = ct_switch->ct_switch_decl.cases;
|
|
|
|
unsigned case_count = vec_size(cases);
|
|
int matched_case = (int)case_count;
|
|
int default_case = (int)case_count;
|
|
for (unsigned i = 0; i < case_count; i++)
|
|
{
|
|
Decl *kase = cases[i];
|
|
Expr *expr = kase->ct_case_decl.expr;
|
|
Expr *to_expr = kase->ct_case_decl.to_expr;
|
|
if (expr)
|
|
{
|
|
if (is_type)
|
|
{
|
|
if (!sema_analyse_ct_expr(context, expr)) return false;
|
|
if (expr->type != type_typeid)
|
|
{
|
|
SEMA_ERROR(expr, "A type was expected here not %s.", type_quoted_error_string(expr->type));
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!sema_analyse_expr_rhs(context, type, expr, false)) return false;
|
|
if (to_expr && !sema_analyse_expr_rhs(context, type, to_expr, false)) return false;
|
|
}
|
|
if (expr->expr_kind != EXPR_CONST)
|
|
{
|
|
SEMA_ERROR(expr, "The $case must have a constant expression.");
|
|
return false;
|
|
}
|
|
if (!cond)
|
|
{
|
|
if (!expr->const_expr.b) continue;
|
|
if (matched_case == case_count) matched_case = (int)i;
|
|
continue;
|
|
}
|
|
if (to_expr && to_expr->expr_kind != EXPR_CONST)
|
|
{
|
|
SEMA_ERROR(to_expr, "The $case must have a constant expression.");
|
|
return false;
|
|
}
|
|
ExprConst *const_expr = &expr->const_expr;
|
|
ExprConst *const_to_expr = to_expr ? &to_expr->const_expr : const_expr;
|
|
if (to_expr && expr_const_compare(const_expr, const_to_expr, BINARYOP_GT))
|
|
{
|
|
SEMA_ERROR(to_expr, "The end of a range must be less or equal to the beginning.");
|
|
return false;
|
|
}
|
|
// Check that it is unique.
|
|
for (unsigned j = 0; j < i; j++)
|
|
{
|
|
Decl *other_case = cases[j];
|
|
|
|
// Default.
|
|
if (!other_case->ct_case_decl.expr) continue;
|
|
ExprConst *other_const = &other_case->ct_case_decl.expr->const_expr;
|
|
ExprConst *other_const_to = other_case->ct_case_decl.to_expr
|
|
? &other_case->ct_case_decl.to_expr->const_expr : other_const;
|
|
if (expr_const_in_range(const_expr, other_const, other_const_to))
|
|
{
|
|
SEMA_ERROR(kase, "'%s' appears more than once.", expr_const_to_error_string(const_expr));
|
|
SEMA_NOTE(cases[j]->ct_case_decl.expr, "The previous $case was here.");
|
|
return false;
|
|
}
|
|
}
|
|
if (expr_const_in_range(switch_expr_const, const_expr, const_to_expr))
|
|
{
|
|
matched_case = (int)i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (default_case < case_count)
|
|
{
|
|
SEMA_ERROR(kase, "More than one $default is not allowed.");
|
|
SEMA_NOTE(cases[default_case], "The previous $default was here.");
|
|
return false;
|
|
}
|
|
default_case = (int)i;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (matched_case == case_count) matched_case = default_case;
|
|
|
|
for (int i = matched_case; i < case_count; i++)
|
|
{
|
|
Decl **body = cases[i]->ct_case_decl.body;
|
|
if (body)
|
|
{
|
|
sema_append_decls(context->unit, body);
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void sema_analysis_pass_conditional_compilation(Module *module)
|
|
{
|
|
|
|
DEBUG_LOG("Pass: Top level conditionals %s", module->name->module);
|
|
VECEACH(module->units, index)
|
|
{
|
|
CompilationUnit *unit = module->units[index];
|
|
for (unsigned i = 0; i < vec_size(unit->ct_ifs); i++)
|
|
{
|
|
// Also handle switch!
|
|
SemaContext context;
|
|
sema_context_init(&context, unit);
|
|
Decl *decl = unit->ct_ifs[i];
|
|
bool success;
|
|
switch (decl->decl_kind)
|
|
{
|
|
case DECL_CT_IF:
|
|
success = sema_analyse_top_level_if(&context, decl);
|
|
break;
|
|
case DECL_CT_SWITCH:
|
|
success = sema_analyse_top_level_switch(&context, decl);
|
|
break;
|
|
case DECL_CT_INCLUDE:
|
|
sema_append_decls(context.unit, decl->include.decls);
|
|
success = true;
|
|
break;
|
|
default:
|
|
UNREACHABLE
|
|
}
|
|
sema_context_destroy(&context);
|
|
if (!success) goto END;
|
|
}
|
|
}
|
|
END:
|
|
DEBUG_LOG("Pass finished with %d error(s).", global_context.errors_found);
|
|
}
|
|
|
|
void sema_analysis_pass_ct_assert(Module *module)
|
|
{
|
|
DEBUG_LOG("Pass: $assert checks %s", module->name->module);
|
|
VECEACH(module->units, index)
|
|
{
|
|
SemaContext context;
|
|
sema_context_init(&context, module->units[index]);
|
|
Decl **asserts = context.unit->ct_asserts;
|
|
bool success = true;
|
|
VECEACH(asserts, i)
|
|
{
|
|
if (!sema_analyse_ct_assert_stmt(&context, asserts[i]->ct_assert_decl))
|
|
{
|
|
success = false;
|
|
break;
|
|
}
|
|
}
|
|
sema_context_destroy(&context);
|
|
if (!success) break;
|
|
}
|
|
DEBUG_LOG("Pass finished with %d error(s).", global_context.errors_found);
|
|
}
|
|
|
|
void sema_analysis_pass_ct_echo(Module *module)
|
|
{
|
|
DEBUG_LOG("Pass: $echo checks %s", module->name->module);
|
|
VECEACH(module->units, index)
|
|
{
|
|
SemaContext context;
|
|
sema_context_init(&context, module->units[index]);
|
|
Decl **echos = context.unit->ct_echos;
|
|
bool success = true;
|
|
VECEACH(echos, i)
|
|
{
|
|
if (!sema_analyse_ct_echo_stmt(&context, echos[i]->ct_echo_decl))
|
|
{
|
|
success = false;
|
|
break;
|
|
}
|
|
}
|
|
sema_context_destroy(&context);
|
|
if (!success) break;
|
|
}
|
|
DEBUG_LOG("Pass finished with %d error(s).", global_context.errors_found);
|
|
}
|
|
|
|
static inline bool analyse_func_body(SemaContext *context, Decl *decl)
|
|
{
|
|
if (!decl->func_decl.body) return true;
|
|
if (decl->is_extern)
|
|
{
|
|
SEMA_ERROR(decl, "'extern' functions should never have a body.");
|
|
return decl_poison(decl);
|
|
}
|
|
// Don't analyse functions that are tests.
|
|
if (decl->func_decl.attr_test && !active_target.testing) return true;
|
|
if (!sema_analyse_function_body(context, decl)) return decl_poison(decl);
|
|
return true;
|
|
}
|
|
|
|
void sema_analysis_pass_decls(Module *module)
|
|
{
|
|
DEBUG_LOG("Pass: Decl analysis %s", module->name->module);
|
|
|
|
VECEACH(module->units, index)
|
|
{
|
|
CompilationUnit *unit = module->units[index];
|
|
SemaContext context;
|
|
sema_context_init(&context, unit);
|
|
context.active_scope = (DynamicScope)
|
|
{
|
|
.depth = 0,
|
|
.scope_id = 0,
|
|
.label_start = 0,
|
|
.current_local = 0,
|
|
};
|
|
VECEACH(unit->attributes, i)
|
|
{
|
|
sema_analyse_decl(&context, unit->attributes[i]);
|
|
}
|
|
VECEACH(unit->enums, i)
|
|
{
|
|
sema_analyse_decl(&context, unit->enums[i]);
|
|
}
|
|
VECEACH(unit->types, i)
|
|
{
|
|
sema_analyse_decl(&context, unit->types[i]);
|
|
}
|
|
VECEACH(unit->macros, i)
|
|
{
|
|
sema_analyse_decl(&context, unit->macros[i]);
|
|
}
|
|
VECEACH(unit->methods, i)
|
|
{
|
|
sema_analyse_decl(&context, unit->methods[i]);
|
|
}
|
|
VECEACH(unit->macro_methods, i)
|
|
{
|
|
sema_analyse_decl(&context, unit->macro_methods[i]);
|
|
}
|
|
VECEACH(unit->vars, i)
|
|
{
|
|
sema_analyse_decl(&context, unit->vars[i]);
|
|
}
|
|
VECEACH(unit->functions, i)
|
|
{
|
|
sema_analyse_decl(&context, unit->functions[i]);
|
|
}
|
|
if (unit->main_function && unit->main_function->is_synthetic)
|
|
{
|
|
sema_analyse_decl(&context, unit->main_function);
|
|
}
|
|
VECEACH(unit->generic_defines, i)
|
|
{
|
|
sema_analyse_decl(&context, unit->generic_defines[i]);
|
|
}
|
|
sema_context_destroy(&context);
|
|
}
|
|
DEBUG_LOG("Pass finished with %d error(s).", global_context.errors_found);
|
|
}
|
|
|
|
void sema_analysis_pass_lambda(Module *module)
|
|
{
|
|
DEBUG_LOG("Extra pass: Lambda analysis %s", module->name->module);
|
|
|
|
VECEACH(module->units, index)
|
|
{
|
|
while (vec_size(module->lambdas_to_evaluate))
|
|
{
|
|
Decl *lambda = VECLAST(module->lambdas_to_evaluate);
|
|
CompilationUnit *unit = lambda->unit;
|
|
SemaContext context;
|
|
sema_context_init(&context, unit);
|
|
vec_pop(module->lambdas_to_evaluate);
|
|
if (analyse_func_body(&context, lambda))
|
|
{
|
|
vec_add(unit->lambdas, lambda);
|
|
}
|
|
sema_context_destroy(&context);
|
|
}
|
|
}
|
|
|
|
DEBUG_LOG("Pass finished with %d error(s).", global_context.errors_found);
|
|
}
|
|
|
|
void sema_analysis_pass_functions(Module *module)
|
|
{
|
|
DEBUG_LOG("Pass: Function analysis %s", module->name->module);
|
|
|
|
VECEACH(module->units, index)
|
|
{
|
|
CompilationUnit *unit = module->units[index];
|
|
SemaContext context;
|
|
sema_context_init(&context, unit);
|
|
VECEACH(unit->xxlizers, i)
|
|
{
|
|
sema_analyse_decl(&context, unit->xxlizers[i]);
|
|
}
|
|
VECEACH(unit->methods, i)
|
|
{
|
|
analyse_func_body(&context, unit->methods[i]);
|
|
}
|
|
VECEACH(unit->functions, i)
|
|
{
|
|
analyse_func_body(&context, unit->functions[i]);
|
|
}
|
|
if (unit->main_function && unit->main_function->is_synthetic) analyse_func_body(&context, unit->main_function);
|
|
sema_context_destroy(&context);
|
|
}
|
|
|
|
DEBUG_LOG("Pass finished with %d error(s).", global_context.errors_found);
|
|
}
|