From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1643) id 792E338936D4; Wed, 8 Jun 2022 12:43:48 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 792E338936D4 Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Thomas Schwinge To: gcc-cvs@gcc.gnu.org Subject: [gcc/devel/rust/master] util: Add Optional wrapper class X-Act-Checkin: gcc X-Git-Author: Arthur Cohen X-Git-Refname: refs/heads/devel/rust/master X-Git-Oldrev: a6f5bc6054520f0dc8479215dc1204196ae7767f X-Git-Newrev: b088d47cdc1514d5f92801481cbb412d1e01aeeb Message-Id: <20220608124348.792E338936D4@sourceware.org> Date: Wed, 8 Jun 2022 12:43:48 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 08 Jun 2022 12:43:48 -0000 https://gcc.gnu.org/g:b088d47cdc1514d5f92801481cbb412d1e01aeeb commit b088d47cdc1514d5f92801481cbb412d1e01aeeb Author: Arthur Cohen Date: Thu May 5 15:18:08 2022 +0200 util: Add Optional wrapper class The class provides an easy to use alternative to raw pointers or member + bool pairs. It should be easy to extend and improve upon. Diff: --- gcc/rust/Make-lang.in | 3 +- gcc/rust/parse/rust-parse-impl.h | 2 +- gcc/rust/rust-lang.cc | 2 + gcc/rust/util/rust-make-unique.h | 6 +- gcc/rust/util/rust-optional.h | 241 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 250 insertions(+), 4 deletions(-) diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index ae385e0bec9..89ab0c8e1bd 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -289,7 +289,8 @@ RUST_INCLUDES = -I $(srcdir)/rust \ -I $(srcdir)/rust/util \ -I $(srcdir)/rust/typecheck \ -I $(srcdir)/rust/privacy \ - -I $(srcdir)/rust/lint + -I $(srcdir)/rust/lint \ + -I $(srcdir)/rust/util # add files that require cross-folder includes - currently rust-lang.o, rust-lex.o CFLAGS-rust/rust-lang.o += $(RUST_INCLUDES) diff --git a/gcc/rust/parse/rust-parse-impl.h b/gcc/rust/parse/rust-parse-impl.h index 23ab32c832e..f74c66125b8 100644 --- a/gcc/rust/parse/rust-parse-impl.h +++ b/gcc/rust/parse/rust-parse-impl.h @@ -22,7 +22,7 @@ along with GCC; see the file COPYING3. If not see #define INCLUDE_ALGORITHM #include "rust-diagnostics.h" -#include "util/rust-make-unique.h" +#include "rust-make-unique.h" namespace Rust { // Left binding powers of operations. diff --git a/gcc/rust/rust-lang.cc b/gcc/rust/rust-lang.cc index 73f9839ee3e..dd8c608789a 100644 --- a/gcc/rust/rust-lang.cc +++ b/gcc/rust/rust-lang.cc @@ -37,6 +37,7 @@ #include "rust-cfg-parser.h" #include "rust-privacy-ctx.h" #include "rust-ast-resolve-item.h" +#include "rust-optional.h" #include // note: header files must be in this order or else forward declarations don't @@ -461,6 +462,7 @@ run_rust_tests () rust_privacy_ctx_test (); rust_crate_name_validation_test (); rust_simple_path_resolve_test (); + rust_optional_test (); } } // namespace selftest diff --git a/gcc/rust/util/rust-make-unique.h b/gcc/rust/util/rust-make-unique.h index 77b203ee1c9..f33f9125f89 100644 --- a/gcc/rust/util/rust-make-unique.h +++ b/gcc/rust/util/rust-make-unique.h @@ -24,8 +24,10 @@ namespace Rust { template -std::unique_ptr make_unique(Ts &&...params) { - return std::unique_ptr(new T(std::forward(params)...)); +std::unique_ptr +make_unique (Ts &&... params) +{ + return std::unique_ptr (new T (std::forward (params)...)); } } // namespace Rust diff --git a/gcc/rust/util/rust-optional.h b/gcc/rust/util/rust-optional.h new file mode 100644 index 00000000000..c1b547a29d6 --- /dev/null +++ b/gcc/rust/util/rust-optional.h @@ -0,0 +1,241 @@ +// Copyright (C) 2020-2022 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_OPTIONAL_H +#define RUST_OPTIONAL_H + +#include "config.h" +#include "rust-system.h" + +#include "selftest.h" + +namespace Rust { + +/** + * Tagged union to try and simulate a sum type. This is safer and more ergonomic + * than one of the two alternatives we're currently using in the compiler: + * + * 1. Storing a raw pointer, which can be `nullptr` or valid + * + * This is wildly unsafe, and usable in conjunction with local references, stack + * variables, or pointers managed elsewhere, which can cause crashes, hard to + * debug issues or undefined behavior. Likewise, if you do not check for the + * pointer's validity, this will cause a crash. + * + * 2. Storing an extra boolean alongside the object + * + * This causes implementors to use a "dummy object": Either an empty version or + * an error version. But what happens if what you really wanted to store was + * the empty or error version? You can also easily incorporate logic bugs if you + * forget to check for the associated boolean. + * + * The `Optional` type has the same "ergonomic" cost: You need to check + * whether your option is valid or not. However, the main advantage is that it + * is more restrictive: You can only acess the member it contains "safely". + * It is similar to storing a value + an associated boolean, but has the + * advantage of making up only one member in your class. + * You also benefit from some helper methods such as `map()`. + * + * You also get helper functions and operator overloading to "seamlessly" + * replace raw pointer alternatives. + * + * ```c++ + * MyType *raw_pointer = something_that_can_fail(); + * if (raw_pointer) + * raw_pointer->method(); + * + * // or + * + * Optional opt = something_that_can_fail2(); + * if (opt) + * opt->method(); + * + * // equivalent to + * + * if (opt.is_some()) + * opt.get().method(); + * ``` + */ +template class Optional +{ +private: + struct Empty + { + }; + + enum Kind + { + Some, + None + } kind; + + union Content + { + Empty empty; + T value; + + Content () = default; + } content; + + Optional (Kind kind, Content content) : kind (kind), content (content) {} + +public: + Optional (const Optional &other) = default; + Optional (Optional &&other) = default; + + static Optional some (T value) + { + Content content; + content.value = value; + + return Optional (Kind::Some, content); + } + + static Optional none () + { + Content content; + content.empty = Empty (); + + return Optional (Kind::None, content); + } + + bool is_some () const { return kind == Kind::Some; } + bool is_none () const { return !is_some (); } + + /** + * Enable boolean-like comparisons. + */ + operator bool () { return is_some (); } + + /** + * Enables dereferencing to access the contained value + */ + T &operator* () { return get (); } + const T &operator* () const { return get (); } + T *operator-> () { return &get (); } + const T *operator-> () const { return &get (); } + + const T &get () const + { + rust_assert (is_some ()); + + return content.value; + } + + T &get () + { + rust_assert (is_some ()); + + return content.value; + } + + T take () + { + rust_assert (is_some ()); + + auto to_return = std::move (content.value); + + content.empty = Empty (); + kind = Kind::None; + + return to_return; + } + + template Optional map (std::function functor) + { + if (is_none ()) + return Optional::none (); + + auto value = functor (take ()); + + return Optional::some (value); + } +}; + +} // namespace Rust + +#ifdef CHECKING_P + +static void +rust_optional_create () +{ + auto opt = Rust::Optional::some (15); + + ASSERT_TRUE (opt.is_some ()); + ASSERT_EQ (opt.get (), 15); + + Rust::Optional const_opt = Rust::Optional::some (15); + const int &value = const_opt.get (); + + ASSERT_EQ (value, 15); +} + +static void +rust_optional_operators () +{ + auto opt = Rust::Optional::some (15); + + // as bool + ASSERT_TRUE (opt); + + // deref + ASSERT_EQ (*opt, 15); + + class Methodable + { + public: + int method () { return 15; } + }; + + auto m_opt = Rust::Optional::some (Methodable ()); + ASSERT_EQ (m_opt->method (), 15); +} + +static void +rust_optional_take () +{ + auto opt = Rust::Optional::some (15); + auto value = opt.take (); + + ASSERT_EQ (value, 15); + ASSERT_TRUE (opt.is_none ()); +} + +static void +rust_optional_map () +{ + auto opt = Rust::Optional::some (15); + auto twice = opt.map ([] (int value) { return value * 2; }); + + ASSERT_FALSE (opt); + ASSERT_TRUE (twice); + ASSERT_EQ (*twice, 30); +} + +static void +rust_optional_test () +{ + rust_optional_create (); + rust_optional_operators (); + rust_optional_take (); + rust_optional_map (); +} + +#endif // !CHECKING_P + +#endif // !RUST_OPTIONAL_H