-
Notifications
You must be signed in to change notification settings - Fork 167
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
gcc/rust/ChangeLog: * ast/rust-desugar-for-loops.cc: New file. * ast/rust-desugar-for-loops.h: New file. * Make-lang.in: Compile it.
- Loading branch information
1 parent
9f88012
commit e414242
Showing
7 changed files
with
1,935 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
// Copyright (C) 2024 Free Software Foundation, Inc. | ||
|
||
// This file is part of GCC. | ||
|
||
// GCC is free software; you can redistribute it and/or modify it under | ||
// the terms of the GNU General Public License as published by the Free | ||
// Software Foundation; either version 3, or (at your option) any later | ||
// version. | ||
|
||
// GCC is distributed in the hope that it will be useful, but WITHOUT ANY | ||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
// for more details. | ||
|
||
// You should have received a copy of the GNU General Public License | ||
// along with GCC; see the file COPYING3. If not see | ||
// <http://www.gnu.org/licenses/>. | ||
|
||
#include "rust-desugar-for-loops.h" | ||
#include "rust-ast-visitor.h" | ||
#include "rust-ast.h" | ||
#include "rust-hir-map.h" | ||
#include "rust-path.h" | ||
#include "rust-pattern.h" | ||
#include "rust-stmt.h" | ||
#include "rust-expr.h" | ||
#include "rust-ast-builder.h" | ||
|
||
namespace Rust { | ||
namespace AST { | ||
|
||
DesugarForLoops::DesugarForLoops () {} | ||
|
||
void | ||
DesugarForLoops::go (AST::Crate &crate) | ||
{ | ||
DefaultASTVisitor::visit (crate); | ||
} | ||
|
||
void | ||
replace_for_loop (std::unique_ptr<Expr> &for_loop, | ||
std::unique_ptr<Expr> &&expanded) | ||
{ | ||
for_loop = std::move (expanded); | ||
} | ||
|
||
MatchArm | ||
DesugarForLoops::DesugarCtx::make_match_arm (std::unique_ptr<Pattern> &&path) | ||
{ | ||
auto patterns = std::vector<std::unique_ptr<Pattern>> (); | ||
patterns.emplace_back (std::move (path)); | ||
|
||
return MatchArm (std::move (patterns), loc); | ||
} | ||
|
||
MatchCase | ||
DesugarForLoops::DesugarCtx::make_break_arm () | ||
{ | ||
auto arm = make_match_arm (std::unique_ptr<Pattern> (new PathInExpression ( | ||
builder.path_in_expression (LangItem::Kind::OPTION_NONE)))); | ||
|
||
auto break_expr = std::unique_ptr<Expr> ( | ||
new BreakExpr (Lifetime::error (), nullptr, {}, loc)); | ||
|
||
return MatchCase (std::move (arm), std::move (break_expr)); | ||
} | ||
|
||
MatchCase | ||
DesugarForLoops::DesugarCtx::make_continue_arm () | ||
{ | ||
auto val = builder.identifier_pattern ("val"); | ||
|
||
auto patterns = std::vector<std::unique_ptr<Pattern>> (); | ||
patterns.emplace_back (std::move (val)); | ||
|
||
auto pattern_item = std::unique_ptr<TupleStructItems> ( | ||
new TupleStructItemsNoRange (std::move (patterns))); | ||
auto pattern = std::unique_ptr<Pattern> (new TupleStructPattern ( | ||
builder.path_in_expression (LangItem::Kind::OPTION_SOME), | ||
std::move (pattern_item))); | ||
|
||
auto val_arm = make_match_arm (std::move (pattern)); | ||
|
||
auto next = builder.identifier ("__next"); | ||
|
||
auto assignment = std::unique_ptr<Expr> ( | ||
new AssignmentExpr (std::move (next), builder.identifier ("val"), {}, loc)); | ||
|
||
return MatchCase (std::move (val_arm), std::move (assignment)); | ||
} | ||
|
||
std::unique_ptr<Stmt> | ||
DesugarForLoops::DesugarCtx::statementify (std::unique_ptr<Expr> &&expr) | ||
{ | ||
return std::unique_ptr<Stmt> (new ExprStmt (std::move (expr), loc, true)); | ||
} | ||
|
||
std::unique_ptr<Expr> | ||
DesugarForLoops::desugar (AST::ForLoopExpr &expr) | ||
{ | ||
auto ctx = DesugarCtx (expr.get_locus ()); | ||
|
||
auto into_iter = std::make_unique<PathInExpression> ( | ||
ctx.builder.path_in_expression (LangItem::Kind::INTOITER_INTOITER)); | ||
auto next = std::make_unique<PathInExpression> ( | ||
ctx.builder.path_in_expression (LangItem::Kind::ITERATOR_NEXT)); | ||
|
||
// IntoIterator::into_iter(<head>) | ||
auto into_iter_call | ||
= ctx.builder.call (std::move (into_iter), | ||
expr.get_iterator_expr ().clone_expr ()); | ||
|
||
// Iterator::next(iter) | ||
auto next_call = ctx.builder.call ( | ||
std::move (next), ctx.builder.ref (ctx.builder.identifier ("iter"), true)); | ||
|
||
// None => break, | ||
auto break_arm = ctx.make_break_arm (); | ||
// Some(val) => { __next = val; }, | ||
auto continue_arm = ctx.make_continue_arm (); | ||
|
||
// match <next_call> { | ||
// <continue_arm> | ||
// <break_arm> | ||
// } | ||
auto match_next | ||
= ctx.builder.match (std::move (next_call), | ||
{std::move (continue_arm), std::move (break_arm)}); | ||
|
||
// let mut __next; | ||
auto let_next | ||
= ctx.builder.let (ctx.builder.identifier_pattern ("__next", true)); | ||
// let <pattern> = __next; | ||
auto let_pat = ctx.builder.let (expr.get_pattern ().clone_pattern (), nullptr, | ||
ctx.builder.identifier ("__next")); | ||
|
||
auto loop_stmts = std::vector<std::unique_ptr<Stmt>> (); | ||
loop_stmts.emplace_back (std::move (let_next)); | ||
loop_stmts.emplace_back (ctx.statementify (std::move (match_next))); | ||
loop_stmts.emplace_back (std::move (let_pat)); | ||
loop_stmts.emplace_back ( | ||
ctx.statementify (expr.get_loop_block ().clone_expr ())); | ||
|
||
// loop { | ||
// <let_next>; | ||
// <match_next>; | ||
// <let_pat>; | ||
// | ||
// <body>; | ||
// } | ||
auto loop = ctx.builder.loop (std::move (loop_stmts)); | ||
|
||
auto mut_iter_pattern = ctx.builder.identifier_pattern ("iter", true); | ||
auto match_iter | ||
= ctx.builder.match (std::move (into_iter_call), | ||
{ctx.builder.match_case (std::move (mut_iter_pattern), | ||
std::move (loop))}); | ||
|
||
auto let_result = ctx.builder.let (ctx.builder.identifier_pattern ("result"), | ||
nullptr, std::move (match_iter)); | ||
auto result_return = ctx.builder.identifier ("result"); | ||
|
||
return ctx.builder.block (std::move (let_result), std::move (result_return)); | ||
} | ||
|
||
void | ||
DesugarForLoops::maybe_desugar_expr (std::unique_ptr<Expr> &expr) | ||
{ | ||
if (expr->get_expr_kind () == AST::Expr::Kind::Loop) | ||
{ | ||
auto &loop = static_cast<AST::BaseLoopExpr &> (*expr); | ||
|
||
if (loop.get_loop_kind () == AST::BaseLoopExpr::Kind::For) | ||
{ | ||
auto &for_loop = static_cast<AST::ForLoopExpr &> (loop); | ||
|
||
auto desugared = desugar (for_loop); | ||
|
||
replace_for_loop (expr, std::move (desugared)); | ||
} | ||
} | ||
} | ||
|
||
void | ||
DesugarForLoops::visit (AST::BlockExpr &block) | ||
{ | ||
for (auto &stmt : block.get_statements ()) | ||
if (stmt->get_stmt_kind () == AST::Stmt::Kind::Expr) | ||
maybe_desugar_expr (static_cast<AST::ExprStmt &> (*stmt).get_expr_ptr ()); | ||
|
||
if (block.has_tail_expr ()) | ||
maybe_desugar_expr (block.get_tail_expr_ptr ()); | ||
|
||
DefaultASTVisitor::visit (block); | ||
} | ||
|
||
} // namespace AST | ||
} // namespace Rust |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
// Copyright (C) 2024 Free Software Foundation, Inc. | ||
|
||
// This file is part of GCC. | ||
|
||
// GCC is free software; you can redistribute it and/or modify it under | ||
// the terms of the GNU General Public License as published by the Free | ||
// Software Foundation; either version 3, or (at your option) any later | ||
// version. | ||
|
||
// GCC is distributed in the hope that it will be useful, but WITHOUT ANY | ||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
// for more details. | ||
|
||
// You should have received a copy of the GNU General Public License | ||
// along with GCC; see the file COPYING3. If not see | ||
// <http://www.gnu.org/licenses/>. | ||
|
||
#ifndef RUST_DESUGAR_FOR_LOOPS | ||
#define RUST_DESUGAR_FOR_LOOPS | ||
|
||
#include "rust-ast-builder.h" | ||
#include "rust-ast-visitor.h" | ||
#include "rust-expr.h" | ||
|
||
namespace Rust { | ||
namespace AST { | ||
|
||
// Desugar for-loops into a set of other AST nodes. The desugar is of the | ||
// following form: | ||
// | ||
// ``` | ||
// for <pat> in <head> <body> | ||
// ``` | ||
// | ||
// becomes: | ||
// | ||
// ``` | ||
// { | ||
// let result = match ::std::iter::IntoIterator::into_iter(<head>) { | ||
// mut iter => { | ||
// loop { | ||
// let mut __next; | ||
// match ::std::iter::Iterator::next(&mut iter) { | ||
// ::std::option::Option::Some(val) => __next = val, | ||
// ::std::option::Option::None => break | ||
// }; | ||
// let <pat> = __next; | ||
// | ||
// <body>; | ||
// } | ||
// } | ||
// }; | ||
// result | ||
// } | ||
// ``` | ||
// | ||
// NOTE: In a perfect world, this would be an immutable visitor which would take | ||
// ownership of the AST node and return a new one, instead of mutating this one | ||
// in place. Nevertheless, this isn't Rust, and doing immutable visitors in C++ | ||
// sucks, and the world isn't perfect, so we are impure and sad. | ||
// | ||
// NOTE: This class could eventually be removed in favor of | ||
// an HIR desugar. This would avoid mutating the AST and would be cleaner. | ||
// However, it requires multiple changes in the way we do typechecking and name | ||
// resolution, as this desugar creates new bindings. Because of this, these new | ||
// bindings need to be inserted into the name-resolution context outside of the | ||
// name resolution pass, which is difficult. Those bindings are needed because | ||
// of the way the typechecker is currently structured, where it will fetch name | ||
// resolution information in order to typecheck paths - which technically isn't | ||
// necessary. | ||
class DesugarForLoops : public DefaultASTVisitor | ||
{ | ||
using DefaultASTVisitor::visit; | ||
|
||
public: | ||
DesugarForLoops (); | ||
void go (AST::Crate &); | ||
|
||
private: | ||
struct DesugarCtx | ||
{ | ||
DesugarCtx (location_t loc) : builder (Builder (loc)), loc (loc) {} | ||
|
||
Builder builder; | ||
location_t loc; | ||
|
||
MatchArm make_match_arm (std::unique_ptr<Pattern> &&pattern); | ||
MatchCase make_break_arm (); | ||
MatchCase make_continue_arm (); | ||
std::unique_ptr<Stmt> statementify (std::unique_ptr<Expr> &&expr); | ||
}; | ||
|
||
std::unique_ptr<Expr> desugar (AST::ForLoopExpr &expr); | ||
void maybe_desugar_expr (std::unique_ptr<Expr> &expr); | ||
|
||
void visit (AST::BlockExpr &) override; | ||
}; | ||
|
||
} // namespace AST | ||
} // namespace Rust | ||
|
||
#endif // ! RUST_DESUGAR_FOR_LOOPS |
Oops, something went wrong.