From 978661c53db7c9c55980098d356ba4a3dfc8a7db Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Thu, 1 Jun 2017 17:58:38 +0300 Subject: [PATCH] Do not generate default parameter values for synthetic functions This causes a subtle issue with 'copy' function for data class, which has parameters with source elements corresponding to the data class primary constructor parameters: default value expression for such parameters is generated second time (to be overwritten later), which creates duplicate bindings if such expression included any (possibly anonymous) declarations, such as lambdas or anonymous objects. #KT-18208 Fixed --- .../psi2ir/generators/FunctionGenerator.kt | 16 +- .../lambdaInDataClassDefaultParameter.kt | 3 + .../lambdaInDataClassDefaultParameter.txt | 198 ++++++++++++++++++ .../kotlin/ir/IrTextTestCaseGenerated.java | 6 + 4 files changed, 217 insertions(+), 6 deletions(-) create mode 100644 compiler/testData/ir/irText/classes/lambdaInDataClassDefaultParameter.kt create mode 100644 compiler/testData/ir/irText/classes/lambdaInDataClassDefaultParameter.txt diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/FunctionGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/FunctionGenerator.kt index fb5ab87a574..4226c81e0c7 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/FunctionGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/FunctionGenerator.kt @@ -217,13 +217,14 @@ class FunctionGenerator(declarationGenerator: DeclarationGenerator) : Declaratio fun generateSyntheticFunctionParameterDeclarations(irFunction: IrFunction) { declarationGenerator.generateTypeParameterDeclarations(irFunction, irFunction.descriptor.typeParameters) - generateValueParameterDeclarations(irFunction, null, null) + generateValueParameterDeclarations(irFunction, null, null, withDefaultValues = false) } fun generateValueParameterDeclarations( irFunction: IrFunction, ktParameterOwner: KtElement?, - ktReceiverParameterElement: KtElement? + ktReceiverParameterElement: KtElement?, + withDefaultValues: Boolean = true ) { val functionDescriptor = irFunction.descriptor @@ -238,14 +239,15 @@ class FunctionGenerator(declarationGenerator: DeclarationGenerator) : Declaratio val bodyGenerator = createBodyGenerator(irFunction.symbol) functionDescriptor.valueParameters.mapTo(irFunction.valueParameters) { valueParameterDescriptor -> val ktParameter = DescriptorToSourceUtils.getSourceFromDescriptor(valueParameterDescriptor) as? KtParameter - generateValueParameterDeclaration(valueParameterDescriptor, ktParameter, bodyGenerator) + generateValueParameterDeclaration(valueParameterDescriptor, ktParameter, bodyGenerator, withDefaultValues) } } private fun generateValueParameterDeclaration( valueParameterDescriptor: ValueParameterDescriptor, ktParameter: KtParameter?, - bodyGenerator: BodyGenerator + bodyGenerator: BodyGenerator, + withDefaultValues: Boolean ): IrValueParameter = context.symbolTable.declareValueParameter( ktParameter.startOffsetOrUndefined, @@ -253,8 +255,10 @@ class FunctionGenerator(declarationGenerator: DeclarationGenerator) : Declaratio IrDeclarationOrigin.DEFINED, valueParameterDescriptor ).also { - it.defaultValue = ktParameter?.defaultValue?.let { - bodyGenerator.generateExpressionBody(it) + if (withDefaultValues) { + it.defaultValue = ktParameter?.defaultValue?.let { + bodyGenerator.generateExpressionBody(it) + } } } diff --git a/compiler/testData/ir/irText/classes/lambdaInDataClassDefaultParameter.kt b/compiler/testData/ir/irText/classes/lambdaInDataClassDefaultParameter.kt new file mode 100644 index 00000000000..f58d8d63f3b --- /dev/null +++ b/compiler/testData/ir/irText/classes/lambdaInDataClassDefaultParameter.kt @@ -0,0 +1,3 @@ +data class A(val runA: A.(String) -> Unit = {}) + +data class B(val x: Any = object {}) \ No newline at end of file diff --git a/compiler/testData/ir/irText/classes/lambdaInDataClassDefaultParameter.txt b/compiler/testData/ir/irText/classes/lambdaInDataClassDefaultParameter.txt new file mode 100644 index 00000000000..e61c321fffd --- /dev/null +++ b/compiler/testData/ir/irText/classes/lambdaInDataClassDefaultParameter.txt @@ -0,0 +1,198 @@ +FILE /lambdaInDataClassDefaultParameter.kt + CLASS CLASS A + $this: VALUE_PARAMETER this@A: A + CONSTRUCTOR public constructor A(runA: A.(kotlin.String) -> kotlin.Unit = ...) + VALUE_PARAMETER value-parameter runA: A.(kotlin.String) -> kotlin.Unit = ... + EXPRESSION_BODY + BLOCK type=A.(kotlin.String) -> kotlin.Unit origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA local final fun A.(it: kotlin.String): kotlin.Unit + $receiver: VALUE_PARAMETER this@: A + VALUE_PARAMETER value-parameter it: kotlin.String + BLOCK_BODY + RETURN type=kotlin.Nothing from='(String) on A: Unit' + GET_OBJECT 'Unit' type=kotlin.Unit + FUNCTION_REFERENCE '(String) on A: Unit' type=A.(kotlin.String) -> kotlin.Unit origin=LAMBDA + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'constructor Any()' + INSTANCE_INITIALIZER_CALL classDescriptor='A' + PROPERTY public final val runA: A.(kotlin.String) -> kotlin.Unit + FIELD PROPERTY_BACKING_FIELD public final val runA: A.(kotlin.String) -> kotlin.Unit + EXPRESSION_BODY + GET_VAR 'value-parameter runA: A.(String) -> Unit = ...' type=A.(kotlin.String) -> kotlin.Unit origin=INITIALIZE_PROPERTY_FROM_PARAMETER + FUN DEFAULT_PROPERTY_ACCESSOR public final fun (): A.(kotlin.String) -> kotlin.Unit + $this: VALUE_PARAMETER this@A: A + BLOCK_BODY + RETURN type=kotlin.Nothing from='(): A.(String) -> Unit' + GET_FIELD 'runA: A.(String) -> Unit' type=A.(kotlin.String) -> kotlin.Unit origin=null + receiver: GET_VAR 'this@A: A' type=A origin=null + FUN GENERATED_DATA_CLASS_MEMBER public final operator fun component1(): A.(kotlin.String) -> kotlin.Unit + $this: VALUE_PARAMETER this@A: A + BLOCK_BODY + RETURN type=kotlin.Nothing from='component1(): A.(String) -> Unit' + CALL '(): A.(String) -> Unit' type=A.(kotlin.String) -> kotlin.Unit origin=GET_PROPERTY + $this: GET_VAR 'this@A: A' type=A origin=null + FUN GENERATED_DATA_CLASS_MEMBER public final fun copy(runA: A.(kotlin.String) -> kotlin.Unit = ...): A + $this: VALUE_PARAMETER this@A: A + VALUE_PARAMETER value-parameter runA: A.(kotlin.String) -> kotlin.Unit = ... + EXPRESSION_BODY + CALL '(): A.(String) -> Unit' type=A.(kotlin.String) -> kotlin.Unit origin=GET_PROPERTY + $this: GET_VAR 'this@A: A' type=A origin=null + BLOCK_BODY + RETURN type=kotlin.Nothing from='copy(A.(String) -> Unit = ...): A' + CALL 'constructor A(A.(String) -> Unit = ...)' type=A origin=null + runA: GET_VAR 'value-parameter runA: A.(String) -> Unit = ...' type=A.(kotlin.String) -> kotlin.Unit origin=null + FUN GENERATED_DATA_CLASS_MEMBER public open override fun toString(): kotlin.String + $this: VALUE_PARAMETER this@A: A + BLOCK_BODY + RETURN type=kotlin.Nothing from='toString(): String' + STRING_CONCATENATION type=kotlin.String + CONST String type=kotlin.String value='A(' + CONST String type=kotlin.String value='runA=' + CALL '(): A.(String) -> Unit' type=A.(kotlin.String) -> kotlin.Unit origin=GET_PROPERTY + $this: GET_VAR 'this@A: A' type=A origin=null + CONST String type=kotlin.String value=')' + FUN GENERATED_DATA_CLASS_MEMBER public open override fun hashCode(): kotlin.Int + $this: VALUE_PARAMETER this@A: A + BLOCK_BODY + VAR IR_TEMPORARY_VARIABLE var tmp0_result: kotlin.Int + CONST Int type=kotlin.Int value='0' + SET_VAR 'tmp0_result: Int' type=kotlin.Unit origin=EQ + CALL 'hashCode(): Int' type=kotlin.Int origin=null + $this: CALL '(): A.(String) -> Unit' type=A.(kotlin.String) -> kotlin.Unit origin=GET_PROPERTY + $this: GET_VAR 'this@A: A' type=A origin=null + RETURN type=kotlin.Nothing from='hashCode(): Int' + GET_VAR 'tmp0_result: Int' type=kotlin.Int origin=null + FUN GENERATED_DATA_CLASS_MEMBER public open override fun equals(other: kotlin.Any?): kotlin.Boolean + $this: VALUE_PARAMETER this@A: A + VALUE_PARAMETER value-parameter other: kotlin.Any? + BLOCK_BODY + WHEN type=kotlin.Unit origin=null + BRANCH + if: CALL 'EQEQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQEQ + arg0: GET_VAR 'this@A: A' type=A origin=null + arg1: GET_VAR 'value-parameter other: Any?' type=kotlin.Any? origin=null + then: RETURN type=kotlin.Nothing from='equals(Any?): Boolean' + CONST Boolean type=kotlin.Boolean value='true' + WHEN type=kotlin.Unit origin=null + BRANCH + if: TYPE_OP type=kotlin.Boolean origin=NOT_INSTANCEOF typeOperand=A + GET_VAR 'value-parameter other: Any?' type=kotlin.Any? origin=null + then: RETURN type=kotlin.Nothing from='equals(Any?): Boolean' + CONST Boolean type=kotlin.Boolean value='false' + VAR IR_TEMPORARY_VARIABLE val tmp0_other_with_cast: A + TYPE_OP type=A origin=CAST typeOperand=A + GET_VAR 'value-parameter other: Any?' type=kotlin.Any? origin=null + WHEN type=kotlin.Unit origin=null + BRANCH + if: CALL 'NOT(Boolean): Boolean' type=kotlin.Boolean origin=EXCLEQ + arg0: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EXCLEQ + arg0: CALL '(): A.(String) -> Unit' type=A.(kotlin.String) -> kotlin.Unit origin=GET_PROPERTY + $this: GET_VAR 'this@A: A' type=A origin=null + arg1: CALL '(): A.(String) -> Unit' type=A.(kotlin.String) -> kotlin.Unit origin=GET_PROPERTY + $this: GET_VAR 'tmp0_other_with_cast: A' type=A origin=null + then: RETURN type=kotlin.Nothing from='equals(Any?): Boolean' + CONST Boolean type=kotlin.Boolean value='false' + RETURN type=kotlin.Nothing from='equals(Any?): Boolean' + CONST Boolean type=kotlin.Boolean value='true' + CLASS CLASS B + $this: VALUE_PARAMETER this@B: B + CONSTRUCTOR public constructor B(x: kotlin.Any = ...) + VALUE_PARAMETER value-parameter x: kotlin.Any = ... + EXPRESSION_BODY + BLOCK type=B.. origin=OBJECT_LITERAL + CLASS CLASS + $this: VALUE_PARAMETER this@: + CONSTRUCTOR public constructor () + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'constructor Any()' + INSTANCE_INITIALIZER_CALL classDescriptor='' + FUN FAKE_OVERRIDE public open override fun equals(other: kotlin.Any?): kotlin.Boolean + $this: VALUE_PARAMETER this@Any: Any + VALUE_PARAMETER value-parameter other: kotlin.Any? + FUN FAKE_OVERRIDE public open override fun hashCode(): kotlin.Int + $this: VALUE_PARAMETER this@Any: Any + FUN FAKE_OVERRIDE public open override fun toString(): kotlin.String + $this: VALUE_PARAMETER this@Any: Any + CALL 'constructor ()' type=B.. origin=OBJECT_LITERAL + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'constructor Any()' + INSTANCE_INITIALIZER_CALL classDescriptor='B' + PROPERTY public final val x: kotlin.Any + FIELD PROPERTY_BACKING_FIELD public final val x: kotlin.Any + EXPRESSION_BODY + GET_VAR 'value-parameter x: Any = ...' type=kotlin.Any origin=INITIALIZE_PROPERTY_FROM_PARAMETER + FUN DEFAULT_PROPERTY_ACCESSOR public final fun (): kotlin.Any + $this: VALUE_PARAMETER this@B: B + BLOCK_BODY + RETURN type=kotlin.Nothing from='(): Any' + GET_FIELD 'x: Any' type=kotlin.Any origin=null + receiver: GET_VAR 'this@B: B' type=B origin=null + FUN GENERATED_DATA_CLASS_MEMBER public final operator fun component1(): kotlin.Any + $this: VALUE_PARAMETER this@B: B + BLOCK_BODY + RETURN type=kotlin.Nothing from='component1(): Any' + CALL '(): Any' type=kotlin.Any origin=GET_PROPERTY + $this: GET_VAR 'this@B: B' type=B origin=null + FUN GENERATED_DATA_CLASS_MEMBER public final fun copy(x: kotlin.Any = ...): B + $this: VALUE_PARAMETER this@B: B + VALUE_PARAMETER value-parameter x: kotlin.Any = ... + EXPRESSION_BODY + CALL '(): Any' type=kotlin.Any origin=GET_PROPERTY + $this: GET_VAR 'this@B: B' type=B origin=null + BLOCK_BODY + RETURN type=kotlin.Nothing from='copy(Any = ...): B' + CALL 'constructor B(Any = ...)' type=B origin=null + x: GET_VAR 'value-parameter x: Any = ...' type=kotlin.Any origin=null + FUN GENERATED_DATA_CLASS_MEMBER public open override fun toString(): kotlin.String + $this: VALUE_PARAMETER this@B: B + BLOCK_BODY + RETURN type=kotlin.Nothing from='toString(): String' + STRING_CONCATENATION type=kotlin.String + CONST String type=kotlin.String value='B(' + CONST String type=kotlin.String value='x=' + CALL '(): Any' type=kotlin.Any origin=GET_PROPERTY + $this: GET_VAR 'this@B: B' type=B origin=null + CONST String type=kotlin.String value=')' + FUN GENERATED_DATA_CLASS_MEMBER public open override fun hashCode(): kotlin.Int + $this: VALUE_PARAMETER this@B: B + BLOCK_BODY + VAR IR_TEMPORARY_VARIABLE var tmp0_result: kotlin.Int + CONST Int type=kotlin.Int value='0' + SET_VAR 'tmp0_result: Int' type=kotlin.Unit origin=EQ + CALL 'hashCode(): Int' type=kotlin.Int origin=null + $this: CALL '(): Any' type=kotlin.Any origin=GET_PROPERTY + $this: GET_VAR 'this@B: B' type=B origin=null + RETURN type=kotlin.Nothing from='hashCode(): Int' + GET_VAR 'tmp0_result: Int' type=kotlin.Int origin=null + FUN GENERATED_DATA_CLASS_MEMBER public open override fun equals(other: kotlin.Any?): kotlin.Boolean + $this: VALUE_PARAMETER this@B: B + VALUE_PARAMETER value-parameter other: kotlin.Any? + BLOCK_BODY + WHEN type=kotlin.Unit origin=null + BRANCH + if: CALL 'EQEQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQEQ + arg0: GET_VAR 'this@B: B' type=B origin=null + arg1: GET_VAR 'value-parameter other: Any?' type=kotlin.Any? origin=null + then: RETURN type=kotlin.Nothing from='equals(Any?): Boolean' + CONST Boolean type=kotlin.Boolean value='true' + WHEN type=kotlin.Unit origin=null + BRANCH + if: TYPE_OP type=kotlin.Boolean origin=NOT_INSTANCEOF typeOperand=B + GET_VAR 'value-parameter other: Any?' type=kotlin.Any? origin=null + then: RETURN type=kotlin.Nothing from='equals(Any?): Boolean' + CONST Boolean type=kotlin.Boolean value='false' + VAR IR_TEMPORARY_VARIABLE val tmp0_other_with_cast: B + TYPE_OP type=B origin=CAST typeOperand=B + GET_VAR 'value-parameter other: Any?' type=kotlin.Any? origin=null + WHEN type=kotlin.Unit origin=null + BRANCH + if: CALL 'NOT(Boolean): Boolean' type=kotlin.Boolean origin=EXCLEQ + arg0: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EXCLEQ + arg0: CALL '(): Any' type=kotlin.Any origin=GET_PROPERTY + $this: GET_VAR 'this@B: B' type=B origin=null + arg1: CALL '(): Any' type=kotlin.Any origin=GET_PROPERTY + $this: GET_VAR 'tmp0_other_with_cast: B' type=B origin=null + then: RETURN type=kotlin.Nothing from='equals(Any?): Boolean' + CONST Boolean type=kotlin.Boolean value='false' + RETURN type=kotlin.Nothing from='equals(Any?): Boolean' + CONST Boolean type=kotlin.Boolean value='true' diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index 84385635066..d9e715fc282 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -158,6 +158,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { doTest(fileName); } + @TestMetadata("lambdaInDataClassDefaultParameter.kt") + public void testLambdaInDataClassDefaultParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/classes/lambdaInDataClassDefaultParameter.kt"); + doTest(fileName); + } + @TestMetadata("localClasses.kt") public void testLocalClasses() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/classes/localClasses.kt");