aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorArthur Cohen <arthur.cohen@embecosm.com>2025-01-22 17:20:37 +0000
committerArthur Cohen <arthur.cohen@embecosm.com>2025-03-24 13:07:12 +0100
commit4d6a08b4766bf8d97bc0c3d116e3f7fa43b18fef (patch)
tree846e06750eb80f991da3f06a9c0d416d1fca3f9e /gcc
parentf60b2106af745bebd0532fba361761b58a2e1acb (diff)
downloadgcc-4d6a08b4766bf8d97bc0c3d116e3f7fa43b18fef.zip
gcc-4d6a08b4766bf8d97bc0c3d116e3f7fa43b18fef.tar.gz
gcc-4d6a08b4766bf8d97bc0c3d116e3f7fa43b18fef.tar.bz2
gccrs: 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.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/rust/Make-lang.in1
-rw-r--r--gcc/rust/ast/rust-desugar-question-mark.cc167
-rw-r--r--gcc/rust/ast/rust-desugar-question-mark.h79
-rw-r--r--gcc/testsuite/rust/compile/try-expr1.rs84
4 files changed, 331 insertions, 0 deletions
diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index 3e44b12..749c112 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -239,6 +239,7 @@ GRS_OBJS = \
rust/rust-lang-item.o \
rust/rust-collect-lang-items.o \
rust/rust-desugar-for-loops.o \
+ rust/rust-desugar-question-mark.o \
$(END)
# removed object files from here
diff --git a/gcc/rust/ast/rust-desugar-question-mark.cc b/gcc/rust/ast/rust-desugar-question-mark.cc
new file mode 100644
index 0000000..4d2933b
--- /dev/null
+++ b/gcc/rust/ast/rust-desugar-question-mark.cc
@@ -0,0 +1,167 @@
+// 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-question-mark.h"
+#include "rust-ast-builder.h"
+#include "rust-ast-visitor.h"
+
+namespace Rust {
+namespace AST {
+
+DesugarQuestionMark::DesugarQuestionMark () {}
+
+void
+DesugarQuestionMark::go (AST::Crate &crate)
+{
+ DesugarQuestionMark::visit (crate);
+}
+
+void
+DesugarQuestionMark::visit (ExprStmt &stmt)
+{
+ if (stmt.get_expr ().get_expr_kind () == Expr::Kind::ErrorPropagation)
+ desugar_and_replace (stmt.get_expr_ptr ());
+
+ DefaultASTVisitor::visit (stmt);
+}
+
+void
+DesugarQuestionMark::visit (CallExpr &call)
+{
+ if (call.get_function_expr ().get_expr_kind ()
+ == Expr::Kind::ErrorPropagation)
+ desugar_and_replace (call.get_function_expr_ptr ());
+
+ for (auto &arg : call.get_params ())
+ if (arg->get_expr_kind () == Expr::Kind::ErrorPropagation)
+ desugar_and_replace (arg);
+
+ DefaultASTVisitor::visit (call);
+}
+
+void
+DesugarQuestionMark::visit (LetStmt &stmt)
+{
+ if (stmt.has_init_expr ()
+ && stmt.get_init_expr ().get_expr_kind () == Expr::Kind::ErrorPropagation)
+ desugar_and_replace (stmt.get_init_expr_ptr ());
+
+ DefaultASTVisitor::visit (stmt);
+}
+
+MatchArm
+make_match_arm (std::unique_ptr<Pattern> &&pattern)
+{
+ auto loc = pattern->get_locus ();
+
+ auto patterns = std::vector<std::unique_ptr<Pattern>> ();
+ patterns.emplace_back (std::move (pattern));
+
+ return MatchArm (std::move (patterns), loc);
+}
+
+MatchCase
+ok_case (Builder &builder)
+{
+ auto val = builder.identifier_pattern ("val");
+
+ auto patterns = std::vector<std::unique_ptr<Pattern>> ();
+ patterns.emplace_back (std::move (val));
+
+ auto pattern_item = std::unique_ptr<TupleStructItems> (
+ new TupleStructItemsNoRange (std::move (patterns)));
+ auto pattern = std::unique_ptr<Pattern> (new TupleStructPattern (
+ builder.path_in_expression (LangItem::Kind::RESULT_OK),
+ std::move (pattern_item)));
+
+ auto arm = make_match_arm (std::move (pattern));
+
+ auto ret_val = builder.identifier ("val");
+
+ return MatchCase (std::move (arm), std::move (ret_val));
+}
+
+MatchCase
+err_case (Builder &builder)
+{
+ auto val = builder.identifier_pattern ("err");
+
+ auto patterns = std::vector<std::unique_ptr<Pattern>> ();
+ patterns.emplace_back (std::move (val));
+
+ auto pattern_item = std::unique_ptr<TupleStructItems> (
+ new TupleStructItemsNoRange (std::move (patterns)));
+ auto pattern = std::unique_ptr<Pattern> (new TupleStructPattern (
+ builder.path_in_expression (LangItem::Kind::RESULT_ERR),
+ std::move (pattern_item)));
+
+ auto arm = make_match_arm (std::move (pattern));
+
+ auto try_from_err = std::make_unique<PathInExpression> (
+ builder.path_in_expression (LangItem::Kind::TRY_FROM_ERROR));
+ auto from_from = std::make_unique<PathInExpression> (
+ builder.path_in_expression (LangItem::Kind::FROM_FROM));
+
+ auto early_return = builder.return_expr (
+ builder.call (std::move (try_from_err),
+ builder.call (std::move (from_from),
+ builder.identifier ("err"))));
+
+ return MatchCase (std::move (arm), std::move (early_return));
+}
+
+std::unique_ptr<Expr>
+DesugarQuestionMark::desugar (ErrorPropagationExpr &expr)
+{
+ auto builder = Builder (expr.get_locus ());
+
+ // Try::into_result(<expr>)
+ auto try_into = std::make_unique<PathInExpression> (
+ builder.path_in_expression (LangItem::Kind::TRY_INTO_RESULT));
+ auto call = builder.call (std::move (try_into),
+ expr.get_propagating_expr ().clone_expr ());
+
+ // Ok(val) => val,
+ auto ok_match_case = ok_case (builder);
+ // Err(err) => return Try::from_error(From::from(err)),
+ auto err_match_case = err_case (builder);
+
+ auto cases = std::vector<MatchCase> ();
+ cases.emplace_back (ok_match_case);
+ cases.emplace_back (err_match_case);
+
+ // match <call> {
+ // <ok_arm>
+ // <err_arm>
+ // }
+ return std::unique_ptr<MatchExpr> (new MatchExpr (std::move (call),
+ std::move (cases), {}, {},
+ expr.get_locus ()));
+}
+
+void
+DesugarQuestionMark::desugar_and_replace (std::unique_ptr<Expr> &ptr)
+{
+ auto original = static_cast<ErrorPropagationExpr &> (*ptr);
+ auto desugared = desugar (original);
+
+ ptr = std::move (desugared);
+}
+
+} // namespace AST
+} // namespace Rust
diff --git a/gcc/rust/ast/rust-desugar-question-mark.h b/gcc/rust/ast/rust-desugar-question-mark.h
new file mode 100644
index 0000000..e4c513f
--- /dev/null
+++ b/gcc/rust/ast/rust-desugar-question-mark.h
@@ -0,0 +1,79 @@
+// 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_QUESTION_MARK
+#define RUST_DESUGAR_QUESTION_MARK
+
+#include "rust-ast-visitor.h"
+#include "rust-expr.h"
+#include "rust-stmt.h"
+
+namespace Rust {
+namespace AST {
+
+// NOTE: One more complexity compare to desugaring for-loops is that we need to
+// desugar every possible expression... should we do that during lowering
+// instead? but would it get resolved and expanded etc? Not sure...
+
+// The goal of this desugar is to go from this:
+//
+// ```
+// <expr>?
+// ```
+//
+// to this:
+//
+// ```
+// match Try::into_result(<expr>) {
+// Ok(val) => val,
+// Err(err) => return Try::from_err(From::from(err))
+// }
+// ```
+//
+// We use lang items for almost everything, so the actual desugared code looks
+// more like this:
+//
+// ```
+// match #[lang = "into_result"](<expr>) {
+// #[lang = "Ok"](val) => val,
+// #[lang = "Err"](err) => {
+// return #[lang = "from_error"](#[lang ="from"](err))
+// }
+// }
+// ```
+class DesugarQuestionMark : public DefaultASTVisitor
+{
+ using DefaultASTVisitor::visit;
+
+public:
+ DesugarQuestionMark ();
+ void go (AST::Crate &);
+
+private:
+ void desugar_and_replace (std::unique_ptr<Expr> &ptr);
+ std::unique_ptr<Expr> desugar (ErrorPropagationExpr &);
+
+ void visit (AST::ExprStmt &) override;
+ void visit (AST::CallExpr &) override;
+ void visit (AST::LetStmt &) override;
+};
+
+} // namespace AST
+} // namespace Rust
+
+#endif // ! RUST_DESUGAR_QUESTION_MARK
diff --git a/gcc/testsuite/rust/compile/try-expr1.rs b/gcc/testsuite/rust/compile/try-expr1.rs
new file mode 100644
index 0000000..f1a7865
--- /dev/null
+++ b/gcc/testsuite/rust/compile/try-expr1.rs
@@ -0,0 +1,84 @@
+// { dg-additional-options "-frust-compile-until=typecheck" }
+
+#[lang = "sized"]
+trait Sized {}
+
+enum Result {
+ #[lang = "Ok"]
+ Ok(i32),
+ #[lang = "Err"]
+ Err(i32)
+}
+
+pub trait From<T>: Sized {
+ /// Performs the conversion.
+ #[lang = "from"]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn from(_: T) -> Self;
+}
+
+impl<T> From<T> for T {
+ fn from(t: T) -> Self { t }
+}
+
+#[lang = "try"]
+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;
+
+ /// 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: i32) -> 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: i32) -> Self;
+}
+
+impl Try for Result {
+ // type Ok = i32;
+ // type Error = i32;
+
+ fn into_result(self) -> Result {
+ self
+ }
+
+ fn from_ok(v: i32) -> Self {
+ Result::Ok(v)
+ }
+
+ fn from_error(v: i32) -> Self {
+ Result::Err(v)
+ }
+}
+
+fn bar() -> Result {
+ Result::Ok(15)
+}
+
+fn foo() -> Result {
+ let a = bar()?;
+
+ Result::Ok(a)
+}