From 96ddd9bbf301fbf1741ed06e939aaadd25582310 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Mon, 24 Jul 2023 16:22:52 +0200 Subject: [PATCH] K2: report INLINE_CALL_CYCLE error in JVM backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes the following FirLightTreeDiagnosticsTestWithJvmIrBackendGenerated tests:   testPropertyInlineCycle   testInlineCycle   testSuspendInlineCycle   testIndirectInlineCycle #KT-59586 --- .../codegen/inline/GlobalInlineContext.kt | 23 +++++++++++-------- .../kotlin/codegen/inline/PsiInlineCodegen.kt | 12 ++++++++-- .../kotlin/codegen/state/GenerationState.kt | 2 +- .../diagnostics/rendering/CommonRenderers.kt | 4 ++++ .../jvm/diagnostics/JvmBackendErrors.kt | 6 +++++ .../rendering/DefaultErrorMessages.java | 1 + .../jvm/codegen/IrInlineCallGenerator.kt | 15 ++++++++---- 7 files changed, 46 insertions(+), 17 deletions(-) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/GlobalInlineContext.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/GlobalInlineContext.kt index 3b155165690..5f8c1eb7ed8 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/GlobalInlineContext.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/GlobalInlineContext.kt @@ -5,17 +5,14 @@ package org.jetbrains.kotlin.codegen.inline -import com.intellij.psi.PsiElement import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.diagnostics.DiagnosticSink -import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.utils.threadLocal import java.util.* -class GlobalInlineContext(private val diagnostics: DiagnosticSink) { +class GlobalInlineContext { // Ordered set of declarations and inline calls being generated right now. // No call in it should point to a declaration that's before it in the stack. - private val inlineCallsAndDeclarations by threadLocal { LinkedList() } + private val inlineCallsAndDeclarations by threadLocal { LinkedList() } private val inlineDeclarationSet by threadLocal { mutableSetOf() } private val typesUsedInInlineFunctions by threadLocal { LinkedList>() } @@ -30,13 +27,17 @@ class GlobalInlineContext(private val diagnostics: DiagnosticSink) { inlineDeclarationSet.remove(inlineCallsAndDeclarations.removeLast()) } - fun enterIntoInlining(callee: CallableDescriptor?, element: PsiElement?): Boolean { + fun enterIntoInlining( + callee: CallableDescriptor?, + element: InlineFunctionSource?, + reportInlineCallCycle: (InlineFunctionSource, CallableDescriptor) -> Unit, + ): Boolean { if (callee != null && callee.original in inlineDeclarationSet) { - element?.let { diagnostics.report(Errors.INLINE_CALL_CYCLE.on(it, callee.original)) } + element?.let { reportInlineCallCycle(it, callee.original) } for ((call, callTarget) in inlineCallsAndDeclarations.dropWhile { it != callee.original }.zipWithNext()) { // Every call element should be followed by the callee's descriptor. - if (call is PsiElement && callTarget is CallableDescriptor) { - diagnostics.report(Errors.INLINE_CALL_CYCLE.on(call, callTarget)) + if (call is InlineFunctionSource && callTarget is CallableDescriptor) { + reportInlineCallCycle(call, callTarget) } } return false @@ -55,4 +56,6 @@ class GlobalInlineContext(private val diagnostics: DiagnosticSink) { fun recordTypeFromInlineFunction(type: String) = typesUsedInInlineFunctions.peek().add(type) fun isTypeFromInlineFunction(type: String) = typesUsedInInlineFunctions.peek().contains(type) -} \ No newline at end of file + + abstract class InlineFunctionSource +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt index 5936df6a5e2..158e8386660 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.codegen.inline +import com.intellij.psi.PsiElement import org.jetbrains.kotlin.builtins.isSuspendFunctionTypeOrSubtype import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.binding.CalculatedClosure @@ -12,6 +13,7 @@ import org.jetbrains.kotlin.codegen.binding.CodegenBinding import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.load.kotlin.TypeMappingMode import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getParentOfType @@ -58,8 +60,12 @@ class PsiInlineCodegen( ) { (sourceCompiler as PsiSourceCompilerForInline).callDefault = callDefault assert(hiddenParameters.isEmpty()) { "putHiddenParamsIntoLocals() should be called after processHiddenParameters()" } - if (!state.globalInlineContext.enterIntoInlining(functionDescriptor, resolvedCall?.call?.callElement)) { - generateStub(resolvedCall?.call?.callElement?.text ?: "", codegen) + val psiElement = resolvedCall?.call?.callElement + val element = psiElement?.let(::PsiInlineFunctionSource) + if (!state.globalInlineContext.enterIntoInlining(functionDescriptor, element) { reportOn, callee -> + state.diagnostics.report(Errors.INLINE_CALL_CYCLE.on((reportOn as PsiInlineFunctionSource).psi, callee)) + }) { + generateStub(psiElement?.text ?: "", codegen) return } try { @@ -194,6 +200,8 @@ class PsiInlineCodegen( val callerPackage = DescriptorUtils.getParentOfType(caller, PackageFragmentDescriptor::class.java) ?: return false return callerPackage.fqName.asString().startsWith("kotlin.") } + + private class PsiInlineFunctionSource(val psi: PsiElement) : GlobalInlineContext.InlineFunctionSource() } private val FunctionDescriptor.explicitParameters diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt index 41b9fd1de3e..831e11923d4 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt @@ -255,7 +255,7 @@ class GenerationState private constructor( IntrinsicMethods(languageVersionSettings.apiVersion <= ApiVersion.parse(KotlinVersion.CURRENT.toString())!!) val samWrapperClasses: SamWrapperClasses = SamWrapperClasses(this) - val globalInlineContext: GlobalInlineContext = GlobalInlineContext(diagnostics) + val globalInlineContext: GlobalInlineContext = GlobalInlineContext() val mappingsClassesForWhenByEnum: MappingsClassesForWhenByEnum = MappingsClassesForWhenByEnum(this) val jvmRuntimeTypes: JvmRuntimeTypes = JvmRuntimeTypes( module, languageVersionSettings, config.generateOptimizedCallableReferenceSuperClasses diff --git a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/rendering/CommonRenderers.kt b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/rendering/CommonRenderers.kt index 81c7104645f..707da2f7650 100644 --- a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/rendering/CommonRenderers.kt +++ b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/rendering/CommonRenderers.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.diagnostics.rendering import com.intellij.openapi.util.text.StringUtil import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.types.Variance import java.io.PrintWriter import java.io.StringWriter @@ -18,6 +19,9 @@ object CommonRenderers { @JvmField val STRING = Renderer { it } + @JvmField + val NAME = Renderer { it.asString() } + @JvmField val THROWABLE = Renderer { val writer = StringWriter() diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmBackendErrors.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmBackendErrors.kt index 88527e1089e..5c773dbca85 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmBackendErrors.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmBackendErrors.kt @@ -12,7 +12,9 @@ import org.jetbrains.kotlin.diagnostics.error0 import org.jetbrains.kotlin.diagnostics.error1 import org.jetbrains.kotlin.diagnostics.error2 import org.jetbrains.kotlin.diagnostics.rendering.* +import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers.NAME import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers.STRING +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.MemberComparator import org.jetbrains.kotlin.utils.join @@ -33,6 +35,8 @@ object JvmBackendErrors { val SCRIPT_CAPTURING_ENUM by error1() val SCRIPT_CAPTURING_ENUM_ENTRY by error1() + val INLINE_CALL_CYCLE by error1() + init { RootDiagnosticRendererFactory.registerFactory(KtDefaultJvmErrorMessages) } @@ -66,5 +70,7 @@ object KtDefaultJvmErrorMessages : BaseDiagnosticRendererFactory() { map.put(JvmBackendErrors.SCRIPT_CAPTURING_INTERFACE, "Interface {0} captures the script class instance. Try to use class instead", STRING) map.put(JvmBackendErrors.SCRIPT_CAPTURING_ENUM, "Enum class {0} captures the script class instance. Try to use class or anonymous object instead", STRING) map.put(JvmBackendErrors.SCRIPT_CAPTURING_ENUM_ENTRY, "Enum entry {0} captures the script class instance. Try to use class or anonymous object instead", STRING) + + map.put(JvmBackendErrors.INLINE_CALL_CYCLE, "The ''{0}'' invocation is a part of inline cycle", NAME) } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 6cfb8b0b0ed..140eecfb60b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -29,6 +29,7 @@ import java.util.*; import static org.jetbrains.kotlin.diagnostics.Errors.*; import static org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers.*; import static org.jetbrains.kotlin.diagnostics.rendering.Renderers.*; +import static org.jetbrains.kotlin.diagnostics.rendering.Renderers.NAME; import static org.jetbrains.kotlin.diagnostics.rendering.RenderingContext.of; public class DefaultErrorMessages { diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCallGenerator.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCallGenerator.kt index 2b1b2dc05e3..5942c615c9c 100644 --- a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCallGenerator.kt +++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCallGenerator.kt @@ -5,13 +5,16 @@ package org.jetbrains.kotlin.backend.jvm.codegen -import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager +import org.jetbrains.kotlin.backend.jvm.ir.fileParent import org.jetbrains.kotlin.backend.jvm.ir.psiElement import org.jetbrains.kotlin.backend.jvm.ir.suspendFunctionOriginal import org.jetbrains.kotlin.backend.jvm.mapping.IrCallableMethod import org.jetbrains.kotlin.codegen.AsmUtil +import org.jetbrains.kotlin.codegen.inline.GlobalInlineContext +import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression +import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmBackendErrors interface IrInlineCallGenerator : IrCallGenerator { override fun genCall( @@ -20,10 +23,12 @@ interface IrInlineCallGenerator : IrCallGenerator { expression: IrFunctionAccessExpression, isInsideIfCondition: Boolean, ) { - val element = PsiSourceManager.findPsiElement(expression, codegen.irFunction) - ?: PsiSourceManager.findPsiElement(codegen.irFunction) + val element = IrInlineFunctionSource(expression) val descriptor = expression.symbol.owner.suspendFunctionOriginal().toIrBasedDescriptor() - if (!codegen.state.globalInlineContext.enterIntoInlining(descriptor, element)) { + if (!codegen.state.globalInlineContext.enterIntoInlining(descriptor, element) { reportOn, callee -> + codegen.context.ktDiagnosticReporter.at((reportOn as IrInlineFunctionSource).ir, codegen.irFunction.fileParent) + .report(JvmBackendErrors.INLINE_CALL_CYCLE, callee.name) + }) { genCycleStub(expression.psiElement?.text ?: "", codegen) return } @@ -44,4 +49,6 @@ interface IrInlineCallGenerator : IrCallGenerator { fun genCycleStub(text: String, codegen: ExpressionCodegen) { AsmUtil.genThrow(codegen.visitor, "java/lang/UnsupportedOperationException", "Call is a part of inline call cycle: $text") } + + private class IrInlineFunctionSource(val ir: IrElement) : GlobalInlineContext.InlineFunctionSource() }