Skip to content

Commit 950a193

Browse files
committed
ast: Add base for desugaring try expressions
gcc/rust/ChangeLog: * Make-lang.in: Compile it. * ast/rust-desugar-question-mark.cc: New file. * ast/rust-desugar-question-mark.h: New file. gcc/testsuite/ChangeLog: * rust/compile/try-expr1.rs: New test.
1 parent 3ebd4c9 commit 950a193

4 files changed

Lines changed: 331 additions & 0 deletions

File tree

gcc/rust/Make-lang.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ GRS_OBJS = \
239239
rust/rust-lang-item.o \
240240
rust/rust-collect-lang-items.o \
241241
rust/rust-desugar-for-loops.o \
242+
rust/rust-desugar-question-mark.o \
242243
$(END)
243244
# removed object files from here
244245

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
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-question-mark.h"
20+
#include "rust-ast-builder.h"
21+
#include "rust-ast-visitor.h"
22+
23+
namespace Rust {
24+
namespace AST {
25+
26+
DesugarQuestionMark::DesugarQuestionMark () {}
27+
28+
void
29+
DesugarQuestionMark::go (AST::Crate &crate)
30+
{
31+
DesugarQuestionMark::visit (crate);
32+
}
33+
34+
void
35+
DesugarQuestionMark::visit (ExprStmt &stmt)
36+
{
37+
if (stmt.get_expr ().get_expr_kind () == Expr::Kind::ErrorPropagation)
38+
desugar_and_replace (stmt.get_expr_ptr ());
39+
40+
DefaultASTVisitor::visit (stmt);
41+
}
42+
43+
void
44+
DesugarQuestionMark::visit (CallExpr &call)
45+
{
46+
if (call.get_function_expr ().get_expr_kind ()
47+
== Expr::Kind::ErrorPropagation)
48+
desugar_and_replace (call.get_function_expr_ptr ());
49+
50+
for (auto &arg : call.get_params ())
51+
if (arg->get_expr_kind () == Expr::Kind::ErrorPropagation)
52+
desugar_and_replace (arg);
53+
54+
DefaultASTVisitor::visit (call);
55+
}
56+
57+
void
58+
DesugarQuestionMark::visit (LetStmt &stmt)
59+
{
60+
if (stmt.has_init_expr ()
61+
&& stmt.get_init_expr ().get_expr_kind () == Expr::Kind::ErrorPropagation)
62+
desugar_and_replace (stmt.get_init_expr_ptr ());
63+
64+
DefaultASTVisitor::visit (stmt);
65+
}
66+
67+
MatchArm
68+
make_match_arm (std::unique_ptr<Pattern> &&pattern)
69+
{
70+
auto loc = pattern->get_locus ();
71+
72+
auto patterns = std::vector<std::unique_ptr<Pattern>> ();
73+
patterns.emplace_back (std::move (pattern));
74+
75+
return MatchArm (std::move (patterns), loc);
76+
}
77+
78+
MatchCase
79+
ok_case (Builder &builder)
80+
{
81+
auto val = builder.identifier_pattern ("val");
82+
83+
auto patterns = std::vector<std::unique_ptr<Pattern>> ();
84+
patterns.emplace_back (std::move (val));
85+
86+
auto pattern_item = std::unique_ptr<TupleStructItems> (
87+
new TupleStructItemsNoRange (std::move (patterns)));
88+
auto pattern = std::unique_ptr<Pattern> (new TupleStructPattern (
89+
builder.path_in_expression (LangItem::Kind::RESULT_OK),
90+
std::move (pattern_item)));
91+
92+
auto arm = make_match_arm (std::move (pattern));
93+
94+
auto ret_val = builder.identifier ("val");
95+
96+
return MatchCase (std::move (arm), std::move (ret_val));
97+
}
98+
99+
MatchCase
100+
err_case (Builder &builder)
101+
{
102+
auto val = builder.identifier_pattern ("err");
103+
104+
auto patterns = std::vector<std::unique_ptr<Pattern>> ();
105+
patterns.emplace_back (std::move (val));
106+
107+
auto pattern_item = std::unique_ptr<TupleStructItems> (
108+
new TupleStructItemsNoRange (std::move (patterns)));
109+
auto pattern = std::unique_ptr<Pattern> (new TupleStructPattern (
110+
builder.path_in_expression (LangItem::Kind::RESULT_ERR),
111+
std::move (pattern_item)));
112+
113+
auto arm = make_match_arm (std::move (pattern));
114+
115+
auto try_from_err = std::make_unique<PathInExpression> (
116+
builder.path_in_expression (LangItem::Kind::TRY_FROM_ERROR));
117+
auto from_from = std::make_unique<PathInExpression> (
118+
builder.path_in_expression (LangItem::Kind::FROM_FROM));
119+
120+
auto early_return = builder.return_expr (
121+
builder.call (std::move (try_from_err),
122+
builder.call (std::move (from_from),
123+
builder.identifier ("err"))));
124+
125+
return MatchCase (std::move (arm), std::move (early_return));
126+
}
127+
128+
std::unique_ptr<Expr>
129+
DesugarQuestionMark::desugar (ErrorPropagationExpr &expr)
130+
{
131+
auto builder = Builder (expr.get_locus ());
132+
133+
// Try::into_result(<expr>)
134+
auto try_into = std::make_unique<PathInExpression> (
135+
builder.path_in_expression (LangItem::Kind::TRY_INTO_RESULT));
136+
auto call = builder.call (std::move (try_into),
137+
expr.get_propagating_expr ().clone_expr ());
138+
139+
// Ok(val) => val,
140+
auto ok_match_case = ok_case (builder);
141+
// Err(err) => return Try::from_error(From::from(err)),
142+
auto err_match_case = err_case (builder);
143+
144+
auto cases = std::vector<MatchCase> ();
145+
cases.emplace_back (ok_match_case);
146+
cases.emplace_back (err_match_case);
147+
148+
// match <call> {
149+
// <ok_arm>
150+
// <err_arm>
151+
// }
152+
return std::unique_ptr<MatchExpr> (new MatchExpr (std::move (call),
153+
std::move (cases), {}, {},
154+
expr.get_locus ()));
155+
}
156+
157+
void
158+
DesugarQuestionMark::desugar_and_replace (std::unique_ptr<Expr> &ptr)
159+
{
160+
auto original = static_cast<ErrorPropagationExpr &> (*ptr);
161+
auto desugared = desugar (original);
162+
163+
ptr = std::move (desugared);
164+
}
165+
166+
} // namespace AST
167+
} // namespace Rust
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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_QUESTION_MARK
20+
#define RUST_DESUGAR_QUESTION_MARK
21+
22+
#include "rust-ast-visitor.h"
23+
#include "rust-expr.h"
24+
#include "rust-stmt.h"
25+
26+
namespace Rust {
27+
namespace AST {
28+
29+
// NOTE: One more complexity compare to desugaring for-loops is that we need to
30+
// desugar every possible expression... should we do that during lowering
31+
// instead? but would it get resolved and expanded etc? Not sure...
32+
33+
// The goal of this desugar is to go from this:
34+
//
35+
// ```
36+
// <expr>?
37+
// ```
38+
//
39+
// to this:
40+
//
41+
// ```
42+
// match Try::into_result(<expr>) {
43+
// Ok(val) => val,
44+
// Err(err) => return Try::from_err(From::from(err))
45+
// }
46+
// ```
47+
//
48+
// We use lang items for almost everything, so the actual desugared code looks
49+
// more like this:
50+
//
51+
// ```
52+
// match #[lang = "into_result"](<expr>) {
53+
// #[lang = "Ok"](val) => val,
54+
// #[lang = "Err"](err) => {
55+
// return #[lang = "from_error"](#[lang ="from"](err))
56+
// }
57+
// }
58+
// ```
59+
class DesugarQuestionMark : public DefaultASTVisitor
60+
{
61+
using DefaultASTVisitor::visit;
62+
63+
public:
64+
DesugarQuestionMark ();
65+
void go (AST::Crate &);
66+
67+
private:
68+
void desugar_and_replace (std::unique_ptr<Expr> &ptr);
69+
std::unique_ptr<Expr> desugar (ErrorPropagationExpr &);
70+
71+
void visit (AST::ExprStmt &) override;
72+
void visit (AST::CallExpr &) override;
73+
void visit (AST::LetStmt &) override;
74+
};
75+
76+
} // namespace AST
77+
} // namespace Rust
78+
79+
#endif // ! RUST_DESUGAR_QUESTION_MARK
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// { dg-additional-options "-frust-compile-until=typecheck" }
2+
3+
#[lang = "sized"]
4+
trait Sized {}
5+
6+
enum Result {
7+
#[lang = "Ok"]
8+
Ok(i32),
9+
#[lang = "Err"]
10+
Err(i32)
11+
}
12+
13+
pub trait From<T>: Sized {
14+
/// Performs the conversion.
15+
#[lang = "from"]
16+
#[stable(feature = "rust1", since = "1.0.0")]
17+
fn from(_: T) -> Self;
18+
}
19+
20+
impl<T> From<T> for T {
21+
fn from(t: T) -> Self { t }
22+
}
23+
24+
#[lang = "try"]
25+
pub trait Try {
26+
/// The type of this value when viewed as successful.
27+
// #[unstable(feature = "try_trait", issue = "42327")]
28+
// type Ok;
29+
/// The type of this value when viewed as failed.
30+
// #[unstable(feature = "try_trait", issue = "42327")]
31+
// type Error;
32+
33+
/// Applies the "?" operator. A return of `Ok(t)` means that the
34+
/// execution should continue normally, and the result of `?` is the
35+
/// value `t`. A return of `Err(e)` means that execution should branch
36+
/// to the innermost enclosing `catch`, or return from the function.
37+
///
38+
/// If an `Err(e)` result is returned, the value `e` will be "wrapped"
39+
/// in the return type of the enclosing scope (which must itself implement
40+
/// `Try`). Specifically, the value `X::from_error(From::from(e))`
41+
/// is returned, where `X` is the return type of the enclosing function.
42+
#[lang = "into_result"]
43+
#[unstable(feature = "try_trait", issue = "42327")]
44+
fn into_result(self) -> Result;
45+
46+
/// Wrap an error value to construct the composite result. For example,
47+
/// `Result::Err(x)` and `Result::from_error(x)` are equivalent.
48+
#[lang = "from_error"]
49+
#[unstable(feature = "try_trait", issue = "42327")]
50+
fn from_error(v: i32) -> Self;
51+
52+
/// Wrap an OK value to construct the composite result. For example,
53+
/// `Result::Ok(x)` and `Result::from_ok(x)` are equivalent.
54+
#[lang = "from_ok"]
55+
#[unstable(feature = "try_trait", issue = "42327")]
56+
fn from_ok(v: i32) -> Self;
57+
}
58+
59+
impl Try for Result {
60+
// type Ok = i32;
61+
// type Error = i32;
62+
63+
fn into_result(self) -> Result {
64+
self
65+
}
66+
67+
fn from_ok(v: i32) -> Self {
68+
Result::Ok(v)
69+
}
70+
71+
fn from_error(v: i32) -> Self {
72+
Result::Err(v)
73+
}
74+
}
75+
76+
fn bar() -> Result {
77+
Result::Ok(15)
78+
}
79+
80+
fn foo() -> Result {
81+
let a = bar()?;
82+
83+
Result::Ok(a)
84+
}

0 commit comments

Comments
 (0)