From 499a70699bcaff13998fa8eb6136c15dfa0b7f41 Mon Sep 17 00:00:00 2001 From: Arthur Cohen Date: Tue, 22 Jul 2025 13:30:11 +0200 Subject: [PATCH] gccrs: desugar: Handle try-blocks gcc/rust/ChangeLog: * Make-lang.in: Compile it. * ast/rust-expression-yeast.cc (ExpressionYeast::dispatch): Dispatch to try-block desugar. * ast/rust-desugar-try-block.cc: New file. * ast/rust-desugar-try-block.h: New file. gcc/testsuite/ChangeLog: * rust/compile/try_block1.rs: New test. --- gcc/rust/Make-lang.in | 2 +- gcc/rust/ast/rust-desugar-try-block.cc | 60 ++++++++++++++++ gcc/rust/ast/rust-desugar-try-block.h | 42 +++++++++++ gcc/rust/ast/rust-expression-yeast.cc | 4 ++ gcc/testsuite/rust/compile/try_block1.rs | 89 ++++++++++++++++++++++++ 5 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 gcc/rust/ast/rust-desugar-try-block.cc create mode 100644 gcc/rust/ast/rust-desugar-try-block.h create mode 100644 gcc/testsuite/rust/compile/try_block1.rs diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index 506435d2003..12c9a427143 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -246,7 +246,7 @@ GRS_OBJS = \ rust/rust-desugar-for-loops.o \ rust/rust-desugar-question-mark.o \ rust/rust-desugar-apit.o \ - # rust/rust-desugar-try-block.o \ + rust/rust-desugar-try-block.o \ $(END) # removed object files from here diff --git a/gcc/rust/ast/rust-desugar-try-block.cc b/gcc/rust/ast/rust-desugar-try-block.cc new file mode 100644 index 00000000000..cd03350e98b --- /dev/null +++ b/gcc/rust/ast/rust-desugar-try-block.cc @@ -0,0 +1,60 @@ +// 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 +// . + +#include "rust-desugar-try-block.h" +#include "rust-ast-builder.h" +#include "rust-expr.h" + +namespace Rust { +namespace AST { + +DesugarTryBlock::DesugarTryBlock () {} + +void +DesugarTryBlock::go (std::unique_ptr &ptr) +{ + auto original = static_cast (*ptr); + auto desugared = DesugarTryBlock ().desugar (original); + + ptr = std::move (desugared); +} + +std::unique_ptr +DesugarTryBlock::desugar (TryExpr &expr) +{ + auto builder = Builder (expr.get_locus ()); + auto &block = expr.get_block_expr (); + + if (block.has_statements ()) + rust_sorry_at (expr.get_locus (), + "cannot desugar try-blocks with statements"); + + auto tail_expr = builder.tuple (); + + if (block.has_tail_expr ()) + tail_expr = block.get_tail_expr ().clone_expr (); + + // Wrap in Try::from_ok call + auto from_ok = builder.path_in_expression (LangItem::Kind::TRY_FROM_OK); + auto call = builder.call (ptrify (from_ok), std::move (tail_expr)); + + return builder.block (std::move (call)); +} + +} // namespace AST +} // namespace Rust diff --git a/gcc/rust/ast/rust-desugar-try-block.h b/gcc/rust/ast/rust-desugar-try-block.h new file mode 100644 index 00000000000..bfd046307af --- /dev/null +++ b/gcc/rust/ast/rust-desugar-try-block.h @@ -0,0 +1,42 @@ +// 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 +// . + +#ifndef RUST_DESUGAR_TRY_BLOCK +#define RUST_DESUGAR_TRY_BLOCK + +#include "rust-expr.h" + +namespace Rust { +namespace AST { + +// FIXME: Add documentation +class DesugarTryBlock +{ +public: + static void go (std::unique_ptr &ptr); + +private: + DesugarTryBlock (); + + std::unique_ptr desugar (TryExpr &); +}; + +} // namespace AST +} // namespace Rust + +#endif // ! RUST_DESUGAR_TRY_BLOCK diff --git a/gcc/rust/ast/rust-expression-yeast.cc b/gcc/rust/ast/rust-expression-yeast.cc index 1089b71034c..6c0b3d3cced 100644 --- a/gcc/rust/ast/rust-expression-yeast.cc +++ b/gcc/rust/ast/rust-expression-yeast.cc @@ -19,6 +19,7 @@ #include "rust-expression-yeast.h" #include "rust-ast-visitor.h" #include "rust-desugar-question-mark.h" +#include "rust-desugar-try-block.h" #include "rust-ast-full.h" namespace Rust { @@ -39,6 +40,9 @@ ExpressionYeast::dispatch (std::unique_ptr &expr) case Expr::Kind::ErrorPropagation: DesugarQuestionMark::go (expr); break; + case Expr::Kind::Try: + DesugarTryBlock::go (expr); + break; default: break; diff --git a/gcc/testsuite/rust/compile/try_block1.rs b/gcc/testsuite/rust/compile/try_block1.rs new file mode 100644 index 00000000000..7ae053683e9 --- /dev/null +++ b/gcc/testsuite/rust/compile/try_block1.rs @@ -0,0 +1,89 @@ +// { dg-additional-options "-frust-edition=2018" } + +#[lang = "sized"] +trait Sized {} + +enum Result { + 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; + + /// 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 { + /// No value + None, + /// Some value `T` + Some(T), +} + +impl Option { + pub fn ok_or(self, err: E) -> Result { + match self { + Some(ok) => Result::Ok(ok), + None => Result::Err(err) + } + } +} + +use Option::*; + +#[unstable(feature = "try_trait", issue = "42327")] +impl Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } + + #[inline] + fn from_ok(v: T) -> Self { + Some(v) + } + + #[inline] + fn from_error(_: NoneError) -> Self { + None + } +} + +fn main() { + let _: Option = try { 15i32 }; +} -- 2.47.2