K2: report INLINE_CALL_CYCLE error in JVM backend
This fixes the following FirLightTreeDiagnosticsTestWithJvmIrBackendGenerated tests: testPropertyInlineCycle testInlineCycle testSuspendInlineCycle testIndirectInlineCycle #KT-59586
This commit is contained in:
@@ -5,17 +5,14 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen.inline
|
package org.jetbrains.kotlin.codegen.inline
|
||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
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 org.jetbrains.kotlin.utils.threadLocal
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class GlobalInlineContext(private val diagnostics: DiagnosticSink) {
|
class GlobalInlineContext {
|
||||||
// Ordered set of declarations and inline calls being generated right now.
|
// 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.
|
// No call in it should point to a declaration that's before it in the stack.
|
||||||
private val inlineCallsAndDeclarations by threadLocal { LinkedList<Any? /* CallableDescriptor | PsiElement? */>() }
|
private val inlineCallsAndDeclarations by threadLocal { LinkedList<Any? /* CallableDescriptor | InlineFunctionSource? */>() }
|
||||||
private val inlineDeclarationSet by threadLocal { mutableSetOf<CallableDescriptor>() }
|
private val inlineDeclarationSet by threadLocal { mutableSetOf<CallableDescriptor>() }
|
||||||
|
|
||||||
private val typesUsedInInlineFunctions by threadLocal { LinkedList<MutableSet<String>>() }
|
private val typesUsedInInlineFunctions by threadLocal { LinkedList<MutableSet<String>>() }
|
||||||
@@ -30,13 +27,17 @@ class GlobalInlineContext(private val diagnostics: DiagnosticSink) {
|
|||||||
inlineDeclarationSet.remove(inlineCallsAndDeclarations.removeLast())
|
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) {
|
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()) {
|
for ((call, callTarget) in inlineCallsAndDeclarations.dropWhile { it != callee.original }.zipWithNext()) {
|
||||||
// Every call element should be followed by the callee's descriptor.
|
// Every call element should be followed by the callee's descriptor.
|
||||||
if (call is PsiElement && callTarget is CallableDescriptor) {
|
if (call is InlineFunctionSource && callTarget is CallableDescriptor) {
|
||||||
diagnostics.report(Errors.INLINE_CALL_CYCLE.on(call, callTarget))
|
reportInlineCallCycle(call, callTarget)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -55,4 +56,6 @@ class GlobalInlineContext(private val diagnostics: DiagnosticSink) {
|
|||||||
fun recordTypeFromInlineFunction(type: String) = typesUsedInInlineFunctions.peek().add(type)
|
fun recordTypeFromInlineFunction(type: String) = typesUsedInInlineFunctions.peek().add(type)
|
||||||
|
|
||||||
fun isTypeFromInlineFunction(type: String) = typesUsedInInlineFunctions.peek().contains(type)
|
fun isTypeFromInlineFunction(type: String) = typesUsedInInlineFunctions.peek().contains(type)
|
||||||
|
|
||||||
|
abstract class InlineFunctionSource
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen.inline
|
package org.jetbrains.kotlin.codegen.inline
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.builtins.isSuspendFunctionTypeOrSubtype
|
import org.jetbrains.kotlin.builtins.isSuspendFunctionTypeOrSubtype
|
||||||
import org.jetbrains.kotlin.codegen.*
|
import org.jetbrains.kotlin.codegen.*
|
||||||
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure
|
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.coroutines.getOrCreateJvmSuspendFunctionView
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
|
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||||
@@ -58,8 +60,12 @@ class PsiInlineCodegen(
|
|||||||
) {
|
) {
|
||||||
(sourceCompiler as PsiSourceCompilerForInline).callDefault = callDefault
|
(sourceCompiler as PsiSourceCompilerForInline).callDefault = callDefault
|
||||||
assert(hiddenParameters.isEmpty()) { "putHiddenParamsIntoLocals() should be called after processHiddenParameters()" }
|
assert(hiddenParameters.isEmpty()) { "putHiddenParamsIntoLocals() should be called after processHiddenParameters()" }
|
||||||
if (!state.globalInlineContext.enterIntoInlining(functionDescriptor, resolvedCall?.call?.callElement)) {
|
val psiElement = resolvedCall?.call?.callElement
|
||||||
generateStub(resolvedCall?.call?.callElement?.text ?: "<no source>", codegen)
|
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 ?: "<no source>", codegen)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -194,6 +200,8 @@ class PsiInlineCodegen(
|
|||||||
val callerPackage = DescriptorUtils.getParentOfType(caller, PackageFragmentDescriptor::class.java) ?: return false
|
val callerPackage = DescriptorUtils.getParentOfType(caller, PackageFragmentDescriptor::class.java) ?: return false
|
||||||
return callerPackage.fqName.asString().startsWith("kotlin.")
|
return callerPackage.fqName.asString().startsWith("kotlin.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class PsiInlineFunctionSource(val psi: PsiElement) : GlobalInlineContext.InlineFunctionSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val FunctionDescriptor.explicitParameters
|
private val FunctionDescriptor.explicitParameters
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ class GenerationState private constructor(
|
|||||||
IntrinsicMethods(languageVersionSettings.apiVersion <= ApiVersion.parse(KotlinVersion.CURRENT.toString())!!)
|
IntrinsicMethods(languageVersionSettings.apiVersion <= ApiVersion.parse(KotlinVersion.CURRENT.toString())!!)
|
||||||
|
|
||||||
val samWrapperClasses: SamWrapperClasses = SamWrapperClasses(this)
|
val samWrapperClasses: SamWrapperClasses = SamWrapperClasses(this)
|
||||||
val globalInlineContext: GlobalInlineContext = GlobalInlineContext(diagnostics)
|
val globalInlineContext: GlobalInlineContext = GlobalInlineContext()
|
||||||
val mappingsClassesForWhenByEnum: MappingsClassesForWhenByEnum = MappingsClassesForWhenByEnum(this)
|
val mappingsClassesForWhenByEnum: MappingsClassesForWhenByEnum = MappingsClassesForWhenByEnum(this)
|
||||||
val jvmRuntimeTypes: JvmRuntimeTypes = JvmRuntimeTypes(
|
val jvmRuntimeTypes: JvmRuntimeTypes = JvmRuntimeTypes(
|
||||||
module, languageVersionSettings, config.generateOptimizedCallableReferenceSuperClasses
|
module, languageVersionSettings, config.generateOptimizedCallableReferenceSuperClasses
|
||||||
|
|||||||
+4
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.diagnostics.rendering
|
|||||||
|
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
@@ -18,6 +19,9 @@ object CommonRenderers {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val STRING = Renderer<String> { it }
|
val STRING = Renderer<String> { it }
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
val NAME = Renderer<Name> { it.asString() }
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
val THROWABLE = Renderer<Throwable> {
|
val THROWABLE = Renderer<Throwable> {
|
||||||
val writer = StringWriter()
|
val writer = StringWriter()
|
||||||
|
|||||||
+6
@@ -12,7 +12,9 @@ import org.jetbrains.kotlin.diagnostics.error0
|
|||||||
import org.jetbrains.kotlin.diagnostics.error1
|
import org.jetbrains.kotlin.diagnostics.error1
|
||||||
import org.jetbrains.kotlin.diagnostics.error2
|
import org.jetbrains.kotlin.diagnostics.error2
|
||||||
import org.jetbrains.kotlin.diagnostics.rendering.*
|
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.diagnostics.rendering.CommonRenderers.STRING
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.MemberComparator
|
import org.jetbrains.kotlin.resolve.MemberComparator
|
||||||
import org.jetbrains.kotlin.utils.join
|
import org.jetbrains.kotlin.utils.join
|
||||||
|
|
||||||
@@ -33,6 +35,8 @@ object JvmBackendErrors {
|
|||||||
val SCRIPT_CAPTURING_ENUM by error1<PsiElement, String>()
|
val SCRIPT_CAPTURING_ENUM by error1<PsiElement, String>()
|
||||||
val SCRIPT_CAPTURING_ENUM_ENTRY by error1<PsiElement, String>()
|
val SCRIPT_CAPTURING_ENUM_ENTRY by error1<PsiElement, String>()
|
||||||
|
|
||||||
|
val INLINE_CALL_CYCLE by error1<PsiElement, Name>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
RootDiagnosticRendererFactory.registerFactory(KtDefaultJvmErrorMessages)
|
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_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, "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.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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -29,6 +29,7 @@ import java.util.*;
|
|||||||
import static org.jetbrains.kotlin.diagnostics.Errors.*;
|
import static org.jetbrains.kotlin.diagnostics.Errors.*;
|
||||||
import static org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers.*;
|
import static org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers.*;
|
||||||
import static org.jetbrains.kotlin.diagnostics.rendering.Renderers.*;
|
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;
|
import static org.jetbrains.kotlin.diagnostics.rendering.RenderingContext.of;
|
||||||
|
|
||||||
public class DefaultErrorMessages {
|
public class DefaultErrorMessages {
|
||||||
|
|||||||
+11
-4
@@ -5,13 +5,16 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
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.psiElement
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.suspendFunctionOriginal
|
import org.jetbrains.kotlin.backend.jvm.ir.suspendFunctionOriginal
|
||||||
import org.jetbrains.kotlin.backend.jvm.mapping.IrCallableMethod
|
import org.jetbrains.kotlin.backend.jvm.mapping.IrCallableMethod
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
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.descriptors.toIrBasedDescriptor
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmBackendErrors
|
||||||
|
|
||||||
interface IrInlineCallGenerator : IrCallGenerator {
|
interface IrInlineCallGenerator : IrCallGenerator {
|
||||||
override fun genCall(
|
override fun genCall(
|
||||||
@@ -20,10 +23,12 @@ interface IrInlineCallGenerator : IrCallGenerator {
|
|||||||
expression: IrFunctionAccessExpression,
|
expression: IrFunctionAccessExpression,
|
||||||
isInsideIfCondition: Boolean,
|
isInsideIfCondition: Boolean,
|
||||||
) {
|
) {
|
||||||
val element = PsiSourceManager.findPsiElement(expression, codegen.irFunction)
|
val element = IrInlineFunctionSource(expression)
|
||||||
?: PsiSourceManager.findPsiElement(codegen.irFunction)
|
|
||||||
val descriptor = expression.symbol.owner.suspendFunctionOriginal().toIrBasedDescriptor()
|
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 ?: "<no source>", codegen)
|
genCycleStub(expression.psiElement?.text ?: "<no source>", codegen)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -44,4 +49,6 @@ interface IrInlineCallGenerator : IrCallGenerator {
|
|||||||
fun genCycleStub(text: String, codegen: ExpressionCodegen) {
|
fun genCycleStub(text: String, codegen: ExpressionCodegen) {
|
||||||
AsmUtil.genThrow(codegen.visitor, "java/lang/UnsupportedOperationException", "Call is a part of inline call cycle: $text")
|
AsmUtil.genThrow(codegen.visitor, "java/lang/UnsupportedOperationException", "Call is a part of inline call cycle: $text")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class IrInlineFunctionSource(val ir: IrElement) : GlobalInlineContext.InlineFunctionSource()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user