mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
Updated scope code and variable tracking. Added trailing body macros.
This commit is contained in:
committed by
Christoffer Lerno
parent
bf8e665120
commit
dafe0e6fb6
@@ -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?
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, ¶ms, 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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))
|
||||
|
||||
117
test/test_suite/macros/macro_with_body.c3t
Normal file
117
test/test_suite/macros/macro_with_body.c3t
Normal 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
|
||||
49
test/test_suite/macros/no_body.c3
Normal file
49
test/test_suite/macros/no_body.c3
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user