From 7c02a1eaad8fd5e086eec00678724e909cd2157a Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Fri, 1 Apr 2016 19:35:54 +0300 Subject: [PATCH] Refactoring: VarianceChecker / VarianceCheckerCore, check variance for a separate class --- .../jetbrains/kotlin/diagnostics/Errors.java | 2 +- .../rendering/DefaultErrorMessages.java | 2 +- .../kotlin/resolve/LazyTopDownAnalyzer.kt | 1 - .../kotlin/resolve/VarianceChecker.kt | 272 ++++++++++-------- .../lazy/descriptors/LazyClassMemberScope.kt | 5 +- 5 files changed, 151 insertions(+), 131 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 895ae986834..7a52aceb929 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -30,7 +30,7 @@ import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; -import org.jetbrains.kotlin.resolve.varianceChecker.VarianceChecker.VarianceConflictDiagnosticData; +import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData; import org.jetbrains.kotlin.types.KotlinType; import java.lang.reflect.Field; diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 572947019e1..0416115e680 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -28,7 +28,7 @@ import org.jetbrains.kotlin.diagnostics.TypeMismatchDueToTypeProjectionsData; import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.psi.KtSimpleNameExpression; import org.jetbrains.kotlin.psi.KtTypeConstraint; -import org.jetbrains.kotlin.resolve.varianceChecker.VarianceChecker.VarianceConflictDiagnosticData; +import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData; import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.util.MappedExtensionProvider; import org.jetbrains.kotlin.util.OperatorNameConventions; diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/LazyTopDownAnalyzer.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/LazyTopDownAnalyzer.kt index bea8893754f..5dbfa46f930 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/LazyTopDownAnalyzer.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/LazyTopDownAnalyzer.kt @@ -29,7 +29,6 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.lazy.* import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyScriptDescriptor -import org.jetbrains.kotlin.resolve.varianceChecker.VarianceChecker import java.util.* class LazyTopDownAnalyzer( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/VarianceChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/VarianceChecker.kt index 93396631911..341e0340734 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/VarianceChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/VarianceChecker.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jetbrains.kotlin.resolve.varianceChecker +package org.jetbrains.kotlin.resolve import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.diagnostics.Errors @@ -23,18 +23,13 @@ import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure.EnrichedProjectionKind.* import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure.* import org.jetbrains.kotlin.descriptors.Visibilities -import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.typeBinding.TypeBinding import com.intellij.psi.PsiElement import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.resolve.typeBinding.createTypeBinding import org.jetbrains.kotlin.resolve.typeBinding.createTypeBindingForReturnType -import org.jetbrains.kotlin.psi.KtCallableDeclaration import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.resolve.TopDownAnalysisContext import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.psi.KtClass -import org.jetbrains.kotlin.psi.KtTypeReference import org.jetbrains.kotlin.types.Variance.* import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor @@ -43,153 +38,178 @@ import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.PropertyAccessorDescriptorImpl import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.descriptors.VariableDescriptor -import org.jetbrains.kotlin.psi.KtTypeParameterListOwner -import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.psi.* +class ManualVariance(val descriptor: TypeParameterDescriptor, val variance: Variance) class VarianceChecker(private val trace: BindingTrace) { + private val core = VarianceCheckerCore(trace.bindingContext, trace) + + fun check(c: TopDownAnalysisContext) { + core.check(c) + } +} + +class VarianceConflictDiagnosticData( + val containingType: KotlinType, + val typeParameter: TypeParameterDescriptor, + val occurrencePosition: Variance +) + +class VarianceCheckerCore( + private val context: BindingContext, + private val diagnosticSink: DiagnosticSink, + private val manualVariance: ManualVariance? = null +) { fun check(c: TopDownAnalysisContext) { checkClasses(c) checkMembers(c) } + fun checkClassOrObject(klass: KtClassOrObject): Boolean { + if (klass is KtClass) { + if (!checkClassHeader(klass)) return false + } + for (member in klass.declarations) { + member as? KtCallableDeclaration ?: continue + val descriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, member) as? CallableMemberDescriptor ?: continue + if (!checkMember(member, descriptor)) return false + } + return true + } + private fun checkClasses(c: TopDownAnalysisContext) { for (jetClassOrObject in c.declaredClasses!!.keys) { if (jetClassOrObject is KtClass) { - for (specifier in jetClassOrObject.getSuperTypeListEntries()) { - specifier.typeReference?.checkTypePosition(trace.bindingContext, OUT_VARIANCE, trace) - } - jetClassOrObject.checkTypeParameters(trace.bindingContext, OUT_VARIANCE, trace) + checkClassHeader(jetClassOrObject) } } } + private fun checkClassHeader(klass: KtClass): Boolean { + var noError = true + for (specifier in klass.getSuperTypeListEntries()) { + noError = noError and specifier.typeReference?.checkTypePosition(context, OUT_VARIANCE) + } + return noError and klass.checkTypeParameters(context, OUT_VARIANCE) + } + private fun checkMembers(c: TopDownAnalysisContext) { for ((declaration, descriptor) in c.members) { - if (!Visibilities.isPrivate(descriptor.visibility)) { - checkCallableDeclaration(trace.bindingContext, declaration, descriptor, trace) - } + checkMember(declaration, descriptor) } } - class VarianceConflictDiagnosticData( - val containingType: KotlinType, - val typeParameter: TypeParameterDescriptor, - val occurrencePosition: Variance - ) + private fun checkMember(member: KtCallableDeclaration, descriptor: CallableMemberDescriptor) = + Visibilities.isPrivate(descriptor.visibility) || checkCallableDeclaration(context, member, descriptor) + + private fun TypeParameterDescriptor.varianceWithManual() = + if (manualVariance != null && this == manualVariance.descriptor) manualVariance.variance else variance + + fun recordPrivateToThisIfNeeded(descriptor: CallableMemberDescriptor) { + if (isIrrelevant(descriptor) || descriptor.visibility != Visibilities.PRIVATE) return + + val psiElement = descriptor.source.getPsi() as? KtCallableDeclaration ?: return + + if (!checkCallableDeclaration(context, psiElement, descriptor)) { + recordPrivateToThis(descriptor) + } + } + + private fun checkCallableDeclaration( + trace: BindingContext, + declaration: KtCallableDeclaration, + descriptor: CallableDescriptor + ): Boolean { + if (isIrrelevant(descriptor)) return true + var noError = true + + noError = noError and declaration.checkTypeParameters(trace, IN_VARIANCE) + + noError = noError and declaration.receiverTypeReference?.checkTypePosition(trace, IN_VARIANCE) + + for (parameter in declaration.valueParameters) { + noError = noError and parameter.typeReference?.checkTypePosition(trace, IN_VARIANCE) + } + + val returnTypePosition = if (descriptor is VariableDescriptor && descriptor.isVar) INVARIANT else OUT_VARIANCE + noError = noError and declaration.createTypeBindingForReturnType(trace)?.checkTypePosition(returnTypePosition) + + return noError + } + + private fun KtTypeParameterListOwner.checkTypeParameters( + trace: BindingContext, + typePosition: Variance + ): Boolean { + var noError = true + for (typeParameter in typeParameters) { + noError = noError and typeParameter.extendsBound?.checkTypePosition(trace, typePosition) + } + for (typeConstraint in typeConstraints) { + noError = noError and typeConstraint.boundTypeReference?.checkTypePosition(trace, typePosition) + } + return noError + } + + private fun KtTypeReference.checkTypePosition(trace: BindingContext, position: Variance) + = createTypeBinding(trace)?.checkTypePosition(position) + + private fun TypeBinding.checkTypePosition(position: Variance) = checkTypePosition(kotlinType, position) + + private fun TypeBinding.checkTypePosition(containingType: KotlinType, position: Variance): Boolean { + val classifierDescriptor = kotlinType.constructor.declarationDescriptor + if (classifierDescriptor is TypeParameterDescriptor) { + val declarationVariance = classifierDescriptor.varianceWithManual() + if (!declarationVariance.allowsPosition(position) + && !kotlinType.annotations.hasAnnotation(KotlinBuiltIns.FQ_NAMES.unsafeVariance)) { + diagnosticSink.report( + Errors.TYPE_VARIANCE_CONFLICT.on( + psiElement, + VarianceConflictDiagnosticData(containingType, classifierDescriptor, position) + ) + ) + } + return declarationVariance.allowsPosition(position) + } + + var noError = true + for (argumentBinding in getArgumentBindings()) { + if (argumentBinding == null || argumentBinding.typeParameterDescriptor == null) continue + + val projectionKind = getEffectiveProjectionKind(argumentBinding.typeParameterDescriptor!!, argumentBinding.typeProjection)!! + val newPosition = when (projectionKind) { + OUT -> position + IN -> position.opposite() + INV -> INVARIANT + STAR -> null // CONFLICTING_PROJECTION error was reported + } + if (newPosition != null) { + noError = noError and argumentBinding.typeBinding.checkTypePosition(containingType, newPosition) + } + } + return noError + } + + private fun isIrrelevant(descriptor: CallableDescriptor): Boolean { + val containingClass = descriptor.containingDeclaration as? ClassDescriptor ?: return true + return containingClass.typeConstructor.parameters.all { it.varianceWithManual() == INVARIANT } + } companion object { - @JvmStatic - fun recordPrivateToThisIfNeeded(trace: BindingTrace, descriptor: CallableMemberDescriptor) { - if (isIrrelevant(descriptor) || descriptor.visibility != Visibilities.PRIVATE) return - - val psiElement = descriptor.source.getPsi() - if (psiElement !is KtCallableDeclaration) return - - if (!checkCallableDeclaration(trace.bindingContext, psiElement, descriptor, DiagnosticSink.DO_NOTHING)) { - recordPrivateToThis(descriptor) - } - } - - private fun isIrrelevant(descriptor: CallableDescriptor): Boolean { - val containingClass = descriptor.containingDeclaration - if (containingClass !is ClassDescriptor) return true - - return containingClass.typeConstructor.parameters.all { it.variance == INVARIANT } - } private fun recordPrivateToThis(descriptor: CallableMemberDescriptor) { - if (descriptor is FunctionDescriptorImpl) { - descriptor.visibility = Visibilities.PRIVATE_TO_THIS; - } - else if (descriptor is PropertyDescriptorImpl) { - descriptor.visibility = Visibilities.PRIVATE_TO_THIS; - for (accessor in descriptor.accessors) { - (accessor as PropertyAccessorDescriptorImpl).visibility = Visibilities.PRIVATE_TO_THIS + when (descriptor) { + is FunctionDescriptorImpl -> descriptor.visibility = Visibilities.PRIVATE_TO_THIS + is PropertyDescriptorImpl -> { + descriptor.visibility = Visibilities.PRIVATE_TO_THIS; + for (accessor in descriptor.accessors) { + (accessor as PropertyAccessorDescriptorImpl).visibility = Visibilities.PRIVATE_TO_THIS + } } + else -> throw IllegalStateException("Unexpected descriptor type: ${descriptor.javaClass.name}") } - else { - throw IllegalStateException("Unexpected descriptor type: ${descriptor.javaClass.name}") - } - } - - private fun checkCallableDeclaration( - trace: BindingContext, - declaration: KtCallableDeclaration, - descriptor: CallableDescriptor, - diagnosticSink: DiagnosticSink - ): Boolean { - if (isIrrelevant(descriptor)) return true - var noError = true - - noError = noError and declaration.checkTypeParameters(trace, IN_VARIANCE, diagnosticSink) - - noError = noError and declaration.receiverTypeReference?.checkTypePosition(trace, IN_VARIANCE, diagnosticSink) - - for (parameter in declaration.valueParameters) { - noError = noError and parameter.typeReference?.checkTypePosition(trace, IN_VARIANCE, diagnosticSink) - } - - val returnTypePosition = if (descriptor is VariableDescriptor && descriptor.isVar) INVARIANT else OUT_VARIANCE - noError = noError and declaration.createTypeBindingForReturnType(trace)?.checkTypePosition(returnTypePosition, diagnosticSink) - - return noError - } - - private fun KtTypeParameterListOwner.checkTypeParameters( - trace: BindingContext, - typePosition: Variance, - diagnosticSink: DiagnosticSink - ): Boolean { - var noError = true - for (typeParameter in typeParameters) { - noError = noError and typeParameter.extendsBound?.checkTypePosition(trace, typePosition, diagnosticSink) - } - for (typeConstraint in typeConstraints) { - noError = noError and typeConstraint.boundTypeReference?.checkTypePosition(trace, typePosition, diagnosticSink) - } - return noError - } - - private fun KtTypeReference.checkTypePosition(trace: BindingContext, position: Variance, diagnosticSink: DiagnosticSink) - = createTypeBinding(trace)?.checkTypePosition(position, diagnosticSink) - - private fun TypeBinding.checkTypePosition(position: Variance, diagnosticSink: DiagnosticSink) - = checkTypePosition(kotlinType, position, diagnosticSink) - - private fun TypeBinding.checkTypePosition(containingType: KotlinType, position: Variance, diagnosticSink: DiagnosticSink): Boolean { - val classifierDescriptor = kotlinType.constructor.declarationDescriptor - if (classifierDescriptor is TypeParameterDescriptor) { - val declarationVariance = classifierDescriptor.variance - if (!declarationVariance.allowsPosition(position) - && !kotlinType.annotations.hasAnnotation(KotlinBuiltIns.FQ_NAMES.unsafeVariance)) { - diagnosticSink.report( - Errors.TYPE_VARIANCE_CONFLICT.on( - psiElement, - VarianceConflictDiagnosticData(containingType, classifierDescriptor, position) - ) - ) - } - return declarationVariance.allowsPosition(position) - } - - var noError = true - for (argumentBinding in getArgumentBindings()) { - if (argumentBinding == null || argumentBinding.typeParameterDescriptor == null) continue - - val projectionKind = getEffectiveProjectionKind(argumentBinding.typeParameterDescriptor!!, argumentBinding.typeProjection)!! - val newPosition = when (projectionKind) { - OUT -> position - IN -> position.opposite() - INV -> INVARIANT - STAR -> null // CONFLICTING_PROJECTION error was reported - } - if (newPosition != null) { - noError = noError and argumentBinding.typeBinding.checkTypePosition(containingType, newPosition, diagnosticSink) - } - } - return noError } private infix fun Boolean.and(other: Boolean?) = if (other == null) this else this and other diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassMemberScope.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassMemberScope.kt index 99457e76bf7..b8391789327 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassMemberScope.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassMemberScope.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DELEGATION import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.FAKE_OVERRIDE import org.jetbrains.kotlin.descriptors.impl.ConstructorDescriptorImpl +import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation @@ -37,7 +38,7 @@ import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProv import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.scopes.MemberScope -import org.jetbrains.kotlin.resolve.varianceChecker.VarianceChecker +import org.jetbrains.kotlin.resolve.VarianceCheckerCore import org.jetbrains.kotlin.storage.NotNullLazyValue import org.jetbrains.kotlin.storage.NullableLazyValue import org.jetbrains.kotlin.types.DeferredType @@ -192,7 +193,7 @@ open class LazyClassMemberScope( if (descriptor.kind != FAKE_OVERRIDE && descriptor.kind != DELEGATION) { OverridingUtil.resolveUnknownVisibilityForMember(descriptor, OverrideResolver.createCannotInferVisibilityReporter(trace)) } - VarianceChecker.recordPrivateToThisIfNeeded(trace, descriptor); + VarianceCheckerCore(trace.bindingContext, DiagnosticSink.DO_NOTHING).recordPrivateToThisIfNeeded(descriptor); } }