CT variables now follow CT scopes. It's now allowed to mutate CT variables in deeper runtime scopes.

This commit is contained in:
Christoffer Lerno
2022-12-07 16:25:08 +01:00
committed by Christoffer Lerno
parent f7659776fc
commit 1ea5625183
19 changed files with 212 additions and 119 deletions

View File

@@ -35,6 +35,13 @@ Decl *decl_new(DeclKind decl_kind, const char *name, SourceSpan span, Visibility
return decl;
}
bool decl_is_ct_var(Decl *decl)
{
if (decl->decl_kind != DECL_VAR) return false;
return decl_var_kind_is_ct(decl->var.kind);
UNREACHABLE;
}
Decl *decl_new_with_type(const char *name, SourceSpan loc, DeclKind decl_type, Visibility visibility)
{
Decl *decl = decl_calloc();

View File

@@ -441,7 +441,6 @@ typedef struct VarDecl_
struct SemaContext_ *context;
SourceSpan span;
} hash_var;
unsigned scope_depth; // CT var
struct
{
void *backend_debug_ref;
@@ -1627,6 +1626,7 @@ typedef struct SemaContext_
Ast **returns_cache;
Expr **macro_varargs;
Decl **macro_params;
Decl** ct_locals;
};
Type *rtype;
struct SemaContext_ *yield_context;
@@ -2082,6 +2082,7 @@ INLINE const char *decl_get_extname(Decl *decl);
static inline Decl *decl_raw(Decl *decl);
static inline DeclKind decl_from_token(TokenType type);
static inline bool decl_is_local(Decl *decl);
bool decl_is_ct_var(Decl *decl);
Decl *decl_find_enum_constant(Decl *decl, const char *name);
AlignSize decl_find_member_offset(Decl *decl, Decl *member);
@@ -3162,6 +3163,11 @@ INLINE bool expr_is_const(Expr *expr)
return expr->expr_kind == EXPR_CONST;
}
INLINE bool decl_var_kind_is_ct(VarDeclKind kind)
{
return kind >= VARDECL_FIRST_CT && kind <= VARDECL_LAST_CT;
}
static inline bool decl_is_local(Decl *decl)
{
if (decl->decl_kind != DECL_VAR) return false;

View File

@@ -702,15 +702,17 @@ typedef enum
VARDECL_PARAM,
VARDECL_MEMBER,
VARDECL_BITMEMBER,
VARDECL_PARAM_CT,
VARDECL_PARAM_CT_TYPE,
VARDECL_PARAM_REF,
VARDECL_PARAM_EXPR,
VARDECL_LOCAL_CT,
VARDECL_LOCAL_CT_TYPE,
VARDECL_UNWRAPPED,
VARDECL_ERASE,
VARDECL_REWRAPPED,
VARDECL_PARAM_CT,
VARDECL_PARAM_CT_TYPE,
VARDECL_LOCAL_CT,
VARDECL_LOCAL_CT_TYPE,
VARDECL_FIRST_CT = VARDECL_PARAM_CT,
VARDECL_LAST_CT = VARDECL_LOCAL_CT_TYPE,
} VarDeclKind;
typedef enum

View File

@@ -2365,10 +2365,8 @@ bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl)
UNREACHABLE
}
decl->var.scope_depth = context->active_scope.depth;
return sema_add_local(context, decl);
FAIL:
decl->var.scope_depth = context->active_scope.depth;
sema_add_local(context, decl);
return decl_poison(decl);
}

View File

@@ -1641,10 +1641,6 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
}
param->var.init_expr = args[i];
VarDeclKind kind = param->var.kind;
if (kind == VARDECL_PARAM_CT_TYPE || kind == VARDECL_PARAM_CT)
{
param->var.scope_depth = context->active_scope.depth + 1;
}
}
Decl **body_params = call_expr->call_expr.body_arguments;
@@ -3928,11 +3924,6 @@ static inline bool sema_binary_analyse_ct_identifier_lvalue(SemaContext *context
return expr_poison(expr);
}
if (decl->var.scope_depth < context->active_scope.depth)
{
SEMA_ERROR(expr, "Cannot modify '%s' inside of a runtime scope.", decl->name);
return false;
}
expr->ct_ident_expr.decl = decl;
expr->resolve_status = RESOLVE_DONE;
return true;
@@ -5367,12 +5358,6 @@ static inline bool sema_expr_analyse_ct_incdec(SemaContext *context, Expr *expr,
return false;
}
if (var->var.scope_depth < context->active_scope.depth)
{
SEMA_ERROR(expr, "Cannot modify '%s' inside of a runtime scope.", var->name);
return false;
}
Expr *end_value = expr_copy(start_value);
// Make the change.

View File

@@ -54,6 +54,8 @@ TokenType sema_splitpathref(const char *string, ArraySize len, Path **path_ref,
void sema_context_init(SemaContext *context, CompilationUnit *unit);
void sema_context_destroy(SemaContext *context);
unsigned sema_context_push_ct_stack(SemaContext *context);
void sema_context_pop_ct_stack(SemaContext *context, unsigned old_state);
bool sema_analyse_function_body(SemaContext *context, Decl *func);
bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts);

View File

@@ -385,8 +385,18 @@ static Decl *sema_resolve_path_symbol(SemaContext *context, NameResolve *name_re
return decl ? decl : sema_find_decl_in_global_new(unit, &global_context.symbols, global_context.module_list, name_resolve, false);
}
static inline Decl *sema_find_ct_local(SemaContext *context, const char *symbol)
{
Decl **locals = context->ct_locals;
FOREACH_BEGIN(Decl *cur, locals)
if (cur->name == symbol) return cur;
FOREACH_END();
return NULL;
}
static inline Decl *sema_find_local(SemaContext *context, const char *symbol)
{
if (symbol[0] == '$') return sema_find_ct_local(context, symbol);
Decl **locals = context->locals;
if (!locals || !context->active_scope.current_local) return NULL;
int64_t first = 0;
@@ -793,6 +803,7 @@ Decl *sema_resolve_symbol(SemaContext *context, const char *symbol, Path *path,
static inline void sema_append_local(SemaContext *context, Decl *decl)
{
assert(!decl_is_ct_var(decl));
Decl ***locals = &context->locals;
size_t locals_size = vec_size(*locals);
size_t current_local = context->active_scope.current_local;
@@ -811,13 +822,32 @@ static inline void sema_append_local(SemaContext *context, Decl *decl)
context->active_scope.current_local++;
}
INLINE bool sema_add_ct_local(SemaContext *context, Decl *decl)
{
assert(decl_is_ct_var(decl));
Decl *other = sema_find_ct_local(context, decl->name);
if (other)
{
sema_shadow_error(decl, other);
decl_poison(decl);
decl_poison(other);
return false;
}
decl->resolve_status = RESOLVE_DONE;
vec_add(context->ct_locals, decl);
return true;
}
bool sema_add_local(SemaContext *context, Decl *decl)
{
CompilationUnit *current_unit = decl->unit = context->unit;
// Ignore synthetic locals.
if (!decl->name) return true;
if (decl->decl_kind == DECL_VAR && decl->var.shadow) goto ADD_VAR;
bool is_var = decl->decl_kind == DECL_VAR;
if (is_var && decl_var_kind_is_ct(decl->var.kind)) return sema_add_ct_local(context, decl);
if (is_var && decl->var.shadow) goto ADD_VAR;
Decl *other = sema_find_local(context, decl->name);
assert(!other || other->unit->module);
@@ -834,6 +864,7 @@ ADD_VAR:
return true;
}
void sema_unwrap_var(SemaContext *context, Decl *decl)
{
Decl *alias = decl_copy(decl);

View File

@@ -1829,9 +1829,14 @@ static inline bool sema_analyse_then_overwrite(SemaContext *context, Ast *statem
static inline bool sema_analyse_ct_if_stmt(SemaContext *context, Ast *statement)
{
unsigned ct_context = sema_context_push_ct_stack(context);
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);
if (res == -1) goto FAILED;
if (res)
{
if (sema_analyse_then_overwrite(context, statement, statement->ct_if_stmt.then)) goto SUCCESS;
goto FAILED;
}
Ast *elif = astptrzero(statement->ct_if_stmt.elif);
while (1)
{
@@ -1839,20 +1844,31 @@ static inline bool sema_analyse_ct_if_stmt(SemaContext *context, Ast *statement)
{
// Turn into NOP!
statement->ast_kind = AST_NOP_STMT;
return true;
goto SUCCESS;
}
// 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);
if (sema_analyse_then_overwrite(context, statement, elif->ct_else_stmt)) goto SUCCESS;
goto FAILED;
}
assert(elif->ast_kind == AST_CT_IF_STMT);
res = sema_check_comp_time_bool(context, elif->ct_if_stmt.expr);
if (res == -1) return false;
if (res) return sema_analyse_then_overwrite(context, statement, elif->ct_if_stmt.then);
if (res == -1) goto FAILED;
if (res)
{
if (sema_analyse_then_overwrite(context, statement, elif->ct_if_stmt.then)) goto SUCCESS;
goto FAILED;
}
elif = astptrzero(elif->ct_if_stmt.elif);
}
SUCCESS:
sema_context_pop_ct_stack(context, ct_context);
return true;
FAILED:
sema_context_pop_ct_stack(context, ct_context);
return false;
}
static inline bool sema_analyse_compound_statement_no_scope(SemaContext *context, Ast *compound_statement)
@@ -2083,9 +2099,10 @@ static bool sema_analyse_switch_body(SemaContext *context, Ast *statement, Sourc
static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statement)
{
unsigned ct_context = sema_context_push_ct_stack(context);
// Evaluate the switch statement
Expr *cond = exprptr(statement->ct_switch_stmt.cond);
if (!sema_analyse_ct_expr(context, cond)) return false;
if (!sema_analyse_ct_expr(context, cond)) goto FAILED;
// If we have a type, then we do different evaluation
// compared to when it is a value.
@@ -2105,7 +2122,7 @@ static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statem
FALLTHROUGH;
default:
SEMA_ERROR(cond, "Only types, strings, integers, floats and booleans may be used with '$switch'.");
return false;
goto FAILED;
}
ExprConst *switch_expr_const = &cond->const_expr;
@@ -2132,39 +2149,39 @@ static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statem
if (!type_is_integer(type) && !type_is_float(type))
{
SEMA_ERROR(to_expr, "$case ranges are only allowed for floats and integers.");
return false;
goto FAILED;
}
}
if (is_type)
{
if (!sema_analyse_ct_expr(context, expr)) return false;
if (!sema_analyse_ct_expr(context, expr)) goto FAILED;
if (expr->type != type_typeid)
{
SEMA_ERROR(expr, "A type was expected here not %s.", type_quoted_error_string(expr->type));
return false;
goto FAILED;
}
}
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 (!sema_analyse_expr_rhs(context, type, expr, false)) goto FAILED;
if (to_expr && !sema_analyse_expr_rhs(context, type, to_expr, false)) goto FAILED;
}
if (!expr_is_const(expr))
{
SEMA_ERROR(expr, "The $case must have a constant expression.");
return false;
goto FAILED;
}
if (to_expr && !expr_is_const(to_expr))
{
SEMA_ERROR(to_expr, "The $case must have a constant expression.");
return false;
goto FAILED;
}
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;
goto FAILED;
}
// Check that it is unique.
for (unsigned j = 0; j < i; j++)
@@ -2177,7 +2194,7 @@ static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statem
{
SEMA_ERROR(stmt, "'%s' appears more than once.", expr_const_to_error_string(const_expr));
SEMA_NOTE(cases[j]->case_stmt.expr, "The previous $case was here.");
return false;
goto FAILED;
}
}
if (expr_const_in_range(switch_expr_const, const_expr, const_to_expr))
@@ -2191,7 +2208,7 @@ static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statem
{
SEMA_ERROR(stmt, "More than one $default is not allowed.");
SEMA_NOTE(cases[default_case], "The previous $default was here.");
return false;
goto FAILED;
}
default_case = (int)i;
continue;
@@ -2211,20 +2228,27 @@ static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statem
if (!body)
{
statement->ast_kind = AST_NOP_STMT;
return true;
goto SUCCESS;
}
return sema_analyse_then_overwrite(context, statement, body->compound_stmt.first_stmt);
if (!sema_analyse_then_overwrite(context, statement, body->compound_stmt.first_stmt)) goto FAILED;
SUCCESS:
sema_context_pop_ct_stack(context, ct_context);
return true;
FAILED:
sema_context_pop_ct_stack(context, ct_context);
return false;
}
static inline bool sema_analyse_ct_foreach_stmt(SemaContext *context, Ast *statement)
{
unsigned ct_context = sema_context_push_ct_stack(context);
Expr *collection = exprptr(statement->ct_foreach_stmt.expr);
if (!sema_analyse_ct_expr(context, collection)) return false;
if (!expr_is_const_untyped_list(collection) && !expr_is_const_initializer(collection))
{
SEMA_ERROR(collection, "Expected a list to iterate over");
return false;
goto FAILED;
}
unsigned count;
ConstInitializer *initializer = NULL;
@@ -2242,11 +2266,15 @@ static inline bool sema_analyse_ct_foreach_stmt(SemaContext *context, Ast *state
else
{
// Empty list
if (init_type == CONST_INIT_ZERO) return true;
if (init_type == CONST_INIT_ZERO)
{
sema_context_pop_ct_stack(context, ct_context);
return true;
}
if (init_type != CONST_INIT_ARRAY_FULL)
{
SEMA_ERROR(collection, "Only regular arrays are allowed here.");
return false;
goto FAILED;
}
count = vec_size(initializer->init_array_full);
}
@@ -2260,48 +2288,52 @@ static inline bool sema_analyse_ct_foreach_stmt(SemaContext *context, Ast *state
const char *index_name = statement->ct_foreach_stmt.index_name;
AstId start = 0;
SCOPE_START;
if (index_name)
if (index_name)
{
index = decl_new_var(index_name, statement->ct_foreach_stmt.index_span, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL);
index->type = type_int;
if (!sema_add_local(context, index)) goto FAILED;
}
Decl *value = decl_new_var(statement->ct_foreach_stmt.value_name, statement->ct_foreach_stmt.value_span, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL);
if (!sema_add_local(context, value)) goto FAILED;
// Get the body
Ast *body = astptr(statement->ct_foreach_stmt.body);
AstId *current = &start;
unsigned loop_context = sema_context_push_ct_stack(context);
for (unsigned i = 0; i < count; i++)
{
sema_context_pop_ct_stack(context, loop_context);
Ast *compound_stmt = copy_ast_single(body);
if (expressions)
{
index = decl_new_var(index_name, statement->ct_foreach_stmt.index_span, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL);
value->var.init_expr = expressions[i];
}
else
{
Expr *expr = expr_new(EXPR_CONST, collection->span);
if (!expr_rewrite_to_const_initializer_index(const_list_type, initializer, expr, i))
{
SEMA_ERROR(collection, "Complex expressions are not allowed.");
goto FAILED;
}
value->var.init_expr = expr;
}
if (index)
{
index->var.init_expr = expr_new_const_int(index->span, type_int, i, true);
index->type = type_int;
if (!sema_add_local(context, index)) return SCOPE_POP_ERROR();
}
Decl *value = decl_new_var(statement->ct_foreach_stmt.value_name, statement->ct_foreach_stmt.value_span, 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;
for (unsigned i = 0; i < count; i++)
{
Ast *compound_stmt = copy_ast_single(body);
if (expressions)
{
value->var.init_expr = expressions[i];
}
else
{
Expr *expr = expr_new(EXPR_CONST, collection->span);
if (!expr_rewrite_to_const_initializer_index(const_list_type, initializer, expr, i))
{
SEMA_ERROR(collection, "Complex expressions are not allowed.");
return false;
}
value->var.init_expr = expr;
}
if (index)
{
index->var.init_expr = expr_new_const_int(index->span, type_int, i, true);
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;
if (!sema_analyse_compound_stmt(context, compound_stmt)) goto FAILED;
*current = astid(compound_stmt);
current = &compound_stmt->next;
}
sema_context_pop_ct_stack(context, ct_context);
statement->ast_kind = AST_COMPOUND_STMT;
statement->compound_stmt.first_stmt = start;
return true;
FAILED:
sema_context_pop_ct_stack(context, ct_context);
return false;
}
static inline bool sema_analyse_switch_stmt(SemaContext *context, Ast *statement)
@@ -2411,6 +2443,7 @@ bool sema_analyse_ct_assert_stmt(SemaContext *context, Ast *statement)
*/
static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement)
{
unsigned for_context = sema_context_push_ct_stack(context);
bool success = false;
ExprId init;
if ((init = statement->for_stmt.init))
@@ -2427,13 +2460,13 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement
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 '$for'");
return false;
goto FAILED;
}
if (!sema_analyse_var_decl_ct(context, decl)) return false;
if (!sema_analyse_var_decl_ct(context, decl)) goto FAILED;
continue;
}
// If expression evaluate it and make sure it is constant.
if (!sema_analyse_ct_expr(context, expr)) return false;
if (!sema_analyse_ct_expr(context, expr)) goto FAILED;
FOREACH_END();
}
ExprId condition = statement->for_stmt.cond;
@@ -2445,16 +2478,18 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement
assert(condition);
// We set a maximum of macro iterations.
// we might consider reducing this.
unsigned current_ct_scope = sema_context_push_ct_stack(context);
for (int i = 0; i < MAX_MACRO_ITERATIONS; i++)
{
sema_context_pop_ct_stack(context, current_ct_scope);
// First evaluate the cond, which we note that we *must* have.
// we need to make a copy
Expr *copy = copy_expr_single(exprptr(condition));
if (!sema_analyse_cond_expr(context, copy)) return false;
if (!sema_analyse_cond_expr(context, copy)) goto FAILED;
if (!expr_is_const(copy))
{
SEMA_ERROR(copy, "Expected a value that can be evaluated at compile time.");
return false;
goto FAILED;
}
// This is simple, since we know we have a boolean, just break if we reached "false"
if (!copy->const_expr.b) break;
@@ -2463,7 +2498,7 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement
Ast *compound_stmt = copy_ast_single(body);
// Analyse the body
if (!sema_analyse_compound_statement_no_scope(context, compound_stmt)) return false;
if (!sema_analyse_compound_statement_no_scope(context, compound_stmt)) goto FAILED;
// Append it.
*current = astid(compound_stmt);
@@ -2471,13 +2506,16 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement
// Copy and evaluate all the expressions in "incr"
FOREACH_BEGIN(Expr *expr, incr_list)
if (!sema_analyse_ct_expr(context, copy_expr_single(expr))) return false;
if (!sema_analyse_ct_expr(context, copy_expr_single(expr))) goto FAILED;
FOREACH_END();
}
// Analysis is done turn the generated statements into a compound statement for lowering.
statement->ast_kind = AST_COMPOUND_STMT;
statement->compound_stmt = (AstCompoundStmt) { .first_stmt = start };
return true;
FAILED:
sema_context_pop_ct_stack(context, for_context);
return false;
}
@@ -2718,6 +2756,7 @@ bool sema_analyse_function_body(SemaContext *context, Decl *func)
.label_start = 0,
.current_local = 0
};
vec_resize(context->ct_locals, 0);
// Clear returns
vec_resize(context->returns, 0);

View File

@@ -321,13 +321,26 @@ void sema_analysis_run(void)
void sema_context_init(SemaContext *context, CompilationUnit *unit)
{
*context = (SemaContext) { .unit = unit, .compilation_unit = unit, .locals = global_context_acquire_locals_list() };
*context = (SemaContext) { .unit = unit, .compilation_unit = unit,
.ct_locals = global_context_acquire_locals_list(),
.locals = global_context_acquire_locals_list() };
}
void sema_context_pop_ct_stack(SemaContext *context, unsigned old_state)
{
vec_resize(context->ct_locals, old_state);
}
unsigned sema_context_push_ct_stack(SemaContext *context)
{
return vec_size(context->ct_locals);
}
void sema_context_destroy(SemaContext *context)
{
if (!context->unit) return;
generic_context_release_locals_list(context->locals);
generic_context_release_locals_list(context->ct_locals);
}
Decl **global_context_acquire_locals_list(void)
@@ -338,6 +351,7 @@ Decl **global_context_acquire_locals_list(void)
}
Decl **result = VECLAST(global_context.locals_list);
vec_pop(global_context.locals_list);
vec_resize(result, 0);
return result;
}

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.3.109"
#define COMPILER_VERSION "0.3.111"