This commit is contained in:
Christoffer Lerno
2022-01-24 22:52:01 +01:00
parent 1e7ad2e241
commit 8922399c36
12 changed files with 316 additions and 134 deletions

View File

@@ -43,6 +43,7 @@ typedef struct
#define MAX_SCOPE_DEPTH 0x100
#define MAX_STRING_BUFFER 0x10000
#define MAX_MACRO_NESTING 1024
#define MAX_MACRO_ITERATIONS 0xFFFFFF
#define MAX_FUNCTION_SIGNATURE_SIZE 2048
#define MAX_PARAMS 512
#define MAX_MEMBERS ((MemberIndex)(((uint64_t)2) << 28))
@@ -1105,7 +1106,7 @@ typedef struct
Expr *init;
Expr *cond;
Expr *incr;
Ast *body;
AstId body;
void *continue_block;
void *exit_block;
} AstForStmt;
@@ -1993,7 +1994,7 @@ MemberIndex sema_get_initializer_const_array_size(SemaContext *context, Expr *in
bool sema_analyse_expr(SemaContext *context, Expr *expr);
bool sema_analyse_inferred_expr(SemaContext *context, Type *to, Expr *expr);
bool sema_analyse_decl(SemaContext *context, Decl *decl);
bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl);
bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local);
bool sema_analyse_ct_assert_stmt(SemaContext *context, Ast *statement);
bool sema_analyse_statement(SemaContext *context, Ast *statement);

View File

@@ -294,8 +294,6 @@ Ast *ast_copy_deep(Ast *source)
ast->ct_foreach_stmt.body = astid_copy_deep(ast->ct_foreach_stmt.body);
MACRO_COPY_EXPR(ast->ct_foreach_stmt.expr);
return ast;
case AST_CT_FOR_STMT:
TODO
case AST_CT_SWITCH_STMT:
MACRO_COPY_EXPR(ast->ct_switch_stmt.cond);
MACRO_COPY_AST_LIST(ast->ct_switch_stmt.body);
@@ -306,9 +304,6 @@ Ast *ast_copy_deep(Ast *source)
case AST_DEFAULT_STMT:
MACRO_COPY_AST(ast->case_stmt.body);
return ast;
case AST_VAR_STMT:
ast->var_stmt = copy_decl(ast->var_stmt);
return ast;
case AST_DEFER_STMT:
assert(!ast->defer_stmt.prev_defer);
MACRO_COPY_AST(ast->defer_stmt.body);
@@ -322,10 +317,11 @@ Ast *ast_copy_deep(Ast *source)
MACRO_COPY_EXPR(ast->expr_stmt);
return ast;
case AST_FOR_STMT:
case AST_CT_FOR_STMT:
copy_flow(ast);
MACRO_COPY_EXPR(ast->for_stmt.cond);
MACRO_COPY_EXPR(ast->for_stmt.incr);
MACRO_COPY_AST(ast->for_stmt.body);
ast->for_stmt.body = astid_copy_deep(ast->for_stmt.body);
MACRO_COPY_EXPR(ast->for_stmt.init);
return ast;
case AST_FOREACH_STMT:

View File

@@ -50,7 +50,6 @@ typedef enum
AST_CASE_STMT,
AST_COMPOUND_STMT,
AST_CONTINUE_STMT,
AST_VAR_STMT,
AST_CT_ASSERT,
AST_CT_COMPOUND_STMT,
AST_CT_IF_STMT,

View File

@@ -391,7 +391,8 @@ void llvm_emit_for_stmt(GenContext *c, Ast *ast)
Expr *incr = ast->for_stmt.incr;
LLVMBasicBlockRef inc_block = incr ? llvm_basic_block_new(c, "loop.inc") : NULL;
LLVMBasicBlockRef body_block = ast_is_not_empty(ast->for_stmt.body) ? llvm_basic_block_new(c, "loop.body") : NULL;
Ast *body = astptr(ast->for_stmt.body);
LLVMBasicBlockRef body_block = ast_is_not_empty(body) ? llvm_basic_block_new(c, "loop.body") : NULL;
LLVMBasicBlockRef cond_block = NULL;
// Skipping first cond? This is do-while semantics
@@ -507,7 +508,7 @@ void llvm_emit_for_stmt(GenContext *c, Ast *ast)
break;
}
// Now emit the body
llvm_emit_stmt(c, ast->for_stmt.body);
llvm_emit_stmt(c, body);
// Did we have a jump to inc yet?
if (inc_block && !llvm_basic_block_is_unused(inc_block))
@@ -1241,7 +1242,6 @@ void llvm_emit_stmt(GenContext *c, Ast *ast)
case AST_DOCS:
case AST_DOC_DIRECTIVE:
case AST_POISONED:
case AST_VAR_STMT:
case AST_IF_CATCH_SWITCH_STMT:
case AST_SCOPING_STMT:
case AST_FOREACH_STMT:

View File

@@ -397,6 +397,33 @@ Expr *parse_expression_list(ParseContext *context, bool allow_decl)
return expr_list;
}
Expr *parse_ct_expression_list(ParseContext *context, bool allow_decl)
{
Expr *expr_list = EXPR_NEW_TOKEN(EXPR_EXPRESSION_LIST, context->tok);
while (1)
{
Expr *expr;
if (tok_is(context, TOKEN_VAR))
{
ASSIGN_DECL_ELSE(Decl *decl, parse_var_decl(context), poisoned_expr);
if (!allow_decl)
{
SEMA_TOKEN_ERROR(context->tok, "This looks like a declaration, which isn't allowed here.");
return poisoned_expr;
}
expr = expr_new(EXPR_DECL, decl->span);
expr->decl_expr = decl;
}
else
{
ASSIGN_EXPR_ELSE(expr, parse_expr(context), poisoned_expr);
}
vec_add(expr_list->expression_list, expr);
if (!try_consume(context, TOKEN_COMMA)) break;
}
return expr_list;
}
/**
* @param left must be null.
* @return Expr*

View File

@@ -828,6 +828,38 @@ static inline bool is_function_start(ParseContext *context)
}
Decl *parse_var_decl(ParseContext *context)
{
TokenId start = context->tok.id;
advance_and_verify(context, TOKEN_VAR);
Decl *decl;
switch (context->tok.type)
{
case TOKEN_CT_IDENT:
decl = decl_new_var(context->tok.id, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL);
advance(context);
if (try_consume(context, TOKEN_EQ))
{
ASSIGN_EXPR_ELSE(decl->var.init_expr, parse_expr(context), poisoned_decl);
}
break;
case TOKEN_CT_TYPE_IDENT:
decl = decl_new_var(context->tok.id, NULL, VARDECL_LOCAL_CT_TYPE, VISIBLE_LOCAL);
advance(context);
if (try_consume(context, TOKEN_EQ))
{
ASSIGN_EXPR_ELSE(decl->var.init_expr, parse_expr(context), poisoned_decl);
}
break;
default:
SEMA_TOKEN_ERROR(context->tok, "Expected a compile time variable name ('$Foo' or '$foo').");
return poisoned_decl;
}
decl->span.loc = start;
RANGE_EXTEND_PREV(decl);
return decl;
}
bool parse_next_is_decl(ParseContext *context)
{
TokenType next_tok = context->next_tok.type;

View File

@@ -344,7 +344,8 @@ static inline Ast* parse_for_stmt(ParseContext *context)
CONSUME_OR(TOKEN_RPAREN, poisoned_ast);
extend_ast_with_prev_token(context, ast);
ASSIGN_AST_ELSE(ast->for_stmt.body, parse_stmt(context), poisoned_ast);
ASSIGN_AST_ELSE(Ast *body, parse_stmt(context), poisoned_ast);
ast->for_stmt.body = astid(body);
return ast;
}
@@ -527,35 +528,8 @@ static inline Ast *parse_decl_or_expr_stmt(ParseContext *context)
*/
static inline Ast *parse_var_stmt(ParseContext *context)
{
Ast *ast = AST_NEW_TOKEN(AST_VAR_STMT, context->tok);
TokenId start = context->tok.id;
advance_and_verify(context, TOKEN_VAR);
Decl *decl;
switch (context->tok.type)
{
case TOKEN_CT_IDENT:
decl = decl_new_var(context->tok.id, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL);
advance(context);
if (try_consume(context, TOKEN_EQ))
{
ASSIGN_EXPR_ELSE(decl->var.init_expr, parse_expr(context), poisoned_ast);
}
break;
case TOKEN_CT_TYPE_IDENT:
decl = decl_new_var(context->tok.id, NULL, VARDECL_LOCAL_CT_TYPE, VISIBLE_LOCAL);
advance(context);
if (try_consume(context, TOKEN_EQ))
{
ASSIGN_EXPR_ELSE(decl->var.init_expr, parse_expr(context), poisoned_ast);
}
break;
default:
SEMA_TOKEN_ERROR(context->tok, "Expected a compile time variable name ('$Foo' or '$foo').");
return poisoned_ast;
}
decl->span.loc = start;
ast->var_stmt = decl;
RANGE_EXTEND_PREV(decl);
Ast *ast = AST_NEW_TOKEN(AST_DECLARE_STMT, context->tok);
ASSIGN_DECL_ELSE(ast->var_stmt, parse_var_decl(context), poisoned_ast);
RANGE_EXTEND_PREV(ast);
TRY_CONSUME_EOS();
return ast;
@@ -667,7 +641,7 @@ static inline Ast *parse_return(ParseContext *context)
/**
* ct_foreach_stmt
* | CT_FOREACH '(' CT_IDENT (',' CT_IDENT)? ':' expr ')' statement
* | CT_FOREACH '(' CT_IDENT (',' CT_IDENT)? ':' expr ')' ':' statement* CT_ENDFOREACH EOS
* ;
*
* @return
@@ -701,6 +675,47 @@ static inline Ast* parse_ct_foreach_stmt(ParseContext *context)
return ast;
}
/**
* ct_for_stmt
* | CT_FOR '(' decl_expr_list? ';' expression_list? ';' expression_list? ')' ':' statement* CT_ENDFOR ';'
* ;
*/
static inline Ast* parse_ct_for_stmt(ParseContext *context)
{
Ast *ast = AST_NEW_TOKEN(AST_CT_FOR_STMT, context->tok);
advance_and_verify(context, TOKEN_CT_FOR);
CONSUME_OR(TOKEN_LPAREN, poisoned_ast);
if (!TOKEN_IS(TOKEN_EOS))
{
ASSIGN_EXPR_ELSE(ast->for_stmt.init, parse_ct_expression_list(context, true), poisoned_ast);
}
CONSUME_OR(TOKEN_EOS, poisoned_ast);
// Cond is required.
ASSIGN_EXPR_ELSE(ast->for_stmt.cond, parse_expr(context), poisoned_ast);
CONSUME_OR(TOKEN_EOS, poisoned_ast);
if (!TOKEN_IS(TOKEN_RPAREN))
{
ast->for_stmt.incr = parse_ct_expression_list(context, false);
}
CONSUME_OR(TOKEN_RPAREN, poisoned_ast);
CONSUME_OR(TOKEN_COLON, poisoned_ast);
Ast *body = new_ast(AST_COMPOUND_STMT, ast->span);
ast->for_stmt.body = astid(body);
AstId *current = &body->compound_stmt.first_stmt;
while (!try_consume(context, TOKEN_CT_ENDFOR))
{
ASSIGN_AST_ELSE(Ast *stmt, parse_stmt(context), poisoned_ast);
*current = astid(stmt);
current = &stmt->next;
}
return ast;
}
/**
* CTSWITCH '(' expression ')' ':' '{' ct_switch_body '}'
*
@@ -866,7 +881,7 @@ Ast *parse_stmt(ParseContext *context)
case TOKEN_CT_FOREACH:
return parse_ct_foreach_stmt(context);
case TOKEN_CT_FOR:
TODO
return parse_ct_for_stmt(context);
case TOKEN_CT_UNREACHABLE:
return parse_unreachable_stmt(context);
case TOKEN_STAR:

View File

@@ -46,8 +46,10 @@ bool parse_attributes(ParseContext *context, Attr ***attributes_ref);
bool parse_switch_body(ParseContext *context, Ast ***cases, TokenType case_type, TokenType default_type,
bool allow_multiple_values);
Expr *parse_ct_expression_list(ParseContext *context, bool allow_decl);
Expr *parse_expression_list(ParseContext *context, bool allow_decls);
Decl *parse_decl_after_type(ParseContext *context, TypeInfo *type);
Decl *parse_var_decl(ParseContext *context);
bool parse_parameters(ParseContext *context, Visibility visibility, Decl ***params_ref);
bool parse_arg_list(ParseContext *context, Expr ***result, TokenType param_end, bool *unsplat);

View File

@@ -1756,6 +1756,81 @@ bool sema_analyse_decl_type(SemaContext *context, Type *type, SourceSpan span)
}
return true;
}
bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl)
{
Expr *init;
assert(decl->decl_kind == DECL_VAR);
switch (decl->var.kind)
{
case VARDECL_LOCAL_CT_TYPE:
// Locally declared compile time type.
if (decl->var.type_info)
{
SEMA_ERROR(decl->var.type_info, "Compile time type variables may not have a type.");
return false;
}
if ((init = decl->var.init_expr))
{
if (!sema_analyse_expr_lvalue(context, init)) return false;
if (init->expr_kind != EXPR_TYPEINFO)
{
SEMA_ERROR(decl->var.init_expr, "Expected a type assigned to %s.", decl->name);
return false;
}
}
break;
case VARDECL_LOCAL_CT:
if (decl->var.type_info && !sema_resolve_type_info(context, decl->var.type_info)) return false;
if (decl->var.type_info)
{
decl->type = decl->var.type_info->type->canonical;
if (!type_is_builtin(decl->type->type_kind))
{
SEMA_ERROR(decl->var.type_info, "Compile time variables may only be built-in types.");
return false;
}
if ((init = decl->var.init_expr))
{
if (!sema_analyse_expr_rhs(context, decl->type, init, false)) return false;
if (!expr_is_constant_eval(init, CONSTANT_EVAL_ANY))
{
SEMA_ERROR(init, "Expected a constant expression assigned to %s.", decl->name);
return false;
}
}
else
{
TODO // generate.
// decl->var.init_expr =
}
}
else
{
if ((init = decl->var.init_expr))
{
if (!sema_analyse_expr(context, init)) return false;
if (!expr_is_constant_eval(init, CONSTANT_EVAL_ANY))
{
SEMA_ERROR(init, "Expected a constant expression assigned to %s.", decl->name);
return false;
}
decl->type = init->type;
}
else
{
decl->type = type_void;
}
}
break;
default:
UNREACHABLE
}
decl->var.scope_depth = context->active_scope.depth;
return sema_add_local(context, decl);
}
/**
* Analyse a regular global or local declaration, e.g. int x = 123
*/

View File

@@ -674,7 +674,7 @@ static inline bool sema_analyse_while_stmt(SemaContext *context, Ast *statement)
.cond = cond,
.flow = statement->while_stmt.flow,
.incr = NULL,
.body = body,
.body = astid(body),
};
statement->for_stmt = for_stmt;
return success;
@@ -750,99 +750,25 @@ END:;
.cond = expr,
.flow = flow,
.incr = NULL,
.body = body,
.body = astid(body),
};
statement->for_stmt = for_stmt;
return true;
}
static inline bool sema_analyse_declare_stmt(SemaContext *context, Ast *statement)
{
return sema_analyse_var_decl(context, statement->declare_stmt, true);
}
/**
* Check "var $foo = ... " and "var $Foo = ..."
*/
static inline bool sema_analyse_var_stmt(SemaContext *context, Ast *statement)
{
// 1. Pick the declaration.
Decl *decl = statement->var_stmt;
// 2. Convert it to a NOP early
statement->ast_kind = AST_NOP_STMT;
Expr *init;
assert(decl->decl_kind == DECL_VAR);
switch (decl->var.kind)
VarDeclKind kind = statement->declare_stmt->var.kind;
if (kind == VARDECL_LOCAL_CT_TYPE || kind == VARDECL_LOCAL_CT)
{
case VARDECL_LOCAL_CT_TYPE:
// Locally declared compile time type.
if (decl->var.type_info)
{
SEMA_ERROR(decl->var.type_info, "Compile time type variables may not have a type.");
return false;
}
if ((init = decl->var.init_expr))
{
if (!sema_analyse_expr_lvalue(context, init)) return false;
if (init->expr_kind != EXPR_TYPEINFO)
{
SEMA_ERROR(decl->var.init_expr, "Expected a type assigned to %s.", decl->name);
return false;
}
}
break;
case VARDECL_LOCAL_CT:
if (decl->var.type_info && !sema_resolve_type_info(context, decl->var.type_info)) return false;
if (decl->var.type_info)
{
decl->type = decl->var.type_info->type->canonical;
if (!type_is_builtin(decl->type->type_kind))
{
SEMA_ERROR(decl->var.type_info, "Compile time variables may only be built-in types.");
return false;
}
if ((init = decl->var.init_expr))
{
if (!sema_analyse_expr_rhs(context, decl->type, init, false)) return false;
if (!expr_is_constant_eval(init, CONSTANT_EVAL_ANY))
{
SEMA_ERROR(init, "Expected a constant expression assigned to %s.", decl->name);
return false;
}
}
else
{
TODO // generate.
// decl->var.init_expr =
}
}
else
{
if ((init = decl->var.init_expr))
{
if (!sema_analyse_expr(context, init)) return false;
if (!expr_is_constant_eval(init, CONSTANT_EVAL_ANY))
{
SEMA_ERROR(init, "Expected a constant expression assigned to %s.", decl->name);
return false;
}
decl->type = init->type;
}
else
{
decl->type = type_void;
}
}
break;
default:
UNREACHABLE
if (!sema_analyse_var_decl_ct(context, statement->declare_stmt)) return false;
statement->ast_kind = AST_NOP_STMT;
return true;
}
decl->var.scope_depth = context->active_scope.depth;
return sema_add_local(context, decl);
return sema_analyse_var_decl(context, statement->declare_stmt, true);
}
static inline bool sema_analyse_expr_stmt(SemaContext *context, Ast *statement)
@@ -939,7 +865,7 @@ static inline bool sema_analyse_for_stmt(SemaContext *context, Ast *statement)
}
assert(statement->for_stmt.body);
Ast *body = statement->for_stmt.body;
Ast *body = astptr(statement->for_stmt.body);
// Create the for body scope.
SCOPE_START_WITH_LABEL(statement->for_stmt.flow.label)
@@ -1424,7 +1350,7 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen
.cond = binary,
.incr = inc,
.flow = flow,
.body = compound_stmt
.body = astid(compound_stmt)
};
statement->ast_kind = AST_FOR_STMT;
return sema_analyse_for_stmt(context, statement);
@@ -2421,6 +2347,85 @@ static bool sema_analyse_compound_stmt(SemaContext *context, Ast *statement)
return success;
}
static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement)
{
bool success = false;
// Enter for scope
SCOPE_OUTER_START
Expr *init;
if ((init = statement->for_stmt.init))
{
assert(init->expr_kind == EXPR_EXPRESSION_LIST);
Expr **expressions = init->expression_list;
VECEACH (expressions, i)
{
Expr *expr = expressions[i];
if (expr->expr_kind == EXPR_DECL)
{
Decl *decl = expr->decl_expr;
if (decl->decl_kind != DECL_VAR || (decl->var.kind != VARDECL_LOCAL_CT && decl->var.kind != VARDECL_LOCAL_CT_TYPE))
{
SEMA_ERROR(expr, "Only 'var $foo' and 'var $Type' declarations are allowed in a '$for'");
goto EXIT_ERROR;
}
if (!sema_analyse_var_decl_ct(context, decl)) goto EXIT_ERROR;
continue;
}
if (!sema_analyse_expr(context, expr)) goto EXIT_ERROR;
if (!expr_is_constant_eval(expr, CONSTANT_EVAL_FOLDABLE))
{
SEMA_ERROR(expr, "Only constant expressions are allowed.");
goto EXIT_ERROR;
}
}
}
Expr *condition = statement->for_stmt.cond;
Expr *incr = statement->for_stmt.incr;
Ast *body = astptr(statement->for_stmt.body);
AstId start = 0;
AstId *current = &start;
assert(condition);
for (int i = 0; i < MAX_MACRO_ITERATIONS; i++)
{
Expr *copy = copy_expr(condition);
if (!sema_analyse_cond_expr(context, copy)) goto EXIT_ERROR;
if (copy->expr_kind != EXPR_CONST)
{
SEMA_ERROR(copy, "Expected a value that can be evaluated at compile time.");
goto EXIT_ERROR;
}
if (!copy->const_expr.b) break;
Ast *compound_stmt = ast_copy_deep(body);
if (!sema_analyse_compound_stmt(context, compound_stmt)) goto EXIT_ERROR;
*current = astid(compound_stmt);
current = &compound_stmt->next;
if (incr)
{
Expr **exprs = incr->expression_list;
VECEACH(exprs, j)
{
copy = copy_expr(exprs[j]);
if (!sema_analyse_expr(context, copy)) goto EXIT_ERROR;
if (copy->expr_kind != EXPR_CONST)
{
SEMA_ERROR(copy, "Expected a value that can be evaluated at compile time.");
goto EXIT_ERROR;
}
}
}
}
statement->ast_kind = AST_COMPOUND_STMT;
statement->compound_stmt.first_stmt = start;
statement->compound_stmt.defer_list = (DeferList) { 0, 0 };
success = true;
EXIT_ERROR:
SCOPE_OUTER_END;
return success;
}
static bool sema_analyse_ct_compound_stmt(SemaContext *context, Ast *statement)
{
bool all_ok = ast_ok(statement);
@@ -2491,8 +2496,6 @@ static inline bool sema_analyse_statement_inner(SemaContext *context, Ast *state
return false;
case AST_DEFER_STMT:
return sema_analyse_defer_stmt(context, statement);
case AST_VAR_STMT:
return sema_analyse_var_stmt(context, statement);
case AST_DO_STMT:
return sema_analyse_do_stmt(context, statement);
case AST_EXPR_STMT:
@@ -2523,7 +2526,7 @@ static inline bool sema_analyse_statement_inner(SemaContext *context, Ast *state
case AST_CT_FOREACH_STMT:
return sema_analyse_ct_foreach_stmt(context, statement);
case AST_CT_FOR_STMT:
TODO
return sema_analyse_ct_for_stmt(context, statement);
}
UNREACHABLE

View File

@@ -1 +1 @@
#define COMPILER_VERSION "PRE.18"
#define COMPILER_VERSION "PRE.19"

View File

@@ -0,0 +1,32 @@
// #target: x64-darwin
module test;
extern fn void printf(char*, ...);
fn void main()
{
$for (var $i = 0; $i < 3; $i++):
printf("Foo %d\n", $i);
$endfor;
$for (var $i = 0, var $j = 100; $i < 4;):
printf("Foo %d %d\n", $i++, $j--);
$endfor;
}
/* #expect: test.ll
define void @test.main() #0 {
entry:
call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i32 0, i32 0), i32 0)
call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.1, i32 0, i32 0), i32 1)
call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.2, i32 0, i32 0), i32 2)
call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.3, i32 0, i32 0), i32 0, i32 100)
call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.4, i32 0, i32 0), i32 1, i32 99)
call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.5, i32 0, i32 0), i32 2, i32 98)
call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.6, i32 0, i32 0), i32 3, i32 97)
ret void
}