Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gcc/rust/Make-lang.in
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ GRS_OBJS = \
rust/rust-collect-lang-items.o \
rust/rust-expression-yeast.o \
rust/rust-desugar-for-loops.o \
rust/rust-desugar-while-let.o \
rust/rust-desugar-question-mark.o \
rust/rust-desugar-apit.o \
rust/rust-desugar-try-block.o \
Expand Down
104 changes: 104 additions & 0 deletions gcc/rust/ast/rust-desugar-while-let.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright (C) 2025 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-while-let.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 {

DesugarWhileLet::DesugarWhileLet () {}

MatchCase
DesugarWhileLet::DesugarCtx::make_break_arm ()
{
auto arm = builder.match_arm (builder.wildcard ());

auto break_expr
= std::unique_ptr<Expr> (new BreakExpr (tl::nullopt, nullptr, {}, loc));

return MatchCase (std::move (arm), std::move (break_expr));
}

MatchCase
DesugarWhileLet::DesugarCtx::make_continue_arm (
std::unique_ptr<Pattern> &&pattern, std::unique_ptr<BlockExpr> &&body)
{
auto arm = builder.match_arm (std::move (pattern));

return MatchCase (std::move (arm), std::move (body));
}

std::unique_ptr<Expr>
DesugarWhileLet::desugar (WhileLetLoopExpr &expr)
{
rust_assert (expr.get_patterns ().size () == 1);

auto pattern = expr.get_patterns ()[0]->clone_pattern ();
auto body = expr.get_loop_block ().clone_block_expr ();
auto scrutinee = expr.get_scrutinee_expr ().clone_expr ();

auto ctx = DesugarCtx (expr.get_locus ());

// _ => break,
auto break_arm = ctx.make_break_arm ();

// <pattern> => <body>,
auto continue_arm
= ctx.make_continue_arm (std::move (pattern), std::move (body));

// match <scrutinee> {
// <continue_arm>
// <break_arm>
// }
auto match_expr
= ctx.builder.match (std::move (scrutinee),
{std::move (continue_arm), std::move (break_arm)});

auto loop_stmts = std::vector<std::unique_ptr<Stmt>> ();
loop_stmts.emplace_back (ctx.builder.statementify (std::move (match_expr)));

// loop {
// <match_expr>
// }
return ctx.builder.loop (std::move (loop_stmts));
}

void
DesugarWhileLet::go (std::unique_ptr<Expr> &ptr)
{
rust_assert (ptr->get_expr_kind () == Expr::Kind::Loop);

auto &loop = static_cast<BaseLoopExpr &> (*ptr);

rust_assert (loop.get_loop_kind () == BaseLoopExpr::Kind::WhileLet);

auto &while_let = static_cast<WhileLetLoopExpr &> (loop);
auto desugared = DesugarWhileLet ().desugar (while_let);

ptr = std::move (desugared);
}

} // namespace AST
} // namespace Rust
71 changes: 71 additions & 0 deletions gcc/rust/ast/rust-desugar-while-let.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (C) 2025 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_WHILE_LET_H
#define RUST_DESUGAR_WHILE_LET_H

#include "rust-ast-builder.h"
#include "rust-expr.h"

namespace Rust {
namespace AST {

// Desugar while-let into a set of other AST nodes. The desugar is of the
// following form:
//
// ```
// whilet let <pat> = <expr> <body>
// ```
//
// becomes:
//
// ```
// loop {
// match <expr> {
// <pat> => <body>,
// _ => break
// }
// }
// ```
class DesugarWhileLet
{
public:
static void go (std::unique_ptr<Expr> &ptr);

private:
DesugarWhileLet ();

struct DesugarCtx
{
DesugarCtx (location_t loc) : builder (Builder (loc)), loc (loc) {}

Builder builder;
location_t loc;

MatchCase make_break_arm ();
MatchCase make_continue_arm (std::unique_ptr<Pattern> &&pattern,
std::unique_ptr<BlockExpr> &&body);
};

std::unique_ptr<Expr> desugar (WhileLetLoopExpr &expr);
};

} // namespace AST
} // namespace Rust

#endif // ! RUST_DESUGAR_WHILE_LET_H
6 changes: 4 additions & 2 deletions gcc/rust/ast/rust-expression-yeast.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "rust-desugar-try-block.h"
#include "rust-desugar-for-loops.h"
#include "rust-ast-full.h"
#include "rust-desugar-while-let.h"
#include "rust-expr.h"
#include "rust-stmt.h"

Expand All @@ -44,9 +45,10 @@ ExpressionYeast::dispatch_loops (std::unique_ptr<Expr> &loop_expr)
case BaseLoopExpr::Kind::For:
DesugarForLoops::go (loop_expr);
break;
case BaseLoopExpr::Kind::Loop:
case BaseLoopExpr::Kind::While:
case BaseLoopExpr::Kind::WhileLet:
DesugarWhileLet::go (loop_expr);
break;
default:
break;
}
}
Expand Down
109 changes: 109 additions & 0 deletions gcc/testsuite/rust/compile/while_let1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// use self::Ordering::*;
// use Ordering::*;

// enum Ordering {
// A,
// B,
// }

// fn foo(_: Ordering) {}

// fn main() {
// let a = A;

// foo(a);
// foo(B);
// }

#[lang = "sized"]
trait Sized {}

enum Result<T, E> {
Ok(T),
Err(E),
}

pub trait Try {
/// The type of this value when viewed as successful.
#[unstable(feature = "try_trait", issue = "42327")]
type Ok;
/// The type of this value when viewed as failed.
#[unstable(feature = "try_trait", issue = "42327")]
type Error;

/// Applies the "?" operator. A return of `Ok(t)` means that the
/// execution should continue normally, and the result of `?` is the
/// value `t`. A return of `Err(e)` means that execution should branch
/// to the innermost enclosing `catch`, or return from the function.
///
/// If an `Err(e)` result is returned, the value `e` will be "wrapped"
/// in the return type of the enclosing scope (which must itself implement
/// `Try`). Specifically, the value `X::from_error(From::from(e))`
/// is returned, where `X` is the return type of the enclosing function.
#[lang = "into_result"]
#[unstable(feature = "try_trait", issue = "42327")]
fn into_result(self) -> Result<Self::Ok, Self::Error>;

/// Wrap an error value to construct the composite result. For example,
/// `Result::Err(x)` and `Result::from_error(x)` are equivalent.
#[lang = "from_error"]
#[unstable(feature = "try_trait", issue = "42327")]
fn from_error(v: Self::Error) -> Self;

/// Wrap an OK value to construct the composite result. For example,
/// `Result::Ok(x)` and `Result::from_ok(x)` are equivalent.
#[lang = "from_ok"]
#[unstable(feature = "try_trait", issue = "42327")]
fn from_ok(v: Self::Ok) -> Self;
}

pub struct NoneError;

pub enum Option<T> {
/// No value
None,
/// Some value `T`
Some(T),
}

impl<T> Option<T> {
pub fn ok_or<E>(self, err: E) -> Result<T, E> {
match self {
Some(ok) => Result::Ok(ok),
None => Result::Err(err),
}
}
}

use Option::*;

#[unstable(feature = "try_trait", issue = "42327")]
impl<T> Try for Option<T> {
type Ok = T;
type Error = NoneError;

#[inline]
fn into_result(self) -> Result<T, NoneError> {
self.ok_or(NoneError)
}

#[inline]
fn from_ok(v: T) -> Self {
Some(v)
}

#[inline]
fn from_error(_: NoneError) -> Self {
None
}
}

fn foo() -> Option<i32> {
Option::Some(15)
}

fn main() {
// let _: Option<i32> = try { 15i32 };

while let Option::Some(15) = foo() {}
}
Loading