From 2c068b8c5a8c6a454addea4ffb6ce59b4a0a92b3 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Fri, 30 Jul 2021 20:32:56 +0500 Subject: [PATCH] [K/N][IR] Fix for https://youtrack.jetbrains.com/issue/KT-47814 --- .../kotlin/backend/konan/ToplevelPhases.kt | 23 +++++++ .../konan/lower/SamSuperTypesChecker.kt | 63 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SamSuperTypesChecker.kt diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index 0e0beacf12e..d94852cffb9 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataMo import org.jetbrains.kotlin.backend.konan.MemoryModel import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier +import org.jetbrains.kotlin.backend.konan.lower.SamSuperTypesChecker import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport import org.jetbrains.kotlin.backend.konan.serialization.KonanIdSignaturer import org.jetbrains.kotlin.backend.konan.serialization.KonanIrModuleSerializer @@ -149,6 +150,26 @@ internal val copyDefaultValuesToActualPhase = konanUnitPhase( description = "Copy default values from expect to actual declarations" ) +/* + * Sometimes psi2ir produces IR with non-trivial variance in super types of SAM conversions (this is a language design issue). + * Earlier this was solved with just erasing all such variances but this might lead to some other hard to debug problems, + * so after handling the majority of corner cases correctly in psi2ir it is safe to assume that such cases don't get here and + * even if they do, then it's better to throw an error right away than to dig out weird crashes down the pipeline or even at runtime. + * We explicitly check this, also fixing older klibs built with previous compiler versions by applying the same trick as before. + */ +internal val checkSamSuperTypesPhase = konanUnitPhase( + op = { + irModule!!.files + .forEach { SamSuperTypesChecker(this, it, mode = SamSuperTypesChecker.Mode.THROW).run() } + // TODO: This is temporary for handling klibs produced with earlier compiler versions. + irModules.values + .flatMap { it.files } + .forEach { SamSuperTypesChecker(this, it, mode = SamSuperTypesChecker.Mode.ERASE).run() } + }, + name = "CheckSamSuperTypes", + description = "Check SAM conversions super types" +) + internal val serializerPhase = konanUnitPhase( op = { val expectActualLinker = config.configuration.get(CommonConfigurationKeys.EXPECT_ACTUAL_LINKER) ?: false @@ -424,6 +445,7 @@ val toplevelPhase: CompilerPhase<*, Unit, Unit> = namedUnitPhase( psiToIrPhase then destroySymbolTablePhase then copyDefaultValuesToActualPhase then + checkSamSuperTypesPhase then serializerPhase then specialBackendChecksPhase then namedUnitPhase( @@ -484,5 +506,6 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) { disableIf(destroySymbolTablePhase, isDescriptorsOnlyLibrary) disableIf(copyDefaultValuesToActualPhase, isDescriptorsOnlyLibrary) disableIf(specialBackendChecksPhase, isDescriptorsOnlyLibrary) + disableIf(checkSamSuperTypesPhase, isDescriptorsOnlyLibrary) } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SamSuperTypesChecker.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SamSuperTypesChecker.kt new file mode 100644 index 00000000000..7ed908b2f37 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SamSuperTypesChecker.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan.lower + +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.reportCompilationError +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrTypeOperator +import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall +import org.jetbrains.kotlin.ir.types.IrSimpleType +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.IrTypeProjection +import org.jetbrains.kotlin.ir.types.impl.buildSimpleType +import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.types.Variance + +internal class SamSuperTypesChecker(private val context: Context, + private val irFile: IrFile, + private val mode: Mode) { + enum class Mode { + ERASE, + THROW + } + + private fun IrType.eraseProjections(owner: IrElement): IrType { + if (this !is IrSimpleType) return this + return buildSimpleType { + this.classifier = this@eraseProjections.classifier + this.hasQuestionMark = this@eraseProjections.hasQuestionMark + this.annotations = this@eraseProjections.annotations + this.arguments = this@eraseProjections.arguments.mapIndexed { index, argument -> + if (argument !is IrTypeProjection) + argument + else { + if (mode == Mode.THROW && argument.variance != Variance.INVARIANT) { + context.reportCompilationError( + "Unexpected variance in super type argument: ${argument.variance} @$index", irFile, owner) + } + makeTypeProjection(argument.type.eraseProjections(owner), Variance.INVARIANT) + } + } + } + } + + fun run() { + irFile.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression { + expression.transformChildrenVoid(this) + + if (expression.operator == IrTypeOperator.SAM_CONVERSION) + expression.typeOperand = expression.typeOperand.eraseProjections(expression) + return expression + } + }) + } +} \ No newline at end of file