From patchwork Wed Aug 24 11:59:38 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: herron.philip@googlemail.com X-Patchwork-Id: 56990 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 0277539960C0 for ; Wed, 24 Aug 2022 12:04:30 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wm1-x333.google.com (mail-wm1-x333.google.com [IPv6:2a00:1450:4864:20::333]) by sourceware.org (Postfix) with ESMTPS id 974003895FF4; Wed, 24 Aug 2022 12:00:57 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 974003895FF4 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=googlemail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=googlemail.com Received: by mail-wm1-x333.google.com with SMTP id r83-20020a1c4456000000b003a5cb389944so781065wma.4; Wed, 24 Aug 2022 05:00:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20210112; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc; bh=KwRHVm4crOE15Z+PSJan3BiHSbt+LVB39MQHRTyUAB4=; b=V26VJnH/GXTw/YSP1h9f/4u4ehZREEVRjzUbU9ruN6U2MuEDSsjoNU/xMZLjHDJh/J i3i4VFv/lDkczAgzbx/6y+p0hcovqSBMi78/xA4LAJjJSezr3oKTCY9LBeH5OggoxpWO lmIhpM9LhSWJWCl9zPmAjhOSB9LtjcUX3hfC9Xb/Rt2bxBvt4D0W92puZP8UI6jIOp26 xyiP/UCX9VKgFvzjIwzJyoLdBD23HA7t6ty6kWov/Nk54ltuxLUWG4utxLcjbTEr2ZCc BPvOkbUQa/n1NHeJggbeXLN5Vi6y69qsq1tn/QQJfU/7VUtIcKrloYEb6rLM0Q62DpsU vTWA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc; bh=KwRHVm4crOE15Z+PSJan3BiHSbt+LVB39MQHRTyUAB4=; b=gDS2DzYcRPXxnlxoUZl5A6ttM1i7nmXscjPCp+QSr0vJuSF9vPSpU9q3ZtyBG8muxS 6ajjYb6wdNtFvnXMtYLx1ybUjkW73CRo6vbTeH1oGCoygPQDuC1ct5aYkecbL3xSA/bX 7p9S3QYQmnmznAlAxYjoUzxejXJlMbVtxgCq+4Hh6jFWrKE5jedDXze2DJOw7w3VCOL/ DwKCM3DVpFsD0jvPDcz60xPr2tavreU+bqP4ouO+f+jtX4+0Si5qKKp67ZORiV0rX/Qc PON7a2A0gRcx69rdz8HrUxXi9BZHM1z4owqulg8ZVI0ob8DlV7JVJJgYs0fw/8DqTZ19 7F/w== X-Gm-Message-State: ACgBeo3TCKegvwlQpNwGGGlnLbZ9PMKc8S9jOxbCm8TuOG4FjiWpm+H5 N0jBVBaJ6HkbcuhR1p+p3foaie7ytO4= X-Google-Smtp-Source: AA6agR7L8FnM8dySzuBAw02gEDARfcI1toiadiZjTO9p4d35dqlJWX7ZIYS6tABa1DwB87LR5rojFQ== X-Received: by 2002:a1c:7916:0:b0:3a6:3540:5b3c with SMTP id l22-20020a1c7916000000b003a635405b3cmr4924653wme.178.1661342456003; Wed, 24 Aug 2022 05:00:56 -0700 (PDT) Received: from localhost.localdomain ([86.14.124.218]) by smtp.gmail.com with ESMTPSA id cc19-20020a5d5c13000000b0022571d43d32sm1697676wrb.21.2022.08.24.05.00.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Aug 2022 05:00:55 -0700 (PDT) From: herron.philip@googlemail.com X-Google-Original-From: philip.herron@embecosm.com To: gcc-patches@gcc.gnu.org Subject: [PATCH Rust front-end v2 19/37] gccrs: Add implementation of Optional Date: Wed, 24 Aug 2022 12:59:38 +0100 Message-Id: <20220824115956.737931-20-philip.herron@embecosm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220824115956.737931-1-philip.herron@embecosm.com> References: <20220824115956.737931-1-philip.herron@embecosm.com> MIME-Version: 1.0 X-Spam-Status: No, score=-11.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: philip.herron@embecosm.com Cc: Arthur Cohen , gcc-rust@gcc.gnu.org Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" From: Arthur Cohen Add an Optional class to improve error handling --- gcc/rust/util/rust-optional-test.cc | 111 +++++++++++ gcc/rust/util/rust-optional.h | 278 ++++++++++++++++++++++++++++ 2 files changed, 389 insertions(+) create mode 100644 gcc/rust/util/rust-optional-test.cc create mode 100644 gcc/rust/util/rust-optional.h diff --git a/gcc/rust/util/rust-optional-test.cc b/gcc/rust/util/rust-optional-test.cc new file mode 100644 index 00000000000..9d5b4ba5735 --- /dev/null +++ b/gcc/rust/util/rust-optional-test.cc @@ -0,0 +1,111 @@ +// 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 +// . + +#include "rust-optional.h" + +#include "config.h" +#include "selftest.h" + +#if 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_reference () +{ + auto value = std::vector (); + value.emplace_back ("rust"); + value.emplace_back ("+"); + value.emplace_back ("gcc"); + value.emplace_back ("="); + value.emplace_back ("<3"); + + auto opt = Rust::Optional &>::some (value); + + ASSERT_EQ (opt->at (0), "rust"); + ASSERT_EQ (opt->at (2), "gcc"); +} + +#endif /* #if CHECKING_P */ + +void +rust_optional_test () +{ +#if CHECKING_P + rust_optional_create (); + rust_optional_operators (); + rust_optional_take (); + rust_optional_map (); + rust_optional_reference (); + +#endif /* #if CHECKING_P */ +} diff --git a/gcc/rust/util/rust-optional.h b/gcc/rust/util/rust-optional.h new file mode 100644 index 00000000000..56465400250 --- /dev/null +++ b/gcc/rust/util/rust-optional.h @@ -0,0 +1,278 @@ +// 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 &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; + + 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