mirror of
https://github.com/odin-lang/Odin.git
synced 2026-02-14 23:33:15 +00:00
Add #force_inline, #force_no_inline and #unroll for the transition to deprecate and then remove the keywords inline and no_inline
`inline for` will be replaced with `#unroll for`
This commit is contained in:
@@ -1050,12 +1050,74 @@ parse_foreign_decl :: proc(p: ^Parser) -> ^ast.Decl {
|
||||
}
|
||||
|
||||
|
||||
parse_unrolled_for_loop :: proc(p: ^Parser, inline_tok: tokenizer.Token) -> ^ast.Stmt {
|
||||
for_tok := expect_token(p, .For);
|
||||
val0, val1: ^ast.Expr;
|
||||
in_tok: tokenizer.Token;
|
||||
expr: ^ast.Expr;
|
||||
body: ^ast.Stmt;
|
||||
|
||||
bad_stmt := false;
|
||||
|
||||
if p.curr_tok.kind != .In {
|
||||
idents := parse_ident_list(p, false);
|
||||
switch len(idents) {
|
||||
case 1:
|
||||
val0 = idents[0];
|
||||
case 2:
|
||||
val0, val1 = idents[0], idents[1];
|
||||
case:
|
||||
error(p, for_tok.pos, "expected either 1 or 2 identifiers");
|
||||
bad_stmt = true;
|
||||
}
|
||||
}
|
||||
|
||||
in_tok = expect_token(p, .In);
|
||||
|
||||
prev_allow_range := p.allow_range;
|
||||
prev_level := p.expr_level;
|
||||
p.allow_range = true;
|
||||
p.expr_level = -1;
|
||||
|
||||
expr = parse_expr(p, false);
|
||||
|
||||
p.expr_level = prev_level;
|
||||
p.allow_range = prev_allow_range;
|
||||
|
||||
if allow_token(p, .Do) {
|
||||
body = convert_stmt_to_body(p, parse_stmt(p));
|
||||
} else {
|
||||
body = parse_block_stmt(p, false);
|
||||
}
|
||||
|
||||
if bad_stmt {
|
||||
return ast.new(ast.Bad_Stmt, inline_tok.pos, end_pos(p.prev_tok));
|
||||
}
|
||||
|
||||
range_stmt := ast.new(ast.Inline_Range_Stmt, inline_tok.pos, body.end);
|
||||
range_stmt.inline_pos = inline_tok.pos;
|
||||
range_stmt.for_pos = for_tok.pos;
|
||||
range_stmt.val0 = val0;
|
||||
range_stmt.val1 = val1;
|
||||
range_stmt.in_pos = in_tok.pos;
|
||||
range_stmt.expr = expr;
|
||||
range_stmt.body = body;
|
||||
return range_stmt;
|
||||
}
|
||||
|
||||
parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
||||
#partial switch p.curr_tok.kind {
|
||||
|
||||
case .Inline:
|
||||
if peek_token_kind(p, .For) {
|
||||
inline_tok := expect_token(p, .Inline);
|
||||
return parse_unrolled_for_loop(p, inline_tok);
|
||||
}
|
||||
fallthrough;
|
||||
// Operands
|
||||
case .Context, // Also allows for 'context = '
|
||||
.Proc,
|
||||
.Inline, .No_Inline,
|
||||
.No_Inline,
|
||||
.Asm, // Inline assembly
|
||||
.Ident,
|
||||
.Integer, .Float, .Imag,
|
||||
@@ -1065,63 +1127,6 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
||||
// Unary Expressions
|
||||
.Add, .Sub, .Xor, .Not, .And:
|
||||
|
||||
if peek_token_kind(p, .For) {
|
||||
inline_tok := expect_token(p, .Inline);
|
||||
for_tok := expect_token(p, .For);
|
||||
val0, val1: ^ast.Expr;
|
||||
in_tok: tokenizer.Token;
|
||||
expr: ^ast.Expr;
|
||||
body: ^ast.Stmt;
|
||||
|
||||
bad_stmt := false;
|
||||
|
||||
if p.curr_tok.kind != .In {
|
||||
idents := parse_ident_list(p, false);
|
||||
switch len(idents) {
|
||||
case 1:
|
||||
val0 = idents[0];
|
||||
case 2:
|
||||
val0, val1 = idents[0], idents[1];
|
||||
case:
|
||||
error(p, for_tok.pos, "expected either 1 or 2 identifiers");
|
||||
bad_stmt = true;
|
||||
}
|
||||
}
|
||||
|
||||
in_tok = expect_token(p, .In);
|
||||
|
||||
prev_allow_range := p.allow_range;
|
||||
prev_level := p.expr_level;
|
||||
p.allow_range = true;
|
||||
p.expr_level = -1;
|
||||
|
||||
expr = parse_expr(p, false);
|
||||
|
||||
p.expr_level = prev_level;
|
||||
p.allow_range = prev_allow_range;
|
||||
|
||||
if allow_token(p, .Do) {
|
||||
body = convert_stmt_to_body(p, parse_stmt(p));
|
||||
} else {
|
||||
body = parse_block_stmt(p, false);
|
||||
}
|
||||
|
||||
if bad_stmt {
|
||||
return ast.new(ast.Bad_Stmt, inline_tok.pos, end_pos(p.prev_tok));
|
||||
}
|
||||
|
||||
range_stmt := ast.new(ast.Inline_Range_Stmt, inline_tok.pos, body.end);
|
||||
range_stmt.inline_pos = inline_tok.pos;
|
||||
range_stmt.for_pos = for_tok.pos;
|
||||
range_stmt.val0 = val0;
|
||||
range_stmt.val1 = val1;
|
||||
range_stmt.in_pos = in_tok.pos;
|
||||
range_stmt.expr = expr;
|
||||
range_stmt.body = body;
|
||||
return range_stmt;
|
||||
}
|
||||
|
||||
|
||||
s := parse_simple_stmt(p, {Stmt_Allow_Flag.Label});
|
||||
expect_semicolon(p, s);
|
||||
return s;
|
||||
@@ -1261,6 +1266,8 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
||||
es := ast.new(ast.Expr_Stmt, ce.pos, ce.end);
|
||||
es.expr = ce;
|
||||
return es;
|
||||
case "unroll":
|
||||
return parse_unrolled_for_loop(p, tag);
|
||||
case "include":
|
||||
error(p, tag.pos, "#include is not a valid import declaration kind. Did you meant 'import'?");
|
||||
return ast.new(ast.Bad_Stmt, tok.pos, end_pos(tag));
|
||||
@@ -2004,7 +2011,41 @@ check_poly_params_for_type :: proc(p: ^Parser, poly_params: ^ast.Field_List, tok
|
||||
}
|
||||
}
|
||||
|
||||
parse_inlining_operand :: proc(p: ^Parser, lhs: bool, tok: tokenizer.Token) -> ^ast.Expr {
|
||||
expr := parse_unary_expr(p, lhs);
|
||||
|
||||
pi := ast.Proc_Inlining.None;
|
||||
#partial switch tok.kind {
|
||||
case .Inline:
|
||||
pi = .Inline;
|
||||
case .No_Inline:
|
||||
pi = .No_Inline;
|
||||
case .Ident:
|
||||
switch tok.text {
|
||||
case "force_inline":
|
||||
pi = .Inline;
|
||||
case "force_no_inline":
|
||||
pi = .No_Inline;
|
||||
}
|
||||
}
|
||||
|
||||
switch e in &ast.unparen_expr(expr).derived {
|
||||
case ast.Proc_Lit:
|
||||
if e.inlining != .None && e.inlining != pi {
|
||||
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure literal");
|
||||
}
|
||||
e.inlining = pi;
|
||||
case ast.Call_Expr:
|
||||
if e.inlining != .None && e.inlining != pi {
|
||||
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure call");
|
||||
}
|
||||
e.inlining = pi;
|
||||
case:
|
||||
error(p, tok.pos, "'%s' must be followed by a procedure literal or call", tok.text);
|
||||
return ast.new(ast.Bad_Expr, tok.pos, expr.end);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
#partial switch p.curr_tok.kind {
|
||||
@@ -2056,14 +2097,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
dt.type = type;
|
||||
return dt;
|
||||
|
||||
case .Opaque:
|
||||
tok := advance_token(p);
|
||||
warn(p, tok.pos, "opaque is deprecated in favour of #opaque");
|
||||
type := parse_type(p);
|
||||
ot := ast.new(ast.Opaque_Type, tok.pos, type.end);
|
||||
ot.type = type;
|
||||
return ot;
|
||||
|
||||
case .Hash:
|
||||
tok := expect_token(p, .Hash);
|
||||
name := expect_token(p, .Ident);
|
||||
@@ -2164,32 +2197,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
|
||||
case .Inline, .No_Inline:
|
||||
tok := advance_token(p);
|
||||
expr := parse_unary_expr(p, lhs);
|
||||
|
||||
pi := ast.Proc_Inlining.None;
|
||||
#partial switch tok.kind {
|
||||
case .Inline:
|
||||
pi = ast.Proc_Inlining.Inline;
|
||||
case .No_Inline:
|
||||
pi = ast.Proc_Inlining.No_Inline;
|
||||
}
|
||||
|
||||
switch e in &ast.unparen_expr(expr).derived {
|
||||
case ast.Proc_Lit:
|
||||
if e.inlining != ast.Proc_Inlining.None && e.inlining != pi {
|
||||
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure literal");
|
||||
}
|
||||
e.inlining = pi;
|
||||
case ast.Call_Expr:
|
||||
if e.inlining != ast.Proc_Inlining.None && e.inlining != pi {
|
||||
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure call");
|
||||
}
|
||||
e.inlining = pi;
|
||||
case:
|
||||
error(p, tok.pos, "'%s' must be followed by a procedure literal or call", tok.text);
|
||||
return ast.new(ast.Bad_Expr, tok.pos, expr.end);
|
||||
}
|
||||
return expr;
|
||||
return parse_inlining_operand(p, lhs, tok);
|
||||
|
||||
case .Proc:
|
||||
tok := expect_token(p, .Proc);
|
||||
|
||||
@@ -142,7 +142,6 @@ Token_Kind :: enum u32 {
|
||||
Cast, // cast
|
||||
Transmute, // transmute
|
||||
Distinct, // distinct
|
||||
Opaque, // opaque
|
||||
Using, // using
|
||||
Inline, // inline
|
||||
No_Inline, // no_inline
|
||||
@@ -270,7 +269,6 @@ tokens := [Token_Kind.COUNT]string {
|
||||
"cast",
|
||||
"transmute",
|
||||
"distinct",
|
||||
"opaque",
|
||||
"using",
|
||||
"inline",
|
||||
"no_inline",
|
||||
|
||||
176
src/parser.cpp
176
src/parser.cpp
@@ -1865,6 +1865,45 @@ bool ast_on_same_line(Ast *x, Ast *y) {
|
||||
return ast_on_same_line(ast_token(x), y);
|
||||
}
|
||||
|
||||
Ast *parse_force_inlining_operand(AstFile *f, Token token) {
|
||||
Ast *expr = parse_unary_expr(f, false);
|
||||
Ast *e = unparen_expr(expr);
|
||||
if (e->kind != Ast_ProcLit && e->kind != Ast_CallExpr) {
|
||||
syntax_error(expr, "%.*s must be followed by a procedure literal or call, got %.*s", LIT(token.string), LIT(ast_strings[expr->kind]));
|
||||
return ast_bad_expr(f, token, f->curr_token);
|
||||
}
|
||||
ProcInlining pi = ProcInlining_none;
|
||||
if (token.kind == Token_inline) {
|
||||
pi = ProcInlining_inline;
|
||||
} else if (token.kind == Token_no_inline) {
|
||||
pi = ProcInlining_no_inline;
|
||||
} else if (token.kind == Token_Ident) {
|
||||
if (token.string == "force_inline") {
|
||||
pi = ProcInlining_inline;
|
||||
} else if (token.string == "force_no_inline") {
|
||||
pi = ProcInlining_no_inline;
|
||||
}
|
||||
}
|
||||
|
||||
if (pi != ProcInlining_none) {
|
||||
if (e->kind == Ast_ProcLit) {
|
||||
if (expr->ProcLit.inlining != ProcInlining_none &&
|
||||
expr->ProcLit.inlining != pi) {
|
||||
syntax_error(expr, "You cannot apply both 'inline' and 'no_inline' to a procedure literal");
|
||||
}
|
||||
expr->ProcLit.inlining = pi;
|
||||
} else if (e->kind == Ast_CallExpr) {
|
||||
if (expr->CallExpr.inlining != ProcInlining_none &&
|
||||
expr->CallExpr.inlining != pi) {
|
||||
syntax_error(expr, "You cannot apply both 'inline' and 'no_inline' to a procedure call");
|
||||
}
|
||||
expr->CallExpr.inlining = pi;
|
||||
}
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
|
||||
Ast *parse_operand(AstFile *f, bool lhs) {
|
||||
Ast *operand = nullptr; // Operand
|
||||
@@ -1986,6 +2025,9 @@ Ast *parse_operand(AstFile *f, bool lhs) {
|
||||
} else if (name.string == "opaque") {
|
||||
Ast *type = parse_type(f);
|
||||
return ast_opaque_type(f, token, type);
|
||||
} else if (name.string == "force_inline" ||
|
||||
name.string == "force_no_inline") {
|
||||
return parse_force_inlining_operand(f, name);
|
||||
} else {
|
||||
operand = ast_tag_expr(f, token, name, parse_expr(f, false));
|
||||
}
|
||||
@@ -1996,35 +2038,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
|
||||
case Token_no_inline:
|
||||
{
|
||||
Token token = advance_token(f);
|
||||
Ast *expr = parse_unary_expr(f, false);
|
||||
Ast *e = unparen_expr(expr);
|
||||
if (e->kind != Ast_ProcLit && e->kind != Ast_CallExpr) {
|
||||
syntax_error(expr, "%.*s must be followed by a procedure literal or call, got %.*s", LIT(token.string), LIT(ast_strings[expr->kind]));
|
||||
return ast_bad_expr(f, token, f->curr_token);
|
||||
}
|
||||
ProcInlining pi = ProcInlining_none;
|
||||
if (token.kind == Token_inline) {
|
||||
pi = ProcInlining_inline;
|
||||
} else if (token.kind == Token_no_inline) {
|
||||
pi = ProcInlining_no_inline;
|
||||
}
|
||||
if (pi != ProcInlining_none) {
|
||||
if (e->kind == Ast_ProcLit) {
|
||||
if (expr->ProcLit.inlining != ProcInlining_none &&
|
||||
expr->ProcLit.inlining != pi) {
|
||||
syntax_error(expr, "You cannot apply both 'inline' and 'no_inline' to a procedure literal");
|
||||
}
|
||||
expr->ProcLit.inlining = pi;
|
||||
} else if (e->kind == Ast_CallExpr) {
|
||||
if (expr->CallExpr.inlining != ProcInlining_none &&
|
||||
expr->CallExpr.inlining != pi) {
|
||||
syntax_error(expr, "You cannot apply both 'inline' and 'no_inline' to a procedure call");
|
||||
}
|
||||
expr->CallExpr.inlining = pi;
|
||||
}
|
||||
}
|
||||
|
||||
return expr;
|
||||
return parse_force_inlining_operand(f, token);
|
||||
} break;
|
||||
|
||||
// Parse Procedure Type or Literal or Group
|
||||
@@ -4282,6 +4296,58 @@ Ast *parse_attribute(AstFile *f, Token token, TokenKind open_kind, TokenKind clo
|
||||
}
|
||||
|
||||
|
||||
Ast *parse_unrolled_for_loop(AstFile *f, Token inline_token) {
|
||||
Token for_token = expect_token(f, Token_for);
|
||||
Ast *val0 = nullptr;
|
||||
Ast *val1 = nullptr;
|
||||
Token in_token = {};
|
||||
Ast *expr = nullptr;
|
||||
Ast *body = nullptr;
|
||||
|
||||
bool bad_stmt = false;
|
||||
|
||||
if (f->curr_token.kind != Token_in) {
|
||||
Array<Ast *> idents = parse_ident_list(f, false);
|
||||
switch (idents.count) {
|
||||
case 1:
|
||||
val0 = idents[0];
|
||||
break;
|
||||
case 2:
|
||||
val0 = idents[0];
|
||||
val1 = idents[1];
|
||||
break;
|
||||
default:
|
||||
syntax_error(for_token, "Expected either 1 or 2 identifiers");
|
||||
bad_stmt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
in_token = expect_token(f, Token_in);
|
||||
|
||||
bool prev_allow_range = f->allow_range;
|
||||
isize prev_level = f->expr_level;
|
||||
f->allow_range = true;
|
||||
f->expr_level = -1;
|
||||
expr = parse_expr(f, false);
|
||||
f->expr_level = prev_level;
|
||||
f->allow_range = prev_allow_range;
|
||||
|
||||
if (allow_token(f, Token_do)) {
|
||||
body = convert_stmt_to_body(f, parse_stmt(f));
|
||||
if (build_context.disallow_do) {
|
||||
syntax_error(body, "'do' has been disallowed");
|
||||
} else if (!ast_on_same_line(for_token, body)) {
|
||||
syntax_error(body, "The body of a 'do' be on the same line as the 'for' token");
|
||||
}
|
||||
} else {
|
||||
body = parse_block_stmt(f, false);
|
||||
}
|
||||
if (bad_stmt) {
|
||||
return ast_bad_stmt(f, inline_token, f->curr_token);
|
||||
}
|
||||
return ast_inline_range_stmt(f, inline_token, for_token, val0, val1, in_token, expr, body);
|
||||
}
|
||||
|
||||
Ast *parse_stmt(AstFile *f) {
|
||||
Ast *s = nullptr;
|
||||
Token token = f->curr_token;
|
||||
@@ -4290,55 +4356,7 @@ Ast *parse_stmt(AstFile *f) {
|
||||
case Token_inline:
|
||||
if (peek_token_kind(f, Token_for)) {
|
||||
Token inline_token = expect_token(f, Token_inline);
|
||||
Token for_token = expect_token(f, Token_for);
|
||||
Ast *val0 = nullptr;
|
||||
Ast *val1 = nullptr;
|
||||
Token in_token = {};
|
||||
Ast *expr = nullptr;
|
||||
Ast *body = nullptr;
|
||||
|
||||
bool bad_stmt = false;
|
||||
|
||||
if (f->curr_token.kind != Token_in) {
|
||||
Array<Ast *> idents = parse_ident_list(f, false);
|
||||
switch (idents.count) {
|
||||
case 1:
|
||||
val0 = idents[0];
|
||||
break;
|
||||
case 2:
|
||||
val0 = idents[0];
|
||||
val1 = idents[1];
|
||||
break;
|
||||
default:
|
||||
syntax_error(for_token, "Expected either 1 or 2 identifiers");
|
||||
bad_stmt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
in_token = expect_token(f, Token_in);
|
||||
|
||||
bool prev_allow_range = f->allow_range;
|
||||
isize prev_level = f->expr_level;
|
||||
f->allow_range = true;
|
||||
f->expr_level = -1;
|
||||
expr = parse_expr(f, false);
|
||||
f->expr_level = prev_level;
|
||||
f->allow_range = prev_allow_range;
|
||||
|
||||
if (allow_token(f, Token_do)) {
|
||||
body = convert_stmt_to_body(f, parse_stmt(f));
|
||||
if (build_context.disallow_do) {
|
||||
syntax_error(body, "'do' has been disallowed");
|
||||
} else if (!ast_on_same_line(for_token, body)) {
|
||||
syntax_error(body, "The body of a 'do' be on the same line as the 'for' token");
|
||||
}
|
||||
} else {
|
||||
body = parse_block_stmt(f, false);
|
||||
}
|
||||
if (bad_stmt) {
|
||||
return ast_bad_stmt(f, inline_token, f->curr_token);
|
||||
}
|
||||
return ast_inline_range_stmt(f, inline_token, for_token, val0, val1, in_token, expr, body);
|
||||
return parse_unrolled_for_loop(f, inline_token);
|
||||
}
|
||||
/* fallthrough */
|
||||
case Token_no_inline:
|
||||
@@ -4484,6 +4502,8 @@ Ast *parse_stmt(AstFile *f) {
|
||||
} else if (tag == "panic") {
|
||||
Ast *t = ast_basic_directive(f, hash_token, tag);
|
||||
return ast_expr_stmt(f, parse_call_expr(f, t));
|
||||
} else if (tag == "unroll" || tag == "force_inline") {
|
||||
return parse_unrolled_for_loop(f, name);
|
||||
} else if (tag == "include") {
|
||||
syntax_error(token, "#include is not a valid import declaration kind. Did you mean 'import'?");
|
||||
s = ast_bad_stmt(f, token, f->curr_token);
|
||||
|
||||
Reference in New Issue
Block a user