JVM_IR: Implement some BE diagnostics
TODO proper diagnostics tests with BE diagnostics
This commit is contained in:
@@ -16,37 +16,36 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen
|
package org.jetbrains.kotlin.codegen
|
||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
import org.jetbrains.kotlin.codegen.inline.InlineCall
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
|
||||||
|
|
||||||
class InlineCycleReporter(private val diagnostics: DiagnosticSink) {
|
class InlineCycleReporter(private val diagnostics: DiagnosticSink) {
|
||||||
|
|
||||||
private val processingFunctions = linkedMapOf<PsiElement, CallableDescriptor>()
|
private val processingFunctions = linkedMapOf<Any, InlineCall>()
|
||||||
|
|
||||||
fun enterIntoInlining(call: ResolvedCall<*>?): Boolean {
|
fun enterIntoInlining(call: InlineCall?): Boolean {
|
||||||
//null call for default method inlining
|
// null call for default method inlining
|
||||||
if (call != null) {
|
val id = call?.id
|
||||||
val callElement = call.call.callElement
|
if (id != null) {
|
||||||
if (processingFunctions.contains(callElement)) {
|
if (processingFunctions.contains(id)) {
|
||||||
val cycle = processingFunctions.asSequence().dropWhile { it.key != callElement }
|
val cycle = processingFunctions.values.dropWhile { it.id != id }
|
||||||
cycle.forEach {
|
for (cycleCall in cycle) {
|
||||||
diagnostics.report(Errors.INLINE_CALL_CYCLE.on(it.key, it.value))
|
val callPsiElement = cycleCall.callElement
|
||||||
|
if (callPsiElement != null) {
|
||||||
|
diagnostics.report(Errors.INLINE_CALL_CYCLE.on(callPsiElement, cycleCall.calleeDescriptor))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
processingFunctions.put(callElement, call.resultingDescriptor.original)
|
processingFunctions[id] = call
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun exitFromInliningOf(call: ResolvedCall<*>?) {
|
fun exitFromInliningOf(call: InlineCall?) {
|
||||||
if (call != null) {
|
if (call != null) {
|
||||||
val callElement = call.call.callElement
|
processingFunctions.remove(call.id)
|
||||||
processingFunctions.remove(callElement)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ class GlobalInlineContext(diagnostics: DiagnosticSink) {
|
|||||||
|
|
||||||
private val typesUsedInInlineFunctions = LinkedList<MutableSet<String>>()
|
private val typesUsedInInlineFunctions = LinkedList<MutableSet<String>>()
|
||||||
|
|
||||||
fun enterIntoInlining(call: ResolvedCall<*>?) =
|
fun enterIntoInlining(call: InlineCall?) =
|
||||||
inlineCycleReporter.enterIntoInlining(call).also {
|
inlineCycleReporter.enterIntoInlining(call).also {
|
||||||
if (it) typesUsedInInlineFunctions.push(hashSetOf())
|
if (it) typesUsedInInlineFunctions.push(hashSetOf())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun exitFromInliningOf(call: ResolvedCall<*>?) {
|
fun exitFromInliningOf(call: InlineCall?) {
|
||||||
inlineCycleReporter.exitFromInliningOf(call)
|
inlineCycleReporter.exitFromInliningOf(call)
|
||||||
val pop = typesUsedInInlineFunctions.pop()
|
val pop = typesUsedInInlineFunctions.pop()
|
||||||
typesUsedInInlineFunctions.peek()?.addAll(pop)
|
typesUsedInInlineFunctions.peek()?.addAll(pop)
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 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.codegen.inline
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
|
|
||||||
|
interface InlineCall {
|
||||||
|
val id: Any
|
||||||
|
val calleeDescriptor: CallableDescriptor
|
||||||
|
val callElement: PsiElement?
|
||||||
|
}
|
||||||
|
|
||||||
|
class InlineCallImpl(
|
||||||
|
override val calleeDescriptor: CallableDescriptor,
|
||||||
|
override val callElement: PsiElement
|
||||||
|
) : InlineCall {
|
||||||
|
|
||||||
|
override val id: Any
|
||||||
|
get() = callElement
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun of(resolvedCall: ResolvedCall<*>?) =
|
||||||
|
resolvedCall?.run {
|
||||||
|
InlineCallImpl(resultingDescriptor.original, call.callElement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -60,14 +60,15 @@ class PsiInlineCodegen(
|
|||||||
callDefault: Boolean,
|
callDefault: Boolean,
|
||||||
codegen: ExpressionCodegen
|
codegen: ExpressionCodegen
|
||||||
) {
|
) {
|
||||||
if (!state.globalInlineContext.enterIntoInlining(resolvedCall)) {
|
val inlineCall = InlineCallImpl.of(resolvedCall)
|
||||||
|
if (!state.globalInlineContext.enterIntoInlining(inlineCall)) {
|
||||||
generateStub(resolvedCall, codegen)
|
generateStub(resolvedCall, codegen)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
performInline(resolvedCall?.typeArguments?.keys?.toList(), callDefault, callDefault, codegen.typeSystem)
|
performInline(resolvedCall?.typeArguments?.keys?.toList(), callDefault, callDefault, codegen.typeSystem)
|
||||||
} finally {
|
} finally {
|
||||||
state.globalInlineContext.exitFromInliningOf(resolvedCall)
|
state.globalInlineContext.exitFromInliningOf(inlineCall)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.config.languageVersionSettings
|
|||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
@@ -643,7 +644,7 @@ class ExpressionCodegen(
|
|||||||
val isNonLocalReturn =
|
val isNonLocalReturn =
|
||||||
methodSignatureMapper.mapFunctionName(owner) != methodSignatureMapper.mapFunctionName(irFunction)
|
methodSignatureMapper.mapFunctionName(owner) != methodSignatureMapper.mapFunctionName(irFunction)
|
||||||
if (isNonLocalReturn && state.isInlineDisabled) {
|
if (isNonLocalReturn && state.isInlineDisabled) {
|
||||||
//TODO: state.diagnostics.report(Errors.NON_LOCAL_RETURN_IN_DISABLED_INLINE.on(expression))
|
context.psiErrorBuilder.at(expression, owner).report(Errors.NON_LOCAL_RETURN_IN_DISABLED_INLINE)
|
||||||
genThrow(
|
genThrow(
|
||||||
mv, "java/lang/UnsupportedOperationException",
|
mv, "java/lang/UnsupportedOperationException",
|
||||||
"Non-local returns are not allowed with inlining disabled"
|
"Non-local returns are not allowed with inlining disabled"
|
||||||
|
|||||||
+40
-12
@@ -5,15 +5,14 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter
|
import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.isLambda
|
import org.jetbrains.kotlin.backend.jvm.ir.isLambda
|
||||||
import org.jetbrains.kotlin.codegen.IrExpressionLambda
|
import org.jetbrains.kotlin.codegen.*
|
||||||
import org.jetbrains.kotlin.codegen.JvmKotlinType
|
|
||||||
import org.jetbrains.kotlin.codegen.StackValue
|
|
||||||
import org.jetbrains.kotlin.codegen.ValueKind
|
|
||||||
import org.jetbrains.kotlin.codegen.inline.*
|
import org.jetbrains.kotlin.codegen.inline.*
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||||
@@ -28,6 +27,7 @@ import org.jetbrains.kotlin.ir.types.toKotlinType
|
|||||||
import org.jetbrains.kotlin.ir.util.dump
|
import org.jetbrains.kotlin.ir.util.dump
|
||||||
import org.jetbrains.kotlin.ir.util.getArgumentsWithIr
|
import org.jetbrains.kotlin.ir.util.getArgumentsWithIr
|
||||||
import org.jetbrains.kotlin.ir.util.isSuspend
|
import org.jetbrains.kotlin.ir.util.isSuspend
|
||||||
|
import org.jetbrains.kotlin.ir.util.render
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
@@ -43,15 +43,18 @@ class IrInlineCodegen(
|
|||||||
typeParameterMappings: TypeParameterMappings<IrType>,
|
typeParameterMappings: TypeParameterMappings<IrType>,
|
||||||
sourceCompiler: SourceCompilerForInline,
|
sourceCompiler: SourceCompilerForInline,
|
||||||
reifiedTypeInliner: ReifiedTypeInliner<IrType>
|
reifiedTypeInliner: ReifiedTypeInliner<IrType>
|
||||||
) : InlineCodegen<ExpressionCodegen>(
|
) :
|
||||||
codegen, state, function.descriptor, methodOwner, signature, typeParameterMappings, sourceCompiler, reifiedTypeInliner
|
InlineCodegen<ExpressionCodegen>(
|
||||||
), IrCallGenerator {
|
codegen, state, function.descriptor, methodOwner, signature, typeParameterMappings, sourceCompiler, reifiedTypeInliner
|
||||||
|
),
|
||||||
|
IrCallGenerator {
|
||||||
|
|
||||||
override fun generateAssertFieldIfNeeded(info: RootInliningContext) {
|
override fun generateAssertFieldIfNeeded(info: RootInliningContext) {
|
||||||
if (info.generateAssertField && (sourceCompiler as IrSourceCompilerForInline).isPrimaryCopy) {
|
if (info.generateAssertField && (sourceCompiler as IrSourceCompilerForInline).isPrimaryCopy) {
|
||||||
codegen.classCodegen.generateAssertFieldIfNeeded()?.let {
|
codegen.classCodegen.generateAssertFieldIfNeeded()?.run {
|
||||||
// Generating <clinit> right now, so no longer can insert the initializer into it.
|
// Generating <clinit> right now, so no longer can insert the initializer into it.
|
||||||
// Instead, ask ExpressionCodegen to generate the code for it directly.
|
// Instead, ask ExpressionCodegen to generate the code for it directly.
|
||||||
it.accept(codegen, BlockInfo()).discard()
|
accept(codegen, BlockInfo()).discard()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,13 +152,38 @@ class IrInlineCodegen(
|
|||||||
invocationParamBuilder.markValueParametersStart()
|
invocationParamBuilder.markValueParametersStart()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inner class IrInlineCall(
|
||||||
|
private val irFunctionAccessExpression: IrFunctionAccessExpression
|
||||||
|
) : InlineCall {
|
||||||
|
|
||||||
|
override val calleeDescriptor: CallableDescriptor =
|
||||||
|
irFunctionAccessExpression.symbol.descriptor.original
|
||||||
|
|
||||||
|
override val callElement: PsiElement?
|
||||||
|
get() =
|
||||||
|
codegen.context.psiSourceManager.findPsiElement(irFunctionAccessExpression, function)
|
||||||
|
?: codegen.context.psiSourceManager.findPsiElement(function)
|
||||||
|
|
||||||
|
override val id: Any
|
||||||
|
get() = irFunctionAccessExpression
|
||||||
|
|
||||||
|
override fun toString(): String = irFunctionAccessExpression.render()
|
||||||
|
}
|
||||||
|
|
||||||
override fun genCall(
|
override fun genCall(
|
||||||
callableMethod: IrCallableMethod,
|
callableMethod: IrCallableMethod,
|
||||||
codegen: ExpressionCodegen,
|
codegen: ExpressionCodegen,
|
||||||
expression: IrFunctionAccessExpression
|
expression: IrFunctionAccessExpression
|
||||||
) {
|
) {
|
||||||
// TODO port inlining cycle detection to IrFunctionAccessExpression & pass it
|
val inlineCall = IrInlineCall(expression)
|
||||||
state.globalInlineContext.enterIntoInlining(null)
|
if (!state.globalInlineContext.enterIntoInlining(inlineCall)) {
|
||||||
|
AsmUtil.genThrow(
|
||||||
|
codegen.v,
|
||||||
|
"java/lang/UnsupportedOperationException",
|
||||||
|
"Call is a part of inline call cycle: ${expression.render()}"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
performInline(
|
performInline(
|
||||||
expression.symbol.owner.typeParameters.map { it.symbol },
|
expression.symbol.owner.typeParameters.map { it.symbol },
|
||||||
@@ -164,7 +192,7 @@ class IrInlineCodegen(
|
|||||||
codegen.typeMapper.typeSystem
|
codegen.typeMapper.typeSystem
|
||||||
)
|
)
|
||||||
} finally {
|
} finally {
|
||||||
state.globalInlineContext.exitFromInliningOf(null)
|
state.globalInlineContext.exitFromInliningOf(inlineCall)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,14 +119,12 @@ class PsiSourceManager : SourceManager {
|
|||||||
fileEntriesByIrFile[irFile]
|
fileEntriesByIrFile[irFile]
|
||||||
|
|
||||||
fun <E : PsiElement> findPsiElement(irElement: IrElement, irFile: IrFile, psiElementClass: KClass<E>): E? {
|
fun <E : PsiElement> findPsiElement(irElement: IrElement, irFile: IrFile, psiElementClass: KClass<E>): E? {
|
||||||
val psiFileEntry = fileEntriesByIrFile[irFile]
|
val psiFileEntry = fileEntriesByIrFile[irFile] ?: return null
|
||||||
?: throw AssertionError("No PSI file for irFile $irFile")
|
|
||||||
return psiFileEntry.findPsiElement(irElement, psiElementClass)
|
return psiFileEntry.findPsiElement(irElement, psiElementClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? {
|
fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? {
|
||||||
val psiFileEntry = fileEntriesByIrFile[irFile]
|
val psiFileEntry = fileEntriesByIrFile[irFile] ?: return null
|
||||||
?: throw AssertionError("No PSI file for irFile $irFile")
|
|
||||||
return psiFileEntry.findPsiElement(irElement)
|
return psiFileEntry.findPsiElement(irElement)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-3
@@ -1,14 +1,17 @@
|
|||||||
inline fun a(l: () -> Unit) {
|
inline fun a(q: () -> Unit) {
|
||||||
b(l)
|
b(q)
|
||||||
|
|
||||||
//check that nested not recognized as cycle
|
// check that nested not recognized as cycle
|
||||||
c {
|
c {
|
||||||
c {
|
c {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
withDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun withDefaults(x: Int = 1) = x * 2
|
||||||
|
|
||||||
inline fun b(p: () -> Unit) {
|
inline fun b(p: () -> Unit) {
|
||||||
p()
|
p()
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
compiler/testData/cli/jvm/inlineCycle.kt:2:5: error: the 'b' invocation is a part of inline cycle
|
compiler/testData/cli/jvm/inlineCycle.kt:2:5: error: the 'b' invocation is a part of inline cycle
|
||||||
b(l)
|
b(q)
|
||||||
^
|
^
|
||||||
compiler/testData/cli/jvm/inlineCycle.kt:15:5: error: the 'a' invocation is a part of inline cycle
|
compiler/testData/cli/jvm/inlineCycle.kt:18:5: error: the 'a' invocation is a part of inline cycle
|
||||||
a(p)
|
a(p)
|
||||||
^
|
^
|
||||||
COMPILATION_ERROR
|
COMPILATION_ERROR
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
$TESTDATA_DIR$/inlineCycle.kt
|
||||||
|
-d
|
||||||
|
$TEMP_DIR$
|
||||||
|
-Xuse-ir
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
compiler/testData/cli/jvm/inlineCycle.kt:2:5: error: the 'b' invocation is a part of inline cycle
|
||||||
|
b(q)
|
||||||
|
^
|
||||||
|
compiler/testData/cli/jvm/inlineCycle.kt:18:5: error: the 'a' invocation is a part of inline cycle
|
||||||
|
a(p)
|
||||||
|
^
|
||||||
|
COMPILATION_ERROR
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
$TESTDATA_DIR$/nonLocalDisabled.kt
|
||||||
|
-Xno-inline
|
||||||
|
-d
|
||||||
|
$TEMP_DIR$
|
||||||
|
-Xuse-ir
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
compiler/testData/cli/jvm/nonLocalDisabled.kt:3:9: error: non-local returns are not allowed with inlining disabled
|
||||||
|
return
|
||||||
|
^
|
||||||
|
compiler/testData/cli/jvm/nonLocalDisabled.kt:7:9: error: non-local returns are not allowed with inlining disabled
|
||||||
|
return@a
|
||||||
|
^
|
||||||
|
COMPILATION_ERROR
|
||||||
@@ -305,6 +305,11 @@ public class CliTestGenerated extends AbstractCliTest {
|
|||||||
runTest("compiler/testData/cli/jvm/inlineCycle.args");
|
runTest("compiler/testData/cli/jvm/inlineCycle.args");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineCycle_ir.args")
|
||||||
|
public void testInlineCycle_ir() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/jvm/inlineCycle_ir.args");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("internalArgDisableLanguageFeature.args")
|
@TestMetadata("internalArgDisableLanguageFeature.args")
|
||||||
public void testInternalArgDisableLanguageFeature() throws Exception {
|
public void testInternalArgDisableLanguageFeature() throws Exception {
|
||||||
runTest("compiler/testData/cli/jvm/internalArgDisableLanguageFeature.args");
|
runTest("compiler/testData/cli/jvm/internalArgDisableLanguageFeature.args");
|
||||||
@@ -555,6 +560,11 @@ public class CliTestGenerated extends AbstractCliTest {
|
|||||||
runTest("compiler/testData/cli/jvm/nonLocalDisabled.args");
|
runTest("compiler/testData/cli/jvm/nonLocalDisabled.args");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nonLocalDisabled_ir.args")
|
||||||
|
public void testNonLocalDisabled_ir() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/jvm/nonLocalDisabled_ir.args");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("nonexistentPathInModule.args")
|
@TestMetadata("nonexistentPathInModule.args")
|
||||||
public void testNonexistentPathInModule() throws Exception {
|
public void testNonexistentPathInModule() throws Exception {
|
||||||
runTest("compiler/testData/cli/jvm/nonexistentPathInModule.args");
|
runTest("compiler/testData/cli/jvm/nonexistentPathInModule.args");
|
||||||
|
|||||||
Reference in New Issue
Block a user