Fix bugs with capturing rhs into EXACTLY_ONCE lambda
There are multiple ways to declare a named variable-like entity in Kotlin: 1. val/var variable declaration 2. destructuring declaration 3. parameter of a function 4. parameter of a lambda 5. destructured lambda parameter 6. for-loop's variable declaration 7. catch block exception declaration 8. val in when 9. field declaration Out of them, only variable and field can be assignable, in other words, they can be on the left hand side of an assignment. Val/var variable declarations were already supported. So, we needed to just support field initialization and tell the backend that other ways are prohibited. Function and lambda parameters were already been supported. So, the only thing to explain to the backend are remaining ways. #KT-39113 Fixed #KT-34048 Fixed
This commit is contained in:
@@ -179,6 +179,7 @@ public interface BindingContext {
|
||||
WritableSlice<KtElement, Boolean> USED_AS_RESULT_OF_LAMBDA = Slices.createSimpleSetSlice();
|
||||
|
||||
WritableSlice<VariableDescriptor, CaptureKind> CAPTURED_IN_CLOSURE = new BasicWritableSlice<>(DO_NOTHING);
|
||||
WritableSlice<PropertyDescriptor, Boolean> FIELD_CAPTURED_IN_EXACLY_ONCE_CLOSURE = new BasicWritableSlice<>(DO_NOTHING);
|
||||
WritableSlice<KtDeclaration, PreliminaryDeclarationVisitor> PRELIMINARY_VISITOR = new BasicWritableSlice<>(DO_NOTHING);
|
||||
|
||||
WritableSlice<Box<DeferredType>, Boolean> DEFERRED_TYPE = Slices.createCollectiveSetSlice();
|
||||
|
||||
+59
-5
@@ -10,10 +10,13 @@ import org.jetbrains.kotlin.contracts.description.CallsEffectDeclaration
|
||||
import org.jetbrains.kotlin.contracts.description.ContractProviderKey
|
||||
import org.jetbrains.kotlin.contracts.description.InvocationKind
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BindingContext.CAPTURED_IN_CLOSURE
|
||||
import org.jetbrains.kotlin.resolve.BindingContext.FIELD_CAPTURED_IN_EXACLY_ONCE_CLOSURE
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
@@ -23,6 +26,7 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
||||
import org.jetbrains.kotlin.types.expressions.CaptureKind
|
||||
|
||||
class CapturingInClosureChecker : CallChecker {
|
||||
@@ -40,8 +44,16 @@ class CapturingInClosureChecker : CallChecker {
|
||||
if (isCapturedVariable(variableParent, scopeContainer)) {
|
||||
if (trace.get(CAPTURED_IN_CLOSURE, variable) != CaptureKind.NOT_INLINE) {
|
||||
trace.record(CAPTURED_IN_CLOSURE, variable, getCaptureKind(trace.bindingContext, scopeContainer, variableParent, variable))
|
||||
return
|
||||
}
|
||||
}
|
||||
// Check whether a field is captured in EXACTLY_ONCE contract
|
||||
if (variable !is PropertyDescriptor || scopeContainer !is AnonymousFunctionDescriptor) return
|
||||
val scopeDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(scopeContainer) as? KtFunction ?: return
|
||||
if (scopeContainer.containingDeclaration !is ConstructorDescriptor) return
|
||||
if (isExactlyOnceContract(trace.bindingContext, scopeDeclaration)) {
|
||||
trace.record(FIELD_CAPTURED_IN_EXACLY_ONCE_CLOSURE, variable)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isCapturedVariable(variableParent: DeclarationDescriptor, scopeContainer: DeclarationDescriptor): Boolean {
|
||||
@@ -75,10 +87,52 @@ class CapturingInClosureChecker : CallChecker {
|
||||
) CaptureKind.INLINE_ONLY else CaptureKind.NOT_INLINE
|
||||
}
|
||||
val exactlyOnceContract = isExactlyOnceContract(context, scopeDeclaration)
|
||||
// We cannot box arguments.
|
||||
val isArgument = variable is ValueParameterDescriptor && variableParent is CallableDescriptor
|
||||
if (!exactlyOnceContract) return CaptureKind.NOT_INLINE
|
||||
// We cannot box function/lambda arguments, destructured lambda arguments, for-loop index variables,
|
||||
// exceptions inside catch blocks ans vals in when
|
||||
return if (isArgument(variable, variableParent) ||
|
||||
isDestructuredVariable(variable, variableParent) ||
|
||||
isForLoopParameter(variable) ||
|
||||
isCatchBlockParameter(variable) ||
|
||||
isValInWhen(variable)
|
||||
) {
|
||||
CaptureKind.NOT_INLINE
|
||||
} else {
|
||||
CaptureKind.EXACTLY_ONCE_EFFECT
|
||||
}
|
||||
}
|
||||
|
||||
private fun isArgument(variable: VariableDescriptor, variableParent: DeclarationDescriptor): Boolean =
|
||||
variable is ValueParameterDescriptor && variableParent is CallableDescriptor
|
||||
&& variableParent.valueParameters.contains(variable)
|
||||
return if (exactlyOnceContract && !isArgument) CaptureKind.EXACTLY_ONCE_EFFECT else CaptureKind.NOT_INLINE
|
||||
|
||||
private fun isDestructuredVariable(variable: VariableDescriptor, variableParent: DeclarationDescriptor): Boolean =
|
||||
variable is LocalVariableDescriptor && variableParent is AnonymousFunctionDescriptor &&
|
||||
variableParent.valueParameters.any {
|
||||
it is ValueParameterDescriptorImpl.WithDestructuringDeclaration && it.destructuringVariables.contains(variable)
|
||||
}
|
||||
|
||||
private fun isValInWhen(variable: VariableDescriptor): Boolean {
|
||||
val psi = ((variable as? LocalVariableDescriptor)?.source as? KotlinSourceElement)?.psi ?: return false
|
||||
return (psi.parent as? KtWhenExpression)?.let { it.subjectVariable == psi } == true
|
||||
}
|
||||
|
||||
private fun isCatchBlockParameter(variable: VariableDescriptor): Boolean {
|
||||
val psi = ((variable as? LocalVariableDescriptor)?.source as? KotlinSourceElement)?.psi ?: return false
|
||||
return psi.parent.parent.let { it is KtCatchClause && it.parameterList?.parameters?.contains(psi) == true }
|
||||
}
|
||||
|
||||
private fun isForLoopParameter(variable: VariableDescriptor): Boolean {
|
||||
val psi = ((variable as? LocalVariableDescriptor)?.source as? KotlinSourceElement)?.psi ?: return false
|
||||
if (psi.parent is KtForExpression) {
|
||||
val forExpression = psi.parent as KtForExpression
|
||||
return forExpression.loopParameter == psi
|
||||
} else if (psi.parent is KtDestructuringDeclaration) {
|
||||
val parameter = psi.parent.parent as? KtParameter ?: return false
|
||||
val forExpression = parameter.parent as? KtForExpression ?: return false
|
||||
return forExpression.loopParameter == parameter
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun isExactlyOnceParameter(function: DeclarationDescriptor, parameter: VariableDescriptor): Boolean {
|
||||
|
||||
Reference in New Issue
Block a user