Skip to content

[SPARK-44702][SQL] Fix ExpressionEncoder derivation for case class fi… - #58863

Open
Yuvraj2172 wants to merge 1 commit into
apache:masterfrom
Yuvraj2172:SPARK-44702-refinedtype-encoder
Open

Yuvraj2172 wants to merge 1 commit into
apache:masterfrom
Yuvraj2172:SPARK-44702-refinedtype-encoder

Conversation

@Yuvraj2172

@Yuvraj2172 Yuvraj2172 commented Sep 16, 2026

Copy link
Copy Markdown

…elds tagged with a trait

What changes were proposed in this pull request? ScalaReflection.getConstructorParameters crashed with a MatchError when a case class field's type was a compound type formed with with (e.g. case class Bar(x: Foo with Tag)), since such a type is represented as a RefinedType in Scala reflection, not a TypeRef. This extends the pattern match to handle that case, and also fixes a second issue in constructParams: a RefinedType has no constructor of its own, so member lookup needs to fall back to the type's erasure (which collapses it to its single concrete class parent) to find the real constructor.

Why are the changes needed?

This is a real, reproducible crash reported in the JIRA:

trait Tag
case class Foo(x: Int)
case class Bar(x: Foo with Tag)
ExpressionEncoder.apply[Bar]()

throws scala.MatchError: Foo with Tag (of class ...RefinedType0). Trait tags like this are a common pattern for adding compile-time-only type safety without extra runtime fields, and there's no reason encoder derivation should fail on them.

Does this PR introduce any user-facing change? Yes. Case classes with fields typed as SomeType with SomeTrait can now have an ExpressionEncoder derived for them successfully, instead of throwing a MatchError.

How was this patch tested?

Added a new test to ScalaReflectionSuite reproducing the JIRA's example (a case class field tagged with a marker trait) and asserting the derived schema is correct. All 38 tests in the suite pass, including the existing generic-type, value-class, and trait-without-companion edge cases, confirming no regressions.

…elds tagged with a trait

### What changes were proposed in this pull request?
`ScalaReflection.getConstructorParameters` crashed with a `MatchError` when
a case class field's type was a compound type formed with `with` (e.g.
`case class Bar(x: Foo with Tag)`), since such a type is represented as a
`RefinedType` in Scala reflection, not a `TypeRef`. This extends the
pattern match to handle that case, and also fixes a second issue in
`constructParams`: a `RefinedType` has no constructor of its own, so
member lookup needs to fall back to the type's erasure (which collapses
it to its single concrete class parent) to find the real constructor.

### Why are the changes needed?
This is a real, reproducible crash reported in the JIRA:
```scala
trait Tag
case class Foo(x: Int)
case class Bar(x: Foo with Tag)
ExpressionEncoder.apply[Bar]()
```
throws `scala.MatchError: Foo with Tag (of class ...RefinedType0)`. Trait
tags like this are a common pattern for adding compile-time-only type
safety without extra runtime fields, and there's no reason encoder
derivation should fail on them.

### Does this PR introduce any user-facing change?
Yes. Case classes with fields typed as `SomeType with SomeTrait` can now
have an `ExpressionEncoder` derived for them successfully, instead of
throwing a `MatchError`.

### How was this patch tested?
Added a new test to `ScalaReflectionSuite` reproducing the JIRA's example
(a case class field tagged with a marker trait) and asserting the derived
schema is correct. All 38 tests in the suite pass, including the existing
generic-type, value-class, and trait-without-companion edge cases,
confirming no regressions.
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