From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-ed1-x52d.google.com (mail-ed1-x52d.google.com [IPv6:2a00:1450:4864:20::52d]) by sourceware.org (Postfix) with ESMTPS id 4316B3860C3B for ; Thu, 1 Aug 2024 14:59:44 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 4316B3860C3B Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=embecosm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=embecosm.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 4316B3860C3B Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::52d ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722524434; cv=none; b=N1RE5wxBWHVTq4WQds4EAXg69ah3ndstiZmVoQT9wzIgS5C9kN9fFoBXTl/6LmNrjMogiQN8A06Yrqz40MSOXAMpGWMApgXZmFxgoXi3hcPJmhlpxgzCrEv4H2nO/sO0iZYdwr9iQMEG9M5fTY3fq/CzoXUXW/to1//73d3VOA8= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722524434; c=relaxed/simple; bh=kBzlGLUMvOUfN/mWDgQT+1YnqGU3QZ42p+L067S1c8Y=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=h4uwlP5hfFoyYzL/CgW71Rr8K9bKW80i91dRl9eZBUmQweWiHJW1di5nOp4zUN7tni7pR08rFoClazDckc7gmHM2PtqsOgqZZaAd1p21QI4DuZDFiViJVSuHWQcBmL6p6+FEvb22crYVWGo7aUl6Ako7yPzNnKdrecvenWNMSj0= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-ed1-x52d.google.com with SMTP id 4fb4d7f45d1cf-5a2ffc34677so10147858a12.2 for ; Thu, 01 Aug 2024 07:59:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=embecosm.com; s=google; t=1722524382; x=1723129182; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=VEt8cvtrtH4lm1PsfOW8BL5xeioUZOwQVp3/Ics+DT0=; b=YLluIG5H7G2aAywmlhNUSqr+sEhYsgO/2o0Vt+A4scNCS3prvYvjPPSKByLhjo+rCk JmTMnT03SHaI/M5BD3yMXxZG9LIuBpVh4Z4rW06akyEo/2nV8WuPK9iBJQceZrZVy/gD D7E+Ue2a9RQlhkXTo5u/MI7HLQuztNpEVSPVUvmdz0DQPhPuCQNPmbIHKmtJr4x6ZEfF +0kltW8yFXVE6QcbNNgJnb3UzQRbOASxvQPazLjlydsrGsCpO2Ib6FweGfla+vDuTPUY Xj7OHwDfgCmi9AbUfM0SazIS2PKRRzTVaYydILnaWDnUK1j3/TmLZEsIhE0DpC9XVCc0 cOMA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1722524382; x=1723129182; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=VEt8cvtrtH4lm1PsfOW8BL5xeioUZOwQVp3/Ics+DT0=; b=XNu89DvDfUIbcf6fBZx34BlbC7o1i0JdFR7ogi7BJXOOE8f59ukCwLBGB/XXdYxclj vNgQUm6LwPIh8XO876EyMcq/MgSqe17ofNqd/uAEmy863pWL58SVqGpZBA96QibthNnt Pch0rbKyfsWjplHPXzBJfXukNWB0eBsPR8li5Fy5QW+cUit0RrNwZRj0Iw7PgQinrJqz Fqqr0TQaMUsKY/PFzTrHkEr4Mxl729Rg666XFhl9ObugkIyde0w385AVepFaY/5ZYv1w 4TrQzqmrD4JxnbmFBjpNicyOdAYmKf2lba3LmUAeuJcjP/L084ABoEiUMHq2rYhTPPCT eLdA== X-Gm-Message-State: AOJu0YySVby+zgaKmAiilyhmapr5kzG+8bBcMqwDhBLXICo2KLUUJL3Y FYUXQx0ipfJRlFQ3XQBhapyalGrRugWabZBwxr7wnB1neZdMCZsjWUYWngM7lG9KRXIB1bVcurP F16q7 X-Google-Smtp-Source: AGHT+IHB6xEFS0dvPvzaQ/G85Qk11eGM4EIc5y2EGqekJiO63DDmL6AtAb+0SQ+96k/ZAMib4poKUg== X-Received: by 2002:aa7:d454:0:b0:57c:a886:c402 with SMTP id 4fb4d7f45d1cf-5b7f3adb30amr450992a12.12.1722524382279; Thu, 01 Aug 2024 07:59:42 -0700 (PDT) Received: from platypus.lan ([2a04:cec2:9:dc84:3622:6733:ff49:ee91]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-5ac63590592sm10252456a12.25.2024.08.01.07.59.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 01 Aug 2024 07:59:41 -0700 (PDT) From: Arthur Cohen To: gcc-patches@gcc.gnu.org Cc: gcc-rust@gcc.gnu.org, Pierre-Emmanuel Patry Subject: [PATCH 078/125] gccrs: Add support for ambiguous use declarations Date: Thu, 1 Aug 2024 16:57:14 +0200 Message-ID: <20240801145809.366388-80-arthur.cohen@embecosm.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240801145809.366388-2-arthur.cohen@embecosm.com> References: <20240801145809.366388-2-arthur.cohen@embecosm.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-14.2 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: From: Pierre-Emmanuel Patry Glob use declarations may lead to ambiguous situation where two definitions with the same name are imported in a given scope. The compiler now handles shadowable items inserted after non shadowable items. An error is now thrown when multiple shadowable items are imported and used in the same rib. gcc/rust/ChangeLog: * resolve/rust-early-name-resolver-2.0.cc (Early::visit): Adapt resolved type to the new API. (Early::visit_attributes): Retrieve the node id from the definition. * resolve/rust-forever-stack.h: Change the return type of getter functions. Those functions now return a definition type instead of a node id. * resolve/rust-forever-stack.hxx: Change member function implementation in the forever stack to accomodate it's API changes. * resolve/rust-late-name-resolver-2.0.cc (Late::visit): Use internal node id. Emit an error when resolving multiple ambiguous values. * resolve/rust-rib.cc (Rib::Definition::Definition): Add a default constructor. (Rib::Definition::is_ambiguous): Add a new function to determine whether a function definition is ambiguous or not. (Rib::Definition::to_string): Add a member function to convert a given definition to a string. (Rib::insert): Add new rules for value insertion in a rib. Insertion order does not impact the result anymore: inserting a shadowable value after a non shadowable one does not trigger an error anymore. All shadowable values inserted in a rib are kepts until being replaced by a non shadowable one. (Rib::get): Return a definition instead of a node id. * resolve/rust-rib.h: Update function prototypes. * resolve/rust-toplevel-name-resolver-2.0.cc (TopLevel::handle_use_glob): Update return value container to match the new function's prototype. (TopLevel::handle_use_dec): Likewise. (flatten_glob): Likewise. Signed-off-by: Pierre-Emmanuel Patry --- .../resolve/rust-early-name-resolver-2.0.cc | 18 ++--- gcc/rust/resolve/rust-forever-stack.h | 10 +-- gcc/rust/resolve/rust-forever-stack.hxx | 39 ++++++----- .../resolve/rust-late-name-resolver-2.0.cc | 17 +++-- gcc/rust/resolve/rust-rib.cc | 69 +++++++++++++++---- gcc/rust/resolve/rust-rib.h | 19 ++++- .../rust-toplevel-name-resolver-2.0.cc | 41 ++++++----- 7 files changed, 142 insertions(+), 71 deletions(-) diff --git a/gcc/rust/resolve/rust-early-name-resolver-2.0.cc b/gcc/rust/resolve/rust-early-name-resolver-2.0.cc index 982c696d2af..af148b0c1c0 100644 --- a/gcc/rust/resolve/rust-early-name-resolver-2.0.cc +++ b/gcc/rust/resolve/rust-early-name-resolver-2.0.cc @@ -152,9 +152,11 @@ Early::visit (AST::MacroInvocation &invoc) // https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope - tl::optional definition = tl::nullopt; + tl::optional definition = tl::nullopt; if (path.get_segments ().size () == 1) - definition = textual_scope.get (path.get_final_segment ().as_string ()); + definition + = textual_scope.get (path.get_final_segment ().as_string ()) + .map ([] (NodeId id) { return Rib::Definition::NonShadowable (id); }); // we won't have changed `definition` from `nullopt` if there are more // than one segments in our path @@ -169,13 +171,13 @@ Early::visit (AST::MacroInvocation &invoc) return; } - insert_once (invoc, *definition); + insert_once (invoc, definition->get_node_id ()); // now do we need to keep mappings or something? or insert "uses" into our // ForeverStack? can we do that? are mappings simpler? auto mappings = Analysis::Mappings::get (); AST::MacroRulesDefinition *rules_def = nullptr; - if (!mappings->lookup_macro_def (definition.value (), &rules_def)) + if (!mappings->lookup_macro_def (definition->get_node_id (), &rules_def)) { // Macro definition not found, maybe it is not expanded yet. return; @@ -212,8 +214,8 @@ Early::visit_attributes (std::vector &attrs) continue; } - auto pm_def - = mappings->lookup_derive_proc_macro_def (definition.value ()); + auto pm_def = mappings->lookup_derive_proc_macro_def ( + definition->get_node_id ()); rust_assert (pm_def.has_value ()); @@ -234,8 +236,8 @@ Early::visit_attributes (std::vector &attrs) "could not resolve attribute macro invocation"); return; } - auto pm_def - = mappings->lookup_attribute_proc_macro_def (definition.value ()); + auto pm_def = mappings->lookup_attribute_proc_macro_def ( + definition->get_node_id ()); rust_assert (pm_def.has_value ()); diff --git a/gcc/rust/resolve/rust-forever-stack.h b/gcc/rust/resolve/rust-forever-stack.h index bba5875d435..3dab45e7e77 100644 --- a/gcc/rust/resolve/rust-forever-stack.h +++ b/gcc/rust/resolve/rust-forever-stack.h @@ -480,21 +480,21 @@ public: * @param name Name of the identifier to locate in this scope or an outermore * scope * - * @return a valid option with the NodeId if the identifier is present in the - * current map, an empty one otherwise. + * @return a valid option with the Definition if the identifier is present in + * the current map, an empty one otherwise. */ - tl::optional get (const Identifier &name); + tl::optional get (const Identifier &name); /** * Resolve a path to its definition in the current `ForeverStack` * * // TODO: Add documentation for `segments` * - * @return a valid option with the NodeId if the path is present in the + * @return a valid option with the Definition if the path is present in the * current map, an empty one otherwise. */ template - tl::optional resolve_path (const std::vector &segments); + tl::optional resolve_path (const std::vector &segments); // FIXME: Documentation tl::optional to_canonical_path (NodeId id); diff --git a/gcc/rust/resolve/rust-forever-stack.hxx b/gcc/rust/resolve/rust-forever-stack.hxx index 008adff4676..6b622b8aef1 100644 --- a/gcc/rust/resolve/rust-forever-stack.hxx +++ b/gcc/rust/resolve/rust-forever-stack.hxx @@ -90,11 +90,13 @@ ForeverStack::pop () rust_debug ("popping link"); for (const auto &kv : cursor ().rib.get_values ()) - rust_debug ("current_rib: k: %s, v: %d", kv.first.c_str (), kv.second); + rust_debug ("current_rib: k: %s, v: %s", kv.first.c_str (), + kv.second.to_string ().c_str ()); if (cursor ().parent.has_value ()) for (const auto &kv : cursor ().parent.value ().rib.get_values ()) - rust_debug ("new cursor: k: %s, v: %d", kv.first.c_str (), kv.second); + rust_debug ("new cursor: k: %s, v: %s", kv.first.c_str (), + kv.second.to_string ().c_str ()); update_cursor (cursor ().parent.value ()); } @@ -222,22 +224,22 @@ ForeverStack::update_cursor (Node &new_cursor) } template -tl::optional +tl::optional ForeverStack::get (const Identifier &name) { - tl::optional resolved_node = tl::nullopt; + tl::optional resolved_definition = tl::nullopt; // TODO: Can we improve the API? have `reverse_iter` return an optional? - reverse_iter ([&resolved_node, &name] (Node ¤t) { + reverse_iter ([&resolved_definition, &name] (Node ¤t) { auto candidate = current.rib.get (name.as_string ()); return candidate.map_or ( - [&resolved_node] (NodeId found) { + [&resolved_definition] (Rib::Definition found) { // for most namespaces, we do not need to care about various ribs - they // are available from all contexts if defined in the current scope, or // an outermore one. so if we do have a candidate, we can return it // directly and stop iterating - resolved_node = found; + resolved_definition = found; return KeepGoing::No; }, @@ -245,16 +247,16 @@ ForeverStack::get (const Identifier &name) KeepGoing::Yes); }); - return resolved_node; + return resolved_definition; } template <> -tl::optional inline ForeverStack::get ( +tl::optional inline ForeverStack::get ( const Identifier &name) { - tl::optional resolved_node = tl::nullopt; + tl::optional resolved_definition = tl::nullopt; - reverse_iter ([&resolved_node, &name] (Node ¤t) { + reverse_iter ([&resolved_definition, &name] (Node ¤t) { // looking up for labels cannot go through function ribs // TODO: What other ribs? if (current.rib.kind == Rib::Kind::Function) @@ -264,15 +266,15 @@ tl::optional inline ForeverStack::get ( // FIXME: Factor this in a function with the generic `get` return candidate.map_or ( - [&resolved_node] (NodeId found) { - resolved_node = found; + [&resolved_definition] (Rib::Definition found) { + resolved_definition = found; return KeepGoing::No; }, KeepGoing::Yes); }); - return resolved_node; + return resolved_definition; } /* Check if an iterator points to the last element */ @@ -444,7 +446,7 @@ ForeverStack::resolve_segments ( template template -tl::optional +tl::optional ForeverStack::resolve_path (const std::vector &segments) { // TODO: What to do if segments.empty() ? @@ -472,8 +474,9 @@ ForeverStack::dfs (ForeverStack::Node &starting_point, NodeId to_find) auto values = starting_point.rib.get_values (); for (auto &kv : values) - if (kv.second.id == to_find) - return {{starting_point, kv.first}}; + for (auto id : kv.second.ids) + if (id == to_find) + return {{starting_point, kv.first}}; for (auto &child : starting_point.children) { @@ -582,7 +585,7 @@ ForeverStack::stream_rib (std::stringstream &stream, const Rib &rib, stream << next << "rib: {\n"; for (const auto &kv : rib.get_values ()) - stream << next_next << kv.first << ": " << kv.second.id << "\n"; + stream << next_next << kv.first << ": " << kv.second.to_string () << "\n"; stream << next << "},\n"; } diff --git a/gcc/rust/resolve/rust-late-name-resolver-2.0.cc b/gcc/rust/resolve/rust-late-name-resolver-2.0.cc index d8bd9ac524f..5c8d976b417 100644 --- a/gcc/rust/resolve/rust-late-name-resolver-2.0.cc +++ b/gcc/rust/resolve/rust-late-name-resolver-2.0.cc @@ -159,7 +159,7 @@ Late::visit (AST::IdentifierExpr &expr) { // TODO: same thing as visit(PathInExpression) here? - tl::optional resolved = tl::nullopt; + tl::optional resolved = tl::nullopt; auto label = ctx.labels.get (expr.get_ident ()); auto value = ctx.values.get (expr.get_ident ()); @@ -179,7 +179,8 @@ Late::visit (AST::IdentifierExpr &expr) return; } - ctx.map_usage (Usage (expr.get_node_id ()), Definition (*resolved)); + ctx.map_usage (Usage (expr.get_node_id ()), + Definition (resolved->get_node_id ())); // in the old resolver, resolutions are kept in the resolver, not the mappings // :/ how do we deal with that? @@ -200,7 +201,14 @@ Late::visit (AST::PathInExpression &expr) if (!value.has_value ()) rust_unreachable (); // Should have been resolved earlier - ctx.map_usage (Usage (expr.get_node_id ()), Definition (*value)); + if (value->is_ambiguous ()) + { + rust_error_at (expr.get_locus (), ErrorCode::E0659, "%qs is ambiguous", + expr.as_string ().c_str ()); + return; + } + ctx.map_usage (Usage (expr.get_node_id ()), + Definition (value->get_node_id ())); } void @@ -213,7 +221,8 @@ Late::visit (AST::TypePath &type) auto resolved = ctx.types.get (type.get_segments ().back ()->as_string ()); - ctx.map_usage (Usage (type.get_node_id ()), Definition (*resolved)); + ctx.map_usage (Usage (type.get_node_id ()), + Definition (resolved->get_node_id ())); } } // namespace Resolver2_0 diff --git a/gcc/rust/resolve/rust-rib.cc b/gcc/rust/resolve/rust-rib.cc index dee3a09ad49..a73e2bd6f75 100644 --- a/gcc/rust/resolve/rust-rib.cc +++ b/gcc/rust/resolve/rust-rib.cc @@ -23,9 +23,30 @@ namespace Rust { namespace Resolver2_0 { Rib::Definition::Definition (NodeId id, bool shadowable) - : id (id), shadowable (shadowable) + : ids ({id}), shadowable (shadowable) {} +bool +Rib::Definition::is_ambiguous () const +{ + return shadowable && ids.size () > 1; +} + +std::string +Rib::Definition::to_string () const +{ + std::stringstream out; + out << (shadowable ? "(S)" : "(NS)") << "["; + std::string sep; + for (auto id : ids) + { + out << sep << id; + sep = ","; + } + out << "]"; + return out.str (); +} + Rib::Definition Rib::Definition::Shadowable (NodeId id) { @@ -58,28 +79,46 @@ Rib::Rib (Kind kind, std::unordered_map to_insert) tl::expected Rib::insert (std::string name, Definition def) { - auto res = values.insert ({name, def}); - auto inserted_id = res.first->second.id; - auto existed = !res.second; - - // if we couldn't insert, the element already exists - exit with an error, - // unless shadowing is allowed - if (existed && !def.shadowable) - return tl::make_unexpected (DuplicateNameError (name, inserted_id)); - - // return the NodeId - return inserted_id; + auto it = values.find (name); + if (it == values.end ()) + { + /* No old value */ + values[name] = def; + } + else if (it->second.shadowable && def.shadowable) + { /* Both shadowable */ + auto ¤t = values[name]; + for (auto id : def.ids) + { + if (std::find (current.ids.cbegin (), current.ids.cend (), id) + == current.ids.cend ()) + { + current.ids.push_back (id); + } + } + } + else if (it->second.shadowable) + { /* Only old shadowable : replace value */ + values[name] = def; + } + else /* Neither are shadowable */ + { + return tl::make_unexpected ( + DuplicateNameError (name, it->second.ids.back ())); + } + + return def.ids.back (); } -tl::optional +tl::optional Rib::get (const std::string &name) { auto it = values.find (name); if (it == values.end ()) - return {}; + return tl::nullopt; - return it->second.id; + return it->second; } const std::unordered_map & diff --git a/gcc/rust/resolve/rust-rib.h b/gcc/rust/resolve/rust-rib.h index 732ad76b805..3db17b4840a 100644 --- a/gcc/rust/resolve/rust-rib.h +++ b/gcc/rust/resolve/rust-rib.h @@ -114,9 +114,24 @@ public: static Definition NonShadowable (NodeId id); static Definition Shadowable (NodeId id); - NodeId id; + std::vector ids; bool shadowable; + Definition () = default; + + Definition &operator= (const Definition &) = default; + Definition (Definition const &) = default; + + bool is_ambiguous () const; + + NodeId get_node_id () + { + rust_assert (!is_ambiguous ()); + return ids[0]; + } + + std::string to_string () const; + private: Definition (NodeId id, bool shadowable); }; @@ -163,7 +178,7 @@ public: * * @return tl::nullopt if the key does not exist, the NodeId otherwise */ - tl::optional get (const std::string &name); + tl::optional get (const std::string &name); /* View all the values stored in the rib */ const std::unordered_map &get_values () const; diff --git a/gcc/rust/resolve/rust-toplevel-name-resolver-2.0.cc b/gcc/rust/resolve/rust-toplevel-name-resolver-2.0.cc index 7f4169a4d8e..6929bdb641e 100644 --- a/gcc/rust/resolve/rust-toplevel-name-resolver-2.0.cc +++ b/gcc/rust/resolve/rust-toplevel-name-resolver-2.0.cc @@ -401,7 +401,8 @@ TopLevel::handle_use_glob (AST::SimplePath glob) if (!resolved.has_value ()) return false; - auto result = Analysis::Mappings::get ()->lookup_ast_module (*resolved); + auto result + = Analysis::Mappings::get ()->lookup_ast_module (resolved->get_node_id ()); if (!result.has_value ()) return false; @@ -428,7 +429,7 @@ TopLevel::handle_use_dec (AST::SimplePath path) auto resolve_and_insert = [this, &found, &declared_name, locus] (Namespace ns, const AST::SimplePath &path) { - tl::optional resolved = tl::nullopt; + tl::optional resolved = tl::nullopt; // FIXME: resolve_path needs to return an `expected` so // that we can improve it with hints or location or w/ever. and maybe @@ -450,22 +451,22 @@ TopLevel::handle_use_dec (AST::SimplePath path) } // FIXME: Ugly - (void) resolved.map ( - [this, &found, &declared_name, locus, ns, path] (NodeId id) { - found = true; - - // what do we do with the id? - insert_or_error_out (declared_name, locus, id, ns); - auto result = node_forwarding.find (id); - if (result != node_forwarding.cend () - && result->second != path.get_node_id ()) - rust_error_at (path.get_locus (), "%<%s%> defined multiple times", - declared_name.c_str ()); - else // No previous thing has inserted this into our scope - node_forwarding.insert ({id, path.get_node_id ()}); - - return id; - }); + (void) resolved.map ([this, &found, &declared_name, locus, ns, + path] (Rib::Definition def) { + found = true; + + // what do we do with the id? + insert_or_error_out (declared_name, locus, def.get_node_id (), ns); + auto result = node_forwarding.find (def.get_node_id ()); + if (result != node_forwarding.cend () + && result->second != path.get_node_id ()) + rust_error_at (path.get_locus (), "%<%s%> defined multiple times", + declared_name.c_str ()); + else // No previous thing has inserted this into our scope + node_forwarding.insert ({def.get_node_id (), path.get_node_id ()}); + + return def.get_node_id (); + }); }; // do this for all namespaces (even Labels?) @@ -587,7 +588,9 @@ flatten_glob (const AST::UseTreeGlob &glob, std::vector &paths, // (PE): Get path rib auto rib = ctx.values.resolve_path (glob.get_path ().get_segments ()) - .and_then ([&] (NodeId id) { return ctx.values.to_rib (id); }); + .and_then ([&] (Rib::Definition def) { + return ctx.values.to_rib (def.get_node_id ()); + }); if (rib.has_value ()) { auto value = rib.value ().get_values (); -- 2.45.2