From 630df45492d9ab068de9398e3d10d828ea8be1f4 Mon Sep 17 00:00:00 2001 From: Edward Poot Date: Thu, 23 Oct 2025 17:25:59 +0200 Subject: [PATCH] Add test cases for Kotlin private constructor instantiation edge cases. This commit adds two test cases to demonstrate a bug in Kotlin constructor resolution when dealing with private constructors. The first test case shows the failure scenario where a private constructor without default parameter values causes a runtime exception. The second test case demonstrates that adding default parameter values to the private constructor doesn't display the same issue. The bug seems to occur because the code incorrectly assumes that private constructors always generate synthetic parameters for both the default mask and the DefaultConstructorMarker. However, when a private primary constructor has no default parameter values, the Kotlin compiler only generates the DefaultConstructorMarker variant, not the mask parameter. References #3389 Signed-off-by: Edward Poot --- .../data/mapping/model/InlineClasses.kt | 5 +++++ ...ssGeneratingEntityInstantiatorUnitTests.kt | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/test/kotlin/org/springframework/data/mapping/model/InlineClasses.kt b/src/test/kotlin/org/springframework/data/mapping/model/InlineClasses.kt index 5bf4926eb1..6bc2389a22 100644 --- a/src/test/kotlin/org/springframework/data/mapping/model/InlineClasses.kt +++ b/src/test/kotlin/org/springframework/data/mapping/model/InlineClasses.kt @@ -20,6 +20,7 @@ import java.time.LocalDate /** * @author Mark Paluch + * @author Edward Poot */ @JvmInline value class MyValueClass(val id: String) @@ -46,6 +47,10 @@ data class WithMyValueClass(val id: MyValueClass) { // --------- } +data class WithMyValueClassPrivateConstructor private constructor(val id: MyValueClass) + +data class WithMyValueClassPrivateConstructorAndDefaultValue private constructor(val id: MyValueClass = MyValueClass("id")) + @JvmInline value class MyNullableValueClass(val id: String? = "id") diff --git a/src/test/kotlin/org/springframework/data/mapping/model/KotlinClassGeneratingEntityInstantiatorUnitTests.kt b/src/test/kotlin/org/springframework/data/mapping/model/KotlinClassGeneratingEntityInstantiatorUnitTests.kt index a9c98d57a3..0d0b731877 100644 --- a/src/test/kotlin/org/springframework/data/mapping/model/KotlinClassGeneratingEntityInstantiatorUnitTests.kt +++ b/src/test/kotlin/org/springframework/data/mapping/model/KotlinClassGeneratingEntityInstantiatorUnitTests.kt @@ -30,6 +30,7 @@ import kotlin.reflect.KClass * * @author Mark Paluch * @author Sebastien Deleuze + * @author Edward Poot */ @Suppress("UNCHECKED_CAST") class KotlinClassGeneratingEntityInstantiatorUnitTests { @@ -189,6 +190,24 @@ class KotlinClassGeneratingEntityInstantiatorUnitTests { assertThat(instance.id.id).isEqualTo("hello") } + @Test // GH-3389 + fun `should use private default constructor for types using value class`() { + + every { provider.getParameterValue(any()) } returns "hello" + val instance = construct(WithMyValueClassPrivateConstructor::class) + + assertThat(instance.id.id).isEqualTo("hello") + } + + @Test // GH-3389 + fun `should use private default constructor for types using value class with default value`() { + + every { provider.getParameterValue(any()) } returns "hello" + val instance = construct(WithMyValueClassPrivateConstructorAndDefaultValue::class) + + assertThat(instance.id.id).isEqualTo("hello") + } + @Test fun `should use default constructor for types using nullable value class`() {