Skip to content

Commit 25dfee3

Browse files
committed
desugar: Add base for desugaring while-let loops
gcc/rust/ChangeLog: * Make-lang.in: * ast/rust-expression-yeast.cc (ExpressionYeast::dispatch_loops): Call DesugarWhileLet. * ast/rust-desugar-while-let.cc: New file. * ast/rust-desugar-while-let.h: New file. gcc/testsuite/ChangeLog: * rust/compile/while_let1.rs: New test.
1 parent f8c5a0a commit 25dfee3

5 files changed

Lines changed: 289 additions & 2 deletions

File tree

gcc/rust/Make-lang.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ GRS_OBJS = \
245245
rust/rust-collect-lang-items.o \
246246
rust/rust-expression-yeast.o \
247247
rust/rust-desugar-for-loops.o \
248+
rust/rust-desugar-while-let.o \
248249
rust/rust-desugar-question-mark.o \
249250
rust/rust-desugar-apit.o \
250251
rust/rust-desugar-try-block.o \
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright (C) 2025 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-while-let.h"
20+
#include "rust-ast.h"
21+
#include "rust-hir-map.h"
22+
#include "rust-path.h"
23+
#include "rust-pattern.h"
24+
#include "rust-stmt.h"
25+
#include "rust-expr.h"
26+
#include "rust-ast-builder.h"
27+
28+
namespace Rust {
29+
namespace AST {
30+
31+
DesugarWhileLet::DesugarWhileLet () {}
32+
33+
MatchCase
34+
DesugarWhileLet::DesugarCtx::make_break_arm ()
35+
{
36+
auto arm = builder.match_arm (builder.wildcard ());
37+
38+
auto break_expr
39+
= std::unique_ptr<Expr> (new BreakExpr (tl::nullopt, nullptr, {}, loc));
40+
41+
return MatchCase (std::move (arm), std::move (break_expr));
42+
}
43+
44+
MatchCase
45+
DesugarWhileLet::DesugarCtx::make_continue_arm (
46+
std::unique_ptr<Pattern> &&pattern, std::unique_ptr<BlockExpr> &&body)
47+
{
48+
auto arm = builder.match_arm (std::move (pattern));
49+
50+
return MatchCase (std::move (arm), std::move (body));
51+
}
52+
53+
std::unique_ptr<Expr>
54+
DesugarWhileLet::desugar (WhileLetLoopExpr &expr)
55+
{
56+
rust_assert (expr.get_patterns ().size () == 1);
57+
58+
auto pattern = expr.get_patterns ()[0]->clone_pattern ();
59+
auto body = expr.get_loop_block ().clone_block_expr ();
60+
auto scrutinee = expr.get_scrutinee_expr ().clone_expr ();
61+
62+
auto ctx = DesugarCtx (expr.get_locus ());
63+
64+
// _ => break,
65+
auto break_arm = ctx.make_break_arm ();
66+
67+
// <pattern> => <body>,
68+
auto continue_arm
69+
= ctx.make_continue_arm (std::move (pattern), std::move (body));
70+
71+
// match <scrutinee> {
72+
// <continue_arm>
73+
// <break_arm>
74+
// }
75+
auto match_expr
76+
= ctx.builder.match (std::move (scrutinee),
77+
{std::move (continue_arm), std::move (break_arm)});
78+
79+
auto loop_stmts = std::vector<std::unique_ptr<Stmt>> ();
80+
loop_stmts.emplace_back (ctx.builder.statementify (std::move (match_expr)));
81+
82+
// loop {
83+
// <match_expr>
84+
// }
85+
return ctx.builder.loop (std::move (loop_stmts));
86+
}
87+
88+
void
89+
DesugarWhileLet::go (std::unique_ptr<Expr> &ptr)
90+
{
91+
rust_assert (ptr->get_expr_kind () == Expr::Kind::Loop);
92+
93+
auto &loop = static_cast<BaseLoopExpr &> (*ptr);
94+
95+
rust_assert (loop.get_loop_kind () == BaseLoopExpr::Kind::WhileLet);
96+
97+
auto &while_let = static_cast<WhileLetLoopExpr &> (loop);
98+
auto desugared = DesugarWhileLet ().desugar (while_let);
99+
100+
ptr = std::move (desugared);
101+
}
102+
103+
} // namespace AST
104+
} // namespace Rust
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright (C) 2025 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_WHILE_LET_H
20+
#define RUST_DESUGAR_WHILE_LET_H
21+
22+
#include "rust-ast-builder.h"
23+
#include "rust-expr.h"
24+
25+
namespace Rust {
26+
namespace AST {
27+
28+
// Desugar while-let into a set of other AST nodes. The desugar is of the
29+
// following form:
30+
//
31+
// ```
32+
// whilet let <pat> = <expr> <body>
33+
// ```
34+
//
35+
// becomes:
36+
//
37+
// ```
38+
// loop {
39+
// match <expr> {
40+
// <pat> => <body>,
41+
// _ => break
42+
// }
43+
// }
44+
// ```
45+
class DesugarWhileLet
46+
{
47+
public:
48+
static void go (std::unique_ptr<Expr> &ptr);
49+
50+
private:
51+
DesugarWhileLet ();
52+
53+
struct DesugarCtx
54+
{
55+
DesugarCtx (location_t loc) : builder (Builder (loc)), loc (loc) {}
56+
57+
Builder builder;
58+
location_t loc;
59+
60+
MatchCase make_break_arm ();
61+
MatchCase make_continue_arm (std::unique_ptr<Pattern> &&pattern,
62+
std::unique_ptr<BlockExpr> &&body);
63+
};
64+
65+
std::unique_ptr<Expr> desugar (WhileLetLoopExpr &expr);
66+
};
67+
68+
} // namespace AST
69+
} // namespace Rust
70+
71+
#endif // ! RUST_DESUGAR_WHILE_LET_H

gcc/rust/ast/rust-expression-yeast.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "rust-desugar-try-block.h"
2323
#include "rust-desugar-for-loops.h"
2424
#include "rust-ast-full.h"
25+
#include "rust-desugar-while-let.h"
2526
#include "rust-expr.h"
2627
#include "rust-stmt.h"
2728

@@ -44,9 +45,10 @@ ExpressionYeast::dispatch_loops (std::unique_ptr<Expr> &loop_expr)
4445
case BaseLoopExpr::Kind::For:
4546
DesugarForLoops::go (loop_expr);
4647
break;
47-
case BaseLoopExpr::Kind::Loop:
48-
case BaseLoopExpr::Kind::While:
4948
case BaseLoopExpr::Kind::WhileLet:
49+
DesugarWhileLet::go (loop_expr);
50+
break;
51+
default:
5052
break;
5153
}
5254
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// use self::Ordering::*;
2+
// use Ordering::*;
3+
4+
// enum Ordering {
5+
// A,
6+
// B,
7+
// }
8+
9+
// fn foo(_: Ordering) {}
10+
11+
// fn main() {
12+
// let a = A;
13+
14+
// foo(a);
15+
// foo(B);
16+
// }
17+
18+
#[lang = "sized"]
19+
trait Sized {}
20+
21+
enum Result<T, E> {
22+
Ok(T),
23+
Err(E),
24+
}
25+
26+
pub trait Try {
27+
/// The type of this value when viewed as successful.
28+
#[unstable(feature = "try_trait", issue = "42327")]
29+
type Ok;
30+
/// The type of this value when viewed as failed.
31+
#[unstable(feature = "try_trait", issue = "42327")]
32+
type Error;
33+
34+
/// Applies the "?" operator. A return of `Ok(t)` means that the
35+
/// execution should continue normally, and the result of `?` is the
36+
/// value `t`. A return of `Err(e)` means that execution should branch
37+
/// to the innermost enclosing `catch`, or return from the function.
38+
///
39+
/// If an `Err(e)` result is returned, the value `e` will be "wrapped"
40+
/// in the return type of the enclosing scope (which must itself implement
41+
/// `Try`). Specifically, the value `X::from_error(From::from(e))`
42+
/// is returned, where `X` is the return type of the enclosing function.
43+
#[lang = "into_result"]
44+
#[unstable(feature = "try_trait", issue = "42327")]
45+
fn into_result(self) -> Result<Self::Ok, Self::Error>;
46+
47+
/// Wrap an error value to construct the composite result. For example,
48+
/// `Result::Err(x)` and `Result::from_error(x)` are equivalent.
49+
#[lang = "from_error"]
50+
#[unstable(feature = "try_trait", issue = "42327")]
51+
fn from_error(v: Self::Error) -> Self;
52+
53+
/// Wrap an OK value to construct the composite result. For example,
54+
/// `Result::Ok(x)` and `Result::from_ok(x)` are equivalent.
55+
#[lang = "from_ok"]
56+
#[unstable(feature = "try_trait", issue = "42327")]
57+
fn from_ok(v: Self::Ok) -> Self;
58+
}
59+
60+
pub struct NoneError;
61+
62+
pub enum Option<T> {
63+
/// No value
64+
None,
65+
/// Some value `T`
66+
Some(T),
67+
}
68+
69+
impl<T> Option<T> {
70+
pub fn ok_or<E>(self, err: E) -> Result<T, E> {
71+
match self {
72+
Some(ok) => Result::Ok(ok),
73+
None => Result::Err(err),
74+
}
75+
}
76+
}
77+
78+
use Option::*;
79+
80+
#[unstable(feature = "try_trait", issue = "42327")]
81+
impl<T> Try for Option<T> {
82+
type Ok = T;
83+
type Error = NoneError;
84+
85+
#[inline]
86+
fn into_result(self) -> Result<T, NoneError> {
87+
self.ok_or(NoneError)
88+
}
89+
90+
#[inline]
91+
fn from_ok(v: T) -> Self {
92+
Some(v)
93+
}
94+
95+
#[inline]
96+
fn from_error(_: NoneError) -> Self {
97+
None
98+
}
99+
}
100+
101+
fn foo() -> Option<i32> {
102+
Option::Some(15)
103+
}
104+
105+
fn main() {
106+
// let _: Option<i32> = try { 15i32 };
107+
108+
while let Option::Some(15) = foo() {}
109+
}

0 commit comments

Comments
 (0)