Skip to content

Clarify local scope semantics and add explicit type check in SumLocalStep - #3616

Open
xiazcy wants to merge 1 commit into
3.7-devfrom
sum-local-semantics
Open

Clarify local scope semantics and add explicit type check in SumLocalStep#3616
xiazcy wants to merge 1 commit into
3.7-devfrom
sum-local-semantics

Conversation

@xiazcy

@xiazcy xiazcy commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes a non-deterministic type-safety hole in SumLocalStep and documents the Scope.local boxing semantics in the
provider semantics specification.

Problem

sum(local) on a single non-numeric scalar (e.g. g.inject("hello").sum(local)) would non-deterministically either
throw ClassCastException or pass the value through as identity, depending on JIT compilation state. This is because
the generic bound E extends Number is erased at runtime, and the arithmetic call that would enforce it
(NumberHelper.add) is only reached when there are 2+ elements. mean(local) was already correct (always calls
div()), while min(local)/max(local) are unaffected (they use Comparable bounds, and NumberHelper.min/max
correctly falls back to compareTo for non-Numbers).

Fix

Added an explicit instanceof Number check in SumLocalStep after untilNonNull() returns the first element, making
the error deterministic.

Documentation

Added a "Local scope and single values" section ([[gremlin-semantics-local-scope-boxing]]) to
gremlin-semantics.asciidoc specifying:

  • The wrapping dispatch (LIST/SET/array/MAP iterate directly; anything else wraps to a single-element sequence)
  • Scalar numeric identity behavior (sum(local) on 2929)
  • Type error contract for incompatible input (regardless of collection size)

Tests

  • Gherkin (Sum.feature): sum(local) on a non-numeric list (error) and a single non-numeric scalar (error),
    with GLV translations for .NET, Go, JS, Python
  • Unit (SumLocalStepTest): numeric scalar identity + non-numeric error
  • Unit (MeanLocalStepTest): numeric scalar identity + non-numeric error
  • Unit (MinLocalStepTest): numeric identity, String identity (valid Comparable), String-list min, numeric-list
    min
  • Unit (MaxLocalStepTest): numeric identity, String identity, String-list max, numeric-list max
  • Unit (IteratorUtilsTest): Number singleton wrapping + generic Object singleton wrapping

VOTE +1

… boxing semantics

Add an instanceof Number guard in SumLocalStep to deterministically reject non-numeric input on the single-element path, fixing a JIT-dependent type-erasure hole where sum(local) on a non-numeric scalar would sometimes pass through as identity instead of erroring.

Document the Scope.local boxing/wrapping contract in the semantics doc (scalars are coerced to single-element sequences) and add tests locking the behavior for sum, mean, min, and max local steps, plus the IteratorUtils singleton-wrapping contract.

Assisted-by: Kiro:claude-opus-4 [kiro-cli]
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