(root)/
gcc-13.2.0/
gcc/
rust/
util/
rust-optional.h
       1  // Copyright (C) 2020-2023 Free Software Foundation, Inc.
       2  
       3  // This file is part of GCC.
       4  
       5  // GCC is free software; you can redistribute it and/or modify it under
       6  // the terms of the GNU General Public License as published by the Free
       7  // Software Foundation; either version 3, or (at your option) any later
       8  // version.
       9  
      10  // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      11  // WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12  // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      13  // for more details.
      14  
      15  // You should have received a copy of the GNU General Public License
      16  // along with GCC; see the file COPYING3.  If not see
      17  // <http://www.gnu.org/licenses/>.
      18  
      19  #ifndef RUST_OPTIONAL_H
      20  #define RUST_OPTIONAL_H
      21  
      22  #include "config.h"
      23  #include "rust-system.h"
      24  
      25  #include "selftest.h"
      26  
      27  namespace Rust {
      28  
      29  /**
      30   * Tagged union to try and simulate a sum type. This is safer and more ergonomic
      31   * than one of the two alternatives we're currently using in the compiler:
      32   *
      33   * 1. Storing a raw pointer, which can be `nullptr` or valid
      34   *
      35   * This is wildly unsafe, and usable in conjunction with local references, stack
      36   * variables, or pointers managed elsewhere, which can cause crashes, hard to
      37   * debug issues or undefined behavior. Likewise, if you do not check for the
      38   * pointer's validity, this will cause a crash.
      39   *
      40   * 2. Storing an extra boolean alongside the object
      41   *
      42   * This causes implementors to use a "dummy object": Either an empty version or
      43   * an error version. But what happens if what you really wanted to store was
      44   * the empty or error version? You can also easily incorporate logic bugs if you
      45   * forget to check for the associated boolean.
      46   *
      47   * The `Optional<T>` type has the same "ergonomic" cost: You need to check
      48   * whether your option is valid or not. However, the main advantage is that it
      49   * is more restrictive: You can only acess the member it contains "safely".
      50   * It is similar to storing a value + an associated boolean, but has the
      51   * advantage of making up only one member in your class.
      52   * You also benefit from some helper methods such as `map()`.
      53   *
      54   * You also get helper functions and operator overloading to "seamlessly"
      55   * replace raw pointer alternatives.
      56   *
      57   * ```c++
      58   * MyType *raw_pointer = something_that_can_fail();
      59   * if (raw_pointer)
      60   *     raw_pointer->method();
      61   *
      62   * // or
      63   *
      64   * Optional<MyType> opt = something_that_can_fail2();
      65   * if (opt)
      66   *     opt->method();
      67   *
      68   * // equivalent to
      69   *
      70   * if (opt.is_some())
      71   *     opt.get().method();
      72   * ```
      73   */
      74  template <typename T> class Optional
      75  {
      76  private:
      77    struct Empty
      78    {
      79    };
      80  
      81    enum Kind
      82    {
      83      Some,
      84      None
      85    } kind;
      86  
      87    union Content
      88    {
      89      Empty empty;
      90      T value;
      91  
      92      Content () = default;
      93    } content;
      94  
      95    Optional<T> (Kind kind, Content content) : kind (kind), content (content) {}
      96  
      97  public:
      98    Optional (const Optional &other) = default;
      99    Optional &operator= (const Optional &other) = default;
     100    Optional (Optional &&other) = default;
     101  
     102    static Optional<T> some (T value)
     103    {
     104      Content content;
     105      content.value = value;
     106  
     107      return Optional (Kind::Some, content);
     108    }
     109  
     110    static Optional<T> none ()
     111    {
     112      Content content;
     113      content.empty = Empty ();
     114  
     115      return Optional (Kind::None, content);
     116    }
     117  
     118    bool is_some () const { return kind == Kind::Some; }
     119    bool is_none () const { return !is_some (); }
     120  
     121    /**
     122     * Enable boolean-like comparisons.
     123     */
     124    operator bool () { return is_some (); }
     125  
     126    /**
     127     * Enables dereferencing to access the contained value
     128     */
     129    T &operator* () { return get (); }
     130    const T &operator* () const { return get (); }
     131    T *operator-> () { return &get (); }
     132    const T *operator-> () const { return &get (); }
     133  
     134    const T &get () const
     135    {
     136      rust_assert (is_some ());
     137  
     138      return content.value;
     139    }
     140  
     141    T &get ()
     142    {
     143      rust_assert (is_some ());
     144  
     145      return content.value;
     146    }
     147  
     148    T take ()
     149    {
     150      rust_assert (is_some ());
     151  
     152      auto to_return = std::move (content.value);
     153  
     154      content.empty = Empty ();
     155      kind = Kind::None;
     156  
     157      return to_return;
     158    }
     159  
     160    template <typename U> Optional<U> map (std::function<U (T)> functor)
     161    {
     162      if (is_none ())
     163        return Optional::none ();
     164  
     165      auto value = functor (take ());
     166  
     167      return Optional::some (value);
     168    }
     169  };
     170  
     171  template <typename T> class Optional<T &>
     172  {
     173  private:
     174    struct Empty
     175    {
     176    };
     177  
     178    enum Kind
     179    {
     180      Some,
     181      None
     182    } kind;
     183  
     184    union Content
     185    {
     186      Empty empty;
     187      T *value;
     188  
     189      Content () = default;
     190    } content;
     191  
     192    Optional<T &> (Kind kind, Content content) : kind (kind), content (content) {}
     193  
     194  public:
     195    Optional (const Optional &other) = default;
     196    Optional (Optional &&other) = default;
     197    Optional &operator= (Optional &&other) = default;
     198  
     199    static Optional<T &> some (T &value)
     200    {
     201      Content content;
     202      content.value = &value;
     203  
     204      return Optional (Kind::Some, content);
     205    }
     206  
     207    static Optional<T &> none ()
     208    {
     209      Content content;
     210      content.empty = Empty ();
     211  
     212      return Optional (Kind::None, content);
     213    }
     214  
     215    bool is_some () const { return kind == Kind::Some; }
     216    bool is_none () const { return !is_some (); }
     217  
     218    // FIXME: Can we factor this in a single class?
     219  
     220    /**
     221     * Enable boolean-like comparisons.
     222     */
     223    operator bool () { return is_some (); }
     224  
     225    /**
     226     * Enables dereferencing to access the contained value
     227     */
     228    T &operator* () { return get (); }
     229    const T &operator* () const { return get (); }
     230    T *operator-> () { return &get (); }
     231    const T *operator-> () const { return &get (); }
     232  
     233    const T &get () const
     234    {
     235      rust_assert (is_some ());
     236  
     237      return *content.value;
     238    }
     239  
     240    T &get ()
     241    {
     242      rust_assert (is_some ());
     243  
     244      return *content.value;
     245    }
     246  
     247    T &take ()
     248    {
     249      rust_assert (is_some ());
     250  
     251      auto to_return = std::move (content.value);
     252  
     253      content.empty = Empty ();
     254      kind = Kind::None;
     255  
     256      return *to_return;
     257    }
     258  
     259    template <typename U> Optional<U &> map (std::function<U &(T &)> functor)
     260    {
     261      if (is_none ())
     262        return Optional::none ();
     263  
     264      auto value = functor (take ());
     265  
     266      return Optional::some (value);
     267    }
     268  };
     269  
     270  } // namespace Rust
     271  
     272  #ifdef CHECKING_P
     273  
     274  void
     275  rust_optional_test ();
     276  
     277  #endif // !CHECKING_P
     278  
     279  #endif // !RUST_OPTIONAL_H