Files
c3c/src/compiler/sema_stmts.c

2521 lines
75 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"
// --- Helper functions
static bool sema_analyse_compound_stmt(SemaContext *context, Ast *statement);
typedef enum
{
COND_TYPE_UNWRAP_BOOL,
COND_TYPE_UNWRAP,
COND_TYPE_EVALTYPE_VALUE,
} CondType;
static void sema_unwrappable_from_catch_in_else(SemaContext *c, Expr *cond)
{
assert(cond->expr_kind == EXPR_COND);
Expr *last = VECLAST(cond->cond_expr);
while (last->expr_kind == EXPR_CAST)
{
last = last->cast_expr.expr;
}
if (!last || last->expr_kind != EXPR_CATCH_UNWRAP) return;
Expr **unwrapped = last->catch_unwrap_expr.exprs;
VECEACH(unwrapped, i)
{
Expr *expr = unwrapped[i];
if (expr->expr_kind != EXPR_IDENTIFIER) continue;
Decl *decl = expr->identifier_expr.decl;
if (decl->decl_kind != DECL_VAR) continue;
assert(decl->type->type_kind == TYPE_FAILABLE && "The variable should always be failable at this point.");
// 5. Locals and globals may be unwrapped
switch (decl->var.kind)
{
case VARDECL_LOCAL:
case VARDECL_GLOBAL:
sema_unwrap_var(c, decl);
break;
default:
continue;
}
}
}
// --- Sema analyse stmts
static inline bool sema_analyse_block_return_stmt(SemaContext *context, Ast *statement)
{
assert(context->active_scope.flags & (SCOPE_EXPR_BLOCK | SCOPE_MACRO));
context->active_scope.jump_end = true;
if (statement->return_stmt.expr)
{
Type *block_type = context->expected_block_type;
if (block_type)
{
if (!sema_analyse_expr_rhs(context, block_type, statement->return_stmt.expr, type_is_failable(block_type))) return false;
}
else
{
if (!sema_analyse_expr(context, statement->return_stmt.expr)) return false;
}
}
vec_add(context->returns, statement);
return true;
}
/**
* We have the following possibilities:
* 1. return
* 2. return <non void expr>
* 3. return <void expr>
*
* If we are in a block or a macro expansion we need to handle it differently.
*
* @param context
* @param statement
* @return
*/
static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement)
{
// This might be a return in a function block or a macro which must be treated differently.
if (context->active_scope.flags & (SCOPE_EXPR_BLOCK | SCOPE_MACRO))
{
return sema_analyse_block_return_stmt(context, statement);
}
// 1. We mark that the current scope ends with a jump.
context->active_scope.jump_end = true;
Type *expected_rtype = context->rtype;
assert(expected_rtype && "We should always have known type from a function return.");
Expr *return_expr = statement->return_stmt.expr;
statement->return_stmt.defer = context->active_scope.defer_last;
// 2. First handle the plain return.
if (return_expr == NULL)
{
if (type_no_fail(expected_rtype)->canonical != type_void)
{
SEMA_ERROR(statement, "Expected to return a result of type %s.", type_to_error_string(expected_rtype));
return false;
}
return true;
}
// 3. Evaluate the return value to be the expected return type.
if (!sema_analyse_expr_rhs(context, expected_rtype, return_expr, type_is_failable(expected_rtype))) return false;
assert(type_no_fail(statement->return_stmt.expr->type)->canonical == type_no_fail(expected_rtype)->canonical);
return true;
}
static inline bool sema_analyse_unreachable_stmt(SemaContext *context)
{
context->active_scope.jump_end = true;
return true;
}
static inline bool sema_analyse_try_unwrap(SemaContext *context, Expr *expr)
{
assert(expr->expr_kind == EXPR_TRY_UNWRAP);
Expr *ident = expr->try_unwrap_expr.variable;
Expr *failable = expr->try_unwrap_expr.init;
// Case A. Unwrapping a single variable.
if (!failable)
{
if (!sema_analyse_expr(context, ident)) return false;
if (ident->expr_kind != EXPR_IDENTIFIER)
{
SEMA_ERROR(ident, "Only single identifiers may be unwrapped using 'try var', maybe you wanted 'try (expr)' instead?");
return false;
}
Decl *decl = ident->identifier_expr.decl;
if (decl->decl_kind != DECL_VAR)
{
SEMA_ERROR(ident, "Expected this to be the name of a failable variable, but it isn't. Did you mistype?");
return false;
}
if (!IS_FAILABLE(decl))
{
if (decl->var.kind == VARDECL_UNWRAPPED)
{
SEMA_ERROR(ident, "This variable is already unwrapped, so you cannot use 'try' on it again, please remove the 'try'.");
return false;
}
SEMA_ERROR(ident, "Expected this variable to be a failable, otherwise it can't be used for unwrap, maybe you didn't intend to use 'try'?");
return false;
}
expr->try_unwrap_expr.decl = decl;
expr->type = type_bool;
sema_unwrap_var(context, decl);
expr->resolve_status = RESOLVE_DONE;
return true;
}
// Case B. We are unwrapping to a variable that may or may not exist.
bool implicit_declaration = false;
TypeInfo *var_type = expr->try_unwrap_expr.type;
// 1. Check if we are doing an implicit declaration.
if (!var_type && ident->expr_kind == EXPR_IDENTIFIER)
{
Decl *decl = sema_resolve_normal_symbol(context, ident->identifier_expr.identifier, NULL, false);
if (!decl) implicit_declaration = true;
}
// 2. If we have a type for the variable, resolve it.
if (var_type)
{
if (!sema_resolve_type_info(context, var_type)) return false;
if (IS_FAILABLE(var_type))
{
SEMA_ERROR(var_type, "Only non-failable types may be used as types for 'try', please remove the '!'.");
return false;
}
}
// 3. We interpret this as an assignment to an existing variable.
if (!var_type && !implicit_declaration)
{
// 3a. Resolve the identifier.
if (!sema_analyse_expr_lvalue(context, ident)) return false;
// 3b. Make sure it's assignable
if (!expr_is_ltype(ident))
{
SEMA_ERROR(ident, "'try' expected an assignable variable or expression here, did you make a mistake?");
return false;
}
if (ident->expr_kind == EXPR_IDENTIFIER) ident->identifier_expr.decl->var.is_written = true;
// 3c. It can't be failable either.
if (IS_FAILABLE(ident))
{
if (ident->expr_kind == EXPR_IDENTIFIER)
{
SEMA_ERROR(ident, "This is a failable variable, you should only have non-failable variables on the left side unless you use 'try' without '='.");
}
else
{
SEMA_ERROR(ident, "This is a failable expression, it can't go on the left hand side of a 'try'.");
}
return false;
}
// 3d. We can now analyse the expression using the variable type.
if (!sema_analyse_expr(context, failable)) return false;
if (!IS_FAILABLE(failable))
{
SEMA_ERROR(failable, "Expected a failable expression to 'try' here. If it isn't a failable, remove 'try'.");
return false;
}
if (!cast_implicit(failable, ident->type)) return false;
expr->try_unwrap_expr.assign_existing = true;
expr->try_unwrap_expr.lhs = ident;
}
else
{
// 4. We are creating a new variable
// 4a. If we had a variable type, then our expression must be an identifier.
if (ident->expr_kind != EXPR_IDENTIFIER)
{
SEMA_ERROR(ident, "A variable name was expected here.");
return false;
}
if (ident->identifier_expr.path)
{
sema_error_range(ident->identifier_expr.path->span, "The variable may not have a path.");
return false;
}
TokenId ident_token = ident->identifier_expr.identifier;
if (TOKTYPE(ident_token) != TOKEN_IDENT)
{
SEMA_ERROR(ident, "Expected a variable starting with a lower case letter.");
return false;
}
// 4b. Evaluate the expression
if (!sema_analyse_expr(context, failable)) return false;
if (!IS_FAILABLE(failable))
{
SEMA_ERROR(failable, "Expected a failable expression to 'try' here. If it isn't a failable, remove 'try'.");
return false;
}
if (var_type)
{
if (!cast_implicit(failable, var_type->type)) return false;
}
// 4c. Create a type_info if needed.
if (!var_type)
{
var_type = type_info_new_base(failable->type->failable, failable->span);
}
// 4d. A new declaration is created.
Decl *decl = decl_new_var(ident_token, var_type, VARDECL_LOCAL, VISIBLE_LOCAL);
// 4e. Analyse it
if (!sema_analyse_var_decl(context, decl, true)) return false;
expr->try_unwrap_expr.decl = decl;
}
expr->try_unwrap_expr.failable = failable;
expr->type = type_bool;
expr->resolve_status = RESOLVE_DONE;
return true;
}
static inline bool sema_analyse_try_unwrap_chain(SemaContext *context, Expr *expr, CondType cond_type)
{
assert(cond_type == COND_TYPE_UNWRAP_BOOL || cond_type == COND_TYPE_UNWRAP);
assert(expr->expr_kind == EXPR_TRY_UNWRAP_CHAIN);
Expr **chain = expr->try_unwrap_chain_expr;
unsigned elements = vec_size(chain);
VECEACH(expr->try_unwrap_chain_expr, i)
{
Expr *chain_element = chain[i];
if (chain_element->expr_kind == EXPR_TRY_UNWRAP)
{
if (!sema_analyse_try_unwrap(context, chain_element)) return false;
continue;
}
if (!sema_analyse_cond_expr(context, chain_element)) return false;
}
expr->type = type_bool;
expr->resolve_status = RESOLVE_DONE;
return true;
}
static inline bool sema_analyse_catch_unwrap(SemaContext *context, Expr *expr)
{
Expr *ident = expr->catch_unwrap_expr.variable;
bool implicit_declaration = false;
TypeInfo *type = expr->catch_unwrap_expr.type;
if (!type && !ident)
{
expr->catch_unwrap_expr.lhs = NULL;
expr->catch_unwrap_expr.decl = NULL;
goto RESOLVE_EXPRS;
}
if (!type && ident->expr_kind == EXPR_IDENTIFIER)
{
Decl *decl = sema_resolve_normal_symbol(context, ident->identifier_expr.identifier, NULL, false);
if (!decl) implicit_declaration = true;
}
if (!type && !implicit_declaration)
{
if (!sema_analyse_expr_lvalue(context, ident)) return false;
if (!expr_is_ltype(ident))
{
SEMA_ERROR(ident, "'catch' expected an assignable variable or expression here, did you make a mistake?");
return false;
}
if (ident->expr_kind == EXPR_IDENTIFIER) ident->identifier_expr.decl->var.is_written = true;
if (ident->type->canonical != type_anyerr)
{
SEMA_ERROR(ident, "Expected the variable to have the type %s, not %s.", type_quoted_error_string(type_anyerr),
type_quoted_error_string(type->type));
return false;
}
expr->catch_unwrap_expr.lhs = ident;
expr->catch_unwrap_expr.decl = NULL;
}
else
{
type = type ? type : type_info_new_base(type_anyerr, expr->span);
if (!sema_resolve_type_info(context, type)) return false;
if (type->type->canonical != type_anyerr)
{
SEMA_ERROR(type, "Expected the type to be %s, not %s.", type_quoted_error_string(type_anyerr),
type_quoted_error_string(type->type));
return false;
}
if (ident->expr_kind != EXPR_IDENTIFIER)
{
SEMA_ERROR(ident, "A variable name was expected here.");
return false;
}
if (ident->identifier_expr.path)
{
sema_error_range(ident->identifier_expr.path->span, "The variable may not have a path.");
return false;
}
TokenId ident_token = ident->identifier_expr.identifier;
if (TOKTYPE(ident_token) != TOKEN_IDENT)
{
SEMA_ERROR(ident, "Expected a variable starting with a lower case letter.");
return false;
}
// 4d. A new declaration is created.
Decl *decl = decl_new_var(ident_token, type, VARDECL_LOCAL, VISIBLE_LOCAL);
decl->var.init_expr = expr_new(EXPR_UNDEF, decl->span);
// 4e. Analyse it
if (!sema_analyse_var_decl(context, decl, true)) return false;
expr->catch_unwrap_expr.decl = decl;
expr->catch_unwrap_expr.lhs = NULL;
}
RESOLVE_EXPRS:;
Expr **exprs = expr->catch_unwrap_expr.exprs;
VECEACH(exprs, i)
{
Expr *fail = exprs[i];
if (!sema_analyse_expr(context, fail)) return false;
if (!type_is_failable(fail->type))
{
SEMA_ERROR(fail, "This expression is not failable, did you add it by mistake?");
return false;
}
}
expr->type = type_anyerr;
expr->resolve_status = RESOLVE_DONE;
return true;
}
static void sema_remove_unwraps_from_try(SemaContext *c, Expr *cond)
{
assert(cond->expr_kind == EXPR_COND);
Expr *last = VECLAST(cond->cond_expr);
if (!last || last->expr_kind != EXPR_TRY_UNWRAP_CHAIN) return;
Expr **chain = last->try_unwrap_chain_expr;
VECEACH(chain, i)
{
Expr *expr = chain[i];
if (expr->expr_kind != EXPR_TRY_UNWRAP) continue;
if (expr->try_unwrap_expr.assign_existing) continue;
if (expr->try_unwrap_expr.failable)
{
sema_erase_var(c, expr->try_unwrap_expr.decl);
}
else
{
sema_erase_unwrapped(c, expr->try_unwrap_expr.decl);
}
}
}
static inline bool sema_analyse_last_cond(SemaContext *context, Expr *expr, CondType cond_type)
{
switch (expr->expr_kind)
{
case EXPR_TRY_UNWRAP_CHAIN:
if (cond_type != COND_TYPE_UNWRAP_BOOL && cond_type != COND_TYPE_UNWRAP)
{
SEMA_ERROR(expr, "Try unwrapping is only allowed inside of a 'while' or 'if' conditional.");
return false;
}
return sema_analyse_try_unwrap_chain(context, expr, cond_type);
case EXPR_CATCH_UNWRAP:
if (cond_type != COND_TYPE_UNWRAP_BOOL && cond_type != COND_TYPE_UNWRAP)
{
SEMA_ERROR(expr, "Catch unwrapping is only allowed inside of a 'while' or 'if' conditional, maybe catch(...) will do what you need?");
return false;
}
return sema_analyse_catch_unwrap(context, expr);
default:
break;
}
if (cond_type != COND_TYPE_EVALTYPE_VALUE) goto NORMAL_EXPR;
// Now we're analysing the last expression in a switch.
// Case 1: switch (var = variant_expr)
if (expr->expr_kind == EXPR_BINARY && expr->binary_expr.operator == BINARYOP_ASSIGN)
{
// No variable on the lhs? Then it can't be a variant unwrap.
Expr *left = expr->binary_expr.left;
if (left->resolve_status == RESOLVE_DONE || left->expr_kind != EXPR_IDENTIFIER || left->identifier_expr.path) goto NORMAL_EXPR;
// Does the identifier exist in the parent scope?
// then again it can't be a variant unwrap.
Decl *decl_for_ident = sema_resolve_normal_symbol(context, left->identifier_expr.identifier, NULL, false);
if (decl_for_ident) goto NORMAL_EXPR;
Expr *right = expr->binary_expr.right;
bool is_deref = right->expr_kind == EXPR_UNARY && right->unary_expr.operator == UNARYOP_DEREF;
if (is_deref) right = right->unary_expr.expr;
if (!sema_analyse_expr_rhs(context, NULL, right, false)) return false;
if (right->type == type_get_ptr(type_any) && is_deref)
{
is_deref = false;
right = expr->binary_expr.right;
if (!sema_analyse_expr_rhs(context, NULL, right, false)) return false;
}
if (right->type != type_any) goto NORMAL_EXPR;
// Found an expansion here
expr->expr_kind = EXPR_VARIANTSWITCH;
expr->variant_switch.new_ident = left->identifier_expr.identifier;
expr->variant_switch.variant_expr = right;
expr->variant_switch.is_deref = is_deref;
expr->variant_switch.is_assign = true;
expr->resolve_status = RESOLVE_DONE;
expr->type = type_typeid;
return true;
}
if (!sema_analyse_expr(context, expr)) return false;
if (expr->type != type_any) return true;
if (expr->expr_kind == EXPR_IDENTIFIER)
{
Decl *decl = expr->identifier_expr.decl;
expr->expr_kind = EXPR_VARIANTSWITCH;
expr->variant_switch.is_deref = false;
expr->variant_switch.is_assign = false;
expr->variant_switch.variable = decl;
expr->type = type_typeid;
expr->resolve_status = RESOLVE_DONE;
return true;
}
return true;
NORMAL_EXPR:
return sema_analyse_expr(context, expr);
}
/**
* An decl-expr-list is a list of a mixture of declarations and expressions.
* The last declaration or expression is propagated. So for example:
*
* int a = 3, b = 4, float c = 4.0
*
* In this case the final value is 4.0 and the type is float.
*/
static inline bool sema_analyse_cond_list(SemaContext *context, Expr *expr, CondType cond_type)
{
assert(expr->expr_kind == EXPR_COND);
Expr **dexprs = expr->cond_expr;
unsigned entries = vec_size(dexprs);
// 1. Special case, there are no entries, so the type is void
if (entries == 0)
{
expr->type = type_void;
return true;
}
// 2. Walk through each of our declarations / expressions as if they were regular expressions.
for (unsigned i = 0; i < entries - 1; i++)
{
if (!sema_analyse_expr(context, dexprs[i])) return false;
}
if (!sema_analyse_last_cond(context, dexprs[entries - 1], cond_type)) return false;
expr->type = dexprs[entries - 1]->type;
expr->resolve_status = RESOLVE_DONE;
return true;
}
/**
* Analyse a conditional expression:
*
* 1. The middle statement in a for loop
* 2. The expression in an if, while
* 3. The expression in a switch
*
* @param context the current context
* @param expr the conditional to evaluate
* @param cast_to_bool if the result is to be cast to bool after
* @return true if it passes analysis.
*/
static inline bool sema_analyse_cond(SemaContext *context, Expr *expr, CondType cond_type)
{
bool cast_to_bool = cond_type == COND_TYPE_UNWRAP_BOOL;
assert(expr->expr_kind == EXPR_COND && "Conditional expressions should always be of type EXPR_DECL_LIST");
// 1. Analyse the declaration list.
if (!sema_analyse_cond_list(context, expr, cond_type)) return false;
// 2. If we get "void", either through a void call or an empty list,
// signal that.
if (expr->type->type_kind == TYPE_VOID)
{
SEMA_ERROR(expr, cast_to_bool ? "Expected a boolean expression." : "Expected an expression resulting in a value.");
return false;
}
// 3. We look at the last element (which is guaranteed to exist because
// the type was not void.
Expr *last = VECLAST(expr->cond_expr);
if (last->expr_kind == EXPR_DECL)
{
// 3c. The declaration case
Decl *decl = last->decl_expr;
Expr *init = decl->var.init_expr;
// 3d. We expect an initialization for the last declaration.
if (!init)
{
SEMA_ERROR(last, "Expected a declaration with initializer.");
return false;
}
// 3e. Expect that it isn't a failable
if (IS_FAILABLE(init) && !decl->var.unwrap)
{
return sema_failed_cast(last, last->type, cast_to_bool ? type_bool : init->type);
return false;
}
// TODO document
if (!decl->var.unwrap && cast_to_bool && cast_to_bool_kind(decl->var.type_info->type) == CAST_ERROR)
{
SEMA_ERROR(last->decl_expr->var.init_expr, "The expression needs to be convertible to a boolean.");
return false;
}
return true;
}
// 3a. Check for failables in case of an expression.
if (IS_FAILABLE(last))
{
if (!cast_to_bool || cast_may_implicit(type_no_fail(last->type), type_bool, false, false))
{
SEMA_ERROR(last, "The expression may not be a failable, but was %s.", type_quoted_error_string(last->type));
return false;
}
sema_failed_cast(last, type_no_fail(last->type), type_bool);
return false;
}
// 3b. Cast to bool if that is needed
if (cast_to_bool)
{
if (!cast_implicit(last, type_bool)) return false;
}
return true;
}
static inline bool sema_analyse_stmt_placement(Expr *cond, Ast *stmt)
{
if (stmt->ast_kind == AST_COMPOUND_STMT) return true;
SourceLocation *end_of_cond = TOKLOC(cond->span.end_loc);
SourceLocation *start_of_then = TOKLOC(stmt->span.loc);
return end_of_cond->row == start_of_then->row;
}
/**
* Check "while" statement, including end of line placement of a single statement.
*/
static inline bool sema_analyse_while_stmt(SemaContext *context, Ast *statement)
{
Expr *cond = statement->while_stmt.cond;
Ast *body = statement->while_stmt.body;
bool success;
// 1. Begin our scope, this is relevant in case we have something like
// while (File *f = @getFileAndClose!()) where the macro pushes a defer into the scope.
SCOPE_START_WITH_LABEL(statement->while_stmt.flow.label)
// 2. Analyze the condition
if (!sema_analyse_cond(context, cond, COND_TYPE_UNWRAP_BOOL))
{
// 2a. In case of error, pop context and exit.
return SCOPE_POP_ERROR();
}
// 4. Push break / continue - which is independent of the scope.
PUSH_BREAKCONT(statement);
// 6. Analyse the statement
success = sema_analyse_statement(context, body);
// 7. Pop break / continue
POP_BREAKCONT();
// 8. Check placement, in case of a single statement, it must be placed on the same line.
if (success && !sema_analyse_stmt_placement(cond, body))
{
SEMA_ERROR(body, "A single statement after 'while' must be placed on the same line, or be enclosed in {}.");
return SCOPE_POP_ERROR();
}
// 9. Pop defers attach them to the statement if needed
context_pop_defers_and_replace_ast(context, body);
// 10. Pop the while scope.
SCOPE_END;
statement->ast_kind = AST_FOR_STMT;
AstForStmt for_stmt = {
.cond = cond,
.flow = statement->while_stmt.flow,
.incr = NULL,
.body = astid(body),
};
statement->for_stmt = for_stmt;
return success;
}
/**
* Check the do ... while (...) statement.
*/
static inline bool sema_analyse_do_stmt(SemaContext *context, Ast *statement)
{
Expr *expr = statement->do_stmt.expr;
Ast *body = statement->do_stmt.body;
bool success;
// 1. Begin pushing the scope and break / continue.
SCOPE_START_WITH_LABEL(statement->do_stmt.flow.label)
PUSH_BREAKCONT(statement);
// 2. We analyze the statement.
success = sema_analyse_statement(context, body);
// 3. Pop break / continue
POP_BREAKCONT();
// 4. Pop any defers
context_pop_defers_and_replace_ast(context, body);
// 5. If the current scope ended in a jump, then
// the do statement has no exit.
statement->do_stmt.flow.no_exit = context->active_scope.jump_end;
// 6. Pop the scope
SCOPE_END;
// 7. We can now exit if there was an error further up.
if (!success) return false;
// 8. Handle the do { } expression
if (!statement->do_stmt.expr)
{
goto END;
}
// 9. We next handle the while test. This is its own scope.
SCOPE_START
// 10. Try to evaluate and implicitly cast to boolean.
if (!sema_analyse_cond_expr(context, expr))
{
// 10a. On failure, pop and return false.
return SCOPE_POP_ERROR();
}
// 11. Pop any defers in the expression.
statement->do_stmt.expr = context_pop_defers_and_wrap_expr(context, expr);
SCOPE_END;
// 13. Check for infinite loops using do ... while (1).
// If it has no break then that means we've reached a statement which ends with a jump.
if (statement->do_stmt.expr->expr_kind == EXPR_CONST && statement->do_stmt.expr->const_expr.b)
{
// Unless there is a break, this won't ever exit.
context->active_scope.jump_end = !statement->do_stmt.flow.has_break;
}
END:;
FlowCommon flow = statement->do_stmt.flow;
flow.skip_first = true;
statement->ast_kind = AST_FOR_STMT;
AstForStmt for_stmt = {
.cond = expr,
.flow = flow,
.incr = NULL,
.body = astid(body),
};
statement->for_stmt = for_stmt;
return true;
}
static inline bool sema_analyse_declare_stmt(SemaContext *context, Ast *statement)
{
VarDeclKind kind = statement->declare_stmt->var.kind;
if (kind == VARDECL_LOCAL_CT_TYPE || kind == VARDECL_LOCAL_CT)
{
if (!sema_analyse_var_decl_ct(context, statement->declare_stmt)) return false;
statement->ast_kind = AST_NOP_STMT;
return true;
}
return sema_analyse_var_decl(context, statement->declare_stmt, true);
}
static inline bool sema_analyse_expr_stmt(SemaContext *context, Ast *statement)
{
if (!sema_analyse_expr(context, statement->expr_stmt)) return false;
return true;
}
static inline bool sema_analyse_defer_stmt(SemaContext *context, Ast *statement)
{
// TODO special parsing of "catch"
bool success;
SCOPE_START_WITH_FLAGS(SCOPE_DEFER)
context->active_scope.defer_last = 0;
context->active_scope.defer_start = 0;
context->active_scope.in_defer = statement;
PUSH_CONTINUE(NULL);
PUSH_BREAK(statement);
PUSH_NEXT(NULL, NULL);
// Only ones allowed.
context->active_scope.flags &= SCOPE_DEFER;
success = sema_analyse_statement(context, statement->defer_stmt.body);
POP_BREAKCONT();
POP_NEXT();
context_pop_defers_and_replace_ast(context, statement->defer_stmt.body);
SCOPE_END;
if (!success) return false;
statement->defer_stmt.prev_defer = context->active_scope.defer_last;
context->active_scope.defer_last = astid(statement);
return true;
}
static inline bool sema_analyse_for_stmt(SemaContext *context, Ast *statement)
{
bool success = true;
bool is_infinite;
// Enter for scope
SCOPE_OUTER_START
is_infinite = statement->for_stmt.cond == NULL;
if (statement->for_stmt.init)
{
success = sema_analyse_expr(context, statement->for_stmt.init);
}
if (success && statement->for_stmt.cond)
{
// Conditional scope start
SCOPE_START
Expr *cond = statement->for_stmt.cond;
if (cond->expr_kind == EXPR_COND)
{
success = sema_analyse_cond(context, cond, COND_TYPE_UNWRAP_BOOL);
}
else
{
success = sema_analyse_cond_expr(context, cond);
}
statement->for_stmt.cond = context_pop_defers_and_wrap_expr(context, cond);
// If this is const true, then set this to infinite and remove the expression.
if (statement->for_stmt.cond->expr_kind == EXPR_CONST && statement->for_stmt.cond->const_expr.b)
{
statement->for_stmt.cond = NULL;
is_infinite = true;
}
// Conditional scope end
SCOPE_END;
}
if (success && statement->for_stmt.incr)
{
// Incr scope start
SCOPE_START
Expr *incr = statement->for_stmt.incr;
success = sema_analyse_expr(context, incr);
statement->for_stmt.incr = context_pop_defers_and_wrap_expr(context, incr);
// Incr scope end
SCOPE_END;
}
if (!success)
{
SCOPE_ERROR_END_OUTER();
return false;
}
assert(statement->for_stmt.body);
Ast *body = astptr(statement->for_stmt.body);
// Create the for body scope.
SCOPE_START_WITH_LABEL(statement->for_stmt.flow.label)
PUSH_BREAKCONT(statement);
success = sema_analyse_statement(context, body);
statement->for_stmt.flow.no_exit = context->active_scope.jump_end;
POP_BREAKCONT();
// End for body scope
context_pop_defers_and_replace_ast(context, body);
SCOPE_END;
context_pop_defers_and_replace_ast(context, statement);
// End for scope
SCOPE_OUTER_END;
if (statement->for_stmt.flow.no_exit && is_infinite && !statement->for_stmt.flow.has_break)
{
context->active_scope.jump_end = true;
}
return success;
}
static Expr *sema_insert_method_macro_call(SemaContext *context, SourceSpan span, Decl *method_decl, Expr *parent, Expr **arguments)
{
Expr *len_call = expr_new(EXPR_CALL, span);
len_call->resolve_status = RESOLVE_RUNNING;
len_call->call_expr.func_ref = method_decl;
len_call->call_expr.arguments = arguments;
len_call->call_expr.body = NULL;
len_call->call_expr.unsplat_last = false;
len_call->call_expr.is_type_method = true;
bool is_macro = method_decl->decl_kind == DECL_MACRO;
if (!is_macro)
{
if (parent->type->type_kind != TYPE_POINTER) expr_insert_addr(parent);
}
if (!sema_expr_analyse_general_call(context, len_call, method_decl, parent, is_macro, false)) return poisoned_expr;
len_call->resolve_status = RESOLVE_DONE;
return len_call;
}
static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statement)
{
// Pull out the relevant data.
Decl *index = statement->foreach_stmt.index;
Decl *var = statement->foreach_stmt.variable;
Expr *enumerator = statement->foreach_stmt.enumeration;
Ast *body = statement->foreach_stmt.body;
AstId first_stmt = 0;
AstId *succ = &first_stmt;
Expr **expressions = NULL;
bool value_by_ref = statement->foreach_stmt.value_by_ref;
bool success = true;
// First fold the enumerator expression, removing any () around it.
while (enumerator->expr_kind == EXPR_GROUP) enumerator = enumerator->inner_expr;
bool iterator_based = false;
// Conditional scope start
SCOPE_START_WITH_FLAGS(SCOPE_COND)
// In the case of foreach (int x : { 1, 2, 3 }) we will infer the int[] type, so pick out the number of elements.
Type *inferred_type = NULL;
// We may have an initializer list, in this case we rely on an inferred type.
if (expr_is_init_list(enumerator) || (enumerator->expr_kind == EXPR_CONST && enumerator->const_expr.const_kind == CONST_LIST))
{
bool may_be_array;
bool is_const_size;
MemberIndex size = sema_get_initializer_const_array_size(context, enumerator, &may_be_array, &is_const_size);
if (!may_be_array)
{
SEMA_ERROR(enumerator,
"This initializer appears to be a struct initializer when, an array initializer was expected.");
return SCOPE_POP_ERROR();
}
if (!is_const_size)
{
SEMA_ERROR(enumerator, "Only constant sized initializers may be implicitly initialized.");
return SCOPE_POP_ERROR();
}
if (size < 0)
{
SEMA_ERROR(enumerator, "The initializer mixes designated initialization with array initialization.");
return SCOPE_POP_ERROR();
}
assert(size >= 0);
TypeInfo *variable_type_info = var->var.type_info;
if (!variable_type_info)
{
SEMA_ERROR(var, "Add the type of your variable here if you want to iterate over an initializer list.");
return SCOPE_POP_ERROR();
}
// First infer the type of the variable.
if (!sema_resolve_type_info(context, variable_type_info)) return false;
// And create the inferred type:
inferred_type = type_get_array(var->var.type_info->type, (ArraySize)size);
}
// because we don't want the index + variable to move into the internal scope
if (!sema_analyse_inferred_expr(context, inferred_type, enumerator))
{
// Exit early here, because semantic checking might be messed up otherwise.
return SCOPE_POP_ERROR();
}
// Pop any possible defers.
enumerator = context_pop_defers_and_wrap_expr(context, enumerator);
// And pop the cond scope.
SCOPE_END;
if (IS_FAILABLE(enumerator))
{
SEMA_ERROR(enumerator, "The expression may not be failable.");
return false;
}
if (statement->foreach_stmt.index_by_ref)
{
SEMA_ERROR(index, "The index cannot be held by reference, did you accidentally add a '&'?");
return false;
}
// Insert a single deref as needed.
Type *flattened_type = enumerator->type->canonical;
if (flattened_type->type_kind == TYPE_POINTER)
{
// Something like Foo** will not be dereferenced, only Foo*
if (flattened_type->pointer->type_kind == TYPE_POINTER)
{
SEMA_ERROR(enumerator, "It is not possible to enumerate an expression of type %s.", type_quoted_error_string(enumerator->type));
return false;
}
expr_insert_deref(enumerator);
}
// At this point we should have dereferenced any pointer or bailed.
assert(!type_is_pointer(enumerator->type));
// Check that we can even index this expression.
Type *value_type = type_get_indexed_type(enumerator->type);
if (value_type && value_by_ref) value_type = type_get_ptr(value_type);
Decl *len = NULL;
Decl *index_macro = NULL;
Type *index_type = type_usize;
if (!value_type)
{
len = sema_find_operator(context, enumerator, OVERLOAD_LEN);
Decl *by_val = sema_find_operator(context, enumerator, OVERLOAD_ELEMENT_AT);
Decl *by_ref = sema_find_operator(context, enumerator, OVERLOAD_ELEMENT_REF);
if (!len || (!by_val && !by_ref))
{
SEMA_ERROR(enumerator, "It's not possible to enumerate an expression of type %s.", type_quoted_error_string(enumerator->type));
return false;
}
if (!by_ref && value_by_ref)
{
SEMA_ERROR(enumerator, "%s does not support 'foreach' with the value by reference.", type_quoted_error_string(enumerator->type));
return false;
}
index_macro = value_by_ref ? by_ref : by_val;
index_type = index_macro->macro_decl.parameters[1]->type;
value_type = index_macro->macro_decl.rtype->type;
}
// Set up the value, assigning the type as needed.
// Element *value = void
if (!var->var.type_info)
{
var->var.type_info = type_info_new_base(value_type, var->span);
}
if (!sema_resolve_type_info(context, var->var.type_info)) return false;
if (type_is_failable(var->var.type_info->type))
{
SEMA_ERROR(var->var.type_info, "The variable may not be a failable.");
return false;
}
// Set up the optional index parameter
Type *index_var_type = NULL;
if (index)
{
if (!index->var.type_info) index->var.type_info = type_info_new_base(index_type, enumerator->span);
if (!sema_resolve_type_info(context, index->var.type_info)) return false;
index_var_type = index->var.type_info->type;
if (type_is_failable(index_var_type))
{
SEMA_ERROR(index->var.type_info, "The index may not be a failable.");
return false;
}
if (!type_is_integer(type_flatten(index_var_type)))
{
SEMA_ERROR(index->var.type_info,
"Index must be an integer type, '%s' is not valid.",
type_to_error_string(index_var_type));
return false;
}
}
// IndexType __idx$ = 0
Decl *idx_decl = decl_new_generated_var("__idx$", index_type, VARDECL_LOCAL, index ? index->span : enumerator->span);
Expr *idx_init = expr_new(EXPR_CONST, idx_decl->span);
expr_rewrite_to_int_const(idx_init, index_type, 0, true);
vec_add(expressions, expr_generate_decl(idx_decl, idx_init));
// We either have "foreach (x : some_var)" or "foreach (x : some_call())"
// So we grab the former by address (implicit &) and the latter as the value.
assert(enumerator->resolve_status == RESOLVE_DONE);
bool is_addr = false;
bool is_variable = false;
if (expr_is_ltype(enumerator))
{
if (enumerator->expr_kind == EXPR_IDENTIFIER)
{
enumerator->identifier_expr.decl->var.is_written = true;
is_variable = true;
}
else
{
is_addr = true;
expr_insert_addr(enumerator);
}
}
Decl *temp = NULL;
if (is_variable)
{
temp = enumerator->identifier_expr.decl;
}
else
{
// Store either "Foo* __enum$ = &some_var;" or "Foo __enum$ = some_call()"
temp = decl_new_generated_var("__enum$", enumerator->type, VARDECL_LOCAL, enumerator->span);
vec_add(expressions, expr_generate_decl(temp, enumerator));
}
// Create @__enum$.len() or @(*__enum$).len()
Expr *enum_val = expr_variable(temp);
if (is_addr) expr_insert_deref(enum_val);
Type *enumerator_type = type_flatten(enum_val->type);
Expr *len_call;
ArraySize array_len = 0;
if (len)
{
ASSIGN_EXPR_ELSE(len_call, sema_insert_method_macro_call(context, enumerator->span, len, enum_val, NULL), false);
}
else
{
if (enumerator_type->type_kind == TYPE_ARRAY)
{
array_len = enumerator_type->array.len;
len_call = NULL;
}
else
{
len_call = expr_new(EXPR_LEN, enumerator->span);
if (!sema_analyse_expr(context, enum_val)) return false;
len_call->len_expr.inner = enum_val;
len_call->resolve_status = RESOLVE_DONE;
len_call->type = type_isize;
}
}
// IndexType __len$ = (IndexType)(@__enum$.len())
Decl *len_decl = NULL;
if (len_call)
{
len_decl = decl_new_generated_var("__len$", idx_init->type, VARDECL_LOCAL, enumerator->span);
if (!cast_implicit(len_call, idx_init->type)) return false;
vec_add(expressions, expr_generate_decl(len_decl, len_call));
}
// Add all declarations to the init
Expr *init_expr = expr_new(EXPR_EXPRESSION_LIST, var->span);
init_expr->expression_list = expressions;
// Create __idx$ < __len$
Expr *binary = expr_new(EXPR_BINARY, idx_decl->span);
binary->binary_expr.operator = BINARYOP_LT;
binary->binary_expr.left = expr_variable(idx_decl);
if (len_decl)
{
binary->binary_expr.right = expr_variable(len_decl);
}
else
{
Expr *rhs = expr_new(EXPR_CONST, enumerator->span);
expr_rewrite_to_int_const(rhs, type_isize, array_len, true);
binary->binary_expr.right = rhs;
}
// Create __idx$++
Expr *inc = expr_new(EXPR_UNARY, idx_decl->span);
inc->unary_expr.expr = expr_variable(idx_decl);
inc->unary_expr.operator = UNARYOP_INC;
// Create IndexType index = __idx$++
if (index)
{
Ast *declare_ast = new_ast(AST_DECLARE_STMT, var->span);
declare_ast->declare_stmt = index;
Expr *load_idx = expr_variable(idx_decl);
if (!cast(load_idx, index_var_type)) return false;
index->var.init_expr = load_idx;
ast_append(&succ, declare_ast);
}
// Create value = (*__$enum)[__idx$]
Ast *value_declare_ast = new_ast(AST_DECLARE_STMT, var->span);
value_declare_ast->declare_stmt = var;
Expr *subscript = expr_new(EXPR_SUBSCRIPT, var->span);
enum_val = expr_variable(temp);
if (is_addr) expr_insert_deref(enum_val);
subscript->subscript_expr.expr = enum_val;
subscript->subscript_expr.index = expr_variable(idx_decl);
if (value_by_ref)
{
Expr *addr = expr_new(EXPR_UNARY, subscript->span);
addr->unary_expr.operator = UNARYOP_ADDR;
addr->unary_expr.expr = subscript;
subscript = addr;
}
var->var.init_expr = subscript;
ast_append(&succ, value_declare_ast);
ast_append(&succ, body);
Ast *compound_stmt = new_ast(AST_COMPOUND_STMT, body->span);
compound_stmt->compound_stmt.first_stmt = first_stmt;
FlowCommon flow = statement->foreach_stmt.flow;
statement->for_stmt = (AstForStmt){ .init = init_expr,
.cond = binary,
.incr = inc,
.flow = flow,
.body = astid(compound_stmt)
};
statement->ast_kind = AST_FOR_STMT;
return sema_analyse_for_stmt(context, statement);
}
static bool sema_analyse_switch_stmt(SemaContext *context, Ast *statement);
static inline bool sema_analyse_if_stmt(SemaContext *context, Ast *statement)
{
// IMPROVE
// convert
// if (!x) A(); else B();
// into
// if (x) B(); else A();
bool else_jump;
bool then_jump;
bool success;
Expr *cond = statement->if_stmt.cond;
SCOPE_OUTER_START
CondType cond_type = statement->if_stmt.then_body->ast_kind == AST_IF_CATCH_SWITCH_STMT
? COND_TYPE_UNWRAP : COND_TYPE_UNWRAP_BOOL;
success = sema_analyse_cond(context, cond, cond_type);
Ast *then = statement->if_stmt.then_body;
bool then_has_braces = then->ast_kind == AST_COMPOUND_STMT || then->ast_kind == AST_IF_CATCH_SWITCH_STMT;
if (statement->if_stmt.else_body)
{
if (!then_has_braces)
{
SEMA_ERROR(then, "if-statements with an 'else' must use '{ }' even around a single statement.");
success = false;
}
if (success && statement->if_stmt.else_body->ast_kind != AST_COMPOUND_STMT &&
statement->if_stmt.else_body->ast_kind != AST_IF_STMT)
{
SEMA_ERROR(statement->if_stmt.else_body,
"An 'else' must use '{ }' even around a single statement.");
success = false;
}
}
if (success && !then_has_braces)
{
SourceLocation *end_of_cond = TOKLOC(cond->span.end_loc);
SourceLocation *start_of_then = TOKLOC(statement->if_stmt.then_body->span.loc);
if (end_of_cond->row != start_of_then->row)
{
SEMA_ERROR(statement->if_stmt.then_body,
"The 'then' part of a single line if-statement must start on the same line as the 'if' or use '{ }'");
success = false;
}
}
if (context->active_scope.jump_end && !context->active_scope.allow_dead_code)
{
SEMA_ERROR(then, "This code can never be executed.");
success = false;
}
if (then->ast_kind == AST_IF_CATCH_SWITCH_STMT)
{
Decl *label = statement->if_stmt.flow.label;
then->switch_stmt.flow.label = label;
statement->if_stmt.flow.label = NULL;
if (label) label->label.parent = astid(then);
SCOPE_START_WITH_LABEL(statement->if_stmt.flow.label);
success = success && sema_analyse_switch_stmt(context, then);
then_jump = context->active_scope.jump_end;
SCOPE_END;
}
else
{
SCOPE_START_WITH_LABEL(statement->if_stmt.flow.label);
success = success && sema_analyse_statement(context, then);
then_jump = context->active_scope.jump_end;
SCOPE_END;
}
else_jump = false;
if (statement->if_stmt.else_body)
{
SCOPE_START_WITH_LABEL(statement->if_stmt.flow.label);
sema_remove_unwraps_from_try(context, statement->if_stmt.cond);
sema_unwrappable_from_catch_in_else(context, statement->if_stmt.cond);
success = success && sema_analyse_statement(context, statement->if_stmt.else_body);
else_jump = context->active_scope.jump_end;
SCOPE_END;
}
context_pop_defers_and_replace_ast(context, statement);
SCOPE_OUTER_END;
if (then_jump)
{
sema_unwrappable_from_catch_in_else(context, statement->if_stmt.cond);
}
if (then_jump && else_jump && !statement->flow.has_break)
{
context->active_scope.jump_end = true;
}
return success;
}
static bool sema_analyse_asm_stmt(SemaContext *context, Ast *stmt)
{
if (!sema_analyse_expr(context, stmt->asm_stmt.body)) return false;
if (stmt->asm_stmt.body->expr_kind != EXPR_CONST
|| stmt->asm_stmt.body->const_expr.const_kind != CONST_STRING)
{
SEMA_ERROR(stmt->asm_stmt.body, "The asm statement requires a constant string here.");
return false;
}
return true;
}
static inline Decl *sema_analyse_label(SemaContext *context, Ast *stmt)
{
Decl *ambiguous;
Decl *dummy;
Decl *target = sema_resolve_normal_symbol(context, stmt->contbreak_stmt.label.span, NULL, false);
if (!target)
{
SEMA_ERROR(stmt, "Cannot find a labelled statement with the name '%s'.", stmt->contbreak_stmt.label.name);
return poisoned_decl;
}
if (target->decl_kind != DECL_LABEL)
{
SEMA_TOKID_ERROR(stmt->contbreak_stmt.label.span, "Expected the name to match a label, not a constant.");
return poisoned_decl;
}
if (context->active_scope.in_defer)
{
if (target->label.scope_defer != astid(context->active_scope.in_defer))
{
SEMA_ERROR(stmt, stmt->ast_kind == AST_BREAK_STMT ? "You cannot break out of a defer." : "You cannot use continue out of a defer.");
return poisoned_decl;
}
}
return target;
}
static bool context_labels_exist_in_scope(SemaContext *context)
{
Decl **locals = context->locals;
for (size_t local = context->active_scope.current_local; local > 0; local--)
{
if (locals[local - 1]->decl_kind == DECL_LABEL) return true;
}
return false;
}
static bool sema_analyse_break_stmt(SemaContext *context, Ast *statement)
{
context->active_scope.jump_end = true;
if (!context->break_target && !statement->contbreak_stmt.is_label)
{
if (context_labels_exist_in_scope(context))
{
SEMA_ERROR(statement, "Unlabelled 'break' is not allowed here.");
}
else
{
SEMA_ERROR(statement, "There is no valid target for 'break', did you make a mistake?");
}
return false;
}
statement->contbreak_stmt.defers.start = context->active_scope.defer_last;
if (statement->contbreak_stmt.label.name)
{
Decl *target = sema_analyse_label(context, statement);
if (!decl_ok(target)) return false;
astptr(target->label.parent)->flow.has_break = true;
statement->contbreak_stmt.ast = target->label.parent;
statement->contbreak_stmt.defers.end = target->label.defer;
return true;
}
statement->contbreak_stmt.defers.end = context->break_defer;
statement->contbreak_stmt.ast = context->break_target;
astptr(context->break_target)->flow.has_break = true;
return true;
}
static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement)
{
context->active_scope.jump_end = true;
if (!context->next_target && !statement->nextcase_stmt.label.name && !statement->nextcase_stmt.expr_or_type_info)
{
if (context->next_switch)
{
SEMA_ERROR(statement, "A plain 'nextcase' is not allowed on the last case.");
}
else
{
SEMA_ERROR(statement, "'nextcase' can only be used inside of a switch.");
}
return false;
}
Ast *parent = context->next_switch;
if (statement->nextcase_stmt.label.name)
{
Decl *target = sema_resolve_normal_symbol(context, statement->nextcase_stmt.label.span, NULL, false);
if (!target)
{
SEMA_ERROR(statement, "Cannot find a switch statement with the name '%s'.", statement->nextcase_stmt.label.name);
return false;
}
if (target->decl_kind != DECL_LABEL)
{
SEMA_TOKID_ERROR(statement->nextcase_stmt.label.span, "Expected the name to match a label, not a constant.");
return false;
}
parent = astptr(target->label.parent);
AstKind kind = parent->ast_kind;
if (kind != AST_SWITCH_STMT && kind != AST_IF_CATCH_SWITCH_STMT)
{
SEMA_TOKID_ERROR(statement->nextcase_stmt.label.span, "Expected the label to match a 'switch' or 'if-catch' statement.");
return false;
}
bool defer_mismatch = false;
defer_mismatch = context->active_scope.in_defer != parent->switch_stmt.scope_defer;
if (defer_mismatch)
{
SEMA_ERROR(statement, "This 'nextcase' would jump out of a defer which is not allowed.");
return false;
}
assert(statement->nextcase_stmt.expr_or_type_info);
}
statement->nextcase_stmt.defers.start = context->active_scope.defer_last;
statement->nextcase_stmt.defers.end = parent->switch_stmt.defer;
// Plain next.
if (!statement->nextcase_stmt.expr_or_type_info)
{
assert(context->next_target);
statement->nextcase_stmt.case_switch_stmt = context->next_target;
return true;
}
Expr *cond = parent->switch_stmt.cond;
if (statement->nextcase_stmt.is_type)
{
TypeInfo *type_info = statement->nextcase_stmt.expr_or_type_info;
if (!sema_resolve_type_info(context, type_info)) return false;
Ast **cases;
statement->nextcase_stmt.defers.end = parent->switch_stmt.defer;
if (parent->switch_stmt.cond->type->canonical != type_typeid)
{
SEMA_ERROR(statement, "Unexpected 'type' in as an 'nextcase' destination.");
SEMA_PREV(statement, "The 'switch' here uses expected a type '%s'.", type_to_error_string(parent->switch_stmt.cond->type));
return false;
}
cases = parent->switch_stmt.cases;
Ast *default_stmt = NULL;
Type *type = type_info->type->canonical;
VECEACH(cases, i)
{
Ast *case_stmt = cases[i];
if (case_stmt->ast_kind == AST_DEFAULT_STMT)
{
default_stmt = case_stmt;
break;
}
Expr *expr = case_stmt->case_stmt.expr;
if (expr->expr_kind == EXPR_CONST && expr->const_expr.typeid == type)
{
statement->nextcase_stmt.case_switch_stmt = astid(case_stmt);
return true;
}
}
if (default_stmt)
{
statement->nextcase_stmt.case_switch_stmt = astid(default_stmt);
return true;
}
SEMA_ERROR(type_info, "There is no case for type '%s'.", type_to_error_string(type_info->type));
return false;
}
Expr *target = statement->nextcase_stmt.expr_or_type_info;
Type *expected_type = parent->ast_kind == AST_SWITCH_STMT ? cond->type : type_anyerr;
if (!sema_analyse_expr_rhs(context, expected_type, target, false)) return false;
if (target->expr_kind == EXPR_CONST)
{
Ast *default_stmt = NULL;
statement->nextcase_stmt.defers.end = parent->switch_stmt.defer;
VECEACH(parent->switch_stmt.cases, i)
{
Ast *case_stmt = parent->switch_stmt.cases[i];
if (case_stmt->ast_kind == AST_DEFAULT_STMT)
{
default_stmt = case_stmt;
break;
}
ExprConst *const_expr = &case_stmt->case_stmt.expr->const_expr;
ExprConst *to_const_expr = case_stmt->case_stmt.to_expr ? &case_stmt->case_stmt.to_expr->const_expr : const_expr;
if (expr_const_compare(&target->const_expr, const_expr, BINARYOP_GE) &&
expr_const_compare(&target->const_expr, to_const_expr, BINARYOP_LE))
{
statement->nextcase_stmt.case_switch_stmt = astid(case_stmt);
return true;
}
}
if (default_stmt)
{
statement->nextcase_stmt.case_switch_stmt = astid(default_stmt);
return true;
}
SEMA_ERROR(statement, "'nextcase' needs to jump to an exact case statement.");
return false;
}
statement->nextcase_stmt.case_switch_stmt = astid(parent);
statement->nextcase_stmt.switch_expr = target;
return true;
}
static bool sema_analyse_continue_stmt(SemaContext *context, Ast *statement)
{
context->active_scope.jump_end = true;
statement->contbreak_stmt.defers.start = context->active_scope.defer_last;
if (!context->continue_target && !statement->contbreak_stmt.label.name)
{
SEMA_ERROR(statement, "'continue' is not allowed here.");
return false;
}
if (statement->contbreak_stmt.label.name)
{
Decl *target = sema_analyse_label(context, statement);
if (!decl_ok(target)) return false;
Ast *parent = astptr(target->label.parent);
switch (parent->ast_kind)
{
case AST_FOR_STMT:
case AST_WHILE_STMT:
break;
case AST_DO_STMT:
if (parent->do_stmt.expr) break;
FALLTHROUGH;
default:
SEMA_ERROR(statement, "'continue' may only be used with 'for', 'while' and 'do-while' statements.");
return false;
}
statement->contbreak_stmt.ast = target->label.parent;
statement->contbreak_stmt.defers.end = target->label.defer;
return true;
}
statement->contbreak_stmt.defers.end = context->continue_defer;
statement->contbreak_stmt.ast = context->continue_target;
return true;
}
static inline bool sema_analyse_then_overwrite(SemaContext *context, Ast *statement, Ast *replacement)
{
if (!sema_analyse_statement(context, replacement)) return false;
// Overwrite but store link.
AstId next = statement->next;
assert(!replacement->next);
*statement = *replacement;
statement->next = next;
return true;
}
static bool sema_analyse_ct_if_stmt(SemaContext *context, Ast *statement)
{
int res = sema_check_comp_time_bool(context, statement->ct_if_stmt.expr);
if (res == -1) return false;
if (res)
{
return sema_analyse_then_overwrite(context, statement, statement->ct_if_stmt.then);
}
Ast *elif = statement->ct_if_stmt.elif;
while (1)
{
if (!elif)
{
// Turn into NOP!
statement->ast_kind = AST_NOP_STMT;
return true;
}
// We found else, then just replace with that.
if (elif->ast_kind == AST_CT_ELSE_STMT)
{
return sema_analyse_then_overwrite(context, statement, elif->ct_else_stmt);
}
assert(elif->ast_kind == AST_CT_ELIF_STMT);
res = sema_check_comp_time_bool(context, elif->ct_elif_stmt.expr);
if (res == -1) return false;
if (res)
{
return sema_analyse_then_overwrite(context, statement, elif->ct_elif_stmt.then);
}
elif = elif->ct_elif_stmt.elif;
}
}
static inline bool sema_analyse_compound_statement_no_scope(SemaContext *context, Ast *compound_statement)
{
bool all_ok = ast_ok(compound_statement);
AstId current = compound_statement->compound_stmt.first_stmt;
while (current)
{
Ast *ast = ast_next(&current);
if (!sema_analyse_statement(context, ast))
{
ast_poison(ast);
all_ok = false;
}
}
context_pop_defers_to(context, &compound_statement->compound_stmt.defer_list);
return all_ok;
}
static inline bool sema_check_type_case(SemaContext *context, Type *switch_type, Ast *case_stmt, Ast **cases, unsigned index)
{
Expr *expr = case_stmt->case_stmt.expr;
if (!sema_analyse_expr_rhs(context, type_typeid, expr, false)) return false;
if (expr->expr_kind == EXPR_CONST)
{
Type *my_type = expr->const_expr.typeid;
for (unsigned i = 0; i < index; i++)
{
Ast *other = cases[i];
if (other->ast_kind != AST_CASE_STMT) continue;
Expr *other_expr = other->case_stmt.expr;
if (other_expr->expr_kind == EXPR_CONST && other_expr->const_expr.typeid == my_type)
{
SEMA_ERROR(case_stmt, "The same type appears more than once.");
SEMA_PREV(other, "Here is the case with that type.");
return false;
}
}
}
return true;
}
static inline ExprConst *flatten_enum_const(Expr *expr)
{
ExprConst *const_expr = &expr->const_expr;
if (const_expr->const_kind == CONST_ENUM)
{
const_expr->const_kind = CONST_INTEGER;
Decl *enum_val = const_expr->enum_val;
Expr *enum_expr = enum_val->enum_constant.expr;
assert(enum_expr->expr_kind == EXPR_CONST);
ExprConst *enum_const = &enum_expr->const_expr;
assert(enum_const->const_kind == CONST_INTEGER);
*const_expr = *enum_const;
}
return const_expr;
}
static inline bool sema_check_value_case(SemaContext *context, Type *switch_type, Ast *case_stmt, Ast **cases, unsigned index, bool *if_chained, bool *max_ranged)
{
assert(switch_type);
Expr *expr = case_stmt->case_stmt.expr;
Expr *to_expr = case_stmt->case_stmt.to_expr;
// 1. Try to do implicit conversion to the correct type.
if (!sema_analyse_expr_rhs(context, switch_type, expr, false)) return false;
if (to_expr && !sema_analyse_expr_rhs(context, switch_type, to_expr, false)) return false;
if (expr->expr_kind != EXPR_CONST || (to_expr && to_expr->expr_kind != EXPR_CONST))
{
*if_chained = true;
return true;
}
ExprConst *const_expr = flatten_enum_const(expr);
ExprConst *to_const_expr = to_expr ? flatten_enum_const(to_expr) : const_expr;
if (!*max_ranged && type_is_integer(expr->type) && to_const_expr != const_expr)
{
if (int_comp(const_expr->ixx, to_const_expr->ixx, BINARYOP_GT))
{
sema_error_range((SourceSpan){ expr->span.loc, to_expr->span.end_loc },
"The range is not valid because the first value (%s) is greater than the second (%s). "
"It would work if you swapped their order.",
int_to_str(const_expr->ixx, 10),
int_to_str(to_const_expr->ixx, 10));
return false;
}
Int128 range = int_sub(to_const_expr->ixx, const_expr->ixx).i;
Int128 max_range = { .low = active_target.switchrange_max_size };
if (i128_comp(range, max_range, type_i128) == CMP_GT)
{
*max_ranged = true;
}
}
for (unsigned i = 0; i < index; i++)
{
Ast *other = cases[i];
if (other->ast_kind != AST_CASE_STMT) continue;
ExprConst *other_const = &other->case_stmt.expr->const_expr;
ExprConst *other_to_const = other->case_stmt.to_expr ? &other->case_stmt.to_expr->const_expr : other_const;
if (expr_const_compare(const_expr, other_to_const, BINARYOP_LE) && expr_const_compare(to_const_expr, other_const, BINARYOP_GE))
{
SEMA_ERROR(case_stmt, "The same case value appears more than once.");
SEMA_PREV(other, "Here is the previous use of that value.");
return false;
}
}
return true;
}
static bool sema_analyse_switch_body(SemaContext *context, Ast *statement, SourceSpan expr_span, Type *switch_type, Ast **cases, ExprVariantSwitch *variant, Decl *var_holder)
{
bool use_type_id = false;
if (!type_is_comparable(switch_type))
{
sema_error_range(expr_span, "You cannot test '%s' for equality, and only values that supports '==' for comparison can be used in a switch.", type_to_error_string(switch_type));
return false;
}
// We need an if chain if this isn't an integer type.
bool if_chain = !type_is_integer(type_flatten(switch_type));
Ast *default_case = NULL;
assert(context->active_scope.defer_start == context->active_scope.defer_last);
bool exhaustive = false;
unsigned case_count = vec_size(cases);
bool success = true;
bool max_ranged = false;
bool type_switch = switch_type == type_typeid;
for (unsigned i = 0; i < case_count; i++)
{
Ast *stmt = cases[i];
Ast *next = (i < case_count - 1) ? cases[i + 1] : NULL;
PUSH_NEXT(next, statement);
switch (stmt->ast_kind)
{
case AST_CASE_STMT:
if (type_switch)
{
if (!sema_check_type_case(context, switch_type, stmt, cases, i))
{
success = false;
break;;
}
}
else
{
if (!sema_check_value_case(context, switch_type, stmt, cases, i, &if_chain, &max_ranged))
{
success = false;
break;
}
}
break;
case AST_DEFAULT_STMT:
exhaustive = true;
if (default_case)
{
SEMA_ERROR(stmt, "'default' may only appear once in a single 'switch', please remove one.");
SEMA_PREV(default_case, "Here is the previous use.");
success = false;
}
default_case = stmt;
break;
default:
UNREACHABLE;
}
POP_NEXT();
}
bool all_jump_end = exhaustive;
for (unsigned i = 0; i < case_count; i++)
{
Ast *stmt = cases[i];
SCOPE_START
PUSH_BREAK(statement);
Ast *next = (i < case_count - 1) ? cases[i + 1] : NULL;
PUSH_NEXT(next, statement);
Ast *body = stmt->case_stmt.body;
if (stmt->ast_kind == AST_CASE_STMT && body && type_switch && var_holder && stmt->case_stmt.expr->expr_kind == EXPR_CONST)
{
if (variant->is_assign)
{
Type *real_type = type_get_ptr(stmt->case_stmt.expr->const_expr.typeid);
TokenId name = variant->new_ident;
Decl *new_var = decl_new_var(name,
type_info_new_base(variant->is_deref
? real_type->pointer : real_type, source_span_from_token_id(name)),
VARDECL_LOCAL, VISIBLE_LOCAL);
Expr *var_result = expr_variable(var_holder);
if (!cast(var_result, real_type)) return false;
if (variant->is_deref)
{
expr_insert_deref(var_result);
}
new_var->var.init_expr = var_result;
Ast *decl_ast = new_ast(AST_DECLARE_STMT, new_var->span);
decl_ast->declare_stmt = new_var;
ast_prepend(&body->compound_stmt.first_stmt, decl_ast);
}
else
{
Type *type = type_get_ptr(stmt->case_stmt.expr->const_expr.typeid);
Decl *alias = decl_new_var(var_holder->name_token,
type_info_new_base(type, stmt->case_stmt.expr->span),
VARDECL_LOCAL, VISIBLE_LOCAL);
Expr *ident_converted = expr_variable(var_holder);
if (!cast(ident_converted, type)) return false;
alias->var.init_expr = ident_converted;
alias->var.shadow = true;
Ast *decl_ast = new_ast(AST_DECLARE_STMT, alias->span);
decl_ast->declare_stmt = alias;
ast_prepend(&body->compound_stmt.first_stmt, decl_ast);
}
}
success = success && (!body || sema_analyse_compound_statement_no_scope(context, body));
POP_BREAK();
POP_NEXT();
all_jump_end &= (!body | context->active_scope.jump_end);
SCOPE_END;
}
statement->flow.no_exit = all_jump_end;
statement->switch_stmt.if_chain = if_chain || max_ranged;
if (!success) return false;
return success;
}
static bool sema_analyse_ct_switch_body(SemaContext *context, Ast *statement)
{
Expr *cond = statement->ct_switch_stmt.cond;
Type *type = cond->type;
bool is_type = type == type_typeid;
ExprConst *switch_expr_const = &cond->const_expr;
Ast **cases = statement->ct_switch_stmt.body;
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++)
{
Ast *stmt = cases[i];
switch (stmt->ast_kind)
{
case AST_CASE_STMT:
{
Expr *expr = stmt->case_stmt.expr;
Expr *to_expr = stmt->case_stmt.to_expr;
if (is_type)
{
if (!sema_analyse_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 (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++)
{
Ast *other_stmt = cases[j];
if (other_stmt->ast_kind == AST_DEFAULT_STMT) continue;
ExprConst *other_const = &other_stmt->case_stmt.expr->const_expr;
ExprConst *other_const_to = other_stmt->case_stmt.to_expr ? &other_stmt->case_stmt.to_expr->const_expr : other_const;
if (expr_const_compare(const_expr, other_const_to, BINARYOP_LE) && expr_const_compare(const_to_expr, other_const, BINARYOP_GE))
{
SEMA_ERROR(stmt, "'%s' appears more than once.", expr_const_to_error_string(const_expr));
SEMA_PREV(cases[j]->case_stmt.expr, "The previous $case was here.");
return false;
}
}
if (expr_const_compare(switch_expr_const, const_expr, BINARYOP_GE) && expr_const_compare(switch_expr_const, const_to_expr, BINARYOP_LE))
{
matched_case = (int) i;
}
break;
}
case AST_DEFAULT_STMT:
if (default_case < case_count)
{
SEMA_ERROR(stmt, "More than one $default is not allowed.");
SEMA_PREV(cases[default_case], "The previous $default was here.");
return false;
}
default_case = (int)i;
continue;
default:
UNREACHABLE;
}
}
if (matched_case == case_count) matched_case = default_case;
Ast *body = NULL;
for (int i = matched_case; i < case_count; i++)
{
body = cases[i]->case_stmt.body;
if (body) break;
}
if (!body)
{
statement->ast_kind = AST_NOP_STMT;
return true;
}
if (!sema_analyse_statement(context, body)) return false;
*statement = *body;
return true;
}
static bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statement)
{
Expr *cond = statement->ct_switch_stmt.cond;
if (!sema_analyse_ct_expr(context, cond)) return false;
if (cond->expr_kind != EXPR_CONST)
{
SEMA_ERROR(cond, "A compile time $switch must be over a constant value.");
return false;
}
return sema_analyse_ct_switch_body(context, statement);
}
static bool sema_analyse_ct_foreach_stmt(SemaContext *context, Ast *statement)
{
Expr *collection = statement->ct_foreach_stmt.expr;
if (!sema_analyse_ct_expr(context, collection)) return false;
if (collection->expr_kind != EXPR_INITIALIZER_LIST)
{
SEMA_ERROR(collection, "Expected a list to iterate over");
return false;
}
if (!expr_is_constant_eval(collection, CONSTANT_EVAL_ANY))
{
SEMA_ERROR(collection, "A compile time $foreach must be over a constant value.");
return false;
}
Expr **expression = collection->initializer_list;
Decl *index = NULL;
TokenId index_token = statement->ct_foreach_stmt.index;
AstId start = 0;
SCOPE_START;
if (index_token.index)
{
index = decl_new_var(index_token, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL);
index->type = type_int;
if (!sema_add_local(context, index)) return SCOPE_POP_ERROR();
}
Decl *value = decl_new_var(statement->ct_foreach_stmt.value, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL);
if (!sema_add_local(context, value)) return SCOPE_POP_ERROR();
// Get the body
Ast *body = astptr(statement->ct_foreach_stmt.body);
AstId *current = &start;
VECEACH(expression, i)
{
Ast *compound_stmt = ast_copy_deep(body);
value->var.init_expr = expression[i];
if (index)
{
Expr *expr = expr_new(EXPR_CONST, index->span);
expr_const_set_int(&expr->const_expr, i, TYPE_I32);
expr->const_expr.narrowable = true;
expr->type = type_int;
index->var.init_expr = expr;
index->type = type_int;
}
if (!sema_analyse_compound_stmt(context, compound_stmt)) return SCOPE_POP_ERROR();
*current = astid(compound_stmt);
current = &compound_stmt->next;
}
SCOPE_END;
statement->ast_kind = AST_COMPOUND_STMT;
statement->compound_stmt.first_stmt = start;
statement->compound_stmt.defer_list = (DeferList) { 0, 0 };
return true;
}
static bool sema_analyse_switch_stmt(SemaContext *context, Ast *statement)
{
statement->switch_stmt.scope_defer = context->active_scope.in_defer;
SCOPE_START_WITH_LABEL(statement->switch_stmt.flow.label);
Expr *cond = statement->switch_stmt.cond;
Type *switch_type;
ExprVariantSwitch var_switch;
Decl *variant_decl = NULL;
if (statement->ast_kind == AST_SWITCH_STMT)
{
if (!sema_analyse_cond(context, cond, COND_TYPE_EVALTYPE_VALUE)) return false;
Expr *last = VECLAST(cond->cond_expr);
switch_type = last->type->canonical;
if (last->expr_kind == EXPR_VARIANTSWITCH)
{
var_switch = last->variant_switch;
Expr *inner;
if (var_switch.is_assign)
{
inner = expr_new(EXPR_DECL, last->span);
variant_decl = decl_new_generated_var(".variant", type_any, VARDECL_LOCAL, last->span);
variant_decl->var.init_expr = var_switch.variant_expr;
inner->decl_expr = variant_decl;
if (!sema_analyse_expr(context, inner)) return false;
}
else
{
inner = expr_new(EXPR_IDENTIFIER, last->span);
variant_decl = var_switch.variable;
inner->identifier_expr.decl = variant_decl;
inner->type = type_any;
inner->resolve_status = RESOLVE_DONE;
}
last->type = type_typeid;
last->expr_kind = EXPR_TYPEOFANY;
last->inner_expr = inner;
switch_type = type_typeid;
cond->type = type_typeid;
}
}
else
{
switch_type = type_anyerr;
}
statement->switch_stmt.defer = context->active_scope.defer_last;
if (!sema_analyse_switch_body(context, statement, cond ? cond->span : statement->span,
switch_type->canonical,
statement->switch_stmt.cases, variant_decl ? &var_switch : NULL, variant_decl))
{
return SCOPE_POP_ERROR();
}
context_pop_defers_and_replace_ast(context, statement);
SCOPE_END;
if (statement->flow.no_exit && !statement->flow.has_break)
{
context->active_scope.jump_end = true;
}
return true;
}
bool sema_analyse_ct_assert_stmt(SemaContext *context, Ast *statement)
{
Expr *expr = statement->ct_assert_stmt.expr;
Expr *message = statement->ct_assert_stmt.message;
if (message)
{
if (!sema_analyse_expr(context, message)) return false;
if (message->expr_kind != EXPR_CONST || message->const_expr.const_kind != CONST_STRING)
{
SEMA_ERROR(message, "Expected a string as the error message.");
}
}
int res = sema_check_comp_time_bool(context, expr);
if (res == -1) return false;
if (!res)
{
if (message)
{
SEMA_ERROR(expr, "Compile time assert - %.*s", message->const_expr.string.len, message->const_expr.string.chars);
}
else
{
SEMA_ERROR(expr, "Compile time assert failed.");
}
return false;
}
statement->ast_kind = AST_NOP_STMT;
return true;
}
static inline bool sema_analyse_scoping_stmt(SemaContext *context, Ast *statement)
{
Expr **exprs = statement->scoping_stmt.scoped->expression_list;
unsigned scoped_count = vec_size(exprs);
AstId first;
AstId *succ = &first;
for (unsigned i = 0; i < scoped_count; i++)
{
Expr *expr = exprs[i];
if (!sema_analyse_expr_lvalue(context, expr)) return false;
if (!expr_is_ltype(expr))
{
SEMA_ERROR(expr, "Expected an assignable value.");
return false;
}
if (!expr_is_pure(expr))
{
SEMA_ERROR(expr, "A value with side effects (e.g. function calls) is not allowed here.");
return false;
}
Decl *new_decl = decl_new_generated_var(".scope", expr->type, VARDECL_LOCAL, expr->span);
new_decl->var.init_expr = expr;
Ast *declare = new_ast(AST_DECLARE_STMT, expr->span);
declare->declare_stmt = new_decl;
ast_append(&succ, declare);
Ast *defer_restore = new_ast(AST_DEFER_STMT, expr->span);
Expr *restore_expr = expr_new(EXPR_BINARY, expr->span);
Expr *rhs = expr_new(EXPR_IDENTIFIER, expr->span);
rhs->resolve_status = RESOLVE_DONE;
rhs->identifier_expr.decl = new_decl;
rhs->type = expr->type;
restore_expr->binary_expr = (ExprBinary) { .left = MACRO_COPY_EXPR(expr), .right = rhs, .operator = BINARYOP_ASSIGN };
Ast *restore_stmt = new_ast(AST_EXPR_STMT, expr->span);
restore_stmt->expr_stmt = restore_expr;
defer_restore->defer_stmt.body = restore_stmt;
ast_append(&succ, defer_restore);
}
ast_append(&succ, statement->scoping_stmt.stmt);
statement->ast_kind = AST_COMPOUND_STMT;
statement->compound_stmt = (AstCompoundStmt) { .first_stmt = first };
return sema_analyse_compound_stmt(context, statement);
}
bool sema_analyse_assert_stmt(SemaContext *context, Ast *statement)
{
Expr *expr = statement->assert_stmt.expr;
Expr *message = statement->assert_stmt.message;
if (message)
{
if (!sema_analyse_expr(context, message)) return false;
if (message->expr_kind != EXPR_CONST || message->const_expr.const_kind != CONST_STRING)
{
SEMA_ERROR(message, "Expected a string as the error message.");
}
}
if (expr->expr_kind == EXPR_TRY_UNWRAP_CHAIN)
{
if (!sema_analyse_try_unwrap_chain(context, expr, COND_TYPE_UNWRAP_BOOL)) return false;
}
else
{
if (!sema_analyse_cond_expr(context, expr)) return false;
}
return true;
}
static bool sema_analyse_compound_stmt(SemaContext *context, Ast *statement)
{
bool success;
bool ends_with_jump;
SCOPE_START
success = sema_analyse_compound_statement_no_scope(context, statement);
ends_with_jump = context->active_scope.jump_end;
SCOPE_END;
context->active_scope.jump_end = ends_with_jump;
return success;
}
static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement)
{
bool success = false;
// Enter for scope
SCOPE_OUTER_START
Expr *init;
if ((init = statement->for_stmt.init))
{
assert(init->expr_kind == EXPR_EXPRESSION_LIST);
Expr **expressions = init->expression_list;
VECEACH (expressions, i)
{
Expr *expr = expressions[i];
if (expr->expr_kind == EXPR_DECL)
{
Decl *decl = expr->decl_expr;
if (decl->decl_kind != DECL_VAR || (decl->var.kind != VARDECL_LOCAL_CT && decl->var.kind != VARDECL_LOCAL_CT_TYPE))
{
SEMA_ERROR(expr, "Only 'var $foo' and 'var $Type' declarations are allowed in a '$for'");
goto EXIT_ERROR;
}
if (!sema_analyse_var_decl_ct(context, decl)) goto EXIT_ERROR;
continue;
}
if (!sema_analyse_expr(context, expr)) goto EXIT_ERROR;
if (!expr_is_constant_eval(expr, CONSTANT_EVAL_FOLDABLE))
{
SEMA_ERROR(expr, "Only constant expressions are allowed.");
goto EXIT_ERROR;
}
}
}
Expr *condition = statement->for_stmt.cond;
Expr *incr = statement->for_stmt.incr;
Ast *body = astptr(statement->for_stmt.body);
AstId start = 0;
AstId *current = &start;
assert(condition);
for (int i = 0; i < MAX_MACRO_ITERATIONS; i++)
{
Expr *copy = copy_expr(condition);
if (!sema_analyse_cond_expr(context, copy)) goto EXIT_ERROR;
if (copy->expr_kind != EXPR_CONST)
{
SEMA_ERROR(copy, "Expected a value that can be evaluated at compile time.");
goto EXIT_ERROR;
}
if (!copy->const_expr.b) break;
Ast *compound_stmt = ast_copy_deep(body);
if (!sema_analyse_compound_stmt(context, compound_stmt)) goto EXIT_ERROR;
*current = astid(compound_stmt);
current = &compound_stmt->next;
if (incr)
{
Expr **exprs = incr->expression_list;
VECEACH(exprs, j)
{
copy = copy_expr(exprs[j]);
if (!sema_analyse_expr(context, copy)) goto EXIT_ERROR;
if (copy->expr_kind != EXPR_CONST)
{
SEMA_ERROR(copy, "Expected a value that can be evaluated at compile time.");
goto EXIT_ERROR;
}
}
}
}
statement->ast_kind = AST_COMPOUND_STMT;
statement->compound_stmt.first_stmt = start;
statement->compound_stmt.defer_list = (DeferList) { 0, 0 };
success = true;
EXIT_ERROR:
SCOPE_OUTER_END;
return success;
}
static bool sema_analyse_ct_compound_stmt(SemaContext *context, Ast *statement)
{
bool all_ok = ast_ok(statement);
AstId current = statement->compound_stmt.first_stmt;
while (current)
{
Ast *stmt = ast_next(&current);
if (!sema_analyse_statement(context, stmt))
{
ast_poison(stmt);
all_ok = false;
}
}
return all_ok;
}
static inline bool sema_analyse_statement_inner(SemaContext *context, Ast *statement)
{
if (statement->ast_kind == AST_POISONED)
{
return false;
}
if (context->active_scope.jump_end && !context->active_scope.allow_dead_code)
{
if (statement->ast_kind == AST_UNREACHABLE_STMT)
{
context->active_scope.allow_dead_code = true;
return true;
}
//SEMA_ERROR(statement, "This code will never execute.");
context->active_scope.allow_dead_code = true;
//return false;
}
switch (statement->ast_kind)
{
case AST_POISONED:
case AST_SCOPED_STMT:
case AST_DOCS:
case AST_DOC_DIRECTIVE:
case AST_IF_CATCH_SWITCH_STMT:
UNREACHABLE
case AST_SCOPING_STMT:
return sema_analyse_scoping_stmt(context, statement);
case AST_ASM_STMT:
return sema_analyse_asm_stmt(context, statement);
case AST_ASSERT_STMT:
return sema_analyse_assert_stmt(context, statement);
case AST_BREAK_STMT:
return sema_analyse_break_stmt(context, statement);
case AST_CASE_STMT:
SEMA_ERROR(statement, "Unexpected 'case' outside of switch");
return false;
case AST_COMPOUND_STMT:
return sema_analyse_compound_stmt(context, statement);
case AST_CONTINUE_STMT:
return sema_analyse_continue_stmt(context, statement);
case AST_CT_ASSERT:
return sema_analyse_ct_assert_stmt(context, statement);
case AST_CT_COMPOUND_STMT:
return sema_analyse_ct_compound_stmt(context, statement);
case AST_CT_IF_STMT:
return sema_analyse_ct_if_stmt(context, statement);
case AST_DECLARE_STMT:
return sema_analyse_declare_stmt(context, statement);
case AST_DEFAULT_STMT:
SEMA_ERROR(statement, "Unexpected 'default' outside of switch");
return false;
case AST_DEFER_STMT:
return sema_analyse_defer_stmt(context, statement);
case AST_DO_STMT:
return sema_analyse_do_stmt(context, statement);
case AST_EXPR_STMT:
return sema_analyse_expr_stmt(context, statement);
case AST_FOREACH_STMT:
return sema_analyse_foreach_stmt(context, statement);
case AST_FOR_STMT:
return sema_analyse_for_stmt(context, statement);
case AST_IF_STMT:
return sema_analyse_if_stmt(context, statement);
case AST_NOP_STMT:
return true;
case AST_RETURN_STMT:
return sema_analyse_return_stmt(context, statement);
case AST_SWITCH_STMT:
return sema_analyse_switch_stmt(context, statement);
case AST_NEXT_STMT:
return sema_analyse_nextcase_stmt(context, statement);
case AST_UNREACHABLE_STMT:
return sema_analyse_unreachable_stmt(context);
case AST_WHILE_STMT:
return sema_analyse_while_stmt(context, statement);
case AST_CT_SWITCH_STMT:
return sema_analyse_ct_switch_stmt(context, statement);
case AST_CT_ELIF_STMT:
case AST_CT_ELSE_STMT:
UNREACHABLE
case AST_CT_FOREACH_STMT:
return sema_analyse_ct_foreach_stmt(context, statement);
case AST_CT_FOR_STMT:
return sema_analyse_ct_for_stmt(context, statement);
}
UNREACHABLE
}
bool sema_analyse_statement(SemaContext *context, Ast *statement)
{
if (sema_analyse_statement_inner(context, statement)) return true;
return ast_poison(statement);
}
static bool sema_analyse_requires(SemaContext *context, Ast *docs, AstId **asserts)
{
if (!docs) return true;
Ast **directives = docs->directives;
VECEACH(directives, i)
{
Ast *directive = directives[i];
if (directive->doc_directive.kind != DOC_DIRECTIVE_REQUIRE) continue;
Expr *comment = directive->doc_directive.contract.comment;
Expr *declexpr = directive->doc_directive.contract.decl_exprs;
if (comment)
{
if (comment->expr_kind != EXPR_CONST || comment->const_expr.const_kind != CONST_STRING)
{
SEMA_ERROR(comment, "Expected a string here.");
return false;
}
}
assert(declexpr->expr_kind == EXPR_COND);
VECEACH(declexpr->cond_expr, j)
{
Expr *expr = declexpr->cond_expr[j];
if (expr->expr_kind == EXPR_DECL)
{
SEMA_ERROR(expr, "Only expressions are allowed.");
return false;
}
if (!sema_analyse_cond_expr(context, expr)) return false;
Ast *assert = new_ast(AST_ASSERT_STMT, expr->span);
assert->assert_stmt.expr = expr;
assert->assert_stmt.message = comment;
ast_append(asserts, assert);
}
}
return true;
}
bool sema_analyse_function_body(SemaContext *context, Decl *func)
{
if (!decl_ok(func)) return false;
FunctionSignature *signature = &func->func_decl.function_signature;
FunctionPrototype *prototype = func->type->func.prototype;
context->current_function = func;
context->rtype = prototype->rtype;
context->active_scope = (DynamicScope) {
.scope_id = 0,
.depth = 0,
.local_decl_start = 0,
.current_local = 0
};
// Clear returns
vec_resize(context->returns, 0);
context->scope_id = 0;
context->continue_target = 0;
context->next_target = 0;
context->next_switch = 0;
context->break_target = 0;
func->func_decl.annotations = CALLOCS(FuncAnnotations);
SCOPE_START
assert(context->active_scope.depth == 1);
Decl **params = signature->params;
VECEACH(params, i)
{
if (!sema_add_local(context, params[i])) return false;
}
AstId assert_first = 0;
AstId *next = &assert_first;
if (!sema_analyse_requires(context, func->docs, &next)) return false;
if (func->func_decl.attr_naked)
{
AstId current = func->func_decl.body->compound_stmt.first_stmt;
while (current)
{
Ast *stmt = ast_next(&current);
if (stmt->ast_kind != AST_ASM_STMT)
{
SEMA_ERROR(stmt, "Only asm statements are allowed inside of a naked function.");
return false;
}
}
assert_first = 0;
}
else
{
if (!sema_analyse_compound_statement_no_scope(context, func->func_decl.body)) return false;
assert(context->active_scope.depth == 1);
if (!context->active_scope.jump_end)
{
Type *canonical_rtype = type_no_fail(prototype->rtype)->canonical;
if (canonical_rtype != type_void)
{
// IMPROVE better pointer to end.
SEMA_TOKID_ERROR(func->name_token, "Missing return statement at the end of the function.");
return false;
}
}
}
if (assert_first)
{
Ast *ast = new_ast(AST_COMPOUND_STMT, func->func_decl.body->span);
ast->compound_stmt.first_stmt = assert_first;
ast_prepend(&func->func_decl.body->compound_stmt.first_stmt, ast);
}
SCOPE_END;
return true;
}