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:
	* backend/rust-compile-expr.h:
	* checks/errors/borrowck/rust-bir-builder-expr-stmt.cc (ExprStmtBuilder::visit):
	* checks/errors/borrowck/rust-bir-builder-expr-stmt.h:
	* checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h:
	* checks/errors/borrowck/rust-bir-builder-struct.h:
	* checks/errors/borrowck/rust-function-collector.h:
	* checks/errors/privacy/rust-privacy-reporter.cc (PrivacyReporter::visit):
	* checks/errors/privacy/rust-privacy-reporter.h:
	* checks/errors/rust-const-checker.cc (ConstChecker::visit):
	* checks/errors/rust-const-checker.h:
	* checks/errors/rust-unsafe-checker.cc (UnsafeChecker::visit):
	* checks/errors/rust-unsafe-checker.h:
	* hir/rust-ast-lower-block.h:
	* hir/rust-ast-lower.cc (do_if_let_desugaring):
	(ASTLoweringIfLetBlock::visit):
	* hir/rust-ast-lower.h (struct_field_name_exists):
	(translate_visibility):
	* hir/rust-hir-dump.cc (Dump::do_ifletexpr):
	(Dump::visit):
	* hir/rust-hir-dump.h:
	* hir/tree/rust-hir-expr.h (class IfLetExpr):
	(class IfLetExprConseqElse):
	* hir/tree/rust-hir-full-decls.h (class IfLetExpr):
	(class IfLetExprConseqElse):
	* hir/tree/rust-hir-visitor.h:
	* hir/tree/rust-hir.cc (IfLetExpr::as_string):
	(IfLetExprConseqElse::as_string):
	(IfLetExpr::accept_vis):
	(IfLetExprConseqElse::accept_vis):
	* hir/tree/rust-hir.h:
	* typecheck/rust-hir-type-check-expr.cc (TypeCheckExpr::visit):
	* typecheck/rust-hir-type-check-expr.h:

gcc/testsuite/ChangeLog:

	* rust/compile/if_let_expr.rs:
	* 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 Jul 2, 2024
1 parent 12be7fa commit f3e83bb
Show file tree
Hide file tree
Showing 29 changed files with 243 additions and 462 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 @@ -179,8 +177,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 @@ -76,8 +76,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 @@ -554,18 +554,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 @@ -100,8 +100,6 @@ class ExprStmtBuilder final : public AbstractExprBuilder,
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
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,6 @@ class LazyBooleanExprBuilder : public AbstractExprBuilder
{
return_place (ExprStmtBuilder (ctx).build (expr));
}
void visit (HIR::IfLetExpr &expr) override
{
return_place (ExprStmtBuilder (ctx).build (expr));
}
void visit (HIR::IfLetExprConseqElse &expr) override
{
return_place (ExprStmtBuilder (ctx).build (expr));
}
void visit (HIR::MatchExpr &expr) override
{
return_place (ExprStmtBuilder (ctx).build (expr));
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 @@ -590,21 +590,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
8 changes: 3 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,7 @@ class ASTLoweringIfLetBlock : public ASTLoweringBase
private:
ASTLoweringIfLetBlock () : ASTLoweringBase (), translated (nullptr) {}

HIR::IfLetExpr *translated;
HIR::MatchExpr *translated;
};

class ASTLoweringExprWithBlock : public ASTLoweringBase
Expand All @@ -149,9 +149,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
141 changes: 112 additions & 29 deletions gcc/rust/hir/rust-ast-lower.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "rust-ast-lower-type.h"
#include "rust-ast-lower-pattern.h"
#include "rust-ast-lower-struct-field-expr.h"
#include "rust-expr.h"

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

void
do_if_let_desugaring (AST::IfLetExpr &expr)
{
HIR::Expr *branch_value
= ASTLoweringExpr::translate (expr.get_value_expr ());


}

void
ASTLoweringIfLetBlock::visit (AST::IfLetExpr &expr)
{
std::vector<std::unique_ptr<HIR::Pattern>> patterns;
// Desugar:
// if let Some(y) = some_value {
// bar();
// }
//
// into:
//
// match some_value {
// Some(y) => {bar();},
// _ => ()
// }

HIR::Expr *branch_value
= ASTLoweringExpr::translate (expr.get_value_expr ());

std::vector<HIR::MatchCase> match_arms;
HIR::Expr *kase_expr
= ASTLoweringExpr::translate (expr.get_if_block ());

std::vector<std::unique_ptr<HIR::Pattern>> match_arm_patterns;

// 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));
}
HIR::Expr *value_ptr = ASTLoweringExpr::translate (expr.get_value_expr ());
{
HIR::Pattern *ptrn = ASTLoweringPattern::translate (*pattern);
match_arm_patterns.push_back (std::unique_ptr<HIR::Pattern> (ptrn));
}

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));

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)
{
std::vector<std::unique_ptr<HIR::Pattern>> patterns;

// desugar:
// if let Some(y) = some_value {
// bar();
// } else {
// baz();
// }
//
// into
// match some_value {
// Some(y) => {bar();},
// _ => {baz();}
// }
//

HIR::Expr *branch_value
= ASTLoweringExpr::translate (expr.get_value_expr ());

std::vector<HIR::MatchCase> match_arms;
HIR::Expr *kase_expr
= ASTLoweringExpr::translate (expr.get_if_block ());

std::vector<std::unique_ptr<HIR::Pattern>> match_arm_patterns;

// FIXME: same, why do we have a vector of patterns?
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::ExprWithBlock *else_block
= ASTLoweringExprWithBlock::translate (expr.get_else_block (),
&ignored_terminated);

rust_assert (else_block);
// FIXME: 2 previous q? on having many patterns probably have the same answers
// as to why a single MatchArm has a vector of patterns.
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::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 ());
HIR::MatchCase kase (std::move (mapping), std::move (arm),
std::unique_ptr<HIR::Expr> (kase_expr));
match_arms.push_back (std::move (kase));

std::vector<std::unique_ptr<HIR::Pattern>> match_arm_patterns_wildcard;
Analysis::NodeMapping mapping_default_2 (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_2, 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,
{});

Analysis::NodeMapping mapping_default (crate_num, expr.get_node_id (),
mappings.get_next_hir_id (crate_num),
UNKNOWN_LOCAL_DEFID);
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
Loading

0 comments on commit f3e83bb

Please sign in to comment.