Support val initialization in non-inline function with EXACTLY_ONCE effect

by generating a box for the value.
 #KT-26126 Fixed
This commit is contained in:
Ilmir Usmanov
2018-12-26 21:31:46 +03:00
parent a52f430d8f
commit 8a01da6ec6
20 changed files with 278 additions and 73 deletions
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2010-2018 JetBrains s.r.o. 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.resolve;
@@ -33,6 +22,7 @@ import org.jetbrains.kotlin.resolve.calls.tower.TowerLevelsKt;
import org.jetbrains.kotlin.resolve.diagnostics.MutableDiagnosticsWithSuppression;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeUtils;
import org.jetbrains.kotlin.types.expressions.CaptureKind;
import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo;
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt;
import org.jetbrains.kotlin.util.slicedMap.MutableSlicedMap;
@@ -204,8 +194,15 @@ public class BindingContextUtils {
return bindingContext.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null;
}
public static boolean isVarCapturedInClosure(BindingContext bindingContext, DeclarationDescriptor descriptor) {
return isCapturedInClosure(bindingContext, descriptor) && ((VariableDescriptor) descriptor).isVar();
public static boolean isCapturedInClosureWithExactlyOnceEffect(BindingContext bindingContext, DeclarationDescriptor descriptor) {
if (!(descriptor instanceof VariableDescriptor) || descriptor instanceof PropertyDescriptor) return false;
VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor;
return bindingContext.get(CAPTURED_IN_CLOSURE, variableDescriptor) == CaptureKind.EXACTLY_ONCE_EFFECT;
}
public static boolean isBoxedLocalCapturedInClosure(BindingContext bindingContext, DeclarationDescriptor descriptor) {
return (isCapturedInClosure(bindingContext, descriptor) && ((VariableDescriptor) descriptor).isVar()) ||
isCapturedInClosureWithExactlyOnceEffect(bindingContext, descriptor);
}
@NotNull
@@ -1,28 +1,24 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2010-2018 JetBrains s.r.o. 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.resolve.calls.checkers
import com.intellij.psi.PsiElement
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.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContext.CAPTURED_IN_CLOSURE
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.callUtil.getValueArgumentForExpression
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
import org.jetbrains.kotlin.resolve.inline.InlineUtil
@@ -43,8 +39,7 @@ class CapturingInClosureChecker : CallChecker {
val scopeContainer = scope.ownerDescriptor
if (isCapturedVariable(variableParent, scopeContainer)) {
if (trace.get(CAPTURED_IN_CLOSURE, variable) != CaptureKind.NOT_INLINE) {
val inline = isCapturedInInline(trace.bindingContext, scopeContainer, variableParent)
trace.record(CAPTURED_IN_CLOSURE, variable, if (inline) CaptureKind.INLINE_ONLY else CaptureKind.NOT_INLINE)
trace.record(CAPTURED_IN_CLOSURE, variable, getCaptureKind(trace.bindingContext, scopeContainer, variableParent))
}
}
}
@@ -61,17 +56,34 @@ class CapturingInClosureChecker : CallChecker {
return true
}
private fun isCapturedInInline(
private fun getCaptureKind(
context: BindingContext, scopeContainer: DeclarationDescriptor, variableParent: DeclarationDescriptor
): Boolean {
): CaptureKind {
val scopeDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(scopeContainer)
if (!InlineUtil.canBeInlineArgument(scopeDeclaration)) return false
if (!InlineUtil.canBeInlineArgument(scopeDeclaration)) return CaptureKind.NOT_INLINE
if (InlineUtil.isInlinedArgument(scopeDeclaration as KtFunction, context, false)) {
val exactlyOnceContract = isExactlyOnceContract(context, scopeDeclaration as KtFunction)
if (InlineUtil.isInlinedArgument(scopeDeclaration, context, exactlyOnceContract)) {
val scopeContainerParent = scopeContainer.containingDeclaration ?: error("parent is null for $scopeContainer")
return !isCapturedVariable(variableParent, scopeContainerParent) ||
isCapturedInInline(context, scopeContainerParent, variableParent)
return if (
!isCapturedVariable(variableParent, scopeContainerParent) ||
getCaptureKind(context, scopeContainerParent, variableParent) == CaptureKind.INLINE_ONLY
) CaptureKind.INLINE_ONLY else CaptureKind.NOT_INLINE
}
return false
if (exactlyOnceContract) return CaptureKind.EXACTLY_ONCE_EFFECT
return CaptureKind.NOT_INLINE
}
private fun isExactlyOnceContract(bindingContext: BindingContext, argument: KtFunction): Boolean {
val call = KtPsiUtil.getParentCallIfPresent(argument) ?: return false
val resolvedCall = call.getResolvedCall(bindingContext) ?: return false
val descriptor = resolvedCall.getResultingDescriptor()
val valueArgument = resolvedCall.call.getValueArgumentForExpression(argument) ?: return false
val mapping = resolvedCall.getArgumentMapping(valueArgument) as? ArgumentMatch ?: return false
val parameter = mapping.valueParameter
val contractDescription = descriptor.getUserData(ContractProviderKey)?.getContractDescription() ?: return false
val effect = contractDescription.effects.filterIsInstance<CallsEffectDeclaration>()
.find { it.variableReference.descriptor == parameter } ?: return false
return effect.kind == InvocationKind.EXACTLY_ONCE
}
}
@@ -1,22 +1,12 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2010-2018 JetBrains s.r.o. 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.types.expressions;
public enum CaptureKind {
NOT_INLINE,
INLINE_ONLY
INLINE_ONLY,
EXACTLY_ONCE_EFFECT
}