Updated scope code and variable tracking. Added trailing body macros.

This commit is contained in:
Christoffer Lerno
2021-06-09 17:12:48 +02:00
committed by Christoffer Lerno
parent bf8e665120
commit dafe0e6fb6
21 changed files with 1249 additions and 778 deletions

View File

@@ -96,10 +96,11 @@ work on Windows. Also, parts of the code is still rough and needs to be made sol
- [x] Built-in linking
- [x] CT only macros evaluating to constants
- [x] range initializers e.g. `{ [1..2] = 2 }`
- [x] Trailing body macros e.g. `@foo(1, 100; int a) { bar(a); };`
- [ ] Anonymous structs
- [ ] Complete C ABI conformance *in progress*
- [ ] Debug info *in progress*
- [ ] Virtual type
- [ ] Virtual type *in progress*
- [ ] Enum associated data support
- [ ] Windows support
- [ ] All attributes
@@ -111,7 +112,6 @@ work on Windows. Also, parts of the code is still rough and needs to be made sol
- [ ] Complex macros
- [ ] Escape macros
- [ ] Implicit capturing macros
- [ ] Trailing body macros
- [ ] Subarray initializers
- [ ] Bitstructs
- [ ] `asm` section
@@ -120,7 +120,6 @@ work on Windows. Also, parts of the code is still rough and needs to be made sol
- [ ] Pre-post conditions
- [ ] Stdlib inclusion
- [ ] String functions
- [ ] Compile time incremental arrays
- [ ] Simd vector types
#### What can you help with?

View File

@@ -1082,6 +1082,10 @@ static void fprint_ast_recursive(Context *context, FILE *file, Ast *ast, int ind
if (!ast) return;
switch (ast->ast_kind)
{
case AST_YIELD_STMT:
DUMP("(yield");
printf("Missing data\n");
DUMPEND();
case AST_CT_ASSERT:
DUMP("($assert");
DUMPEXPR(ast->ct_assert_stmt.expr);

View File

@@ -290,7 +290,7 @@ typedef struct VarDecl_
union
{
void *backend_debug_ref;
void *scope;
unsigned scope_depth;
};
union
{
@@ -414,6 +414,7 @@ typedef struct
{
bool failable : 1;
Decl **parameters;
Decl **body_parameters;
TypeInfo *rtype; // May be null!
struct Ast_ *body;
} MacroDecl;
@@ -422,6 +423,7 @@ typedef struct
{
struct Ast_ **cases;
Decl **parameters;
Decl **body_parameters;
TypeInfo *rtype; // May be null!
Path *path; // For redefinition
} GenericDecl;
@@ -461,7 +463,7 @@ typedef struct
bool next_target : 1;
void *break_target;
void *continue_target;
ScopeId scope_id;
AstId scope_defer;
AstId parent;
} LabelDecl;
@@ -481,6 +483,7 @@ typedef struct Decl_
bool needs_additional_pad : 1;
bool is_substruct : 1;
bool has_variable_array : 1;
bool has_body_param : 1;
void *backend_ref;
const char *cname;
AlignSize alignment;
@@ -593,6 +596,8 @@ typedef struct
bool unsplat_last : 1;
Expr *function;
Expr **arguments;
Decl **body_arguments;
Ast *body;
} ExprCall;
typedef struct
@@ -912,7 +917,7 @@ typedef struct
{
struct
{
ScopeId scope_id;
Ast* scope_defer;
};
struct
{
@@ -963,7 +968,7 @@ typedef struct
FlowCommon flow;
bool is_switch : 1;
bool has_err_var : 1;
ScopeId scope_id;
Ast* scope_defer;
AstId defer;
union
{
@@ -1079,6 +1084,13 @@ typedef struct
Expr *expr;
} AstAssertStmt;
typedef struct
{
Expr **values;
Decl **declarations;
Ast *ast;
} AstYieldStmt;
typedef struct
{
DocDirectiveKind kind;
@@ -1144,6 +1156,7 @@ typedef struct Ast_
AstAssertStmt assert_stmt;
Ast **directives;
AstDocDirective doc_directive;
AstYieldStmt yield_stmt;
};
} Ast;
@@ -1178,10 +1191,24 @@ typedef struct DynamicScope_
bool jump_end : 1;
ScopeFlags flags;
Decl **local_decl_start;
Decl **current_local;
AstId defer_last;
AstId defer_start;
Ast *in_defer;
unsigned depth;
} DynamicScope;
typedef struct MacroScope_
{
Decl *macro;
Decl **locals_start;
unsigned depth;
Decl **yield_symbol_start;
Decl **yield_symbol_end;
Decl **yield_args;
Ast *yield_body;
bool in_yield;
} MacroScope;
typedef union
{
@@ -1252,7 +1279,6 @@ typedef struct Context_
AstId next_target;
Ast *next_switch;
AstId next_defer;
DynamicScope *current_scope;
struct
{
Type *expected_block_type;
@@ -1264,19 +1290,13 @@ typedef struct Context_
Type *rtype;
bool failable_return;
int in_volatile_section;
struct
{
Decl **macro_locals_start;
int macro_counter;
int macro_nesting;
};
Decl **last_local;
MacroScope macro_scope;
struct {
STable external_symbols;
Decl **external_symbol_list;
};
Decl* locals[MAX_LOCALS];
DynamicScope scopes[MAX_SCOPE_DEPTH];
DynamicScope active_scope;
Lexer *lexer;
Token tok;
TokenId prev_tok;

View File

@@ -337,6 +337,9 @@ Ast *copy_ast(Ast *source)
case AST_RETURN_STMT:
MACRO_COPY_EXPR(ast->return_stmt.expr);
return ast;
case AST_SCOPED_STMT:
MACRO_COPY_AST(ast->scoped_stmt.stmt);
return ast;
case AST_SWITCH_STMT:
copy_flow(ast);
MACRO_COPY_EXPR(ast->switch_stmt.cond);
@@ -356,8 +359,8 @@ Ast *copy_ast(Ast *source)
MACRO_COPY_EXPR(ast->while_stmt.cond);
MACRO_COPY_AST(ast->while_stmt.body);
return ast;
case AST_SCOPED_STMT:
MACRO_COPY_AST(ast->scoped_stmt.stmt);
case AST_YIELD_STMT:
MACRO_COPY_EXPR_LIST(ast->yield_stmt.values);
return ast;
}
UNREACHABLE;

View File

@@ -77,6 +77,7 @@ typedef enum
AST_UNREACHABLE_STMT,
AST_VOLATILE_STMT,
AST_WHILE_STMT,
AST_YIELD_STMT,
AST_SCOPED_STMT,
} AstKind;
@@ -438,6 +439,7 @@ typedef enum
TOKEN_VIRTUAL,
TOKEN_VOLATILE,
TOKEN_WHILE,
TOKEN_YIELD,
TOKEN_CT_ASSERT, // $assert
TOKEN_CT_CASE, // $case

View File

@@ -6,12 +6,16 @@
void gencontext_emit_try_stmt(GenContext *context, Ast *pAst);
bool ast_is_empty_compound_stmt(Ast *ast)
static bool ast_is_not_empty(Ast *ast)
{
if (ast->ast_kind != AST_COMPOUND_STMT) return false;
return !vec_size(ast->compound_stmt.stmts) && ast->compound_stmt.defer_list.start == ast->compound_stmt.defer_list.end;
if (!ast) return false;
if (ast->ast_kind != AST_COMPOUND_STMT) return true;
if (vec_size(ast->compound_stmt.stmts)) return true;
return ast->compound_stmt.defer_list.start != ast->compound_stmt.defer_list.end;
}
void llvm_emit_compound_stmt(GenContext *context, Ast *ast)
{
if (llvm_use_debug(context))
@@ -247,13 +251,13 @@ void llvm_emit_if(GenContext *c, Ast *ast)
LLVMBasicBlockRef else_block = exit_block;
// Only generate a target if
if (!ast_is_empty_compound_stmt(ast->if_stmt.then_body))
if (ast_is_not_empty(ast->if_stmt.then_body))
{
then_block = llvm_basic_block_new(c, "if.then");
}
// We have an optional else block.
if (ast->if_stmt.else_body && !ast_is_empty_compound_stmt(ast->if_stmt.else_body))
if (ast_is_not_empty(ast->if_stmt.else_body))
{
else_block = llvm_basic_block_new(c, "if.else");
}
@@ -337,7 +341,7 @@ void gencontext_emit_for_stmt(GenContext *c, Ast *ast)
// We have 3 optional parts, which makes this code bit complicated.
LLVMBasicBlockRef exit_block = llvm_basic_block_new(c, "for.exit");
LLVMBasicBlockRef inc_block = ast->for_stmt.incr ? llvm_basic_block_new(c, "for.inc") : NULL;
LLVMBasicBlockRef body_block = ast->for_stmt.body->compound_stmt.stmts ? llvm_basic_block_new(c, "for.body") : NULL;
LLVMBasicBlockRef body_block = ast_is_not_empty(ast->for_stmt.body) ? llvm_basic_block_new(c, "for.body") : NULL;
LLVMBasicBlockRef cond_block = ast->for_stmt.cond ? llvm_basic_block_new(c, "for.cond") : NULL;
// Break is simple it always jumps out.
@@ -1193,6 +1197,26 @@ void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *pani
llvm_emit_block(c, ok_block);
}
void llvm_emit_yield_stmt(GenContext *c, Ast *ast)
{
Decl **declarations = ast->yield_stmt.declarations;
Expr **values = ast->yield_stmt.values;
// Create backend refs on demand.
foreach(declarations, i)
{
Decl *decl = declarations[i];
if (!decl->backend_ref) llvm_emit_local_var_alloca(c, decl);
}
// Set the values
foreach(values, i)
{
Expr *expr = values[i];
BEValue value;
llvm_emit_expr(c, &value, expr);
llvm_store_bevalue_aligned(c, declarations[i]->backend_ref, &value, declarations[i]->alignment);
}
llvm_emit_stmt(c, ast->yield_stmt.ast);
}
void llvm_emit_stmt(GenContext *c, Ast *ast)
{
@@ -1205,6 +1229,9 @@ void llvm_emit_stmt(GenContext *c, Ast *ast)
case AST_POISONED:
case AST_DEFINE_STMT:
UNREACHABLE
case AST_YIELD_STMT:
llvm_emit_yield_stmt(c, ast);
break;
case AST_TRY_STMT:
gencontext_emit_try_stmt(c, ast);
break;

View File

@@ -455,17 +455,37 @@ static Expr *parse_call_expr(Context *context, Expr *left)
Expr **params = NULL;
advance_and_verify(context, TOKEN_LPAREN);
bool unsplat = false;
Decl **body_args = NULL;
if (!TOKEN_IS(TOKEN_RPAREN))
{
if (!parse_param_list(context, &params, TOKEN_RPAREN, &unsplat)) return poisoned_expr;
}
TRY_CONSUME_OR(TOKEN_RPAREN, "Expected the ending ')' here", poisoned_expr);
if (try_consume(context, TOKEN_EOS) && left->expr_kind == EXPR_MACRO_IDENTIFIER)
{
if (!parse_macro_argument_declarations(context, VISIBLE_LOCAL, &body_args, false)) return poisoned_expr;
}
if (!TOKEN_IS(TOKEN_RPAREN))
{
SEMA_TOKID_ERROR(context->prev_tok, "Expected the ending ')' here.");
return poisoned_expr;
}
advance(context);
Expr *call = EXPR_NEW_EXPR(EXPR_CALL, left);
call->call_expr.function = left;
call->call_expr.arguments = params;
call->call_expr.unsplat_last = unsplat;
call->call_expr.body_arguments = body_args;
RANGE_EXTEND_PREV(call);
if (body_args && !TOKEN_IS(TOKEN_LBRACE))
{
SEMA_TOKEN_ERROR(context->tok, "Expected a macro body here.");
return poisoned_expr;
}
if (TOKEN_IS(TOKEN_LBRACE) && left->expr_kind == EXPR_MACRO_IDENTIFIER)
{
call->call_expr.body = TRY_AST_OR(parse_compound_stmt(context), poisoned_expr);
}
return call;
}

View File

@@ -1381,13 +1381,15 @@ static inline Ast *parse_generics_statements(Context *context)
return ast;
}
static bool parse_macro_arguments(Context *context, Visibility visibility, Decl ***params_ref)
bool parse_macro_argument_declarations(Context *context, Visibility visibility, Decl ***params_ref, bool allow_vararg)
{
CONSUME_OR(TOKEN_LPAREN, false);
*params_ref = NULL;
bool vararg = false;
while (!try_consume(context, TOKEN_RPAREN))
while (!TOKEN_IS(TOKEN_EOS) && !TOKEN_IS(TOKEN_RPAREN))
{
if (*params_ref)
{
TRY_CONSUME(TOKEN_COMMA, false);
}
TypeInfo *parm_type = NULL;
VarDeclKind param_kind;
TEST_TYPE:
@@ -1431,14 +1433,14 @@ static bool parse_macro_arguments(Context *context, Visibility visibility, Decl
return false;
}
// We either have "... var" or "int... var"
if (try_consume(context, TOKEN_ELLIPSIS))
if (allow_vararg && try_consume(context, TOKEN_ELLIPSIS))
{
vararg = true;
}
else
{
parm_type = TRY_TYPE_OR(parse_type(context), false);
if (try_consume(context, TOKEN_ELLIPSIS))
if (allow_vararg && try_consume(context, TOKEN_ELLIPSIS))
{
vararg = true;
}
@@ -1449,9 +1451,25 @@ static bool parse_macro_arguments(Context *context, Visibility visibility, Decl
param->var.vararg = vararg;
advance(context);
vec_add(*params_ref, param);
COMMA_RPAREN_OR(false);
TokenType current_token = context->tok.type;
}
return true;
}
static bool parse_macro_arguments(Context *context, Visibility visibility, Decl ***params_ref, Decl ***body_params, bool *has_trailing_body)
{
CONSUME_OR(TOKEN_LPAREN, false);
*params_ref = NULL;
*body_params = NULL;
*has_trailing_body = false;
if (!parse_macro_argument_declarations(context, visibility, params_ref, true)) return false;
if (try_consume(context, TOKEN_EOS))
{
*has_trailing_body = true;
if (!parse_macro_argument_declarations(context, visibility, body_params, true)) return false;
}
TRY_CONSUME(TOKEN_RPAREN, false);
return true;
}
/**
* generics_declaration
@@ -1482,7 +1500,9 @@ static inline Decl *parse_generics_declaration(Context *context, Visibility visi
decl->generic_decl.path = path;
if (!consume_ident(context, "generic function name")) return poisoned_decl;
decl->generic_decl.rtype = rtype;
if (!parse_macro_arguments(context, visibility, &decl->generic_decl.parameters)) return poisoned_decl;
bool trailing_body = false;
if (!parse_macro_arguments(context, visibility, &decl->generic_decl.parameters, &decl->generic_decl.body_parameters, &trailing_body)) return poisoned_decl;
decl->has_body_param = trailing_body;
Ast **cases = NULL;
if (!parse_switch_body(context, &cases, TOKEN_CASE, TOKEN_DEFAULT, true)) return poisoned_decl;
decl->generic_decl.cases = cases;
@@ -1776,8 +1796,9 @@ static inline Decl *parse_macro_declaration(Context *context, Visibility visibil
}
TRY_CONSUME_OR(TOKEN_IDENT, "Expected a macro name here.", poisoned_decl);
if (!parse_macro_arguments(context, visibility, &decl->macro_decl.parameters)) return poisoned_decl;
bool trailing_body = false;
if (!parse_macro_arguments(context, visibility, &decl->macro_decl.parameters, &decl->macro_decl.body_parameters, &trailing_body)) return poisoned_decl;
decl->has_body_param = trailing_body;
decl->macro_decl.body = TRY_AST_OR(parse_stmt(context), poisoned_decl);
return decl;
}

View File

@@ -679,6 +679,29 @@ static inline Ast *parse_decl_or_expr_stmt(Context *context)
return ast;
}
/**
* yield_stmt = 'yield' (expr (',' expr)*)? EOS
* @param context
* @return
*/
static inline Ast *parse_yield_stmt(Context *context)
{
Ast *ast = AST_NEW_TOKEN(AST_YIELD_STMT, context->tok);
advance_and_verify(context, TOKEN_YIELD);
Expr **exprs = NULL;
while (!TOKEN_IS(TOKEN_EOS))
{
if (exprs)
{
CONSUME_OR(TOKEN_COMMA, poisoned_ast);
}
Expr *expr = TRY_EXPR_OR(parse_expr(context), poisoned_ast);
vec_add(exprs, expr);
}
ast->yield_stmt.values = exprs;
RANGE_EXTEND_PREV(ast);
return ast;
}
/**
* define_stmt
* : define CT_IDENT '=' const_expr EOS
@@ -1014,6 +1037,8 @@ Ast *parse_stmt(Context *context)
return parse_declaration_stmt(context);
case TOKEN_AT:
return parse_expr_stmt(context);
case TOKEN_YIELD:
return parse_yield_stmt(context);
case TOKEN_RETURN:
{
Ast *ast = TRY_AST(parse_return(context));

View File

@@ -52,7 +52,7 @@ bool parse_switch_body(Context *context, Ast ***cases, TokenType case_type, Toke
bool allow_multiple_values);
Expr *parse_expression_list(Context *context);
Decl *parse_decl_after_type(Context *context, TypeInfo *type);
bool parse_macro_argument_declarations(Context *context, Visibility visibility, Decl ***params_ref, bool allow_vararg);
bool parse_param_list(Context *context, Expr ***result, TokenType param_end, bool *unsplat);
Expr *parse_type_compound_literal_expr_after_type(Context *context, TypeInfo *type_info);
Expr *parse_type_access_expr_after_type(Context *context, TypeInfo *type_info);

View File

@@ -10,6 +10,27 @@ static AttributeType sema_analyse_attribute(Context *context, Attr *attr, Attrib
static bool sema_analyse_struct_union(Context *context, Decl *decl);
static bool sema_check_unique_parameters(Decl **decls)
{
STable *table = &global_context.scratch_table;
stable_clear(table);
VECEACH(decls, i)
{
Decl *param = decls[i];
if (param->name)
{
Decl *old = stable_set(table, param->name, param);
if (old)
{
SEMA_ERROR(param, "Parameter name occurred twice, try renaming one of them.");
return false;
}
}
}
return true;
}
static inline bool sema_analyse_struct_member(Context *context, Decl *decl)
{
if (decl->name)
@@ -300,18 +321,32 @@ static bool sema_analyse_struct_union(Context *context, Decl *decl)
}
DEBUG_LOG("Beginning analysis of %s.", decl->name ? decl->name : "anon");
if (decl->name) context_push_scope(context);
bool success;
if (decl->decl_kind == DECL_UNION)
if (decl->name)
{
success = sema_analyse_union_members(context, decl, decl->strukt.members);
SCOPE_START
if (decl->decl_kind == DECL_UNION)
{
success = sema_analyse_union_members(context, decl, decl->strukt.members);
}
else
{
success = sema_analyse_struct_members(context, decl, decl->strukt.members);
}
SCOPE_END;
}
else
{
success = sema_analyse_struct_members(context, decl, decl->strukt.members);
if (decl->decl_kind == DECL_UNION)
{
success = sema_analyse_union_members(context, decl, decl->strukt.members);
}
else
{
success = sema_analyse_struct_members(context, decl, decl->strukt.members);
}
}
DEBUG_LOG("Struct/union size %d, alignment %d.", (int)decl->strukt.size, (int)decl->alignment);
if (decl->name) context_pop_scope(context);
DEBUG_LOG("Analysis complete.");
if (!success) return decl_poison(decl);
return decl_ok(decl);
@@ -804,6 +839,7 @@ static inline bool sema_analyse_macro(Context *context, Decl *decl)
VECEACH(decl->macro_decl.parameters, i)
{
Decl *param = decl->macro_decl.parameters[i];
param->resolve_status = RESOLVE_RUNNING;
assert(param->decl_kind == DECL_VAR);
switch (param->var.kind)
{
@@ -829,7 +865,37 @@ static inline bool sema_analyse_macro(Context *context, Decl *decl)
case VARDECL_ALIAS:
UNREACHABLE
}
param->resolve_status = RESOLVE_DONE;
}
VECEACH(decl->macro_decl.body_parameters, i)
{
Decl *param = decl->macro_decl.body_parameters[i];
param->resolve_status = RESOLVE_RUNNING;
assert(param->decl_kind == DECL_VAR);
switch (param->var.kind)
{
case VARDECL_PARAM:
if (param->var.type_info && !sema_resolve_type_info(context, param->var.type_info)) return false;
break;
case VARDECL_PARAM_EXPR:
case VARDECL_PARAM_CT:
case VARDECL_PARAM_REF:
case VARDECL_PARAM_CT_TYPE:
SEMA_ERROR(param, "Only plain variables are allowed as body parameters.");
break;
case VARDECL_CONST:
case VARDECL_GLOBAL:
case VARDECL_LOCAL:
case VARDECL_MEMBER:
case VARDECL_LOCAL_CT:
case VARDECL_LOCAL_CT_TYPE:
case VARDECL_ALIAS:
UNREACHABLE
}
param->resolve_status = RESOLVE_DONE;
}
if (!sema_check_unique_parameters(decl->macro_decl.parameters)) return false;
if (!sema_check_unique_parameters(decl->macro_decl.body_parameters)) return false;
return true;
}

View File

@@ -1103,12 +1103,23 @@ static bool sema_check_stmt_compile_time(Context *context, Ast *ast)
static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *call_expr, Decl *decl)
{
// TODO failable
if (context->macro_nesting >= MAX_MACRO_NESTING)
if (context->macro_scope.depth >= MAX_MACRO_NESTING)
{
SEMA_ERROR(call_expr, "Too deep nesting (more than %d levels) when evaluating this macro.", MAX_MACRO_NESTING);
return false;
}
if (decl->has_body_param && !call_expr->call_expr.body)
{
SEMA_ERROR(call_expr, "Expected call to have a trailing statement, did you forget to add it?");
return false;
}
if (!decl->has_body_param && call_expr->call_expr.body)
{
SEMA_ERROR(call_expr, "This macro does not support trailing statements, please remove it.");
return false;
}
Expr **args = call_expr->call_expr.arguments;
Decl **func_params = decl->macro_decl.parameters;
@@ -1126,7 +1137,7 @@ static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr
Decl *param = copy_decl(func_params[i]);
vec_add(params, param);
assert(param->decl_kind == DECL_VAR);
assert(param->resolve_status == RESOLVE_NOT_DONE);
assert(param->resolve_status == RESOLVE_DONE);
param->resolve_status = RESOLVE_RUNNING;
// Maybe there's a type, but in general a macro param may be
// typeless.
@@ -1159,10 +1170,9 @@ static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr
// #foo
// We push a scope here as this will prevent the expression from modifying
// compile time variables during evaluation:
context_push_scope(context);
bool ok = sema_analyse_expr_of_required_type(context, param->type, arg, false);
context_pop_scope(context);
if (!ok) return false;
SCOPE_START
if (!sema_analyse_expr_of_required_type(context, param->type, arg, false)) return SCOPE_POP_ERROR();
SCOPE_END;
break;
case VARDECL_PARAM_CT:
// $foo
@@ -1194,8 +1204,7 @@ static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr
}
if (param->type)
{
TODO
// if (!cast_implicit(context, arg, param->type)) return false;
if (!cast_implicit(arg, param->type)) return false;
}
else
{
@@ -1205,77 +1214,133 @@ static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr
param->resolve_status = RESOLVE_DONE;
}
context->macro_nesting++;
context->macro_counter++;
Decl **old_macro_locals_start = context->macro_locals_start;
context->macro_locals_start = context->last_local;
unsigned body_params = vec_size(call_expr->call_expr.body_arguments);
unsigned expected_body_params = vec_size(decl->macro_decl.body_parameters);
if (expected_body_params > body_params)
{
SEMA_ERROR(call_expr, "Not enough parameters for the macro body, expected %d.", expected_body_params);
return false;
}
if (expected_body_params < body_params)
{
SEMA_ERROR(call_expr, "Too many parameters for the macro body, expected %d.", expected_body_params);
return false;
}
for (unsigned i = 0; i < expected_body_params; i++)
{
Decl *body_param = decl->macro_decl.body_parameters[i];
assert(body_param->resolve_status == RESOLVE_DONE);
Decl *body_arg = call_expr->call_expr.body_arguments[i];
if (!body_arg->var.type_info)
{
SEMA_ERROR(body_arg, "Expected a type parameter before this variable name.");
return false;
}
if (!sema_resolve_type_info(context, body_arg->var.type_info)) return false;
body_arg->type = body_arg->var.type_info->type;
if (body_param->var.type_info)
{
Type *declare_type = body_param->var.type_info->type->canonical;
if (declare_type != body_arg->type)
{
SEMA_ERROR(body_arg->var.type_info, "This parameter should be '%s' but was '%s'",
type_to_error_string(declare_type),
type_quoted_error_string(body_arg->type));
return false;
}
}
if (!body_arg->alignment) body_arg->alignment = type_alloca_alignment(body_arg->type);
}
Decl **first_local = context->macro_scope.macro ? context->macro_scope.locals_start : context->locals;
MacroScope old_macro_scope = context->macro_scope;
bool ok = true;
Ast *body = copy_ast(decl->macro_decl.body);
SCOPE_OUTER_START
TypeInfo *foo = decl->macro_decl.rtype;
Ast **saved_returns = context_push_returns(context);
context->expected_block_type = foo ? foo->type : to;
context_push_scope_with_flags(context, SCOPE_MACRO);
for (unsigned i = 0; i < num_args; i++)
{
Decl *param = params[i];
sema_add_local(context, param);
}
VECEACH(body->compound_stmt.stmts, i)
{
if (!sema_analyse_statement(context, body->compound_stmt.stmts[i]))
for (unsigned i = 0; i < expected_body_params; i++)
{
ok = false;
goto EXIT;
Decl *body_arg = call_expr->call_expr.body_arguments[i];
sema_add_local(context, body_arg);
}
}
if (!vec_size(context->returns))
{
if (to)
{
SEMA_ERROR(decl, "Missing return in macro that evaluates to %s.", type_to_error_string(to));
ok = false;
goto EXIT;
}
}
context->macro_scope = (MacroScope){
.macro = decl,
.locals_start = context->active_scope.current_local,
.depth = old_macro_scope.depth + 1,
.yield_symbol_start = first_local,
.yield_body = call_expr->call_expr.body,
.yield_symbol_end = context->active_scope.current_local,
.yield_args = call_expr->call_expr.body_arguments,
};
Expr *first_return_expr = vec_size(context->returns) ? context->returns[0]->return_stmt.expr : NULL;
Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void;
// Let's unify the return statements.
left_canonical = unify_returns(context, left_canonical);
if (!left_canonical)
{
ok = false;
goto EXIT;
}
expr_set_type(call_expr, left_canonical);
if (vec_size(context->returns) == 1)
{
Expr *result = context->returns[0]->return_stmt.expr;
if (result && expr_is_constant_eval(result))
{
if (sema_check_stmt_compile_time(context, body))
Ast *body = copy_ast(decl->macro_decl.body);
TypeInfo *foo = decl->macro_decl.rtype;
Ast **saved_returns = context_push_returns(context);
context->expected_block_type = foo ? foo->type : to;
SCOPE_START_WITH_FLAGS(SCOPE_MACRO);
for (unsigned i = 0; i < num_args; i++)
{
expr_replace(call_expr, result);
Decl *param = params[i];
sema_add_local(context, param);
}
VECEACH(body->compound_stmt.stmts, i)
{
if (!sema_analyse_statement(context, body->compound_stmt.stmts[i]))
{
ok = false;
goto EXIT;
}
}
if (!vec_size(context->returns))
{
if (to)
{
SEMA_ERROR(decl, "Missing return in macro that evaluates to %s.", type_to_error_string(to));
ok = false;
goto EXIT;
}
}
Expr *first_return_expr = vec_size(context->returns) ? context->returns[0]->return_stmt.expr : NULL;
Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void;
// Let's unify the return statements.
left_canonical = unify_returns(context, left_canonical);
if (!left_canonical)
{
ok = false;
goto EXIT;
}
}
}
call_expr->expr_kind = EXPR_MACRO_BLOCK;
call_expr->macro_block.stmts = body->compound_stmt.stmts;
call_expr->macro_block.params = params;
call_expr->macro_block.args = args;
EXIT:
context_pop_scope(context);
context_pop_returns(context, saved_returns);
context->macro_nesting--;
context->macro_locals_start = old_macro_locals_start;
expr_set_type(call_expr, left_canonical);
if (vec_size(context->returns) == 1)
{
Expr *result = context->returns[0]->return_stmt.expr;
if (result && expr_is_constant_eval(result))
{
if (sema_check_stmt_compile_time(context, body))
{
expr_replace(call_expr, result);
goto EXIT;
}
}
}
call_expr->expr_kind = EXPR_MACRO_BLOCK;
call_expr->macro_block.stmts = body->compound_stmt.stmts;
call_expr->macro_block.params = params;
call_expr->macro_block.args = args;
EXIT:
SCOPE_END;
context_pop_returns(context, saved_returns);
SCOPE_OUTER_END;
context->macro_scope = old_macro_scope;
return ok;
}
@@ -2042,10 +2107,11 @@ CHECK_DEEPER:
}
Decl *decl = type->decl;
context_push_scope(context);
add_members_to_context(context, decl);
Decl *member = sema_resolve_symbol_in_current_dynamic_scope(context, kw);
context_pop_scope(context);
Decl *member;
SCOPE_START
add_members_to_context(context, decl);
member = sema_resolve_symbol_in_current_dynamic_scope(context, kw);
SCOPE_END;
if (!member)
@@ -2969,7 +3035,7 @@ static inline bool sema_expr_analyse_ct_identifier_lvalue(Context *context, Expr
return expr_poison(expr);
}
if ((intptr_t)decl->var.scope < (intptr_t)context->current_scope)
if (decl->var.scope_depth < context->active_scope.depth)
{
SEMA_ERROR(expr, "Cannot modify '%s' inside of a runtime scope.", decl->name);
return false;
@@ -4537,7 +4603,7 @@ static inline bool sema_expr_analyse_guard(Context *context, Type *to, Expr *exp
{
Expr *inner = expr->guard_expr.inner;
bool success = sema_analyse_expr(context, to, inner);
expr->guard_expr.defer = context->current_scope->defer_last;
expr->guard_expr.defer = context->active_scope.defer_last;
if (!success) return false;
expr_copy_types(expr, inner);
expr->pure = false;
@@ -4585,36 +4651,36 @@ static inline bool sema_expr_analyse_expr_block(Context *context, Type *to, Expr
Type *prev_expected_block_type = context->expected_block_type;
Ast **saved_returns = context_push_returns(context);
context->expected_block_type = to;
context_push_scope_with_flags(context, SCOPE_EXPR_BLOCK);
VECEACH(expr->expr_block.stmts, i)
{
if (!sema_analyse_statement(context, expr->expr_block.stmts[i]))
SCOPE_START_WITH_FLAGS(SCOPE_EXPR_BLOCK)
VECEACH(expr->expr_block.stmts, i)
{
if (!sema_analyse_statement(context, expr->expr_block.stmts[i]))
{
success = false;
goto EXIT;
}
}
if (!context->active_scope.jump_end && to)
{
SEMA_ERROR(expr, "Expected the block to return with a value of type %s.", type_to_error_string(to));
success = false;
}
if (!vec_size(context->returns)) goto EXIT;
Expr *first_return_expr = context->returns[0]->return_stmt.expr;
Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void;
// Let's unify the return statements.
left_canonical = unify_returns(context, left_canonical);
if (!left_canonical)
{
success = false;
goto EXIT;
}
}
if (!context->current_scope->jump_end && to)
{
SEMA_ERROR(expr, "Expected the block to return with a value of type %s.", type_to_error_string(to));
success = false;
}
if (!vec_size(context->returns)) goto EXIT;
Expr *first_return_expr = context->returns[0]->return_stmt.expr;
Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void;
// Let's unify the return statements.
left_canonical = unify_returns(context, left_canonical);
if (!left_canonical)
{
success = false;
goto EXIT;
}
expr_set_type(expr, left_canonical);
expr_set_type(expr, left_canonical);
EXIT:
context_pop_scope(context);
SCOPE_END;
context_pop_returns(context, saved_returns);
expr->failable = context->expr_failable_return;
context->expr_failable_return = saved_expr_failable_return;

View File

@@ -9,15 +9,38 @@
int sema_check_comp_time_bool(Context *context, Expr *expr);
bool sema_analyse_function_body(Context *context, Decl *func);
void context_pop_scope(Context *context);
void context_pop_scope_error(Context *context);
void context_push_scope_with_flags(Context *context, ScopeFlags flags);
AstId context_start_defer(Context *context);
static inline void context_push_scope(Context *context)
{
context_push_scope_with_flags(context, SCOPE_NONE);
}
#define PUSH_X(ast, X) AstId _old_##X##_defer = context->X##_defer; AstId _old_##X = context->X##_target; context->X##_target = ast ? astid(ast) : 0; context->X##_defer = context->current_scope->defer_last
#define SCOPE_OUTER_START \
do { \
DynamicScope stored_scope = context->active_scope; \
context_change_scope_with_flags(context, SCOPE_NONE);
#define SCOPE_OUTER_END \
assert(context->active_scope.defer_last == context->active_scope.defer_start); \
context->active_scope = stored_scope; \
} while(0)
#define SCOPE_START SCOPE_START_WITH_FLAGS(SCOPE_NONE)
#define SCOPE_START_WITH_FLAGS(flags) \
do { \
DynamicScope old_scope = context->active_scope; \
context_change_scope_with_flags(context, flags);
#define SCOPE_START_WITH_LABEL(label) \
do { \
DynamicScope old_scope = context->active_scope; \
context_change_scope_for_label(context, label);
#define SCOPE_END \
assert(context->active_scope.defer_last == context->active_scope.defer_start); \
context->active_scope = old_scope; \
} while(0)
#define SCOPE_POP_ERROR() (context->active_scope = old_scope, false)
#define SCOPE_ERROR_END_OUTER() \
do { context->active_scope = stored_scope; } while(0)
void context_pop_defers_to(Context *context, DeferList *list);
Expr *context_pop_defers_and_wrap_expr(Context *context, Expr *expr);
void context_pop_defers_and_replace_ast(Context *context, Ast *ast);
void context_change_scope_for_label(Context *context, Decl *label);
void context_change_scope_with_flags(Context *context, ScopeFlags flags);
#define PUSH_X(ast, X) AstId _old_##X##_defer = context->X##_defer; AstId _old_##X = context->X##_target; context->X##_target = ast ? astid(ast) : 0; context->X##_defer = context->active_scope.defer_last
#define POP_X(X) context->X##_target = _old_##X; context->X##_defer = _old_##X##_defer
#define PUSH_CONTINUE(ast) PUSH_X(ast, continue)
#define POP_CONTINUE() POP_X(continue)

View File

@@ -28,15 +28,12 @@ static inline bool matches_subpath(Path *path_to_check, Path *path_to_find)
Decl *sema_resolve_symbol_in_current_dynamic_scope(Context *context, const char *symbol)
{
if (context->current_scope)
Decl **first = context->active_scope.local_decl_start;
Decl **current = context->active_scope.current_local;
while (current > first)
{
Decl **first = context->current_scope->local_decl_start;
Decl **current = context->last_local - 1;
while (current >= first)
{
if (current[0]->name == symbol) return current[0];
current--;
}
current--;
if (current[0]->name == symbol) return current[0];
}
return NULL;
}
@@ -111,11 +108,19 @@ static Decl *sema_resolve_no_path_symbol(Context *context, const char *symbol,
{
Decl *decl = NULL;
if (context->current_scope)
if (context->active_scope.current_local > &context->locals[0])
{
Decl **first = &context->locals[0];
if (context->macro_nesting) first = context->macro_locals_start;
Decl **current = context->last_local - 1;
Decl **current = context->active_scope.current_local - 1;
if (context->macro_scope.macro)
{
first = context->macro_scope.locals_start;
if (context->macro_scope.in_yield)
{
first = context->macro_scope.yield_symbol_start;
current = context->macro_scope.yield_symbol_end - 1;
}
}
while (current >= first)
{
if (current[0]->name == symbol) return current[0];
@@ -343,13 +348,13 @@ Decl *sema_resolve_normal_symbol(Context *context, TokenId symbol, Path *path, b
static inline bool sema_append_local(Context *context, Decl *decl)
{
if (context->last_local == &context->locals[MAX_LOCALS - 1])
if (context->active_scope.current_local == &context->locals[MAX_LOCALS - 1])
{
SEMA_ERROR(decl, "Reached the maximum number of locals.");
return false;
}
context->last_local[0] = decl;
context->last_local++;
context->active_scope.current_local[0] = decl;
context->active_scope.current_local++;
return true;
}

View File

@@ -199,10 +199,14 @@ void sema_analysis_pass_decls(Module *module)
VECEACH(module->contexts, index)
{
Context *context = module->contexts[index];
context->current_scope = &context->scopes[0];
assert(context->current_scope->defer_last < 10000000);
context->current_scope->scope_id = 0;
context->last_local = &context->locals[0];
context->active_scope = (DynamicScope)
{
.depth = 0,
.scope_id = 0,
.local_decl_start = &context->locals[0],
.current_local = &context->locals[0],
};
context->macro_scope = (MacroScope) {};
VECEACH(context->enums, i)
{
sema_analyse_decl(context, context->enums[i]);

File diff suppressed because it is too large Load Diff

View File

@@ -24,3 +24,99 @@ bool sema_resolve_type_info(Context *context, TypeInfo *type_info)
{
return sema_resolve_type_info_maybe_inferred(context, type_info, false);
}
void context_change_scope_with_flags(Context *context, ScopeFlags flags)
{
unsigned depth = context->active_scope.depth + 1;
if (depth > MAX_SCOPE_DEPTH)
{
FATAL_ERROR("Too deeply nested scopes.");
}
Ast *previous_defer = context->active_scope.in_defer;
AstId parent_defer = context->active_scope.defer_last;
Decl **last_local = context->active_scope.current_local;
assert(parent_defer < 1000000);
// Defer and expression blocks introduce their own return/break/continue
// otherwise just merge with the old flags.
if (!(flags & (SCOPE_DEFER | SCOPE_EXPR_BLOCK)))
{
flags = context->active_scope.flags | flags;
}
context->active_scope = (DynamicScope) {
.scope_id = ++context->scope_id,
.allow_dead_code = false,
.jump_end = false,
.depth = depth,
.current_local = last_local,
.local_decl_start = last_local,
.in_defer = previous_defer,
.defer_last = parent_defer,
.defer_start = parent_defer,
.flags = flags,
};
if (context->scope_id == 0)
{
FATAL_ERROR("Too many scopes.");
}
}
void context_change_scope_for_label(Context *context, Decl *label)
{
context_change_scope_with_flags(context, SCOPE_NONE);
if (label)
{
label->label.defer = context->active_scope.defer_last;
sema_add_local(context, label);
label->label.scope_defer = astid(context->active_scope.in_defer);
}
}
void context_pop_defers_to(Context *context, DeferList *list)
{
list->end = context->active_scope.defer_start;
assert(context->active_scope.defer_last < 10000000);
list->start = context->active_scope.defer_last;
context->active_scope.defer_last = list->end;
}
Expr *context_pop_defers_and_wrap_expr(Context *context, Expr *expr)
{
DeferList defers = { 0, 0 };
context_pop_defers_to(context, &defers);
if (defers.end == defers.start) return expr;
Expr *wrap = expr_new(EXPR_SCOPED_EXPR, expr->span);
expr_copy_types(wrap, expr);
wrap->resolve_status = RESOLVE_DONE;
wrap->expr_scope.expr = expr;
wrap->expr_scope.defers = defers;
return expr;
}
void context_pop_defers_and_replace_ast(Context *context, Ast *ast)
{
DeferList defers = { 0, 0 };
context_pop_defers_to(context, &defers);
if (defers.end == defers.start) return;
if (ast->ast_kind == AST_DEFER_STMT)
{
assert(defers.start == astid(ast));
*ast = *ast->defer_stmt.body;
return;
}
assert(ast->ast_kind != AST_COMPOUND_STMT);
Ast *replacement = ast_copy(ast);
ast->ast_kind = AST_SCOPED_STMT;
ast->scoped_stmt.stmt = replacement;
ast->scoped_stmt.defers = defers;
}

View File

@@ -252,12 +252,14 @@ const char *token_type_to_string(TokenType type)
return "typeof";
case TOKEN_UNION:
return "union";
case TOKEN_WHILE:
return "while";
case TOKEN_VAR:
return "var";
case TOKEN_VOLATILE:
return "volatile";
case TOKEN_WHILE:
return "while";
case TOKEN_YIELD:
return "yield";
// Named types
case TOKEN_VIRTUAL:

View File

@@ -330,6 +330,8 @@ static inline void* _expand(void *vec, size_t element_size)
#define CONCAT(a, b) CONCAT_INNER(a, b)
#define VECEACH(_vec, _index) \
for (unsigned _index = 0, CONCAT(__vecsize_, __LINE__) = vec_size(_vec); _index < CONCAT(__vecsize_, __LINE__); _index++)
#define foreach(_vec, _index) \
for (unsigned _index = 0, CONCAT(__vecsize_, __LINE__) = vec_size(_vec); _index < CONCAT(__vecsize_, __LINE__); _index++)
#define VECNEW(_type, _capacity) ((_type *)(_vec_new(sizeof(_type), _capacity) + 1))

View File

@@ -0,0 +1,117 @@
module withbody;
extern func int printf(char *, ...);
struct Foo
{
int x;
}
func int Foo.mutate(Foo *foo)
{
printf("Mutating\n");
return 10 * ++foo.x;
}
macro macro_with_body(foo, &x; x, y)
{
x = foo.x;
yield foo.mutate(), x;
}
macro repeat(int times; x)
{
for (int i = 0; i < times; i++)
{
yield i + 1;
}
}
func void main()
{
Foo f = { 33 };
int y;
@macro_with_body(f, y; int x, int dy)
{
printf("Got values %d, %d\n", x, dy);
};
@repeat(10; int loop)
{
printf("Repeat %d\n", loop);
};
}
// #expect: withbody.ll
define i32 @withbody.Foo__mutate(%Foo* %0)
entry:
%foo = alloca %Foo*, align 8
store %Foo* %0, %Foo** %foo, align 8
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.2, i32 0, i32 0))
%2 = load %Foo*, %Foo** %foo, align 8
%3 = getelementptr inbounds %Foo, %Foo* %2, i32 0, i32 0
%4 = load i32, i32* %3, align 8
%add = add i32 %4, 1
store i32 %add, i32* %3, align 8
%mul = mul i32 10, %add
ret i32 %mul
define void @main()
%f = alloca %Foo, align 4
%y = alloca i32, align 4
%foo = alloca %Foo, align 4
%x = alloca i32, align 4
%dy = alloca i32, align 4
%times = alloca i32, align 4
%i = alloca i32, align 4
%loop = alloca i32, align 4
%0 = bitcast %Foo* %f to i8*
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %0, i8* align 4 bitcast (%Foo* @.__const to i8*), i32 4, i1 false)
store i32 0, i32* %y, align 4
%1 = load %Foo, %Foo* %f, align 4
store %Foo %1, %Foo* %foo, align 4
%2 = getelementptr inbounds %Foo, %Foo* %foo, i32 0, i32 0
%3 = load i32, i32* %2, align 4
store i32 %3, i32* %y, align 4
%4 = call i32 @withbody.Foo__mutate(%Foo* %foo)
store i32 %4, i32* %x, align 4
%5 = load i32, i32* %y, align 4
store i32 %5, i32* %dy, align 4
%6 = load i32, i32* %x, align 4
%7 = load i32, i32* %dy, align 4
%8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* @.str, i32 0, i32 0), i32 %6, i32 %7)
br label %expr_block.exit
expr_block.exit:
store i32 10, i32* %times, align 4
store i32 0, i32* %i, align 4
br label %for.cond
for.cond:
%9 = load i32, i32* %i, align 4
%10 = load i32, i32* %times, align 4
%lt = icmp slt i32 %9, %10
br i1 %lt, label %for.body, label %for.exit
for.body:
%11 = load i32, i32* %i, align 4
%add = add i32 %11, 1
store i32 %add, i32* %loop, align 4
%12 = load i32, i32* %loop, align 4
%13 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.1, i32 0, i32 0), i32 %12)
br label %for.inc
for.inc:
%14 = load i32, i32* %i, align 4
%add1 = add i32 %14, 1
store i32 %add1, i32* %i, align 4
br label %for.cond
for.exit:
br label %expr_block.exit2
expr_block.exit2:
ret void

View File

@@ -0,0 +1,49 @@
module test;
extern func int printf(char *, ...);
macro foo(x; y)
{
yield x;
}
func void test()
{
@foo(10); // #error: Expected call to have a trailing statement, did you forget to add it?
}
func void test3()
{
@foo(10) // #error: Not enough parameters
{
};
}
func void test5()
{
@foo(10; int a, int b) // #error: Too many parameters for the macro body, expected
{
};
}
macro foo_no(x)
{
return x;
}
func void test2()
{
@foo_no(10) // #error: This macro does not support trailing statements
{
printf("foek");
};
}
macro foo2(x)
{
yield x; // #error: 'yield' can only be used in macros that takes trailing bodies, use ';' after the regular parameters.
}
func void test4()
{
@foo2(10);
}