From 525f3bad59825e48aca3abe0f0485f3f37da07d5 Mon Sep 17 00:00:00 2001 From: Philip Metzger Date: Thu, 21 May 2026 19:38:22 +0200 Subject: [PATCH] starlark: Support unpacking `Set`'s It didn't support it yet and it is useful for embedder's. I mostly followed stepancheg's advice in the bug. --- .../src/tests/derive/module/unpack_value.rs | 15 +++++ .../starlark/src/values/types/set.rs | 2 + .../starlark/src/values/types/set/refs.rs | 16 +++++ .../starlark/src/values/types/set/unpack.rs | 63 +++++++++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 starlark-rust/starlark/src/values/types/set/unpack.rs diff --git a/starlark-rust/starlark/src/tests/derive/module/unpack_value.rs b/starlark-rust/starlark/src/tests/derive/module/unpack_value.rs index bc16bb24c9240..4809fd4758526 100644 --- a/starlark-rust/starlark/src/tests/derive/module/unpack_value.rs +++ b/starlark-rust/starlark/src/tests/derive/module/unpack_value.rs @@ -26,6 +26,7 @@ use crate::values::Value; use crate::values::ValueOf; use crate::values::dict::UnpackDictEntries; use crate::values::list::UnpackList; +use crate::values::set::UnpackSetEntries; // TODO(nmj): Figure out default values here. ValueOf = 5 should work. #[starlark_module] @@ -115,6 +116,10 @@ fn validate_module(builder: &mut GlobalsBuilder) { }, } } + fn with_set<'v>(v: ValueOf<'v, UnpackSetEntries>) -> anyhow::Result<(Value<'v>, String)> { + let repr = v.typed.entries.iter().join(", "); + Ok((v.value, repr)) + } } // The standard error these raise on incorrect types @@ -175,3 +180,13 @@ fn test_either_of() { a.fail("with_either(noop(None))", BAD); a.fail("with_either(noop({}))", BAD); } + +#[test] +fn test_set_of() { + let mut a = Assert::new(); + a.globals_add(validate_module); + a.eq("(set([2]), '2')", "with_set(set([2]))"); + a.eq("(set([2, 3]), '2, 3')", "with_set(set([2,3]))"); + a.fail("with_set(noop(None))", BAD); + a.fail("with_set(noop({}))", BAD); +} diff --git a/starlark-rust/starlark/src/values/types/set.rs b/starlark-rust/starlark/src/values/types/set.rs index 8905eb82e99e7..e8b0f60459711 100644 --- a/starlark-rust/starlark/src/values/types/set.rs +++ b/starlark-rust/starlark/src/values/types/set.rs @@ -19,6 +19,8 @@ pub(crate) mod methods; pub(crate) mod refs; pub(crate) mod set; +pub(crate) mod unpack; pub(crate) mod value; pub use crate::values::set::refs::SetMut; pub use crate::values::set::refs::SetRef; +pub use crate::values::set::unpack::UnpackSetEntries; diff --git a/starlark-rust/starlark/src/values/types/set/refs.rs b/starlark-rust/starlark/src/values/types/set/refs.rs index 178d8c07ab19f..2914791b3dca2 100644 --- a/starlark-rust/starlark/src/values/types/set/refs.rs +++ b/starlark-rust/starlark/src/values/types/set/refs.rs @@ -54,6 +54,22 @@ impl<'v> Clone for SetRef<'v> { } } +impl<'v> SetRef<'v> { + /// Downcast the value to a set. + pub fn from_value(x: Value<'v>) -> Option> { + if x.unpack_frozen().is_some() { + x.downcast_ref::>().map(|x| SetRef { + aref: Either::Right(coerce(&x.0)), + }) + } else { + let ptr = x.downcast_ref::>>>()?; + Some(SetRef { + aref: Either::Left(ptr.0.borrow()), + }) + } + } +} + impl<'v> Dupe for SetRef<'v> {} /// Mutably borrowed `Set`. diff --git a/starlark-rust/starlark/src/values/types/set/unpack.rs b/starlark-rust/starlark/src/values/types/set/unpack.rs new file mode 100644 index 0000000000000..5deafddd5b276 --- /dev/null +++ b/starlark-rust/starlark/src/values/types/set/unpack.rs @@ -0,0 +1,63 @@ +/* + * Copyright 2018 The Starlark in Rust Authors. + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::values::UnpackValue; +use crate::values::Value; +use crate::values::set::SetRef; +use crate::values::type_repr::SetType; +use crate::values::type_repr::StarlarkTypeRepr; + +/// Unpack a `Set`. +/// +pub struct UnpackSetEntries { + /// Entries of the set. + pub entries: Vec, +} + +impl Default for UnpackSetEntries { + fn default() -> Self { + UnpackSetEntries { + entries: Vec::new(), + } + } +} + +impl StarlarkTypeRepr for UnpackSetEntries { + type Canonical = as StarlarkTypeRepr>::Canonical; + + fn starlark_type_repr() -> crate::typing::Ty { + SetType::::starlark_type_repr() + } +} + +impl<'v, K: UnpackValue<'v>> UnpackValue<'v> for UnpackSetEntries { + type Error = K::Error; + + fn unpack_value_impl(value: Value<'v>) -> Result, Self::Error> { + let Some(set) = SetRef::unpack_value_opt(value) else { + return Ok(None); + }; + let mut entries = Vec::with_capacity(set.aref.content.len()); + for k in set.aref.iter() { + let Some(k) = K::unpack_value_impl(k)? else { + return Ok(None); + }; + entries.push(k); + } + Ok(Some(UnpackSetEntries { entries })) + } +}