{"atom":"regression/untyped-param-rejects-mu","family":"regression","slug":"untyped-param-rejects-mu","header":{"atom":"regression/untyped-param-rejects-mu","kind":"whole-program — a minimal repro, not yet reduced to an atom","source":"curated"},"axes":null,"cols":null,"engines":["rakudo-2026.07","rakupp-3.14.0"],"kinds":["ref","pp"],"defFrom":"rakupp:t/regression/untyped-param-rejects-mu.raku","counts":{"a":1,"f":0,"d":0,"c":0,"r":0,"n":0},"files":["atoms/regression/untyped-param-rejects-mu.grid"],"records":[["0001","","# Regression: an untyped routine parameter accepted `Mu`.\n#\n# In Raku an untyped parameter is implicitly Any-constrained, and `Any` is every\n# type BELOW Mu — so `sub f($x) { }; f(Mu)` is a type-check failure. Raku++ bound\n# it happily, because typeCheckBind returned early for EVERY type object and was\n# never called at all for an untyped parameter.\n#\n# The distinction that makes this subtle, and which this file exists to pin: a\n# BLOCK's untyped parameter is Mu-constrained, not Any-constrained. So the pointy\n# and bare-block cases below must keep working — and `for (Mu) -> $x` and\n# `.map(-> $x {…})` depend on it, which is how a too-eager fix announces itself.\n#\n# Found porting JSON::Native to an explicit signature: its tests passed on Raku++\n# and failed on Rakudo, because Raku++ never applied the constraint.\n#\n# Contract: exit 0 + last line PASS.\nmy @fail;\n\nsub check($got, $want, $desc) {\n    @fail.push(\"$desc: got {$got.raku}, want {$want.raku}\") unless $got eqv $want;\n}\nsub dies(&c, $desc)  { check ((try { c(); 'lived' }) // 'died'), 'died',  $desc }\nsub lives(&c, $desc) { check ((try { c(); 'lived' }) // 'died'), 'lived', $desc }\n\n# --- routines: an untyped parameter is Any-constrained, so Mu is refused ------\ndies { sub f($x) { }; f(Mu) },                    'a named sub refuses Mu';\ndies { (sub ($x) { })(Mu) },                      'an anonymous sub refuses Mu';\ndies { class C { method mth($x) { } }; C.mth(Mu) }, 'a method refuses Mu';\ndies { sub f(:$y) { }; f(:y(Mu)) },               'a named parameter refuses Mu';\ndies { sub f($x = 5) { }; f(Mu) },                'an optional parameter refuses Mu';\n\n# An explicitly written Any refuses Mu wherever it appears — blocks included.\ndies { sub f(Any $x) { }; f(Mu) },                'an explicit Any parameter refuses Mu';\ndies { (-> Any $x { })(Mu) },                     'an explicit Any refuses Mu in a block too';\n\n# --- blocks: an untyped parameter is Mu-constrained, so Mu binds --------------\nlives { (-> $x { })(Mu) },                        'a pointy block accepts Mu';\nlives { ({ $^a })(Mu) },                          'a bare block accepts Mu';\nlives { my $b = -> $x { }; $b(Mu) },              'a stored pointy block accepts Mu';\nlives { for (Mu) -> $x { } },                     'for -> $x accepts Mu';\nlives { (Mu,).map(-> $x { }).eager },             'map -> $x accepts Mu';\n\n# --- and everything that was already legal stays legal ------------------------\nlives { sub f(Mu $x) { }; f(Mu) },                'an explicit Mu parameter accepts Mu';\nlives { sub f($x) { }; f(Any) },                  'an untyped parameter still accepts Any';\nlives { sub f($x) { }; f(42) },                   'an untyped parameter still accepts a value';\nlives { sub f($x) { }; my $u; f($u) },            'an untyped parameter accepts an empty slot';\nlives { sub f(*@a) { }; f(Mu) },                  'a slurpy accepts Mu';\nlives { sub f(|c) { }; f(Mu) },                   'a capture accepts Mu';\nlives { sub f(Int $x) { }; f(Int) },              'a type object still binds its own type';\n\n# Values still reach the body intact — the check must not have eaten anything.\ncheck (sub ($x) { $x + 1 })(41), 42, 'an ordinary argument still binds and is usable';\ncheck (-> $x { $x.^name })(Mu), 'Mu', 'a block receives Mu itself, not a substitute';\n\n# Multi-dispatch already told Mu and Any apart, and must continue to.\nmulti pick-t(Mu  $x) { 'Mu'  }\nmulti pick-t(Any $x) { 'Any' }\ncheck pick-t(Mu),  'Mu',  'a Mu candidate wins for Mu';\ncheck pick-t(42),  'Any', 'the Any candidate wins for a value';\ncheck pick-t(Any), 'Any', 'the Any candidate wins for Any';\n\nif @fail {\n    note \"FAILED:\\n\" ~ @fail.map({ \"  - $_\" }).join(\"\\n\");\n    exit 1;\n}\nsay \"PASS\";","output PASS","a",["PASS","PASS"],0,[["budget","20s"]]]]}