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
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -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"
|
||||
|
||||
+40
-12
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@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");
|
||||
|
||||
Reference in New Issue
Block a user