Skip to content

Fix IndexOutOfRangeException with value objects implicit conversions#538

Merged
dadhi merged 2 commits intomasterfrom
copilot/fix-index-out-of-range-exception-again
Apr 27, 2026
Merged

Fix IndexOutOfRangeException with value objects implicit conversions#538
dadhi merged 2 commits intomasterfrom
copilot/fix-index-out-of-range-exception-again

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 25, 2026

Fixes System.IndexOutOfRangeException (and InvalidProgramException for non-abstract classes) thrown by CompileFast when using implicit conversion operators declared on an abstract or non-abstract base class within a closure.

Root Cause

In TryEmitConvert, the else if (methodParamType != sourceType) branch unconditionally assumed that whenever the conversion operator's parameter type differs from the source type, it must be a Nullable<sourceType> wrapping case. It called GetNullableConstructor()type.GetConstructors()[0], but abstract classes have no public constructors, so this returned an empty array → IndexOutOfRangeException. For non-abstract base classes it emitted a wrong newobj instruction → InvalidProgramException.

Fix

In src/FastExpressionCompiler/FastExpressionCompiler.cs, updated the else if (methodParamType != sourceType) branch to only emit Newobj when Nullable.GetUnderlyingType(methodParamType) == sourceType. For the polymorphic base class case (methodParamType is a base type/interface of sourceType), no additional emit is needed — the value already on the stack is assignment-compatible with the conversion method's parameter.

Test

Added regression test Issue500_IndexOutOfRangeException_with_value_objects_implicit_conversions (registered for both FastExpressionCompiler and FastExpressionCompiler.LightExpression) covering:

  1. Abstract base class + closure — the exact case from the original report
  2. Non-abstract base class + closure — previously caused InvalidProgramException
  3. Direct parameter with explicit method reference — exercises the same code path without a closure

All 1672 tests pass.

@dadhi
Copy link
Copy Markdown
Owner

dadhi commented Apr 27, 2026

@copilot please proceed

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 27, 2026

@copilot please proceed

Done. The root cause was in TryEmitConvert (commit ffd609d): when an implicit conversion operator is declared on an abstract/base class (e.g., PrimitiveValueObject<string, MyPrimitive>), the parameter type differs from the source type (MyPrimitive). The code incorrectly assumed this always meant a Nullable<sourceType> wrapping case and called GetNullableConstructor()GetConstructors()[0]IndexOutOfRangeException for abstract classes (no public constructors).

Fix: In the else if (methodParamType != sourceType) branch, only emit Newobj when Nullable.GetUnderlyingType(methodParamType) == sourceType. For the polymorphic base class case, no extra emit is needed since the value on the stack is already assignment-compatible.

Added regression test Issue500 with 3 cases: abstract base class closure, non-abstract base class closure, and direct parameter with explicit method. All 1672 tests pass.

Copilot AI changed the title [WIP] Fix IndexOutOfRangeException with value objects conversions Fix IndexOutOfRangeException with value objects implicit conversions Apr 27, 2026
@dadhi dadhi marked this pull request as ready for review April 27, 2026 13:24
@dadhi dadhi merged commit cba4229 into master Apr 27, 2026
2 checks passed
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.

Exception System.IndexOutOfRangeException with value objects implicit conversions starting from v5.0.3

2 participants