// Copyright (C) 2020-2023 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 &operator= (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); } }; 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; Optional &operator= (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 (); } // FIXME: Can we factor this in a single class? /** * 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 void rust_optional_test (); #endif // !CHECKING_P #endif // !RUST_OPTIONAL_H