Skip to content

Commit 5a99a6a

Browse files
authored
Merge pull request #1163 from ronnuriel/goto
Implement ``goto`` in LPython for CPython, LLVM and C
2 parents 9209be2 + d5b21d6 commit 5a99a6a

File tree

14 files changed

+380
-10
lines changed

14 files changed

+380
-10
lines changed

integration_tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ RUN(NAME generics_list_01 LABELS cpython llvm)
275275
RUN(NAME test_statistics LABELS cpython llvm)
276276
RUN(NAME test_str_attributes LABELS cpython llvm)
277277
RUN(NAME kwargs_01 LABELS cpython llvm)
278+
RUN(NAME test_01_goto LABELS cpython llvm c)
278279

279280
RUN(NAME func_inline_01 LABELS llvm wasm)
280281
RUN(NAME func_static_01 LABELS cpython llvm c wasm)

integration_tests/test_01_goto.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from ltypes import with_goto, i32
2+
3+
@with_goto
4+
def f() -> i32:
5+
i:i32
6+
for i in range(10):
7+
if i == 5:
8+
goto .end
9+
10+
label .end
11+
assert i == 5
12+
return i
13+
14+
@with_goto
15+
def g(size: i32) -> i32:
16+
i:i32
17+
18+
i = 0
19+
label .loop
20+
if i >= size:
21+
goto .end
22+
i += 1
23+
goto .loop
24+
25+
label .end
26+
return i
27+
28+
def test_goto():
29+
print(f())
30+
print(g(10))
31+
print(g(20))
32+
assert g(30) == 30
33+
assert g(40) == 40
34+
35+
test_goto()

src/libasr/ASR.asdl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,10 @@ stmt
173173
-- GoTo points to a GoToTarget with the corresponding target_id within
174174
-- the same procedure. We currently use `int` IDs to link GoTo with
175175
-- GoToTarget to avoid issues with serialization.
176-
| GoTo(int target_id)
176+
| GoTo(int target_id, identifier name)
177177
-- An empty statement, a target of zero or more GoTo statements
178178
-- the `id` is only unique within a procedure
179-
| GoToTarget(int id)
179+
| GoToTarget(int id, identifier name)
180180
| If(expr test, stmt* body, stmt* orelse)
181181
| IfArithmetic(expr test, int lt_label, int eq_label, int gt_label)
182182
| Print(expr? fmt, expr* values, expr? separator, expr? end)

src/libasr/asr_utils.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1746,7 +1746,8 @@ class ReplaceReturnWithGotoVisitor: public ASR::BaseStmtReplacer<ReplaceReturnWi
17461746
}
17471747

17481748
void replace_Return(ASR::Return_t* x) {
1749-
*current_stmt = ASRUtils::STMT(ASR::make_GoTo_t(al, x->base.base.loc, goto_label));
1749+
*current_stmt = ASRUtils::STMT(ASR::make_GoTo_t(al, x->base.base.loc, goto_label,
1750+
s2c(al, "__" + std::to_string(goto_label))));
17501751
has_replacement_happened = true;
17511752
}
17521753

src/libasr/codegen/asr_to_c.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,14 @@ R"(
11261126
src = "strlen(" + src + ")";
11271127
}
11281128

1129+
void visit_GoTo(const ASR::GoTo_t &x) {
1130+
std::string indent(indentation_level*indentation_spaces, ' ');
1131+
src = indent + "goto " + std::string(x.m_name) + ";\n";
1132+
}
1133+
1134+
void visit_GoToTarget(const ASR::GoToTarget_t &x) {
1135+
src = std::string(x.m_name) + ":\n";
1136+
}
11291137
};
11301138

11311139
Result<std::string> asr_to_c(Allocator &al, ASR::TranslationUnit_t &asr,

src/lpython/semantics/python_ast_to_asr.cpp

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <fstream>
22
#include <iostream>
33
#include <map>
4+
#include <set>
45
#include <memory>
56
#include <string>
67
#include <cmath>
@@ -2843,7 +2844,6 @@ class SymbolTableVisitor : public CommonVisitor<SymbolTableVisitor> {
28432844
bool current_procedure_interface = false;
28442845
bool overload = false;
28452846
bool vectorize = false, is_inline = false, is_static = false;
2846-
28472847
Vec<ASR::ttype_t*> tps;
28482848
tps.reserve(al, x.m_args.n_args);
28492849
bool is_restriction = false;
@@ -2866,6 +2866,8 @@ class SymbolTableVisitor : public CommonVisitor<SymbolTableVisitor> {
28662866
vectorize = true;
28672867
} else if (name == "restriction") {
28682868
is_restriction = true;
2869+
} else if (name == "with_goto") {
2870+
// TODO: Use goto attribute in function?
28692871
} else if (name == "inline") {
28702872
is_inline = true;
28712873
} else if (name == "static") {
@@ -3211,11 +3213,14 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
32113213

32123214
public:
32133215
ASR::asr_t *asr;
3216+
std::map<std::string, std::tuple<int64_t, bool, Location>> goto_name2id;
3217+
int64_t gotoids;
32143218

32153219

32163220
BodyVisitor(Allocator &al, ASR::asr_t *unit, diag::Diagnostics &diagnostics,
32173221
bool main_module, std::map<int, ASR::symbol_t*> &ast_overload)
3218-
: CommonVisitor(al, nullptr, diagnostics, main_module, ast_overload, ""), asr{unit}
3222+
: CommonVisitor(al, nullptr, diagnostics, main_module, ast_overload, ""), asr{unit},
3223+
gotoids{0}
32193224
{}
32203225

32213226
// Transforms statements to a list of ASR statements
@@ -3300,6 +3305,8 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
33003305
}
33013306

33023307
void visit_FunctionDef(const AST::FunctionDef_t &x) {
3308+
goto_name2id.clear();
3309+
gotoids = 0;
33033310
SymbolTable *old_scope = current_scope;
33043311
ASR::symbol_t *t = current_scope->get_symbol(x.m_name);
33053312
if (ASR::is_a<ASR::Function_t>(*t)) {
@@ -3320,6 +3327,13 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
33203327
}
33213328
current_scope = old_scope;
33223329
tmp = nullptr;
3330+
3331+
for( auto itr: goto_name2id ) {
3332+
if( !std::get<1>(itr.second) ) {
3333+
throw SemanticError("Label '" + itr.first + "' is not defined in '"
3334+
+ std::string(x.m_name) + "'", std::get<2>(itr.second));
3335+
}
3336+
}
33233337
}
33243338

33253339
void visit_Import(const AST::Import_t &/*x*/) {
@@ -3969,7 +3983,36 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
39693983
void visit_Attribute(const AST::Attribute_t &x) {
39703984
if (AST::is_a<AST::Name_t>(*x.m_value)) {
39713985
std::string value = AST::down_cast<AST::Name_t>(x.m_value)->m_id;
3986+
if( value == "label" ) {
3987+
std::string labelname = x.m_attr;
3988+
if( goto_name2id.find(labelname) == goto_name2id.end() ) {
3989+
goto_name2id[labelname] = std::make_tuple(gotoids, true, x.base.base.loc);
3990+
gotoids += 1;
3991+
} else if( !std::get<1>(goto_name2id[labelname]) ) {
3992+
goto_name2id[labelname] = std::make_tuple(
3993+
std::get<0>(goto_name2id[labelname]),
3994+
true,
3995+
std::get<2>(goto_name2id[labelname])
3996+
);
3997+
}
3998+
int id = std::get<0>(goto_name2id[labelname]);
3999+
tmp = ASR::make_GoToTarget_t(al, x.base.base.loc, id, x.m_attr);
4000+
return ;
4001+
}
4002+
4003+
if (value == "goto"){
4004+
std::string labelname = std::string(x.m_attr);
4005+
if( goto_name2id.find(labelname) == goto_name2id.end() ) {
4006+
goto_name2id[labelname] = std::make_tuple(gotoids, false, x.base.base.loc);
4007+
gotoids += 1;
4008+
}
4009+
int id = std::get<0>(goto_name2id[labelname]);
4010+
tmp = ASR::make_GoTo_t(al, x.base.base.loc, id, x.m_attr);
4011+
return ;
4012+
}
4013+
39724014
ASR::symbol_t *t = current_scope->resolve_symbol(value);
4015+
39734016
if (!t) {
39744017
throw SemanticError("'" + value + "' is not defined in the scope",
39754018
x.base.base.loc);
@@ -4543,8 +4586,14 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
45434586
return;
45444587
}
45454588
this->visit_expr(*x.m_value);
4546-
ASRUtils::EXPR(tmp);
4547-
tmp = nullptr;
4589+
4590+
// If tmp is a statement and not an expression
4591+
// never cast into expression using ASRUtils::EXPR
4592+
// Just ignore and exit the function naturally.
4593+
if( !ASR::is_a<ASR::stmt_t>(*tmp) ) {
4594+
LFORTRAN_ASSERT(ASR::is_a<ASR::expr_t>(*tmp));
4595+
tmp = nullptr;
4596+
}
45484597
}
45494598

45504599

0 commit comments

Comments
 (0)