[PL] Deep rework of the partial linkage
1. Leaving no unbound symbols in the IR tree, KT-54491:
- All unbound symbols are bound to synthetic stub declarations
- Improved detection of the root cause for every partially linked classifier
- Improved error messages
2. Visibility valiation, KT-54469
3. Always check deserialized symbols:
If the deserialized symbol mismatches the symbol kind at the call site in the deserializer then generate and reference another symbol with the same signature. In case PL is off, just throw IrSymbolTypeMismatchException.
4. Handle class inheritance violation:
- Detect illegal inheritance (ex: inheriting from a final class)
- Detect invalid constructor delegation (ex: delegating to another class than the direct superclass)
- Simplification: Reduce the number of PartialLinkageCase subclasses
- Reworked error message generation to have shorter and clearer messages
5. Handle class transformations and all known side-effects, examples:
- nested <-> inner
- class <-> enum/object
- adding/removing subclasses of sealed class
- adding/removing enum entries
6. Check direct instantiation of abstract class.
Such instantiation could be possible if a class was non-abstract in the previous version of a library.
7. Handle unlinked annotations on declarations.
Such annotations are removed from the IR. The appropriate compiler error message is produced for every individual case.
8. Handle value argument count mismatch at call sites
9. Handle calling suspend function from non-suspend context.
This could happen if a suspen function was non-suspend in the previous version of a library.
10. Handle overriding inline callables.
Only the leaf final callable can be marked with `inline`.
11. Detect illegal non-local returns from noinline/crossinline lambdas.
This commit is contained in:
committed by
Space Team
parent
974ee3139c
commit
2a4d880037
+4
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageSupportForLowerings
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFileSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
@@ -82,6 +83,9 @@ interface CommonBackendContext : BackendContext, LoggingContext, ErrorReportingC
|
||||
*/
|
||||
val inlineClassesUtils: InlineClassesUtils
|
||||
get() = DefaultInlineClassesUtils
|
||||
|
||||
val partialLinkageSupport: PartialLinkageSupportForLowerings
|
||||
get() = PartialLinkageSupportForLowerings.DISABLED
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.builtins.StandardNames.KOTLIN_REFLECT_FQ_NAME
|
||||
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||
|
||||
+8
-5
@@ -537,13 +537,16 @@ class LocalDeclarationsLowering(
|
||||
|
||||
// NOTE: if running before InitializersLowering, we can instead look for constructors that have
|
||||
// IrInstanceInitializerCall. However, Native runs these two lowerings in opposite order.
|
||||
val constructorsCallingSuper = constructors
|
||||
val constructorsByDelegationKinds: Map<ConstructorDelegationKind, List<LocalClassConstructorContext>> = constructors
|
||||
.asSequence()
|
||||
.map { localClassConstructors[it]!! }
|
||||
.filter { it.declaration.callsSuper(context.irBuiltIns) }
|
||||
.toList()
|
||||
.groupBy { it.declaration.delegationKind(context.irBuiltIns) }
|
||||
|
||||
assert(constructorsCallingSuper.any()) { "Expected at least one constructor calling super; class: $irClass" }
|
||||
val constructorsCallingSuper = constructorsByDelegationKinds[ConstructorDelegationKind.CALLS_SUPER].orEmpty()
|
||||
|
||||
assert(constructorsCallingSuper.isNotEmpty() || constructorsByDelegationKinds[ConstructorDelegationKind.PARTIAL_LINKAGE_ERROR] != null) {
|
||||
"Expected at least one constructor calling super; class: $irClass"
|
||||
}
|
||||
|
||||
val usedCaptureFields = createFieldsForCapturedValues(localClassContext)
|
||||
irClass.declarations += usedCaptureFields
|
||||
@@ -1049,7 +1052,7 @@ class LocalDeclarationsLowering(
|
||||
// other restrictions on IR (e.g. after the initializers are moved you can no longer create fields
|
||||
// with initializers) which makes that hard to implement.
|
||||
val constructorContext = declaration.constructors.mapNotNull { localClassConstructors[it] }
|
||||
.singleOrNull { it.declaration.callsSuper(context.irBuiltIns) }
|
||||
.singleOrNull { it.declaration.delegationKind(context.irBuiltIns) == ConstructorDelegationKind.CALLS_SUPER }
|
||||
localClasses[declaration] = LocalClassContext(declaration, data.inInlineFunctionScope, constructorContext)
|
||||
}
|
||||
|
||||
|
||||
+38
-5
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.linkage.partial.isPartialLinkageRuntimeError
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
||||
@@ -168,14 +169,26 @@ open class IrBuildingTransformer(private val context: BackendContext) : IrElemen
|
||||
}
|
||||
}
|
||||
|
||||
fun IrConstructor.callsSuper(irBuiltIns: IrBuiltIns): Boolean {
|
||||
enum class ConstructorDelegationKind {
|
||||
/** Calls another constructor of the same class. */
|
||||
CALLS_THIS,
|
||||
|
||||
/** Calls the constructor of the super class. */
|
||||
CALLS_SUPER,
|
||||
|
||||
/** The actual delegation is not known. The constructor call was replaced by a partial linkage error. */
|
||||
PARTIAL_LINKAGE_ERROR
|
||||
}
|
||||
|
||||
fun IrConstructor.delegationKind(irBuiltIns: IrBuiltIns): ConstructorDelegationKind {
|
||||
val constructedClass = parent as IrClass
|
||||
val superClass = constructedClass.superTypes
|
||||
.mapNotNull { it as? IrSimpleType }
|
||||
.firstOrNull { (it.classifier.owner as IrClass).run { kind == ClassKind.CLASS || kind == ClassKind.ANNOTATION_CLASS || kind == ClassKind.ENUM_CLASS } }
|
||||
?: irBuiltIns.anyType
|
||||
var callsSuper = false
|
||||
var numberOfCalls = 0
|
||||
var numberOfDelegatingCalls = 0
|
||||
var hasPartialLinkageError = false
|
||||
acceptChildrenVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
@@ -186,7 +199,7 @@ fun IrConstructor.callsSuper(irBuiltIns: IrBuiltIns): Boolean {
|
||||
}
|
||||
|
||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall) {
|
||||
assert(++numberOfCalls == 1) { "More than one delegating constructor call: ${symbol.owner}" }
|
||||
numberOfDelegatingCalls++
|
||||
val delegatingClass = expression.symbol.owner.parent as IrClass
|
||||
// TODO: figure out why Lazy IR multiplies Declarations for descriptors and fix it
|
||||
// It happens because of IrBuiltIns whose IrDeclarations are different for runtime and test
|
||||
@@ -198,11 +211,31 @@ fun IrConstructor.callsSuper(irBuiltIns: IrBuiltIns): Boolean {
|
||||
" call to super class constructor. But was: $delegatingClass with '${delegatingClass.name}' name"
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitExpression(expression: IrExpression) {
|
||||
hasPartialLinkageError = hasPartialLinkageError || expression.isPartialLinkageRuntimeError()
|
||||
super.visitExpression(expression)
|
||||
}
|
||||
})
|
||||
assert(numberOfCalls == 1) { "Expected exactly one delegating constructor call but none encountered: ${symbol.owner}" }
|
||||
return callsSuper
|
||||
|
||||
val delegationKind: ConstructorDelegationKind? = when (numberOfDelegatingCalls) {
|
||||
0 -> if (hasPartialLinkageError) ConstructorDelegationKind.PARTIAL_LINKAGE_ERROR else null
|
||||
1 -> if (callsSuper) ConstructorDelegationKind.CALLS_SUPER else ConstructorDelegationKind.CALLS_THIS
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (delegationKind != null)
|
||||
return delegationKind
|
||||
else
|
||||
throw AssertionError("Expected exactly one delegating constructor call but $numberOfDelegatingCalls encountered: ${symbol.owner}")
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
"Replaced by delegationKind() that is aware of the possible partial linkage side effects",
|
||||
ReplaceWith("delegationKind(irBuiltIns)")
|
||||
)
|
||||
fun IrConstructor.callsSuper(irBuiltIns: IrBuiltIns): Boolean = delegationKind(irBuiltIns) == ConstructorDelegationKind.CALLS_SUPER
|
||||
|
||||
fun ParameterDescriptor.copyAsValueParameter(newOwner: CallableDescriptor, index: Int, name: Name = this.name) = when (this) {
|
||||
is ValueParameterDescriptor -> this.copy(newOwner, name, index)
|
||||
is ReceiverParameterDescriptor -> ValueParameterDescriptorImpl(
|
||||
|
||||
+42
-11
@@ -11,13 +11,13 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
||||
import org.jetbrains.kotlin.ir.builders.irGet
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBody
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageCase.SuspendableFunctionCallWithoutCoroutineContext
|
||||
import org.jetbrains.kotlin.ir.util.irCall
|
||||
import org.jetbrains.kotlin.ir.util.isSuspend
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageUtils.File as PLFile
|
||||
|
||||
/**
|
||||
* Add continuation to suspend function calls.
|
||||
@@ -25,21 +25,23 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
* Additionally materialize continuation for `getContinuation` intrinsic calls.
|
||||
*/
|
||||
abstract class AbstractAddContinuationToFunctionCallsLowering : BodyLoweringPass {
|
||||
abstract val context: CommonBackendContext
|
||||
protected abstract val context: CommonBackendContext
|
||||
|
||||
protected abstract fun IrSimpleFunction.isContinuationItself(): Boolean
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
runOnFilePostfix(irFile, withLocalDeclarations = true)
|
||||
}
|
||||
|
||||
abstract fun IrSimpleFunction.getContinuationParameter() : IrValueParameter
|
||||
|
||||
|
||||
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||
val continuation: IrValueParameter by lazy {
|
||||
val continuation: IrValueParameter? by lazy {
|
||||
(container as IrSimpleFunction).getContinuationParameter()
|
||||
}
|
||||
|
||||
val builder by lazy { context.createIrBuilder(container.symbol) }
|
||||
fun getContinuation() = builder.irGet(continuation)
|
||||
fun getContinuation(): IrGetValue? = continuation?.let(builder::irGet)
|
||||
|
||||
val plFile: PLFile by lazy { PLFile.determineFileFor(container) }
|
||||
|
||||
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitBody(body: IrBody): IrBody {
|
||||
@@ -52,7 +54,7 @@ abstract class AbstractAddContinuationToFunctionCallsLowering : BodyLoweringPass
|
||||
|
||||
if (!expression.isSuspend) {
|
||||
if (expression.symbol == context.ir.symbols.getContinuation)
|
||||
return getContinuation()
|
||||
return getContinuation() ?: expression.throwLinkageError(plFile)
|
||||
return expression
|
||||
}
|
||||
|
||||
@@ -65,11 +67,40 @@ abstract class AbstractAddContinuationToFunctionCallsLowering : BodyLoweringPass
|
||||
newReturnType = newFun.returnType,
|
||||
newSuperQualifierSymbol = expression.superQualifierSymbol
|
||||
).also {
|
||||
it.putValueArgument(it.valueArgumentsCount - 1, getContinuation())
|
||||
it.putValueArgument(it.valueArgumentsCount - 1, getContinuation() ?: return expression.throwLinkageError(plFile))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// IMPORTANT: May return null only if partial linkage is turned on.
|
||||
private fun IrSimpleFunction.getContinuationParameter(): IrValueParameter? {
|
||||
if (isContinuationItself())
|
||||
return dispatchReceiverParameter!!
|
||||
else {
|
||||
val isLoweredSuspendFunction = origin == IrDeclarationOrigin.LOWERED_SUSPEND_FUNCTION
|
||||
if (!isLoweredSuspendFunction) {
|
||||
return if (context.partialLinkageSupport.isEnabled)
|
||||
null
|
||||
else
|
||||
throw IllegalArgumentException("Continuation parameter only exists in lowered suspend functions, but function origin is $origin")
|
||||
}
|
||||
|
||||
val continuation = valueParameters.lastOrNull()
|
||||
require(continuation != null && continuation.origin == IrDeclarationOrigin.CONTINUATION) {
|
||||
"Continuation parameter is expected to be the last one"
|
||||
}
|
||||
return continuation
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrCall.throwLinkageError(file: PLFile): IrCall =
|
||||
context.partialLinkageSupport.throwLinkageError(
|
||||
SuspendableFunctionCallWithoutCoroutineContext(this),
|
||||
element = this,
|
||||
file,
|
||||
suppressWarningInCompilerOutput = false
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user