From 94ce1d1193865f6d506a46d6073af3a64c2b366d Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Tue, 12 Jan 2016 12:11:50 +0300 Subject: [PATCH] Refactor: extract type checking code for local variables to a separate component, extract code that creates descriptors for local variable out of DescriptorResolver --- .../kotlin/resolve/DescriptorResolver.java | 238 +----------------- .../kotlin/resolve/LocalVariableResolver.kt | 183 ++++++++++++++ .../kotlin/resolve/VariableTypeResolver.kt | 163 ++++++++++++ .../DestructuringDeclarationResolver.kt | 6 +- .../ExpressionTypingComponents.java | 7 + .../ExpressionTypingVisitorForStatements.java | 61 +---- 6 files changed, 370 insertions(+), 288 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/LocalVariableResolver.kt create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/VariableTypeResolver.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java index f3356344050..5cd8c017e6d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java @@ -20,8 +20,8 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.intellij.psi.PsiElement; -import kotlin.collections.CollectionsKt; import kotlin.Pair; +import kotlin.collections.CollectionsKt; import kotlin.collections.SetsKt; import kotlin.jvm.functions.Function0; import kotlin.jvm.functions.Function1; @@ -42,22 +42,17 @@ import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo; -import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant; -import org.jetbrains.kotlin.resolve.constants.ConstantValue; -import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator; import org.jetbrains.kotlin.resolve.dataClassUtils.DataClassUtilsKt; import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil; -import org.jetbrains.kotlin.resolve.scopes.ScopeUtils; import org.jetbrains.kotlin.resolve.scopes.LexicalScope; import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind; import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope; +import org.jetbrains.kotlin.resolve.scopes.ScopeUtils; import org.jetbrains.kotlin.resolve.scopes.utils.ScopeUtilsKt; import org.jetbrains.kotlin.resolve.source.KotlinSourceElementKt; import org.jetbrains.kotlin.storage.StorageManager; import org.jetbrains.kotlin.types.*; import org.jetbrains.kotlin.types.checker.KotlinTypeChecker; -import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices; -import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor; import java.util.*; @@ -75,31 +70,25 @@ public class DescriptorResolver { @NotNull private final TypeResolver typeResolver; @NotNull private final AnnotationResolver annotationResolver; - @NotNull private final ExpressionTypingServices expressionTypingServices; - @NotNull private final DelegatedPropertyResolver delegatedPropertyResolver; @NotNull private final StorageManager storageManager; @NotNull private final KotlinBuiltIns builtIns; - @NotNull private final ConstantExpressionEvaluator constantExpressionEvaluator; @NotNull private final SupertypeLoopChecker supertypeLoopsResolver; + @NotNull private final VariableTypeResolver variableTypeResolver; public DescriptorResolver( @NotNull AnnotationResolver annotationResolver, @NotNull KotlinBuiltIns builtIns, - @NotNull DelegatedPropertyResolver delegatedPropertyResolver, - @NotNull ExpressionTypingServices expressionTypingServices, @NotNull StorageManager storageManager, @NotNull TypeResolver typeResolver, - @NotNull ConstantExpressionEvaluator constantExpressionEvaluator, - @NotNull SupertypeLoopChecker supertypeLoopsResolver + @NotNull SupertypeLoopChecker supertypeLoopsResolver, + @NotNull VariableTypeResolver variableTypeResolver ) { this.annotationResolver = annotationResolver; this.builtIns = builtIns; - this.delegatedPropertyResolver = delegatedPropertyResolver; - this.expressionTypingServices = expressionTypingServices; this.storageManager = storageManager; this.typeResolver = typeResolver; - this.constantExpressionEvaluator = constantExpressionEvaluator; this.supertypeLoopsResolver = supertypeLoopsResolver; + this.variableTypeResolver = variableTypeResolver; } public List resolveSupertypes( @@ -670,84 +659,6 @@ public class DescriptorResolver { return variableDescriptor; } - @NotNull - public VariableDescriptor resolveLocalVariableDescriptor( - LexicalScope scope, - KtVariableDeclaration variable, - DataFlowInfo dataFlowInfo, - BindingTrace trace - ) { - DeclarationDescriptor containingDeclaration = scope.getOwnerDescriptor(); - VariableDescriptor result; - KotlinType type; - if (KtPsiUtil.isScriptDeclaration(variable)) { - PropertyDescriptorImpl propertyDescriptor = PropertyDescriptorImpl.create( - containingDeclaration, - annotationResolver.resolveAnnotationsWithArguments(scope, variable.getModifierList(), trace), - Modality.FINAL, - Visibilities.INTERNAL, - variable.isVar(), - KtPsiUtil.safeName(variable.getName()), - CallableMemberDescriptor.Kind.DECLARATION, - KotlinSourceElementKt.toSourceElement(variable), - /* lateInit = */ false, - /* isConst = */ false - ); - // For a local variable the type must not be deferred - type = getVariableType(propertyDescriptor, scope, variable, dataFlowInfo, false, trace); - - ReceiverParameterDescriptor receiverParameter = ((ScriptDescriptor) containingDeclaration).getThisAsReceiverParameter(); - propertyDescriptor.setType(type, Collections.emptyList(), receiverParameter, (KotlinType) null); - initializeWithDefaultGetterSetter(propertyDescriptor); - trace.record(BindingContext.VARIABLE, variable, propertyDescriptor); - result = propertyDescriptor; - } - else { - LocalVariableDescriptor variableDescriptor = - resolveLocalVariableDescriptorWithType(scope, variable, null, trace); - // For a local variable the type must not be deferred - type = getVariableType(variableDescriptor, scope, variable, dataFlowInfo, false, trace); - variableDescriptor.setOutType(type); - result = variableDescriptor; - } - // Type annotations also should be resolved - ForceResolveUtil.forceResolveAllContents(type.getAnnotations()); - return result; - } - - private static void initializeWithDefaultGetterSetter(PropertyDescriptorImpl propertyDescriptor) { - PropertyGetterDescriptorImpl getter = propertyDescriptor.getGetter(); - if (getter == null && !Visibilities.isPrivate(propertyDescriptor.getVisibility())) { - getter = DescriptorFactory.createDefaultGetter(propertyDescriptor, Annotations.Companion.getEMPTY()); - getter.initialize(propertyDescriptor.getType()); - } - - PropertySetterDescriptor setter = propertyDescriptor.getSetter(); - if (setter == null && propertyDescriptor.isVar()) { - setter = DescriptorFactory.createDefaultSetter(propertyDescriptor, Annotations.Companion.getEMPTY()); - } - propertyDescriptor.initialize(getter, setter); - } - - @NotNull - public LocalVariableDescriptor resolveLocalVariableDescriptorWithType( - @NotNull LexicalScope scope, - @NotNull KtVariableDeclaration variable, - @Nullable KotlinType type, - @NotNull BindingTrace trace - ) { - LocalVariableDescriptor variableDescriptor = new LocalVariableDescriptor( - scope.getOwnerDescriptor(), - annotationResolver.resolveAnnotationsWithArguments(scope, variable.getModifierList(), trace), - KtPsiUtil.safeName(variable.getName()), - type, - variable.isVar(), - KotlinSourceElementKt.toSourceElement(variable) - ); - trace.record(BindingContext.VARIABLE, variable, variableDescriptor); - return variableDescriptor; - } - @NotNull public PropertyDescriptor resolvePropertyDescriptor( @NotNull DeclarationDescriptor containingDeclaration, @@ -824,9 +735,10 @@ public class DescriptorResolver { ReceiverParameterDescriptor receiverDescriptor = DescriptorFactory.createExtensionReceiverParameterForCallable(propertyDescriptor, receiverType); - KotlinType type = getVariableType(propertyDescriptor, - ScopeUtils.makeScopeForPropertyInitializer(scopeWithTypeParameters, propertyDescriptor), - property, dataFlowInfo, true, trace); + KotlinType type = variableTypeResolver.process( + propertyDescriptor, ScopeUtils.makeScopeForPropertyInitializer(scopeWithTypeParameters, propertyDescriptor), + property, dataFlowInfo, true, trace + ); propertyDescriptor.setType(type, typeParameterDescriptors, getDispatchReceiverParameterIfNeeded(containingDeclaration), receiverDescriptor); @@ -858,127 +770,6 @@ public class DescriptorResolver { return hasBody; } - @NotNull - private KotlinType getVariableType( - @NotNull final VariableDescriptorWithInitializerImpl variableDescriptor, - @NotNull final LexicalScope scopeForInitializer, - @NotNull final KtVariableDeclaration variable, - @NotNull final DataFlowInfo dataFlowInfo, - boolean notLocal, - @NotNull final BindingTrace trace - ) { - KtTypeReference propertyTypeRef = variable.getTypeReference(); - - boolean hasDelegate = variable instanceof KtProperty && ((KtProperty) variable).hasDelegateExpression(); - if (propertyTypeRef == null) { - if (!variable.hasInitializer()) { - if (hasDelegate && variableDescriptor instanceof PropertyDescriptor) { - final KtProperty property = (KtProperty) variable; - if (property.hasDelegateExpression()) { - return DeferredType.createRecursionIntolerant( - storageManager, - trace, - new Function0() { - @Override - public KotlinType invoke() { - return resolveDelegatedPropertyType(property, (PropertyDescriptor) variableDescriptor, scopeForInitializer, - property.getDelegateExpression(), dataFlowInfo, trace); - } - }); - } - } - if (!notLocal) { - trace.report(VARIABLE_WITH_NO_TYPE_NO_INITIALIZER.on(variable)); - } - return ErrorUtils.createErrorType("No type, no body"); - } - else { - if (notLocal) { - return DeferredType.createRecursionIntolerant( - storageManager, - trace, - new Function0() { - @Override - public KotlinType invoke() { - PreliminaryDeclarationVisitor.Companion.createForDeclaration(variable, trace); - KotlinType - initializerType = resolveInitializerType(scopeForInitializer, variable.getInitializer(), dataFlowInfo, trace); - setConstantForVariableIfNeeded(variableDescriptor, scopeForInitializer, variable, dataFlowInfo, initializerType, trace); - return transformAnonymousTypeIfNeeded(variableDescriptor, variable, initializerType, trace); - } - } - ); - } - else { - KotlinType initializerType = resolveInitializerType(scopeForInitializer, variable.getInitializer(), dataFlowInfo, trace); - setConstantForVariableIfNeeded(variableDescriptor, scopeForInitializer, variable, dataFlowInfo, initializerType, trace); - return initializerType; - } - } - } - else { - KotlinType type = typeResolver.resolveType(scopeForInitializer, propertyTypeRef, trace, true); - setConstantForVariableIfNeeded(variableDescriptor, scopeForInitializer, variable, dataFlowInfo, type, trace); - return type; - } - } - - private void setConstantForVariableIfNeeded( - @NotNull final VariableDescriptorWithInitializerImpl variableDescriptor, - @NotNull final LexicalScope scope, - @NotNull final KtVariableDeclaration variable, - @NotNull final DataFlowInfo dataFlowInfo, - @NotNull final KotlinType variableType, - @NotNull final BindingTrace trace - ) { - if (!shouldRecordInitializerForProperty(variableDescriptor, variableType)) return; - - if (!variable.hasInitializer()) return; - - variableDescriptor.setCompileTimeInitializer( - storageManager.createRecursionTolerantNullableLazyValue(new Function0>() { - @Nullable - @Override - public ConstantValue invoke() { - KtExpression initializer = variable.getInitializer(); - KotlinType initializerType = expressionTypingServices.safeGetType(scope, initializer, variableType, dataFlowInfo, trace); - CompileTimeConstant constant = constantExpressionEvaluator.evaluateExpression(initializer, trace, initializerType); - - if (constant == null) return null; - - if (constant.getUsesNonConstValAsConstant() && variableDescriptor.isConst()) { - trace.report(Errors.NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION.on(initializer)); - } - - return constant.toConstantValue(initializerType); - } - }, null) - ); - } - - @NotNull - private KotlinType resolveDelegatedPropertyType( - @NotNull KtProperty property, - @NotNull PropertyDescriptor propertyDescriptor, - @NotNull LexicalScope scopeForInitializer, - @NotNull KtExpression delegateExpression, - @NotNull DataFlowInfo dataFlowInfo, - @NotNull BindingTrace trace - ) { - - KotlinType type = delegatedPropertyResolver.resolveDelegateExpression( - delegateExpression, property, propertyDescriptor, scopeForInitializer, trace, dataFlowInfo); - - if (type != null) { - LexicalScope delegateFunctionsScope = ScopeUtils.makeScopeForDelegateConventionFunctions(scopeForInitializer, propertyDescriptor); - KotlinType getterReturnType = delegatedPropertyResolver - .getDelegatedPropertyGetMethodReturnType(propertyDescriptor, delegateExpression, type, trace, delegateFunctionsScope); - if (getterReturnType != null) { - return getterReturnType; - } - } - return ErrorUtils.createErrorType("Type from delegate"); - } @Nullable /*package*/ static KotlinType transformAnonymousTypeIfNeeded( @@ -1005,15 +796,6 @@ public class DescriptorResolver { return type; } - @NotNull - private KotlinType resolveInitializerType( - @NotNull LexicalScope scope, - @NotNull KtExpression initializer, - @NotNull DataFlowInfo dataFlowInfo, - @NotNull BindingTrace trace - ) { - return expressionTypingServices.safeGetType(scope, initializer, TypeUtils.NO_EXPECTED_TYPE, dataFlowInfo, trace); - } @Nullable private PropertySetterDescriptor resolvePropertySetterDescriptor( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/LocalVariableResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/LocalVariableResolver.kt new file mode 100644 index 00000000000..8aaeff453a7 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/LocalVariableResolver.kt @@ -0,0 +1,183 @@ +/* + * Copyright 2010-2016 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. + */ + +package org.jetbrains.kotlin.resolve + +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor +import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl +import org.jetbrains.kotlin.diagnostics.Errors.* +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.KtPsiUtil +import org.jetbrains.kotlin.psi.KtVariableDeclaration +import org.jetbrains.kotlin.resolve.calls.context.ContextDependency +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory +import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil +import org.jetbrains.kotlin.resolve.scopes.LexicalScope +import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope +import org.jetbrains.kotlin.resolve.source.toSourceElement +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.expressions.* +import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo + +class LocalVariableResolver( + private val expressionTypingServices: ExpressionTypingServices, + private val modifiersChecker: ModifiersChecker, + private val identifierChecker: IdentifierChecker, + private val dataFlowAnalyzer: DataFlowAnalyzer, + private val annotationResolver: AnnotationResolver, + private val variableTypeResolver: VariableTypeResolver +) { + + fun process( + property: KtProperty, + typingContext: ExpressionTypingContext, + scope: LexicalWritableScope, + facade: ExpressionTypingFacade + ): KotlinTypeInfo { + val context = typingContext.replaceContextDependency(ContextDependency.INDEPENDENT).replaceScope(scope) + val receiverTypeRef = property.receiverTypeReference + if (receiverTypeRef != null) { + context.trace.report(LOCAL_EXTENSION_PROPERTY.on(receiverTypeRef)) + } + + val getter = property.getter + if (getter != null) { + context.trace.report(LOCAL_VARIABLE_WITH_GETTER.on(getter)) + } + + val setter = property.setter + if (setter != null) { + context.trace.report(LOCAL_VARIABLE_WITH_SETTER.on(setter)) + } + + val delegateExpression = property.delegateExpression + if (delegateExpression != null) { + expressionTypingServices.getTypeInfo(delegateExpression, context) + context.trace.report(LOCAL_VARIABLE_WITH_DELEGATE.on(property.delegate!!)) + } + + val propertyDescriptor = resolveLocalVariableDescriptor(scope, property, context.dataFlowInfo, context.trace) + val initializer = property.initializer + var typeInfo: KotlinTypeInfo + if (initializer != null) { + val outType = propertyDescriptor.getType() + typeInfo = facade.getTypeInfo(initializer, context.replaceExpectedType(outType)) + val dataFlowInfo = typeInfo.dataFlowInfo + val type = typeInfo.type + // At this moment we do not take initializer value into account if type is given for a property + // We can comment first part of this condition to take them into account, like here: var s: String? = "xyz" + // In this case s will be not-nullable until it is changed + if (property.typeReference == null && type != null) { + val variableDataFlowValue = DataFlowValueFactory.createDataFlowValueForProperty( + property, propertyDescriptor, context.trace.bindingContext, + DescriptorUtils.getContainingModuleOrNull(scope.ownerDescriptor)) + val initializerDataFlowValue = DataFlowValueFactory.createDataFlowValue(initializer, type, context) + // We cannot say here anything new about initializerDataFlowValue + // except it has the same value as variableDataFlowValue + typeInfo = typeInfo.replaceDataFlowInfo(dataFlowInfo.assign(variableDataFlowValue, initializerDataFlowValue)) + } + } + else { + typeInfo = noTypeInfo(context) + } + + ExpressionTypingUtils.checkVariableShadowing(context.scope, context.trace, propertyDescriptor) + + scope.addVariableDescriptor(propertyDescriptor) + property.checkTypeReferences(context.trace) + modifiersChecker.withTrace(context.trace).checkModifiersForLocalDeclaration(property, propertyDescriptor) + identifierChecker.checkDeclaration(property, context.trace) + return typeInfo.replaceType(dataFlowAnalyzer.checkStatementType(property, context)) + } + + private fun resolveLocalVariableDescriptor( + scope: LexicalScope, + variable: KtVariableDeclaration, + dataFlowInfo: DataFlowInfo, + trace: BindingTrace + ): VariableDescriptor { + val containingDeclaration = scope.ownerDescriptor + val result: VariableDescriptor + val type: KotlinType + if (KtPsiUtil.isScriptDeclaration(variable)) { + val propertyDescriptor = PropertyDescriptorImpl.create( + containingDeclaration, + annotationResolver.resolveAnnotationsWithArguments(scope, variable.modifierList, trace), + Modality.FINAL, + Visibilities.INTERNAL, + variable.isVar, + KtPsiUtil.safeName(variable.name), + CallableMemberDescriptor.Kind.DECLARATION, + variable.toSourceElement(), + /* lateInit = */ false, + /* isConst = */ false + ) + // For a local variable the type must not be deferred + type = variableTypeResolver.process(propertyDescriptor, scope, variable, dataFlowInfo, false, trace) + + val receiverParameter = (containingDeclaration as ScriptDescriptor).thisAsReceiverParameter + propertyDescriptor.setType(type, emptyList(), receiverParameter, null as? KotlinType) + initializeWithDefaultGetterSetter(propertyDescriptor) + trace.record(BindingContext.VARIABLE, variable, propertyDescriptor) + result = propertyDescriptor + } + else { + val variableDescriptor = resolveLocalVariableDescriptorWithType(scope, variable, null, trace) + // For a local variable the type must not be deferred + type = variableTypeResolver.process(variableDescriptor, scope, variable, dataFlowInfo, false, trace) + variableDescriptor.setOutType(type) + result = variableDescriptor + } + // Type annotations also should be resolved + ForceResolveUtil.forceResolveAllContents(type.annotations) + return result + } + + private fun initializeWithDefaultGetterSetter(propertyDescriptor: PropertyDescriptorImpl) { + var getter = propertyDescriptor.getter + if (getter == null && !Visibilities.isPrivate(propertyDescriptor.visibility)) { + getter = DescriptorFactory.createDefaultGetter(propertyDescriptor, Annotations.EMPTY) + getter.initialize(propertyDescriptor.type) + } + + var setter = propertyDescriptor.setter + if (setter == null && propertyDescriptor.isVar) { + setter = DescriptorFactory.createDefaultSetter(propertyDescriptor, Annotations.EMPTY) + } + propertyDescriptor.initialize(getter, setter) + } + + internal fun resolveLocalVariableDescriptorWithType( + scope: LexicalScope, + variable: KtVariableDeclaration, + type: KotlinType?, + trace: BindingTrace + ): LocalVariableDescriptor { + val variableDescriptor = LocalVariableDescriptor( + scope.ownerDescriptor, + annotationResolver.resolveAnnotationsWithArguments(scope, variable.modifierList, trace), + KtPsiUtil.safeName(variable.name), + type, + variable.isVar, + variable.toSourceElement() + ) + trace.record(BindingContext.VARIABLE, variable, variableDescriptor) + return variableDescriptor + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/VariableTypeResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/VariableTypeResolver.kt new file mode 100644 index 00000000000..2ffdd58f3b4 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/VariableTypeResolver.kt @@ -0,0 +1,163 @@ +/* + * Copyright 2010-2016 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. + */ + +package org.jetbrains.kotlin.resolve + +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.impl.VariableDescriptorWithInitializerImpl +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.diagnostics.Errors.VARIABLE_WITH_NO_TYPE_NO_INITIALIZER +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.KtVariableDeclaration +import org.jetbrains.kotlin.resolve.DescriptorResolver.transformAnonymousTypeIfNeeded +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo +import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator +import org.jetbrains.kotlin.resolve.scopes.LexicalScope +import org.jetbrains.kotlin.resolve.scopes.ScopeUtils +import org.jetbrains.kotlin.storage.StorageManager +import org.jetbrains.kotlin.types.DeferredType +import org.jetbrains.kotlin.types.ErrorUtils +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices +import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor + +class VariableTypeResolver( + private val storageManager: StorageManager, + private val expressionTypingServices: ExpressionTypingServices, + private val typeResolver: TypeResolver, + private val constantExpressionEvaluator: ConstantExpressionEvaluator, + private val delegatedPropertyResolver: DelegatedPropertyResolver +) { + + fun process( + variableDescriptor: VariableDescriptorWithInitializerImpl, + scopeForInitializer: LexicalScope, + variable: KtVariableDeclaration, + dataFlowInfo: DataFlowInfo, + notLocal: Boolean, + trace: BindingTrace + ): KotlinType { + val propertyTypeRef = variable.typeReference + + val hasDelegate = variable is KtProperty && variable.hasDelegateExpression() + if (propertyTypeRef == null) { + if (!variable.hasInitializer()) { + if (hasDelegate && variableDescriptor is PropertyDescriptor) { + val property = variable as KtProperty + if (property.hasDelegateExpression()) { + return DeferredType.createRecursionIntolerant( + storageManager, + trace + ) { + resolveDelegatedPropertyType(property, variableDescriptor, scopeForInitializer, + property.delegateExpression!!, dataFlowInfo, trace) + } + } + } + if (!notLocal) { + trace.report(VARIABLE_WITH_NO_TYPE_NO_INITIALIZER.on(variable)) + } + return ErrorUtils.createErrorType("No type, no body") + } + else { + if (notLocal) { + return DeferredType.createRecursionIntolerant( + storageManager, + trace + ) { + PreliminaryDeclarationVisitor.createForDeclaration(variable, trace) + val initializerType = resolveInitializerType(scopeForInitializer, variable.initializer!!, dataFlowInfo, trace) + setConstantForVariableIfNeeded(variableDescriptor, scopeForInitializer, variable, dataFlowInfo, initializerType, trace) + transformAnonymousTypeIfNeeded(variableDescriptor, variable, initializerType, trace) + } + } + else { + val initializerType = resolveInitializerType(scopeForInitializer, variable.initializer!!, dataFlowInfo, trace) + setConstantForVariableIfNeeded(variableDescriptor, scopeForInitializer, variable, dataFlowInfo, initializerType, trace) + return initializerType + } + } + } + else { + val type = typeResolver.resolveType(scopeForInitializer, propertyTypeRef, trace, true) + setConstantForVariableIfNeeded(variableDescriptor, scopeForInitializer, variable, dataFlowInfo, type, trace) + return type + } + } + + private fun setConstantForVariableIfNeeded( + variableDescriptor: VariableDescriptorWithInitializerImpl, + scope: LexicalScope, + variable: KtVariableDeclaration, + dataFlowInfo: DataFlowInfo, + variableType: KotlinType, + trace: BindingTrace + ) { + if (!DescriptorUtils.shouldRecordInitializerForProperty(variableDescriptor, variableType)) return + + if (!variable.hasInitializer()) return + + variableDescriptor.setCompileTimeInitializer( + storageManager.createRecursionTolerantNullableLazyValue( + { + val initializer = variable.initializer + val initializerType = expressionTypingServices.safeGetType(scope, initializer!!, variableType, dataFlowInfo, trace) + val constant = constantExpressionEvaluator.evaluateExpression(initializer, trace, initializerType) + ?: return@createRecursionTolerantNullableLazyValue null + + if (constant.usesNonConstValAsConstant && variableDescriptor.isConst) { + trace.report(Errors.NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION.on(initializer)) + } + + constant.toConstantValue(initializerType) + }, + null + ) + ) + } + + private fun resolveDelegatedPropertyType( + property: KtProperty, + propertyDescriptor: PropertyDescriptor, + scopeForInitializer: LexicalScope, + delegateExpression: KtExpression, + dataFlowInfo: DataFlowInfo, + trace: BindingTrace + ): KotlinType { + val type = delegatedPropertyResolver.resolveDelegateExpression( + delegateExpression, property, propertyDescriptor, scopeForInitializer, trace, dataFlowInfo) + + if (type != null) { + val delegateFunctionsScope = ScopeUtils.makeScopeForDelegateConventionFunctions(scopeForInitializer, propertyDescriptor) + val getterReturnType = delegatedPropertyResolver.getDelegatedPropertyGetMethodReturnType(propertyDescriptor, delegateExpression, type, trace, delegateFunctionsScope) + if (getterReturnType != null) { + return getterReturnType + } + } + return ErrorUtils.createErrorType("Type from delegate") + } + + private fun resolveInitializerType( + scope: LexicalScope, + initializer: KtExpression, + dataFlowInfo: DataFlowInfo, + trace: BindingTrace + ): KotlinType { + return expressionTypingServices.safeGetType(scope, initializer, TypeUtils.NO_EXPECTED_TYPE, dataFlowInfo, trace) + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DestructuringDeclarationResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DestructuringDeclarationResolver.kt index 1cf5d89338c..10fdf0ffc01 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DestructuringDeclarationResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DestructuringDeclarationResolver.kt @@ -21,7 +21,7 @@ import org.jetbrains.kotlin.psi.KtDestructuringDeclaration import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.DescriptorResolver +import org.jetbrains.kotlin.resolve.LocalVariableResolver import org.jetbrains.kotlin.resolve.TypeResolver import org.jetbrains.kotlin.resolve.dataClassUtils.createComponentName import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope @@ -34,7 +34,7 @@ import org.jetbrains.kotlin.types.checker.KotlinTypeChecker class DestructuringDeclarationResolver( private val fakeCallResolver: FakeCallResolver, - private val descriptorResolver: DescriptorResolver, + private val localVariableResolver: LocalVariableResolver, private val typeResolver: TypeResolver, private val symbolUsageValidator: SymbolUsageValidator ) { @@ -72,7 +72,7 @@ class DestructuringDeclarationResolver( if (componentType == null) { componentType = ErrorUtils.createErrorType("$componentName() return type") } - val variableDescriptor = descriptorResolver.resolveLocalVariableDescriptorWithType(writableScope, entry, componentType, context.trace) + val variableDescriptor = localVariableResolver.resolveLocalVariableDescriptorWithType(writableScope, entry, componentType, context.trace) ExpressionTypingUtils.checkVariableShadowing(writableScope, context.trace, variableDescriptor) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingComponents.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingComponents.java index 5fb6932a565..c8f8b98290c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingComponents.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingComponents.java @@ -56,6 +56,8 @@ public class ExpressionTypingComponents { /*package*/ Iterable callCheckers; /*package*/ IdentifierChecker identifierChecker; /*package*/ DeclarationsCheckerBuilder declarationsCheckerBuilder; + /*package*/ LocalVariableResolver localVariableResolver; + @Inject public void setGlobalContext(@NotNull GlobalContext globalContext) { @@ -181,4 +183,9 @@ public class ExpressionTypingComponents { public void setDeclarationsCheckerBuilder(@NotNull DeclarationsCheckerBuilder declarationsCheckerBuilder) { this.declarationsCheckerBuilder = declarationsCheckerBuilder; } + + @Inject + public void setLocalVariableResolver(@NotNull LocalVariableResolver localVariableResolver) { + this.localVariableResolver = localVariableResolver; + } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java index f4aca1ae4ed..5567c95d010 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java @@ -26,8 +26,9 @@ import org.jetbrains.kotlin.diagnostics.Errors; import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.*; -import org.jetbrains.kotlin.resolve.*; -import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; +import org.jetbrains.kotlin.resolve.BindingContext; +import org.jetbrains.kotlin.resolve.BindingContextUtils; +import org.jetbrains.kotlin.resolve.TemporaryBindingTrace; import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults; @@ -102,61 +103,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito @Override public KotlinTypeInfo visitProperty(@NotNull KtProperty property, ExpressionTypingContext typingContext) { - ExpressionTypingContext context = typingContext.replaceContextDependency(INDEPENDENT).replaceScope(scope); - KtTypeReference receiverTypeRef = property.getReceiverTypeReference(); - if (receiverTypeRef != null) { - context.trace.report(LOCAL_EXTENSION_PROPERTY.on(receiverTypeRef)); - } - - KtPropertyAccessor getter = property.getGetter(); - if (getter != null) { - context.trace.report(LOCAL_VARIABLE_WITH_GETTER.on(getter)); - } - - KtPropertyAccessor setter = property.getSetter(); - if (setter != null) { - context.trace.report(LOCAL_VARIABLE_WITH_SETTER.on(setter)); - } - - KtExpression delegateExpression = property.getDelegateExpression(); - if (delegateExpression != null) { - components.expressionTypingServices.getTypeInfo(delegateExpression, context); - context.trace.report(LOCAL_VARIABLE_WITH_DELEGATE.on(property.getDelegate())); - } - - VariableDescriptor propertyDescriptor = components.descriptorResolver. - resolveLocalVariableDescriptor(scope, property, context.dataFlowInfo, context.trace); - KtExpression initializer = property.getInitializer(); - KotlinTypeInfo typeInfo; - if (initializer != null) { - KotlinType outType = propertyDescriptor.getType(); - typeInfo = facade.getTypeInfo(initializer, context.replaceExpectedType(outType)); - DataFlowInfo dataFlowInfo = typeInfo.getDataFlowInfo(); - KotlinType type = typeInfo.getType(); - // At this moment we do not take initializer value into account if type is given for a property - // We can comment first part of this condition to take them into account, like here: var s: String? = "xyz" - // In this case s will be not-nullable until it is changed - if (property.getTypeReference() == null && type != null) { - DataFlowValue variableDataFlowValue = DataFlowValueFactory.createDataFlowValueForProperty( - property, propertyDescriptor, context.trace.getBindingContext(), - DescriptorUtils.getContainingModuleOrNull(scope.getOwnerDescriptor())); - DataFlowValue initializerDataFlowValue = DataFlowValueFactory.createDataFlowValue(initializer, type, context); - // We cannot say here anything new about initializerDataFlowValue - // except it has the same value as variableDataFlowValue - typeInfo = typeInfo.replaceDataFlowInfo(dataFlowInfo.assign(variableDataFlowValue, initializerDataFlowValue)); - } - } - else { - typeInfo = TypeInfoFactoryKt.noTypeInfo(context); - } - - ExpressionTypingUtils.checkVariableShadowing(context.scope, context.trace, propertyDescriptor); - - scope.addVariableDescriptor(propertyDescriptor); - DeclarationsCheckerKt.checkTypeReferences(property, context.trace); - components.modifiersChecker.withTrace(context.trace).checkModifiersForLocalDeclaration(property, propertyDescriptor); - components.identifierChecker.checkDeclaration(property, context.trace); - return typeInfo.replaceType(components.dataFlowAnalyzer.checkStatementType(property, context)); + return components.localVariableResolver.process(property, typingContext, scope, facade); } @Override