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
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.codegen.inline.InlineCall
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
class InlineCycleReporter(private val diagnostics: DiagnosticSink) {
private val processingFunctions = linkedMapOf<PsiElement, CallableDescriptor>()
private val processingFunctions = linkedMapOf<Any, InlineCall>()
fun enterIntoInlining(call: ResolvedCall<*>?): Boolean {
//null call for default method inlining
if (call != null) {
val callElement = call.call.callElement
if (processingFunctions.contains(callElement)) {
val cycle = processingFunctions.asSequence().dropWhile { it.key != callElement }
cycle.forEach {
diagnostics.report(Errors.INLINE_CALL_CYCLE.on(it.key, it.value))
fun enterIntoInlining(call: InlineCall?): Boolean {
// null call for default method inlining
val id = call?.id
if (id != null) {
if (processingFunctions.contains(id)) {
val cycle = processingFunctions.values.dropWhile { it.id != id }
for (cycleCall in cycle) {
val callPsiElement = cycleCall.callElement
if (callPsiElement != null) {
diagnostics.report(Errors.INLINE_CALL_CYCLE.on(callPsiElement, cycleCall.calleeDescriptor))
}
}
return false
}
processingFunctions.put(callElement, call.resultingDescriptor.original)
processingFunctions[id] = call
}
return true
}
fun exitFromInliningOf(call: ResolvedCall<*>?) {
fun exitFromInliningOf(call: InlineCall?) {
if (call != null) {
val callElement = call.call.callElement
processingFunctions.remove(callElement)
processingFunctions.remove(call.id)
}
}
}
@@ -16,12 +16,12 @@ class GlobalInlineContext(diagnostics: DiagnosticSink) {
private val typesUsedInInlineFunctions = LinkedList<MutableSet<String>>()
fun enterIntoInlining(call: ResolvedCall<*>?) =
fun enterIntoInlining(call: InlineCall?) =
inlineCycleReporter.enterIntoInlining(call).also {
if (it) typesUsedInInlineFunctions.push(hashSetOf())
}
fun exitFromInliningOf(call: ResolvedCall<*>?) {
fun exitFromInliningOf(call: InlineCall?) {
inlineCycleReporter.exitFromInliningOf(call)
val pop = typesUsedInInlineFunctions.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,
codegen: ExpressionCodegen
) {
if (!state.globalInlineContext.enterIntoInlining(resolvedCall)) {
val inlineCall = InlineCallImpl.of(resolvedCall)
if (!state.globalInlineContext.enterIntoInlining(inlineCall)) {
generateStub(resolvedCall, codegen)
return
}
try {
performInline(resolvedCall?.typeArguments?.keys?.toList(), callDefault, callDefault, codegen.typeSystem)
} 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.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
@@ -643,7 +644,7 @@ class ExpressionCodegen(
val isNonLocalReturn =
methodSignatureMapper.mapFunctionName(owner) != methodSignatureMapper.mapFunctionName(irFunction)
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(
mv, "java/lang/UnsupportedOperationException",
"Non-local returns are not allowed with inlining disabled"
@@ -5,15 +5,14 @@
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.ir.isInlineParameter
import org.jetbrains.kotlin.backend.jvm.ir.isLambda
import org.jetbrains.kotlin.codegen.IrExpressionLambda
import org.jetbrains.kotlin.codegen.JvmKotlinType
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.ValueKind
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.inline.*
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
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.getArgumentsWithIr
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.jvmSignature.JvmMethodSignature
import org.jetbrains.org.objectweb.asm.Type
@@ -43,15 +43,18 @@ class IrInlineCodegen(
typeParameterMappings: TypeParameterMappings<IrType>,
sourceCompiler: SourceCompilerForInline,
reifiedTypeInliner: ReifiedTypeInliner<IrType>
) : InlineCodegen<ExpressionCodegen>(
codegen, state, function.descriptor, methodOwner, signature, typeParameterMappings, sourceCompiler, reifiedTypeInliner
), IrCallGenerator {
) :
InlineCodegen<ExpressionCodegen>(
codegen, state, function.descriptor, methodOwner, signature, typeParameterMappings, sourceCompiler, reifiedTypeInliner
),
IrCallGenerator {
override fun generateAssertFieldIfNeeded(info: RootInliningContext) {
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.
// 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()
}
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(
callableMethod: IrCallableMethod,
codegen: ExpressionCodegen,
expression: IrFunctionAccessExpression
) {
// TODO port inlining cycle detection to IrFunctionAccessExpression & pass it
state.globalInlineContext.enterIntoInlining(null)
val inlineCall = IrInlineCall(expression)
if (!state.globalInlineContext.enterIntoInlining(inlineCall)) {
AsmUtil.genThrow(
codegen.v,
"java/lang/UnsupportedOperationException",
"Call is a part of inline call cycle: ${expression.render()}"
)
return
}
try {
performInline(
expression.symbol.owner.typeParameters.map { it.symbol },
@@ -164,7 +192,7 @@ class IrInlineCodegen(
codegen.typeMapper.typeSystem
)
} finally {
state.globalInlineContext.exitFromInliningOf(null)
state.globalInlineContext.exitFromInliningOf(inlineCall)
}
}
@@ -119,14 +119,12 @@ class PsiSourceManager : SourceManager {
fileEntriesByIrFile[irFile]
fun <E : PsiElement> findPsiElement(irElement: IrElement, irFile: IrFile, psiElementClass: KClass<E>): E? {
val psiFileEntry = fileEntriesByIrFile[irFile]
?: throw AssertionError("No PSI file for irFile $irFile")
val psiFileEntry = fileEntriesByIrFile[irFile] ?: return null
return psiFileEntry.findPsiElement(irElement, psiElementClass)
}
fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? {
val psiFileEntry = fileEntriesByIrFile[irFile]
?: throw AssertionError("No PSI file for irFile $irFile")
val psiFileEntry = fileEntriesByIrFile[irFile] ?: return null
return psiFileEntry.findPsiElement(irElement)
}
+6 -3
View File
@@ -1,14 +1,17 @@
inline fun a(l: () -> Unit) {
b(l)
inline fun a(q: () -> Unit) {
b(q)
//check that nested not recognized as cycle
// check that nested not recognized as cycle
c {
c {
}
}
withDefaults()
}
inline fun withDefaults(x: Int = 1) = x * 2
inline fun b(p: () -> Unit) {
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
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)
^
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");
}
@TestMetadata("inlineCycle_ir.args")
public void testInlineCycle_ir() throws Exception {
runTest("compiler/testData/cli/jvm/inlineCycle_ir.args");
}
@TestMetadata("internalArgDisableLanguageFeature.args")
public void testInternalArgDisableLanguageFeature() throws Exception {
runTest("compiler/testData/cli/jvm/internalArgDisableLanguageFeature.args");
@@ -555,6 +560,11 @@ public class CliTestGenerated extends AbstractCliTest {
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")
public void testNonexistentPathInModule() throws Exception {
runTest("compiler/testData/cli/jvm/nonexistentPathInModule.args");