From 94d417d9e78609ba19ab80277150a192aa5b754b Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Fri, 3 Apr 2026 18:44:03 +0900 Subject: [PATCH] Fix optional type typecheck to accept nil SigTyOptionalNode#typecheck was delegating directly to the inner type's typecheck, which would reject nil values for T? types like Binding?. Now it skips nil types when checking, since nil is always valid for optional types. This fixes false "wrong type of arguments" for calls like eval("code", nil, "filename") where the second argument is Binding?. Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/typeprof/core/ast/sig_type.rb | 10 ++++++++++ scenario/rbs/optional_nil_typecheck.rb | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 scenario/rbs/optional_nil_typecheck.rb diff --git a/lib/typeprof/core/ast/sig_type.rb b/lib/typeprof/core/ast/sig_type.rb index 083bc74c..cca910ac 100644 --- a/lib/typeprof/core/ast/sig_type.rb +++ b/lib/typeprof/core/ast/sig_type.rb @@ -884,6 +884,16 @@ def contravariant_vertex0(genv, changes, vtx, subst) end def typecheck(genv, changes, vtx, subst) + # For optional type T?, check if all non-nil types match T. + # nil is always acceptable. + changes.add_edge(genv, vtx, changes.target) + has_non_nil = false + vtx.each_type do |ty| + next if ty.is_a?(Type::Bot) + next if ty == genv.nil_type + has_non_nil = true + end + return true unless has_non_nil @type.typecheck(genv, changes, vtx, subst) end diff --git a/scenario/rbs/optional_nil_typecheck.rb b/scenario/rbs/optional_nil_typecheck.rb new file mode 100644 index 00000000..4d561bf0 --- /dev/null +++ b/scenario/rbs/optional_nil_typecheck.rb @@ -0,0 +1,21 @@ +## update: test.rbs +class Object + def bar: (Binding?) -> void +end + +## update: test.rb +eval("hello", nil, "test") + +def bar_nil + bar(nil) +end + +def bar_binding + bar(binding) +end + +## assert +class Object + def bar_nil: -> Object + def bar_binding: -> Object +end