{"atom":"regression/interp-meta-mixin-block-sig-allomorph","family":"regression","slug":"interp-meta-mixin-block-sig-allomorph","header":{"atom":"regression/interp-meta-mixin-block-sig-allomorph","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/interp-meta-mixin-block-sig-allomorph.raku","counts":{"a":1,"f":0,"d":0,"c":0,"r":0,"n":0},"files":["atoms/regression/interp-meta-mixin-block-sig-allomorph.grid"],"records":[["0001","","# Regression: meta-method interpolation, non-composable mixins, the implicit `$_`\n# of a bare block, regex routines, allomorph identity, and `.tree` itemisation.\n#   * `\"$x.^name()\"` — the hand-written postfix chain in the string parser accepted\n#     `.[`, `.{`, `.(` and `.IDENT(`, but the `.IDENT` arm tested only isalpha,\n#     so `^` ended the chain and `.^name()` was emitted as literal text. One meta\n#     sigil (`^`, `?`, `&`) may now sit between the dot and the name. Commit is\n#     still gated on a following `(`, so a bare `\"$x.^name\"` stays literal — which\n#     is what Rakudo does too.\n#   * `1 but B` for a plain class silently built Int+{B}: the collect walk inside\n#     mixinValue accepts any type object as rolish and never consults isRole. An\n#     UNREGISTERED type name is deliberately still allowed, because rakupp does\n#     not register the built-in roles (Numeric, Positional…) as classes and\n#     `1 but Numeric` is legal.\n#   * a bare `{ … }` block carries an implicit `$_`: arity 0, count 1. `-> {…}`\n#     and `sub {…}` do not — so a pointy block now records that it wrote a\n#     signature, even an empty one. A `:( … )` literal is excluded as well, or an\n#     empty signature would start accepting a one-argument call.\n#   * `my regex R {…}` built a Callable with no marker of its origin, so typeName\n#     fell through to \"Sub\".\n#   * an ALLOMORPH is neither of its halves. Quanthash keys, `===` and `eqv` all\n#     compared renderings, and `<42>` renders \"42\" exactly like the Int — so\n#     `42 ∈ <42 55 1>` was True. Narrow on purpose: only allomorphs get identity\n#     keys, so the set operators that compare against a `set(…)` literal are\n#     untouched.\n#   * `.tree(N)` built plain lists, so a later `.flat` descended the whole\n#     structure. Every node BELOW the root is an item.\n# NOT fixed, and backed out during this batch: `42[2]` should throw X::OutOfRange\n# rather than answering Nil. Correct in isolation, but rakupp separately makes\n# `@p[0]` a scalar where it should be a list, and under a throw\n# S03-operators/assign.t dies at line 230 and loses 201 further assertions. The\n# reason is recorded at the site in Interpreter.cpp.\n# Contract: exit 0 + last line PASS.\nmy @fail;\nsub check($got, $want, $what) { @fail.push(\"$what: got $got want $want\") unless $got eq $want }\n\n# meta-method interpolation\nmy $ex = 42;\ncheck(\"$ex.^name(): $ex\", 'Int: 42', 'a meta-method interpolates');\ncheck(\"$ex.^name\",        '42.^name', 'without parens it stays literal');\nsub rgf($z) { \"f($z)\" }\ncheck(\"$ex.&rgf()\",       'f(42)',   'and a .& sub call');\nmy $q = \"hi\";\ncheck(\"$q.uc()\",          'HI',      'an ordinary method is unaffected');\ncheck(\"plain $ex\",        'plain 42', 'as is a bare variable');\n\n# only roles may be mixed in\nclass RgB { }\nrole RgR { }\ncheck((try { 1 but RgB } // 'threw'), 'threw', 'a plain class is not composable');\ncheck($!.^name, 'X::Mixin::NotComposable', 'with the right exception');\ncheck($!.Str, 'Cannot mix in non-composable type RgB into object of type Int', 'and message');\ncheck((1 but RgR).^name,     'Int+{RgR}',     'a role still composes');\ncheck((1 but Numeric).^name, 'Int+{Numeric}', 'and an unregistered built-in role is left alone');\n\n# the implicit $_ of a bare block\ncheck({;}.signature.gist,  '(;; $_? is raw = OUTER::<$_>)', 'a bare block has an implicit $_');\ncheck({;}.signature.arity, '0', 'arity 0');\ncheck({;}.signature.count, '1', 'but count 1');\ncheck((-> {;}).signature.gist,  '()', 'a pointy block wrote an empty signature');\ncheck((sub {;}).signature.gist, '()', 'and so did an anonymous sub');\ncheck(:().gist,                 '()', 'a signature literal is not a block');\ncheck((:($) ~~ :()).gist,   'False', 'so it still binds nothing');\n\n# a named regex is a Regex\nmy regex RgRx { \\N }\ncheck(&RgRx.^name, 'Regex', 'not a Sub');\ncheck((sub {}).^name, 'Sub', 'while a sub still is');\n\n# allomorph identity\ncheck((42 ∈ <42 55 1>).gist,   'False', 'an Int is not the allomorph <42>');\ncheck((<42> ∈ <42 55 1>).gist, 'True',  'but the allomorph is');\ncheck((42 === <42>).gist,      'False', 'nor identical to it');\ncheck((42 eqv <42>).gist,      'False', 'nor eqv');\ncheck((<42> === <42>).gist,    'True',  'two allomorphs are');\ncheck((42 === 42).gist,        'True',  'and two Ints');\ncheck((set(<42>) eqv set(42)).gist, 'False', 'so the sets differ too');\ncheck(set(<42 55>).gist,       'Set(42 55)', 'while the rendering is unchanged');\n\n# .tree itemises below the root\nmy @floors = ('A', ('B','C', ('E','F','G')));\ncheck(@floors.tree(2).flat.elems, '2', 'tree(2) stops flat at depth 1');\ncheck(@floors.tree(1).flat.elems, '6', 'tree(1) does not');\n\nif @fail { note \"FAILED: @fail.join('; ')\"; say 'FAIL' } else { say 'PASS' }","output PASS","a",["PASS","PASS"],0,[["budget","20s"]]]]}