Skip to content

Commit e414242

Browse files
committed
ast: Add DesugarForLoop class
gcc/rust/ChangeLog: * ast/rust-desugar-for-loops.cc: New file. * ast/rust-desugar-for-loops.h: New file. * Make-lang.in: Compile it.
1 parent 9f88012 commit e414242

File tree

7 files changed

+1935
-0
lines changed

7 files changed

+1935
-0
lines changed

gcc/rust/Make-lang.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ GRS_OBJS = \
232232
rust/rust-expand-format-args.o \
233233
rust/rust-lang-item.o \
234234
rust/rust-collect-lang-items.o \
235+
rust/rust-desugar-for-loops.o \
235236
$(END)
236237
# removed object files from here
237238

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
// Copyright (C) 2024 Free Software Foundation, Inc.
2+
3+
// This file is part of GCC.
4+
5+
// GCC is free software; you can redistribute it and/or modify it under
6+
// the terms of the GNU General Public License as published by the Free
7+
// Software Foundation; either version 3, or (at your option) any later
8+
// version.
9+
10+
// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11+
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
12+
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13+
// for more details.
14+
15+
// You should have received a copy of the GNU General Public License
16+
// along with GCC; see the file COPYING3. If not see
17+
// <http://www.gnu.org/licenses/>.
18+
19+
#include "rust-desugar-for-loops.h"
20+
#include "rust-ast-visitor.h"
21+
#include "rust-ast.h"
22+
#include "rust-hir-map.h"
23+
#include "rust-path.h"
24+
#include "rust-pattern.h"
25+
#include "rust-stmt.h"
26+
#include "rust-expr.h"
27+
#include "rust-ast-builder.h"
28+
29+
namespace Rust {
30+
namespace AST {
31+
32+
DesugarForLoops::DesugarForLoops () {}
33+
34+
void
35+
DesugarForLoops::go (AST::Crate &crate)
36+
{
37+
DefaultASTVisitor::visit (crate);
38+
}
39+
40+
void
41+
replace_for_loop (std::unique_ptr<Expr> &for_loop,
42+
std::unique_ptr<Expr> &&expanded)
43+
{
44+
for_loop = std::move (expanded);
45+
}
46+
47+
MatchArm
48+
DesugarForLoops::DesugarCtx::make_match_arm (std::unique_ptr<Pattern> &&path)
49+
{
50+
auto patterns = std::vector<std::unique_ptr<Pattern>> ();
51+
patterns.emplace_back (std::move (path));
52+
53+
return MatchArm (std::move (patterns), loc);
54+
}
55+
56+
MatchCase
57+
DesugarForLoops::DesugarCtx::make_break_arm ()
58+
{
59+
auto arm = make_match_arm (std::unique_ptr<Pattern> (new PathInExpression (
60+
builder.path_in_expression (LangItem::Kind::OPTION_NONE))));
61+
62+
auto break_expr = std::unique_ptr<Expr> (
63+
new BreakExpr (Lifetime::error (), nullptr, {}, loc));
64+
65+
return MatchCase (std::move (arm), std::move (break_expr));
66+
}
67+
68+
MatchCase
69+
DesugarForLoops::DesugarCtx::make_continue_arm ()
70+
{
71+
auto val = builder.identifier_pattern ("val");
72+
73+
auto patterns = std::vector<std::unique_ptr<Pattern>> ();
74+
patterns.emplace_back (std::move (val));
75+
76+
auto pattern_item = std::unique_ptr<TupleStructItems> (
77+
new TupleStructItemsNoRange (std::move (patterns)));
78+
auto pattern = std::unique_ptr<Pattern> (new TupleStructPattern (
79+
builder.path_in_expression (LangItem::Kind::OPTION_SOME),
80+
std::move (pattern_item)));
81+
82+
auto val_arm = make_match_arm (std::move (pattern));
83+
84+
auto next = builder.identifier ("__next");
85+
86+
auto assignment = std::unique_ptr<Expr> (
87+
new AssignmentExpr (std::move (next), builder.identifier ("val"), {}, loc));
88+
89+
return MatchCase (std::move (val_arm), std::move (assignment));
90+
}
91+
92+
std::unique_ptr<Stmt>
93+
DesugarForLoops::DesugarCtx::statementify (std::unique_ptr<Expr> &&expr)
94+
{
95+
return std::unique_ptr<Stmt> (new ExprStmt (std::move (expr), loc, true));
96+
}
97+
98+
std::unique_ptr<Expr>
99+
DesugarForLoops::desugar (AST::ForLoopExpr &expr)
100+
{
101+
auto ctx = DesugarCtx (expr.get_locus ());
102+
103+
auto into_iter = std::make_unique<PathInExpression> (
104+
ctx.builder.path_in_expression (LangItem::Kind::INTOITER_INTOITER));
105+
auto next = std::make_unique<PathInExpression> (
106+
ctx.builder.path_in_expression (LangItem::Kind::ITERATOR_NEXT));
107+
108+
// IntoIterator::into_iter(<head>)
109+
auto into_iter_call
110+
= ctx.builder.call (std::move (into_iter),
111+
expr.get_iterator_expr ().clone_expr ());
112+
113+
// Iterator::next(iter)
114+
auto next_call = ctx.builder.call (
115+
std::move (next), ctx.builder.ref (ctx.builder.identifier ("iter"), true));
116+
117+
// None => break,
118+
auto break_arm = ctx.make_break_arm ();
119+
// Some(val) => { __next = val; },
120+
auto continue_arm = ctx.make_continue_arm ();
121+
122+
// match <next_call> {
123+
// <continue_arm>
124+
// <break_arm>
125+
// }
126+
auto match_next
127+
= ctx.builder.match (std::move (next_call),
128+
{std::move (continue_arm), std::move (break_arm)});
129+
130+
// let mut __next;
131+
auto let_next
132+
= ctx.builder.let (ctx.builder.identifier_pattern ("__next", true));
133+
// let <pattern> = __next;
134+
auto let_pat = ctx.builder.let (expr.get_pattern ().clone_pattern (), nullptr,
135+
ctx.builder.identifier ("__next"));
136+
137+
auto loop_stmts = std::vector<std::unique_ptr<Stmt>> ();
138+
loop_stmts.emplace_back (std::move (let_next));
139+
loop_stmts.emplace_back (ctx.statementify (std::move (match_next)));
140+
loop_stmts.emplace_back (std::move (let_pat));
141+
loop_stmts.emplace_back (
142+
ctx.statementify (expr.get_loop_block ().clone_expr ()));
143+
144+
// loop {
145+
// <let_next>;
146+
// <match_next>;
147+
// <let_pat>;
148+
//
149+
// <body>;
150+
// }
151+
auto loop = ctx.builder.loop (std::move (loop_stmts));
152+
153+
auto mut_iter_pattern = ctx.builder.identifier_pattern ("iter", true);
154+
auto match_iter
155+
= ctx.builder.match (std::move (into_iter_call),
156+
{ctx.builder.match_case (std::move (mut_iter_pattern),
157+
std::move (loop))});
158+
159+
auto let_result = ctx.builder.let (ctx.builder.identifier_pattern ("result"),
160+
nullptr, std::move (match_iter));
161+
auto result_return = ctx.builder.identifier ("result");
162+
163+
return ctx.builder.block (std::move (let_result), std::move (result_return));
164+
}
165+
166+
void
167+
DesugarForLoops::maybe_desugar_expr (std::unique_ptr<Expr> &expr)
168+
{
169+
if (expr->get_expr_kind () == AST::Expr::Kind::Loop)
170+
{
171+
auto &loop = static_cast<AST::BaseLoopExpr &> (*expr);
172+
173+
if (loop.get_loop_kind () == AST::BaseLoopExpr::Kind::For)
174+
{
175+
auto &for_loop = static_cast<AST::ForLoopExpr &> (loop);
176+
177+
auto desugared = desugar (for_loop);
178+
179+
replace_for_loop (expr, std::move (desugared));
180+
}
181+
}
182+
}
183+
184+
void
185+
DesugarForLoops::visit (AST::BlockExpr &block)
186+
{
187+
for (auto &stmt : block.get_statements ())
188+
if (stmt->get_stmt_kind () == AST::Stmt::Kind::Expr)
189+
maybe_desugar_expr (static_cast<AST::ExprStmt &> (*stmt).get_expr_ptr ());
190+
191+
if (block.has_tail_expr ())
192+
maybe_desugar_expr (block.get_tail_expr_ptr ());
193+
194+
DefaultASTVisitor::visit (block);
195+
}
196+
197+
} // namespace AST
198+
} // namespace Rust

gcc/rust/ast/rust-desugar-for-loops.h

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright (C) 2024 Free Software Foundation, Inc.
2+
3+
// This file is part of GCC.
4+
5+
// GCC is free software; you can redistribute it and/or modify it under
6+
// the terms of the GNU General Public License as published by the Free
7+
// Software Foundation; either version 3, or (at your option) any later
8+
// version.
9+
10+
// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11+
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
12+
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13+
// for more details.
14+
15+
// You should have received a copy of the GNU General Public License
16+
// along with GCC; see the file COPYING3. If not see
17+
// <http://www.gnu.org/licenses/>.
18+
19+
#ifndef RUST_DESUGAR_FOR_LOOPS
20+
#define RUST_DESUGAR_FOR_LOOPS
21+
22+
#include "rust-ast-builder.h"
23+
#include "rust-ast-visitor.h"
24+
#include "rust-expr.h"
25+
26+
namespace Rust {
27+
namespace AST {
28+
29+
// Desugar for-loops into a set of other AST nodes. The desugar is of the
30+
// following form:
31+
//
32+
// ```
33+
// for <pat> in <head> <body>
34+
// ```
35+
//
36+
// becomes:
37+
//
38+
// ```
39+
// {
40+
// let result = match ::std::iter::IntoIterator::into_iter(<head>) {
41+
// mut iter => {
42+
// loop {
43+
// let mut __next;
44+
// match ::std::iter::Iterator::next(&mut iter) {
45+
// ::std::option::Option::Some(val) => __next = val,
46+
// ::std::option::Option::None => break
47+
// };
48+
// let <pat> = __next;
49+
//
50+
// <body>;
51+
// }
52+
// }
53+
// };
54+
// result
55+
// }
56+
// ```
57+
//
58+
// NOTE: In a perfect world, this would be an immutable visitor which would take
59+
// ownership of the AST node and return a new one, instead of mutating this one
60+
// in place. Nevertheless, this isn't Rust, and doing immutable visitors in C++
61+
// sucks, and the world isn't perfect, so we are impure and sad.
62+
//
63+
// NOTE: This class could eventually be removed in favor of
64+
// an HIR desugar. This would avoid mutating the AST and would be cleaner.
65+
// However, it requires multiple changes in the way we do typechecking and name
66+
// resolution, as this desugar creates new bindings. Because of this, these new
67+
// bindings need to be inserted into the name-resolution context outside of the
68+
// name resolution pass, which is difficult. Those bindings are needed because
69+
// of the way the typechecker is currently structured, where it will fetch name
70+
// resolution information in order to typecheck paths - which technically isn't
71+
// necessary.
72+
class DesugarForLoops : public DefaultASTVisitor
73+
{
74+
using DefaultASTVisitor::visit;
75+
76+
public:
77+
DesugarForLoops ();
78+
void go (AST::Crate &);
79+
80+
private:
81+
struct DesugarCtx
82+
{
83+
DesugarCtx (location_t loc) : builder (Builder (loc)), loc (loc) {}
84+
85+
Builder builder;
86+
location_t loc;
87+
88+
MatchArm make_match_arm (std::unique_ptr<Pattern> &&pattern);
89+
MatchCase make_break_arm ();
90+
MatchCase make_continue_arm ();
91+
std::unique_ptr<Stmt> statementify (std::unique_ptr<Expr> &&expr);
92+
};
93+
94+
std::unique_ptr<Expr> desugar (AST::ForLoopExpr &expr);
95+
void maybe_desugar_expr (std::unique_ptr<Expr> &expr);
96+
97+
void visit (AST::BlockExpr &) override;
98+
};
99+
100+
} // namespace AST
101+
} // namespace Rust
102+
103+
#endif // ! RUST_DESUGAR_FOR_LOOPS

0 commit comments

Comments
 (0)