Skip to content

Commit

Permalink
rust: Desugar IfLet* into MatchExpr
Browse files Browse the repository at this point in the history
Replace the "regular" AST->HIR lowering for IfLet* with a desugaring
into a MatchExpr.

Desugar a simple if let:

   if let Some(y) = some_value {
     bar();
   }

into:

   match some_value {
     Some(y) => {bar();},
     _ => ()
   }

Same applies for IfLetExprConseqElse (if let with an else block).

Desugar:

   if let Some(y) = some_value {
     bar();
   } else {
     baz();
   }

into:

   match some_value {
     Some(y) => {bar();},
     _ => {baz();}
   }

Fixes #1177

gcc/rust/ChangeLog:

	* backend/rust-compile-block.h: Adjust after removal of
	HIR::IfLetExpr and HIR::IfLetExprConseqElse.
	* backend/rust-compile-expr.h: Likewise.
	* checks/errors/borrowck/rust-bir-builder-expr-stmt.cc
	(ExprStmtBuilder::visit): Likewise.
	* checks/errors/borrowck/rust-bir-builder-expr-stmt.h: Likewise.
	* checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h:
	Likewise.
	* checks/errors/borrowck/rust-bir-builder-struct.h: Likewise.
	* checks/errors/borrowck/rust-function-collector.h: Likewise.
	* checks/errors/privacy/rust-privacy-reporter.cc
	(PrivacyReporter::visit): Likewise.
	* checks/errors/privacy/rust-privacy-reporter.h: Likewise.
	* checks/errors/rust-const-checker.cc (ConstChecker::visit):
	Likewise.
	* checks/errors/rust-const-checker.h: Likewise.
	* checks/errors/rust-unsafe-checker.cc (UnsafeChecker::visit):
	Likewise.
	* checks/errors/rust-unsafe-checker.h: Likewise.
	* hir/rust-ast-lower-block.h (ASTLoweringIfLetBlock::translate):
	Change return type.
	* hir/rust-ast-lower.cc (ASTLoweringIfLetBlock::desugar_iflet):
	New.
	(ASTLoweringIfLetBlock::visit(AST::IfLetExpr &)): Adjust and use
	desugar_iflet.
	* hir/rust-ast-lower.h: Add comment.
	* hir/rust-hir-dump.cc (Dump::do_ifletexpr): Remove.
	(Dump::visit(IfLetExpr&)): Remove.
	(Dump::visit(IfLetExprConseqElse&)): Remove.
	* hir/rust-hir-dump.h (Dump::do_ifletexpr): Remove.
	(Dump::visit(IfLetExpr&)): Remove.
	(Dump::visit(IfLetExprConseqElse&)): Remove.
	* hir/tree/rust-hir-expr.h (class IfLetExpr): Remove.
	(class IfLetExprConseqElse): Remove.
	* hir/tree/rust-hir-full-decls.h (class IfLetExpr): Remove.
	(class IfLetExprConseqElse): Remove.
	* hir/tree/rust-hir-visitor.h: Adjust after removal of
	HIR::IfLetExpr and HIR::IfLetExprConseqElse.
	* hir/tree/rust-hir.cc (IfLetExpr::as_string): Remove.
	(IfLetExprConseqElse::as_string): Remove.
	(IfLetExpr::accept_vis): Remove.
	(IfLetExprConseqElse::accept_vis): Remove.
	* hir/tree/rust-hir.h: Adjust after removal of HIR::IfLetExpr and
	HIR::IfLetExprConseqElse.
	* typecheck/rust-hir-type-check-expr.cc (TypeCheckExpr::visit):
	Likewise.
	* typecheck/rust-hir-type-check-expr.h: Likewise.

gcc/testsuite/ChangeLog:

	* rust/compile/if_let_expr.rs: Adjust.
	* rust/compile/if_let_expr_simple.rs: New test.
	* rust/compile/iflet.rs: New test.
	* rust/execute/torture/iflet.rs: New test.

Signed-off-by: Marc Poulhiès <[email protected]>
  • Loading branch information
dkm committed Sep 5, 2024
1 parent 8ab5dd8 commit 70b157e
Show file tree
Hide file tree
Showing 29 changed files with 244 additions and 458 deletions.
4 changes: 0 additions & 4 deletions gcc/rust/backend/rust-compile-block.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@ class CompileConditionalBlocks : public HIRCompileBase,
void visit (HIR::LoopExpr &) override {}
void visit (HIR::WhileLoopExpr &) override {}
void visit (HIR::WhileLetLoopExpr &) override {}
void visit (HIR::IfLetExpr &) override {}
void visit (HIR::IfLetExprConseqElse &) override {}
void visit (HIR::MatchExpr &) override {}
void visit (HIR::AwaitExpr &) override {}
void visit (HIR::AsyncBlockExpr &) override {}
Expand Down Expand Up @@ -180,8 +178,6 @@ class CompileExprWithBlock : public HIRCompileBase,
void visit (HIR::LoopExpr &) override {}
void visit (HIR::WhileLoopExpr &) override {}
void visit (HIR::WhileLetLoopExpr &) override {}
void visit (HIR::IfLetExpr &) override {}
void visit (HIR::IfLetExprConseqElse &) override {}
void visit (HIR::MatchExpr &) override {}
void visit (HIR::AwaitExpr &) override {}
void visit (HIR::AsyncBlockExpr &) override {}
Expand Down
2 changes: 0 additions & 2 deletions gcc/rust/backend/rust-compile-expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ class CompileExpr : private HIRCompileBase, protected HIR::HIRExpressionVisitor
// TODO
// these need to be sugared in the HIR to if statements and a match
void visit (HIR::WhileLetLoopExpr &) override {}
void visit (HIR::IfLetExpr &) override {}
void visit (HIR::IfLetExprConseqElse &) override {}

// lets not worry about async yet....
void visit (HIR::AwaitExpr &) override {}
Expand Down
12 changes: 0 additions & 12 deletions gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -610,18 +610,6 @@ ExprStmtBuilder::visit (HIR::IfExprConseqElse &expr)
add_jump (else_end_bb, final_start_bb);
}

void
ExprStmtBuilder::visit (HIR::IfLetExpr &expr)
{
rust_sorry_at (expr.get_locus (), "if let expressions are not supported");
}

void
ExprStmtBuilder::visit (HIR::IfLetExprConseqElse &expr)
{
rust_sorry_at (expr.get_locus (), "if let expressions are not supported");
}

void
ExprStmtBuilder::visit (HIR::MatchExpr &expr)
{
Expand Down
2 changes: 0 additions & 2 deletions gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,6 @@ class ExprStmtBuilder final : public AbstractExprBuilder,
void visit (HIR::IfExprConseqElse &expr) override;
void visit (HIR::InlineAsm &expr) override;

void visit (HIR::IfLetExpr &expr) override;
void visit (HIR::IfLetExprConseqElse &expr) override;
void visit (HIR::MatchExpr &expr) override;
void visit (HIR::AwaitExpr &expr) override;
void visit (HIR::AsyncBlockExpr &expr) override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,14 +192,6 @@ class LazyBooleanExprBuilder : public AbstractExprBuilder
{
return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ());
}
void visit (HIR::IfLetExpr &expr) override
{
return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ());
}
void visit (HIR::IfLetExprConseqElse &expr) override
{
return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ());
}
void visit (HIR::MatchExpr &expr) override
{
return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ());
Expand Down
2 changes: 0 additions & 2 deletions gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,6 @@ class StructBuilder : public AbstractBuilder, public HIR::HIRFullVisitor
void visit (HIR::WhileLetLoopExpr &expr) override { rust_unreachable (); }
void visit (HIR::IfExpr &expr) override { rust_unreachable (); }
void visit (HIR::IfExprConseqElse &expr) override { rust_unreachable (); }
void visit (HIR::IfLetExpr &expr) override { rust_unreachable (); }
void visit (HIR::IfLetExprConseqElse &expr) override { rust_unreachable (); }
void visit (HIR::MatchExpr &expr) override { rust_unreachable (); }
void visit (HIR::AwaitExpr &expr) override { rust_unreachable (); }
void visit (HIR::AsyncBlockExpr &expr) override { rust_unreachable (); }
Expand Down
2 changes: 0 additions & 2 deletions gcc/rust/checks/errors/borrowck/rust-function-collector.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,6 @@ class FunctionCollector : public HIR::HIRFullVisitor
void visit (HIR::WhileLetLoopExpr &expr) override {}
void visit (HIR::IfExpr &expr) override {}
void visit (HIR::IfExprConseqElse &expr) override {}
void visit (HIR::IfLetExpr &expr) override {}
void visit (HIR::IfLetExprConseqElse &expr) override {}
void visit (HIR::MatchExpr &expr) override {}
void visit (HIR::AwaitExpr &expr) override {}
void visit (HIR::AsyncBlockExpr &expr) override {}
Expand Down
15 changes: 0 additions & 15 deletions gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -594,21 +594,6 @@ PrivacyReporter::visit (HIR::IfExprConseqElse &expr)
expr.get_else_block ()->accept_vis (*this);
}

void
PrivacyReporter::visit (HIR::IfLetExpr &)
{
// TODO: We need to visit the if_let_expr
// TODO: We need to visit the block as well
}

void
PrivacyReporter::visit (HIR::IfLetExprConseqElse &)
{
// TODO: We need to visit the if_let_expr
// TODO: We need to visit the if_block as well
// TODO: We need to visit the else_block as well
}

void
PrivacyReporter::visit (HIR::MatchExpr &expr)
{
Expand Down
2 changes: 0 additions & 2 deletions gcc/rust/checks/errors/privacy/rust-privacy-reporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,6 @@ types
virtual void visit (HIR::WhileLetLoopExpr &expr);
virtual void visit (HIR::IfExpr &expr);
virtual void visit (HIR::IfExprConseqElse &expr);
virtual void visit (HIR::IfLetExpr &expr);
virtual void visit (HIR::IfLetExprConseqElse &expr);
virtual void visit (HIR::MatchExpr &expr);
virtual void visit (HIR::AwaitExpr &expr);
virtual void visit (HIR::AsyncBlockExpr &expr);
Expand Down
16 changes: 0 additions & 16 deletions gcc/rust/checks/errors/rust-const-checker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -497,22 +497,6 @@ ConstChecker::visit (IfExprConseqElse &expr)
expr.get_else_block ()->accept_vis (*this);
}

void
ConstChecker::visit (IfLetExpr &expr)
{
expr.get_scrutinee_expr ()->accept_vis (*this);
expr.get_if_block ()->accept_vis (*this);
}

void
ConstChecker::visit (IfLetExprConseqElse &expr)
{
expr.get_scrutinee_expr ()->accept_vis (*this);
expr.get_if_block ()->accept_vis (*this);

// TODO: Visit else expression
}

void
ConstChecker::visit (MatchExpr &expr)
{
Expand Down
2 changes: 0 additions & 2 deletions gcc/rust/checks/errors/rust-const-checker.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,6 @@ class ConstChecker : public HIRFullVisitor
virtual void visit (WhileLetLoopExpr &expr) override;
virtual void visit (IfExpr &expr) override;
virtual void visit (IfExprConseqElse &expr) override;
virtual void visit (IfLetExpr &expr) override;
virtual void visit (IfLetExprConseqElse &expr) override;
virtual void visit (MatchExpr &expr) override;
virtual void visit (AwaitExpr &expr) override;
virtual void visit (AsyncBlockExpr &expr) override;
Expand Down
16 changes: 0 additions & 16 deletions gcc/rust/checks/errors/rust-unsafe-checker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -599,22 +599,6 @@ UnsafeChecker::visit (IfExprConseqElse &expr)
expr.get_else_block ()->accept_vis (*this);
}

void
UnsafeChecker::visit (IfLetExpr &expr)
{
expr.get_scrutinee_expr ()->accept_vis (*this);
expr.get_if_block ()->accept_vis (*this);
}

void
UnsafeChecker::visit (IfLetExprConseqElse &expr)
{
expr.get_scrutinee_expr ()->accept_vis (*this);
expr.get_if_block ()->accept_vis (*this);

// TODO: Visit else expression
}

void
UnsafeChecker::visit (MatchExpr &expr)
{
Expand Down
2 changes: 0 additions & 2 deletions gcc/rust/checks/errors/rust-unsafe-checker.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,6 @@ class UnsafeChecker : public HIRFullVisitor
virtual void visit (WhileLetLoopExpr &expr) override;
virtual void visit (IfExpr &expr) override;
virtual void visit (IfExprConseqElse &expr) override;
virtual void visit (IfLetExpr &expr) override;
virtual void visit (IfLetExprConseqElse &expr) override;
virtual void visit (MatchExpr &expr) override;
virtual void visit (AwaitExpr &expr) override;
virtual void visit (AsyncBlockExpr &expr) override;
Expand Down
11 changes: 6 additions & 5 deletions gcc/rust/hir/rust-ast-lower-block.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class ASTLoweringIfLetBlock : public ASTLoweringBase
using Rust::HIR::ASTLoweringBase::visit;

public:
static HIR::IfLetExpr *translate (AST::IfLetExpr &expr)
static HIR::MatchExpr *translate (AST::IfLetExpr &expr)
{
ASTLoweringIfLetBlock resolver;
expr.accept_vis (resolver);
Expand All @@ -135,7 +135,10 @@ class ASTLoweringIfLetBlock : public ASTLoweringBase
private:
ASTLoweringIfLetBlock () : ASTLoweringBase (), translated (nullptr) {}

HIR::IfLetExpr *translated;
void desugar_iflet (AST::IfLetExpr &, HIR::Expr **,
std::vector<HIR::MatchCase> &);

HIR::MatchExpr *translated;
};

class ASTLoweringExprWithBlock : public ASTLoweringBase
Expand All @@ -149,9 +152,7 @@ class ASTLoweringExprWithBlock : public ASTLoweringBase
ASTLoweringExprWithBlock resolver;
expr.accept_vis (resolver);
if (resolver.translated != nullptr)
{
resolver.mappings.insert_hir_expr (resolver.translated);
}
resolver.mappings.insert_hir_expr (resolver.translated);

*terminated = resolver.terminated;
return resolver.translated;
Expand Down
132 changes: 102 additions & 30 deletions gcc/rust/hir/rust-ast-lower.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#include "rust-ast-lower-type.h"
#include "rust-ast-lower-pattern.h"
#include "rust-ast-lower-struct-field-expr.h"
#include "rust-expr.h"
#include "rust-hir-expr.h"

namespace Rust {
namespace HIR {
Expand Down Expand Up @@ -198,62 +200,132 @@ ASTLoweringIfBlock::visit (AST::IfExprConseqElse &expr)
std::unique_ptr<HIR::ExprWithBlock> (else_block), expr.get_locus ());
}

/**
* Lowers the common part "if let 'pattern' = 'expr' { 'if_block' }" of
* IfLetExpr[ConseqElse]:
* - 'expr' is lowered into *BRANCH_VALUE
* - 'pattern' + 'if_block' are lowered and resulting ARM pushed in MATCH_ARMS
*
* Looks like:
*
* match (expr) {
* pattern => {if_block}
* }
*/
void
ASTLoweringIfLetBlock::visit (AST::IfLetExpr &expr)
ASTLoweringIfLetBlock::desugar_iflet (AST::IfLetExpr &expr,
HIR::Expr **branch_value,
std::vector<HIR::MatchCase> &match_arms)
{
std::vector<std::unique_ptr<HIR::Pattern>> patterns;
HIR::Expr *kase_expr;
std::vector<std::unique_ptr<HIR::Pattern>> match_arm_patterns;

*branch_value = ASTLoweringExpr::translate (expr.get_value_expr ());
kase_expr = ASTLoweringExpr::translate (expr.get_if_block ());

// FIXME: if let only accepts a single pattern. Why do we have a vector of
// patterns in the IfLet?
for (auto &pattern : expr.get_patterns ())
{
HIR::Pattern *ptrn = ASTLoweringPattern::translate (*pattern);
patterns.push_back (std::unique_ptr<HIR::Pattern> (ptrn));
match_arm_patterns.push_back (std::unique_ptr<HIR::Pattern> (ptrn));
}
HIR::Expr *value_ptr = ASTLoweringExpr::translate (expr.get_value_expr ());

bool ignored_terminated = false;
HIR::BlockExpr *block
= ASTLoweringBlock::translate (expr.get_if_block (), &ignored_terminated);
HIR::MatchArm arm (std::move (match_arm_patterns), expr.get_locus (), nullptr,
{});

auto crate_num = mappings.get_current_crate ();
Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
mappings.get_next_hir_id (crate_num),
UNKNOWN_LOCAL_DEFID);

translated = new HIR::IfLetExpr (mapping, std::move (patterns),
std::unique_ptr<HIR::Expr> (value_ptr),
std::unique_ptr<HIR::BlockExpr> (block),
expr.get_locus ());
HIR::MatchCase kase (std::move (mapping), std::move (arm),
std::unique_ptr<HIR::Expr> (kase_expr));
match_arms.push_back (std::move (kase));
}

void
ASTLoweringIfLetBlock::visit (AST::IfLetExprConseqElse &expr)
ASTLoweringIfLetBlock::visit (AST::IfLetExpr &expr)
{
std::vector<std::unique_ptr<HIR::Pattern>> patterns;
for (auto &pattern : expr.get_patterns ())
{
HIR::Pattern *ptrn = ASTLoweringPattern::translate (*pattern);
patterns.push_back (std::unique_ptr<HIR::Pattern> (ptrn));
}
HIR::Expr *value_ptr = ASTLoweringExpr::translate (expr.get_value_expr ());
// Desugar:
// if let Some(y) = some_value {
// bar();
// }
//
// into:
//
// match some_value {
// Some(y) => {bar();},
// _ => ()
// }

HIR::Expr *branch_value;

bool ignored_terminated = false;
HIR::BlockExpr *block
= ASTLoweringBlock::translate (expr.get_if_block (), &ignored_terminated);
std::vector<HIR::MatchCase> match_arms;
desugar_iflet (expr, &branch_value, match_arms);

HIR::ExprWithBlock *else_block
= ASTLoweringExprWithBlock::translate (expr.get_else_block (),
&ignored_terminated);
auto crate_num = mappings.get_current_crate ();
Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
mappings.get_next_hir_id (crate_num),
UNKNOWN_LOCAL_DEFID);

translated
= new HIR::MatchExpr (mapping, std::unique_ptr<HIR::Expr> (branch_value),
std::move (match_arms), {}, {}, expr.get_locus ());
}

void
ASTLoweringIfLetBlock::visit (AST::IfLetExprConseqElse &expr)
{
// desugar:
// if let Some(y) = some_value {
// bar();
// } else {
// baz();
// }
//
// into
// match some_value {
// Some(y) => {bar();},
// _ => {baz();}
// }
//

HIR::Expr *branch_value;
std::vector<HIR::MatchCase> match_arms;

rust_assert (else_block);
desugar_iflet (expr, &branch_value, match_arms);

auto crate_num = mappings.get_current_crate ();
Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
mappings.get_next_hir_id (crate_num),
UNKNOWN_LOCAL_DEFID);

translated = new HIR::IfLetExprConseqElse (
mapping, std::move (patterns), std::unique_ptr<HIR::Expr> (value_ptr),
std::unique_ptr<HIR::BlockExpr> (block),
std::unique_ptr<HIR::ExprWithBlock> (else_block), expr.get_locus ());
std::vector<std::unique_ptr<HIR::Pattern>> match_arm_patterns_wildcard;
Analysis::NodeMapping mapping_default (crate_num, expr.get_node_id (),
mappings.get_next_hir_id (crate_num),
UNKNOWN_LOCAL_DEFID);

std::unique_ptr<HIR::WildcardPattern> wc
= std::unique_ptr<HIR::WildcardPattern> (
new HIR::WildcardPattern (mapping_default, expr.get_locus ()));

match_arm_patterns_wildcard.push_back (std::move (wc));

HIR::MatchArm arm_default (std::move (match_arm_patterns_wildcard),
expr.get_locus (), nullptr, {});

HIR::Expr *kase_else_expr
= ASTLoweringExpr::translate (expr.get_else_block ());

HIR::MatchCase kase_else (std::move (mapping_default),
std::move (arm_default),
std::unique_ptr<HIR::Expr> (kase_else_expr));
match_arms.push_back (std::move (kase_else));

translated
= new HIR::MatchExpr (mapping, std::unique_ptr<HIR::Expr> (branch_value),
std::move (match_arms), {}, {}, expr.get_locus ());
}

// rust-ast-lower-struct-field-expr.h
Expand Down
5 changes: 5 additions & 0 deletions gcc/rust/hir/rust-ast-lower.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ struct_field_name_exists (std::vector<HIR::StructField> &fields,
Visibility
translate_visibility (const AST::Visibility &vis);

/**
* Main base class used for lowering AST to HIR.
*
* Every subclass should provide a translate() method that takes an AST node and
* lowers it to some HIR stored in the TRANSLATED member. */
class ASTLowering
{
public:
Expand Down
Loading

0 comments on commit 70b157e

Please sign in to comment.