From 4d497e7f7bbdfbffdb987815f8f96954ca80a97b Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Fri, 3 Apr 2026 16:28:00 +0900 Subject: [PATCH] Handle Array slice assignment (ary[start, len] = val) in builtin Previously, Array#[]= with 3 arguments (slice assignment) fell through to RBS overload resolution, where both (int, int, Elem) and (int, int, Array[Elem]) overloads matched. The Elem overload caused the assigned array to be treated as an element, creating deeply nested union types like Array[Array[Array[Integer]]]. Now the builtin handles 3-argument []= by using SplatBox to extract element types from the assigned value and merge them into the array's element type, preventing type nesting. Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/typeprof/core/builtin.rb | 14 ++++++++++++++ scenario/array/slice_aset.rb | 14 ++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 scenario/array/slice_aset.rb diff --git a/lib/typeprof/core/builtin.rb b/lib/typeprof/core/builtin.rb index 9730e6ed6..cafaa19ab 100644 --- a/lib/typeprof/core/builtin.rb +++ b/lib/typeprof/core/builtin.rb @@ -69,6 +69,20 @@ def array_aset(changes, node, ty, a_args, ret) else false end + elsif a_args.positionals.size == 3 + # ary[start, len] = val + # Use SplatBox to extract element types from the assigned value + elem_vtx = case ty + when Type::Array + ty.get_elem(@genv) + when Type::Instance + ty.mod == @genv.mod_ary ? ty.args[0] : nil + end + return false unless elem_vtx + val = a_args.positionals[2] + splat_ret = changes.add_splat_box(@genv, val).ret + changes.add_edge(@genv, splat_ret, elem_vtx) + true else false end diff --git a/scenario/array/slice_aset.rb b/scenario/array/slice_aset.rb new file mode 100644 index 000000000..c936962af --- /dev/null +++ b/scenario/array/slice_aset.rb @@ -0,0 +1,14 @@ +## update +def test + a = [0] * 10 + a[0, 3] = [1, 2, 3] + a[5, 3] = a[0, 3] + a[0] +end + +test + +## assert +class Object + def test: -> Integer +end