diff options
author | Arthur Cohen <arthur.cohen@embecosm.com> | 2025-01-22 17:20:37 +0000 |
---|---|---|
committer | Arthur Cohen <arthur.cohen@embecosm.com> | 2025-03-24 13:07:12 +0100 |
commit | 4d6a08b4766bf8d97bc0c3d116e3f7fa43b18fef (patch) | |
tree | 846e06750eb80f991da3f06a9c0d416d1fca3f9e /gcc | |
parent | f60b2106af745bebd0532fba361761b58a2e1acb (diff) | |
download | gcc-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.in | 1 | ||||
-rw-r--r-- | gcc/rust/ast/rust-desugar-question-mark.cc | 167 | ||||
-rw-r--r-- | gcc/rust/ast/rust-desugar-question-mark.h | 79 | ||||
-rw-r--r-- | gcc/testsuite/rust/compile/try-expr1.rs | 84 |
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) +} |