JVM_IR: Implement some BE diagnostics

TODO proper diagnostics tests with BE diagnostics
This commit is contained in:
Dmitry Petrov
2020-01-31 14:29:24 +03:00
parent c9df17f2f1
commit 8ef79f932c
14 changed files with 138 additions and 43 deletions
@@ -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)
} }
} }
@@ -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"
@@ -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
View File
@@ -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
View File
@@ -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
+4
View File
@@ -0,0 +1,4 @@
$TESTDATA_DIR$/inlineCycle.kt
-d
$TEMP_DIR$
-Xuse-ir
+7
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
$TESTDATA_DIR$/nonLocalDisabled.kt
-Xno-inline
-d
$TEMP_DIR$
-Xuse-ir
+7
View File
@@ -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");