Summary
new Set(iterable) currently routes through js_set_from_iterable, but that helper only handles a few input shapes directly:
- arrays,
- strings,
null / undefined as empty,
- other pointer-tagged values by treating them as arrays,
- everything else as an empty set with no error.
Node's Set constructor should consume any iterable and should throw TypeError for non-null, non-undefined values that are not iterable.
Node behavior
Checked with Node v25.9.0:
[...new Set([1, 2, 1])];
// [1, 2]
[...new Set(new Set([3, 4]))];
// [3, 4]
[...new Set('aba')];
// ['a', 'b']
[...new Set(new Map([[1, 'a'], [2, 'b']]))].map(v => v.join(':'));
// ['1:a', '2:b']
const customIterable = {
*[Symbol.iterator]() {
yield 'x';
yield 'y';
},
};
[...new Set(customIterable)];
// ['x', 'y']
new Set(null).size; // 0
new Set(undefined).size; // 0
new Set(1);
// TypeError: number 1 is not iterable (cannot read property Symbol(Symbol.iterator))
new Set({ a: 1 });
// TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))
new Set(Symbol('x'));
// TypeError: symbol is not iterable (cannot read property Symbol(Symbol.iterator))
Perry implementation
Lowering/codegen sends any provided Set constructor argument to js_set_from_iterable:
crates/perry-hir/src/lower/expr_new.rs:304 handles new Set() / new Set(iterable).
crates/perry-hir/src/lower/expr_new.rs:316 returns Expr::SetNew for no arguments.
crates/perry-hir/src/lower/expr_new.rs:318 returns Expr::SetNewFromArray(firstArg) for any argument.
crates/perry-codegen/src/expr/logical_collections.rs:551 routes Expr::SetNewFromArray through js_set_from_iterable.
The runtime helper is intentionally lenient rather than Node-compatible:
crates/perry-runtime/src/set.rs:786 documents js_set_from_iterable as creating a Set from any iterable JS value.
crates/perry-runtime/src/set.rs:788 lists the actually handled shapes: Array, String, undefined/null, and anything else.
crates/perry-runtime/src/set.rs:795 explicitly says anything else becomes an empty set with no error, even though JS would throw TypeError.
crates/perry-runtime/src/set.rs:859 treats pointer-tagged values as arrays; non-array objects can be misread as ArrayHeader values rather than using their iterator or throwing.
crates/perry-runtime/src/set.rs:866 returns a fresh empty set for undefined/null/number/etc., so new Set(1) cannot throw the required TypeError through this path.
Expected fix direction
new Set(iterable) should implement the Set constructor's iterable consumption semantics:
- keep
null and undefined as empty-set inputs,
- consume arbitrary iterable objects through their iterator,
- preserve existing array and string fast paths when they match iterator behavior,
- support Map/Set/custom iterables by consuming yielded values,
- throw
TypeError for non-iterable numbers, symbols, and plain objects.
Duplicate search
Searched existing issues/PRs for:
Set constructor iterable
new Set iterable
Set constructor TypeError
new Set non-iterable
js_set_from_iterable TypeError
new Set Map iterable
new Set custom iterator
- PR search for
Set constructor iterable OR js_set_from_iterable
No existing issue covered this Set constructor behavior. Related but not duplicate: #2770 tracks Map constructor iterable/malformed-entry semantics, and older Map/Set iteration bugs (#302, #311, #412) were about for-of/spread behavior rather than constructor validation and arbitrary iterable init.
Summary
new Set(iterable)currently routes throughjs_set_from_iterable, but that helper only handles a few input shapes directly:null/undefinedas empty,Node's Set constructor should consume any iterable and should throw
TypeErrorfor non-null, non-undefined values that are not iterable.Node behavior
Checked with Node v25.9.0:
Perry implementation
Lowering/codegen sends any provided Set constructor argument to
js_set_from_iterable:crates/perry-hir/src/lower/expr_new.rs:304handlesnew Set()/new Set(iterable).crates/perry-hir/src/lower/expr_new.rs:316returnsExpr::SetNewfor no arguments.crates/perry-hir/src/lower/expr_new.rs:318returnsExpr::SetNewFromArray(firstArg)for any argument.crates/perry-codegen/src/expr/logical_collections.rs:551routesExpr::SetNewFromArraythroughjs_set_from_iterable.The runtime helper is intentionally lenient rather than Node-compatible:
crates/perry-runtime/src/set.rs:786documentsjs_set_from_iterableas creating a Set from any iterable JS value.crates/perry-runtime/src/set.rs:788lists the actually handled shapes: Array, String, undefined/null, and anything else.crates/perry-runtime/src/set.rs:795explicitly says anything else becomes an empty set with no error, even though JS would throwTypeError.crates/perry-runtime/src/set.rs:859treats pointer-tagged values as arrays; non-array objects can be misread as ArrayHeader values rather than using their iterator or throwing.crates/perry-runtime/src/set.rs:866returns a fresh empty set for undefined/null/number/etc., sonew Set(1)cannot throw the required TypeError through this path.Expected fix direction
new Set(iterable)should implement the Set constructor's iterable consumption semantics:nullandundefinedas empty-set inputs,TypeErrorfor non-iterable numbers, symbols, and plain objects.Duplicate search
Searched existing issues/PRs for:
Set constructor iterablenew Set iterableSet constructor TypeErrornew Set non-iterablejs_set_from_iterable TypeErrornew Set Map iterablenew Set custom iteratorSet constructor iterable OR js_set_from_iterableNo existing issue covered this Set constructor behavior. Related but not duplicate: #2770 tracks Map constructor iterable/malformed-entry semantics, and older Map/Set iteration bugs (#302, #311, #412) were about for-of/spread behavior rather than constructor validation and arbitrary iterable init.