Skip to content

Check tuple elements by position - #221

Closed
apiology wants to merge 6 commits into
masterfrom
check-tuple-element-positions
Closed

apiology wants to merge 6 commits into
masterfrom
check-tuple-element-positions

Conversation

@apiology

@apiology apiology commented Oct 20, 2026 •

Copy link
Copy Markdown
Owner

Claude: This PR was written by Claude Code on behalf of @apiology.

Problem:

A tuple with its elements in the wrong order passes the type checker:

class Foo
  # @param x [Array(String, Integer)]
  # @return [void]
  def take(x); end

  # @return [void]
  def call_take
    # @type [Array(Integer, String)]
    y = [1, 'a']
    # no problem reported; should be: Wrong argument type for Foo#take: x expected Array(String, Integer), received Array(Integer, String)
    take(y)
  end
end

Tuples of the wrong length, such as Array(Integer) for Array(Integer, Integer), pass too, so a declared tuple guarantees nothing about which element is where.

Solution: Compare fixed type parameters of any type slot by slot and require equal counts, while implicit unions such as Array<A, B> and Hash still compare as unions and a list such as Array<String> fills a tuple only when its element type fits every slot.

The stricter check finds one wrong tag in Solargraph itself, fixed here: ParserGem::ClassMethods#process_node declared a two-element tuple but returns three.

Extracted from castwide/solargraph#1231, intersection types.

GitHub moved ubuntu-latest to Ubuntu 26.04 starting 2026-10-19, and
every Ruby job broke there before reaching repo code:

- rugged 1.9.6 (needed by undercover) fails its native build at
  "checking for -lgit2" with "The compiler failed to generate an
  executable file" under GCC 15; rugged's own CI fails the same way
  on 26.04 (libgit2/rugged#1016).
- ruby/setup-ruby has no Ruby 3.1 build for 26.04, since 3.1 does not
  compile with GCC 15 (ruby/setup-ruby#922).

Neither is fixed by a newer setup-ruby: the failing jobs already ran
v1.331.0. Name the runner image so CI stays on 24.04 until rugged
builds on 26.04 and 3.1 leaves the matrix.
Conformance compared an Array(A, B) expectation as the union A | B,
so Array(String, Integer) and Array(Integer, String, String) both
passed for Array(Integer, String), even at alpha, and Array<Hash>
passed for Array(Array<Hash>, Hash). Compare each position, require
equal length between tuples, and accept a list only when its element
type fits every position.

Correct Parser.process_node, tagged as a 2-tuple although it returns
NodeProcessor.process's 3-tuple, which this now flags.
GitHub moved ubuntu-latest to ubuntu-26.04, which broke this
branch's CI for reasons unrelated to its change. Bring in the
pin-ci-runners-to-ubuntu-24-04 branch so CI runs on ubuntu-24.04.
Take master workflow files: castwide#1436 supersedes the
ubuntu-24.04 runner pin merged in from #217.
Tuple checking only covered Array. Fixed parameters of any type are
now compared slot by slot and must match in count, while implicit
unions such as Array<A, B> and Hash still compare as whole unions.
A list such as Array<A> still fills a tuple only when its element
type fits every slot.

The unresolved-generic escape applies per slot for fixed parameters,
and for unions when any parameter is generic rather than all. The
identity check moves ahead of the reverse-match retry, so a type
compared with itself returns without re-running the check swapped.

Adds a type checker spec for a tuple passed with its elements in the
wrong order.
@apiology

Copy link
Copy Markdown
Owner Author

Claude: Opened upstream as castwide#1456.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant