From d783d99443ee4e2f0782b9456949f5179742b991 Mon Sep 17 00:00:00 2001 From: Victor Petukhov Date: Wed, 3 Feb 2021 12:59:49 +0300 Subject: [PATCH] Use upper bound checker for typealias expansion --- .../jvm/checkers/EnhancedUpperBoundChecker.kt | 57 ++++++++++------- .../jvm/checkers/JavaNullabilityChecker.kt | 31 +-------- .../diagnostics/DefaultErrorMessagesJvm.java | 5 ++ .../resolve/jvm/diagnostics/ErrorsJvm.java | 4 ++ .../kotlin/resolve/DeclarationsChecker.kt | 12 ++-- .../jetbrains/kotlin/resolve/TypeResolver.kt | 14 ++--- .../kotlin/resolve/UpperBoundChecker.kt | 63 ++++++++++++++----- .../kotlin/resolve/calls/CandidateResolver.kt | 23 ++++--- .../DiagnosticReporterByTrackingStrategy.kt | 6 +- .../tower/KotlinToResolvedCallTransformer.kt | 1 + .../java/checkEnhancedUpperBounds.fir.kt | 29 ++++++++- .../java/checkEnhancedUpperBounds.kt | 24 ++++++- .../java/checkEnhancedUpperBounds.txt | 28 ++++++++- ...dUpperBoundsWithEnabledImprovements.fir.kt | 32 ++++++++-- ...ancedUpperBoundsWithEnabledImprovements.kt | 24 ++++++- ...ncedUpperBoundsWithEnabledImprovements.txt | 27 +++++++- .../kotlin/types/TypeAliasExpander.kt | 27 +------- .../types/TypeAliasExpansionReportStrategy.kt | 4 +- .../kotlin/types/TypeWithEnhancement.kt | 34 ++++++++++ .../CompositeResolverForModuleFactory.kt | 1 + 20 files changed, 306 insertions(+), 140 deletions(-) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/EnhancedUpperBoundChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/EnhancedUpperBoundChecker.kt index 73599004670..0a2c5fa18e4 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/EnhancedUpperBoundChecker.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/EnhancedUpperBoundChecker.kt @@ -7,40 +7,49 @@ package org.jetbrains.kotlin.resolve.jvm.checkers import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings -import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtTypeReference import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.UpperBoundChecker -import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm +import org.jetbrains.kotlin.resolve.UpperBoundViolatedReporter +import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS +import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION_BASED_ON_JAVA_ANNOTATIONS import org.jetbrains.kotlin.types.* -import org.jetbrains.kotlin.types.checker.KotlinTypeChecker -class EnhancedUpperBoundChecker(override val languageVersionSettings: LanguageVersionSettings) : UpperBoundChecker { - override fun checkBound( - bound: KotlinType, +// TODO: remove this checker after removing support LV < 1.6 +class EnhancedUpperBoundChecker(languageVersionSettings: LanguageVersionSettings) : UpperBoundChecker(languageVersionSettings) { + val isTypeEnhancementImprovementsEnabled = languageVersionSettings.supportsFeature(LanguageFeature.ImprovementsAroundTypeEnhancement) + + override fun checkBounds( + argumentReference: KtTypeReference?, + argumentType: KotlinType, + typeParameterDescriptor: TypeParameterDescriptor, substitutor: TypeSubstitutor, trace: BindingTrace, - jetTypeArgument: KtTypeReference, - typeArgument: KotlinType - ): Boolean { - val isCheckPassed = super.checkBound(bound, substitutor, trace, jetTypeArgument, typeArgument) + typeAliasUsageElement: KtElement? + ) { + if (typeParameterDescriptor.upperBounds.isEmpty()) return - // The error is already reported, it's unnecessary to do more checks - if (!isCheckPassed) return false + val diagnosticsReporter = UpperBoundViolatedReporter(trace, argumentType, typeParameterDescriptor) + val diagnosticsReporterForWarnings = UpperBoundViolatedReporter( + trace, argumentType, typeParameterDescriptor, + baseDiagnostic = UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS, + diagnosticForTypeAliases = UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION_BASED_ON_JAVA_ANNOTATIONS + ) - val enhancedBound = bound.getEnhancement() ?: return false + for (bound in typeParameterDescriptor.upperBounds) { + val isCheckPassed = checkBound(bound, argumentType, argumentReference, substitutor, typeAliasUsageElement, diagnosticsReporter) - val isTypeEnhancementImprovementsEnabled = - languageVersionSettings.supportsFeature(LanguageFeature.ImprovementsAroundTypeEnhancement) - val substitutedBound = substitutor.safeSubstitute(enhancedBound, Variance.INVARIANT) - if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(typeArgument, substitutedBound)) { - if (isTypeEnhancementImprovementsEnabled) { - trace.report(Errors.UPPER_BOUND_VIOLATED.on(jetTypeArgument, substitutedBound, typeArgument)) - } else { - trace.report(ErrorsJvm.UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS.on(jetTypeArgument, substitutedBound, typeArgument)) - } - return false + // The error is already reported, it's unnecessary to do more checks + if (!isCheckPassed) continue + + // If improvements are enabled, then type parameter's upper bounds will already enhanced, and the error will reported inside the first check + if (isTypeEnhancementImprovementsEnabled) continue + + val enhancedBound = bound.getEnhancementDeeply() ?: continue + + checkBound(enhancedBound, argumentType, argumentReference, substitutor, typeAliasUsageElement, diagnosticsReporterForWarnings) } - return true } } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JavaNullabilityChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JavaNullabilityChecker.kt index 9a6254f7080..c3462845953 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JavaNullabilityChecker.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JavaNullabilityChecker.kt @@ -215,9 +215,9 @@ class JavaNullabilityChecker : AdditionalTypeChecker { if (!doesExpectedTypeContainsEnhancement && !doesExpressionTypeContainsEnhancement) return - val enhancedExpectedType = if (doesExpectedTypeContainsEnhancement) buildTypeWithEnhancement(expectedType) else expectedType + val enhancedExpectedType = if (doesExpectedTypeContainsEnhancement) expectedType.unwrapEnhancementDeeply() else expectedType val enhancedExpressionType = enhanceExpressionTypeByDataFlowNullability( - if (doesExpressionTypeContainsEnhancement) buildTypeWithEnhancement(expressionType) else expressionType, + if (doesExpressionTypeContainsEnhancement) expressionType.unwrapEnhancementDeeply() else expressionType, expressionTypeDataFlowValue, dataFlowInfo ) @@ -253,33 +253,6 @@ class JavaNullabilityChecker : AdditionalTypeChecker { } else { null } - - private fun enhanceTypeArguments(arguments: List) = - arguments.map { argument -> - // TODO: think about star projections with enhancement (e.g. came from Java: Foo<@NotNull ?>) - if (argument.isStarProjection) { - return@map argument - } - val argumentType = argument.type - val enhancedArgumentType = if (argumentType is TypeWithEnhancement) argumentType.enhancement else argumentType - val enhancedDeeplyArgumentType = buildTypeWithEnhancement(enhancedArgumentType) - - argument.replaceType(enhancedDeeplyArgumentType) - } - - fun buildTypeWithEnhancement(type: KotlinType): KotlinType { - val newArguments = enhanceTypeArguments(type.arguments) - val newArgumentsForUpperBound = - if (type is FlexibleType) { - enhanceTypeArguments(type.upperBound.arguments) - } else newArguments - val enhancedType = if (type is TypeWithEnhancement) type.enhancement else type - - return enhancedType.replace( - newArguments = newArguments, - newArgumentsForUpperBound = newArgumentsForUpperBound - ) - } } class EnhancedNullabilityInfo(val enhancedType: KotlinType, val isFromJava: Boolean) { diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java index 52a4ce117ab..ce2a5a46f29 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.utils.StringsKt; import java.util.List; import static kotlin.collections.CollectionsKt.*; +import static org.jetbrains.kotlin.diagnostics.Errors.UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION; import static org.jetbrains.kotlin.diagnostics.rendering.Renderers.*; import static org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.*; @@ -86,6 +87,10 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension { MAP.put(NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS, "Type mismatch: inferred type is {1} but {0} was expected", RENDER_TYPE, RENDER_TYPE); MAP.put(UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS, "Type argument is not within its bounds: should be subtype of ''{0}''", RENDER_TYPE, RENDER_TYPE); + MAP.put(UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION_BASED_ON_JAVA_ANNOTATIONS, + "Type argument resulting from type alias expansion is not within required bounds for ''{2}'': " + + "should be subtype of ''{0}'', substituted type is ''{1}''", + RENDER_TYPE, RENDER_TYPE, NAME); MAP.put(NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER, "Type mismatch: value of a nullable type {0} is used where non-nullable type is expected. " + "This warning will become an error soon. " + diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java index 05edf57b0c1..cb549a51268 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.jvm.diagnostics; import com.intellij.psi.PsiElement; import org.jetbrains.kotlin.descriptors.CallableDescriptor; +import org.jetbrains.kotlin.descriptors.ClassifierDescriptor; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.diagnostics.*; import org.jetbrains.kotlin.name.FqName; @@ -140,6 +141,9 @@ public interface ErrorsJvm { DiagnosticFactory2 UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS = DiagnosticFactory2.create(WARNING); + DiagnosticFactory3 UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION_BASED_ON_JAVA_ANNOTATIONS + = DiagnosticFactory3.create(WARNING); + DiagnosticFactory1 NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER = DiagnosticFactory1.create(WARNING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt index de02be8ba01..7322a4cb65f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt @@ -202,7 +202,8 @@ class DeclarationsChecker( private class TypeAliasDeclarationCheckingReportStrategy( private val trace: BindingTrace, typeAliasDescriptor: TypeAliasDescriptor, - declaration: KtTypeAlias + declaration: KtTypeAlias, + val upperBoundChecker: UpperBoundChecker ) : TypeAliasExpansionReportStrategy { private val typeReference = declaration.getTypeReference() ?: throw AssertionError("Incorrect type alias declaration for $typeAliasDescriptor") @@ -224,15 +225,12 @@ class DeclarationsChecker( } override fun boundsViolationInSubstitution( - bound: KotlinType, + substitutor: TypeSubstitutor, unsubstitutedArgument: KotlinType, argument: KotlinType, typeParameter: TypeParameterDescriptor ) { - // TODO more precise diagnostics - if (!argument.containsTypeAliasParameters() && !bound.containsTypeAliasParameters()) { - trace.report(UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION.on(typeReference, bound, argument, typeParameter)) - } + upperBoundChecker.checkBounds(null, argument, typeParameter, substitutor, trace, typeReference) } override fun repeatedAnnotation(annotation: AnnotationDescriptor) { @@ -243,7 +241,7 @@ class DeclarationsChecker( private fun checkTypeAliasExpansion(declaration: KtTypeAlias, typeAliasDescriptor: TypeAliasDescriptor) { val typeAliasExpansion = TypeAliasExpansion.createWithFormalArguments(typeAliasDescriptor) - val reportStrategy = TypeAliasDeclarationCheckingReportStrategy(trace, typeAliasDescriptor, declaration) + val reportStrategy = TypeAliasDeclarationCheckingReportStrategy(trace, typeAliasDescriptor, declaration, upperBoundChecker) TypeAliasExpander(reportStrategy, true).expandWithoutAbbreviation(typeAliasExpansion, Annotations.EMPTY) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt index d85b0f53cdc..21da0f3ea0b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt @@ -597,7 +597,8 @@ class TypeResolver( c.trace, type, typeAliasQualifierPart.typeArguments ?: typeAliasQualifierPart.expression, descriptor, descriptor.declaredTypeParameters, - argumentElementsFromUserType // TODO arguments from inner scope + argumentElementsFromUserType, // TODO arguments from inner scope + upperBoundChecker ) if (parameters.size != arguments.size) { @@ -659,7 +660,8 @@ class TypeResolver( val typeArgumentsOrTypeName: KtElement?, val typeAliasDescriptor: TypeAliasDescriptor, typeParameters: List, - typeArguments: List + typeArguments: List, + val upperBoundChecker: UpperBoundChecker ) : TypeAliasExpansionReportStrategy { private val mappedArguments = typeParameters.zip(typeArguments).toMap() @@ -690,7 +692,7 @@ class TypeResolver( } override fun boundsViolationInSubstitution( - bound: KotlinType, + substitutor: TypeSubstitutor, unsubstitutedArgument: KotlinType, argument: KotlinType, typeParameter: TypeParameterDescriptor @@ -698,11 +700,7 @@ class TypeResolver( val descriptorForUnsubstitutedArgument = unsubstitutedArgument.constructor.declarationDescriptor val argumentElement = mappedArguments[descriptorForUnsubstitutedArgument] val argumentTypeReferenceElement = argumentElement?.typeReference - if (argumentTypeReferenceElement != null) { - trace.report(UPPER_BOUND_VIOLATED.on(argumentTypeReferenceElement, bound, argument)) - } else if (type != null) { - trace.report(UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION.on(type, bound, argument, typeParameter)) - } + upperBoundChecker.checkBounds(argumentTypeReferenceElement, argument, typeParameter, substitutor, trace, type) } override fun repeatedAnnotation(annotation: AnnotationDescriptor) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/UpperBoundChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/UpperBoundChecker.kt index fd0ebaa36ed..2b29e5cbb9b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/UpperBoundChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/UpperBoundChecker.kt @@ -7,17 +7,21 @@ package org.jetbrains.kotlin.resolve import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.container.DefaultImplementation +import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor -import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.diagnostics.DiagnosticFactory2 +import org.jetbrains.kotlin.diagnostics.DiagnosticFactory3 +import org.jetbrains.kotlin.diagnostics.Errors.UPPER_BOUND_VIOLATED +import org.jetbrains.kotlin.diagnostics.Errors.UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION +import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtTypeReference import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.KotlinTypeChecker +import org.jetbrains.kotlin.types.typeUtil.containsTypeAliasParameters @DefaultImplementation(impl = UpperBoundChecker::class) -interface UpperBoundChecker { - val languageVersionSettings: LanguageVersionSettings - +open class UpperBoundChecker(val languageVersionSettings: LanguageVersionSettings) { fun checkBounds(typeReference: KtTypeReference, type: KotlinType, trace: BindingTrace) { if (type.isError) return @@ -54,30 +58,59 @@ interface UpperBoundChecker { } } - fun checkBounds( - jetTypeArgument: KtTypeReference, - typeArgument: KotlinType, + open fun checkBounds( + argumentReference: KtTypeReference?, + argumentType: KotlinType, typeParameterDescriptor: TypeParameterDescriptor, substitutor: TypeSubstitutor, - trace: BindingTrace + trace: BindingTrace, + typeAliasUsageElement: KtElement? = null, ) { + if (typeParameterDescriptor.upperBounds.isEmpty()) return + + val diagnosticsReporter = UpperBoundViolatedReporter(trace, argumentType, typeParameterDescriptor) + for (bound in typeParameterDescriptor.upperBounds) { - checkBound(bound, substitutor, trace, jetTypeArgument, typeArgument) + checkBound(bound, argumentType, argumentReference, substitutor, typeAliasUsageElement, diagnosticsReporter) } } - fun checkBound( + protected fun checkBound( bound: KotlinType, + argumentType: KotlinType, + argumentReference: KtTypeReference?, substitutor: TypeSubstitutor, - trace: BindingTrace, - jetTypeArgument: KtTypeReference, - typeArgument: KotlinType + typeAliasUsageElement: KtElement? = null, + upperBoundViolatedReporter: UpperBoundViolatedReporter ): Boolean { val substitutedBound = substitutor.safeSubstitute(bound, Variance.INVARIANT) - if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(typeArgument, substitutedBound)) { - trace.report(Errors.UPPER_BOUND_VIOLATED.on(jetTypeArgument, substitutedBound, typeArgument)) + + if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(argumentType, substitutedBound)) { + if (argumentReference != null) { + upperBoundViolatedReporter.report(argumentReference, substitutedBound) + } else if (typeAliasUsageElement != null && !substitutedBound.containsTypeAliasParameters() && !argumentType.containsTypeAliasParameters()) { + upperBoundViolatedReporter.reportForTypeAliasExpansion(typeAliasUsageElement, substitutedBound) + } return false } + return true } } + +class UpperBoundViolatedReporter( + val trace: BindingTrace, + val argumentType: KotlinType, + val typeParameterDescriptor: TypeParameterDescriptor? = null, + val baseDiagnostic: DiagnosticFactory2 = UPPER_BOUND_VIOLATED, + val diagnosticForTypeAliases: DiagnosticFactory3 = UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION +) { + fun report(typeArgumentReference: KtTypeReference, substitutedBound: KotlinType) { + trace.report(baseDiagnostic.on(typeArgumentReference, substitutedBound, argumentType)) + } + + fun reportForTypeAliasExpansion(callElement: KtElement, substitutedBound: KotlinType) { + if (typeParameterDescriptor == null) return + trace.report(diagnosticForTypeAliases.on(callElement, substitutedBound, argumentType, typeParameterDescriptor)) + } +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt index c7868682f92..9e2a09eefb3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt @@ -606,7 +606,8 @@ class CandidateResolver( private val callElement: KtElement, typeAlias: TypeAliasDescriptor, ktTypeArguments: List, - private val trace: BindingTrace + private val trace: BindingTrace, + private val upperBoundChecker: UpperBoundChecker ) : TypeAliasExpansionReportStrategy { init { assert(!typeAlias.expandedType.isError) { "Incorrect type alias: $typeAlias" } @@ -635,7 +636,7 @@ class CandidateResolver( } override fun boundsViolationInSubstitution( - bound: KotlinType, + substitutor: TypeSubstitutor, unsubstitutedArgument: KotlinType, argument: KotlinType, typeParameter: TypeParameterDescriptor @@ -643,11 +644,8 @@ class CandidateResolver( val descriptorForUnsubstitutedArgument = unsubstitutedArgument.constructor.declarationDescriptor val argumentElement = argumentsMapping[descriptorForUnsubstitutedArgument] val argumentTypeReferenceElement = argumentElement?.typeReference - if (argumentTypeReferenceElement != null) { - trace.report(UPPER_BOUND_VIOLATED.on(argumentTypeReferenceElement, bound, argument)) - } else { - trace.report(UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION.on(callElement, bound, argument, typeParameter)) - } + + upperBoundChecker.checkBounds(argumentTypeReferenceElement, argument, typeParameter, substitutor, trace, callElement) } } @@ -665,7 +663,8 @@ class CandidateResolver( val unsubstitutedType = typeAliasDescriptor.expandedType if (unsubstitutedType.isError) return - val reportStrategy = TypeAliasSingleStepExpansionReportStrategy(call.callElement, typeAliasDescriptor, ktTypeArguments, trace) + val reportStrategy = + TypeAliasSingleStepExpansionReportStrategy(call.callElement, typeAliasDescriptor, ktTypeArguments, trace, upperBoundChecker) // TODO refactor TypeResolver // - perform full type alias expansion @@ -694,12 +693,12 @@ class CandidateResolver( val typeParameter = typeParameters[i] val substitutedTypeArgument = substitutedTypeProjection.type val unsubstitutedTypeArgument = unsubstitutedType.arguments[i].type - TypeAliasExpander.checkBoundsInTypeAlias( - reportStrategy, + + reportStrategy.boundsViolationInSubstitution( + boundsSubstitutor, unsubstitutedTypeArgument, substitutedTypeArgument, - typeParameter, - boundsSubstitutor + typeParameter ) checkTypeInTypeAliasSubstitutionRec( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt index 7398559c52d..ad0373ae391 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt @@ -15,6 +15,8 @@ import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnce import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.isNull import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.UpperBoundChecker +import org.jetbrains.kotlin.resolve.UpperBoundViolatedReporter import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.reportTrailingLambdaErrorOr @@ -393,8 +395,8 @@ class DiagnosticReporterByTrackingStrategy( } (position as? ExplicitTypeParameterConstraintPositionImpl)?.let { - val typeArgumentReference = (it.typeArgument as SimpleTypeArgumentImpl).typeReference - trace.report(UPPER_BOUND_VIOLATED.on(typeArgumentReference, error.upperKotlinType, error.lowerKotlinType)) + UpperBoundViolatedReporter(trace, error.upperKotlinType) + .report((it.typeArgument as SimpleTypeArgumentImpl).typeReference, error.lowerKotlinType) } (position as? FixVariableConstraintPositionImpl)?.let { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt index af4e965302c..66fcf59578e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt @@ -84,6 +84,7 @@ class KotlinToResolvedCallTransformer( private val smartCastManager: SmartCastManager, private val typeApproximator: TypeApproximator, private val missingSupertypesResolver: MissingSupertypesResolver, + private val upperBoundChecker: UpperBoundChecker, ) { companion object { private val REPORT_MISSING_NEW_INFERENCE_DIAGNOSTIC diff --git a/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBounds.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBounds.fir.kt index e89ef63ceb7..c74590f7a23 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBounds.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBounds.fir.kt @@ -1,11 +1,36 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE // FULL_JDK // FILE: MapLike.java import java.util.Map; -public interface MapLike<@org.jetbrains.annotations.NotNull K, V> { +public class MapLike<@org.jetbrains.annotations.NotNull K, V> { void putAll(Map map); } +// FILE: ListLike.java +import java.util.Collection; + +public class ListLike> {} + // FILE: main.kt -fun test(map : MapLike) {} +fun test0(map : MapLike<Int?, Int>) {} +fun test11(map : MapLike<K, K>) {} +fun test12(map : MapLike<K?, K>) {} +fun test13(map : MapLike) {} +fun test14(map : MapLike<K?, K>) {} + +class Foo + +typealias A = MapLike +typealias A2 = Foo> +typealias A3 = ListLike> + +fun main1(x: A<Int?>) {} +fun main2(x: A2<Int?>) {} +fun main3(x: A3) {} +fun main3() { + val x = A3() // TODO: support reporting errors on typealias constructor calls + val x2 = A() // TODO: support reporting errors on typealias constructor calls + val y: A3 = A3() +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBounds.kt b/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBounds.kt index f64818275c2..c74590f7a23 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBounds.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBounds.kt @@ -1,16 +1,36 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE // FULL_JDK // FILE: MapLike.java import java.util.Map; -public interface MapLike<@org.jetbrains.annotations.NotNull K, V> { +public class MapLike<@org.jetbrains.annotations.NotNull K, V> { void putAll(Map map); } +// FILE: ListLike.java +import java.util.Collection; + +public class ListLike> {} + // FILE: main.kt fun test0(map : MapLike<Int?, Int>) {} fun test11(map : MapLike<K, K>) {} fun test12(map : MapLike<K?, K>) {} fun test13(map : MapLike) {} fun test14(map : MapLike<K?, K>) {} + +class Foo + +typealias A = MapLike +typealias A2 = Foo> +typealias A3 = ListLike> + +fun main1(x: A<Int?>) {} +fun main2(x: A2<Int?>) {} +fun main3(x: A3) {} +fun main3() { + val x = A3() // TODO: support reporting errors on typealias constructor calls + val x2 = A() // TODO: support reporting errors on typealias constructor calls + val y: A3 = A3() +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBounds.txt b/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBounds.txt index 4307fbac502..1a2ba462509 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBounds.txt +++ b/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBounds.txt @@ -1,15 +1,37 @@ package +public fun main1(/*0*/ x: A /* = MapLike */): kotlin.Unit +public fun main2(/*0*/ x: A2 /* = Foo> */): kotlin.Unit +public fun main3(): kotlin.Unit +public fun main3(/*0*/ x: A3 /* = ListLike> */): kotlin.Unit public fun test0(/*0*/ map: MapLike): kotlin.Unit public fun test11(/*0*/ map: MapLike): kotlin.Unit public fun test12(/*0*/ map: MapLike): kotlin.Unit public fun test13(/*0*/ map: MapLike): kotlin.Unit public fun test14(/*0*/ map: MapLike): kotlin.Unit -public fun test2(/*0*/ map: MapLike): kotlin.Unit -public interface MapLike { +public final class Foo { + public constructor Foo() public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int - public abstract fun putAll(/*0*/ map: kotlin.collections.(Mutable)Map!): kotlin.Unit public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } + +public open class ListLike!> { + public constructor ListLike!>() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open class MapLike { + public constructor MapLike() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public/*package*/ open fun putAll(/*0*/ map: kotlin.collections.(Mutable)Map!): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} +public typealias A = MapLike +public typealias A2 = Foo> +public typealias A3 = ListLike> + diff --git a/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.fir.kt index 9f94e4a8326..a64a67bac51 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.fir.kt @@ -1,17 +1,37 @@ // !LANGUAGE: +ImprovementsAroundTypeEnhancement -// !DIAGNOSTICS: -UNUSED_PARAMETER +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE // FULL_JDK // FILE: MapLike.java import java.util.Map; -public interface MapLike<@org.jetbrains.annotations.NotNull K, V> { +public class MapLike<@org.jetbrains.annotations.NotNull K, V> { void putAll(Map map); } +// FILE: ListLike.java +import java.util.Collection; + +public class ListLike> {} + // FILE: main.kt -fun test0(map : MapLike) {} -fun test11(map : MapLike) {} -fun test12(map : MapLike) {} +fun test0(map : MapLike<Int?, Int>) {} +fun test11(map : MapLike<K, K>) {} +fun test12(map : MapLike<K?, K>) {} fun test13(map : MapLike) {} -fun test14(map : MapLike) {} +fun test14(map : MapLike<K?, K>) {} + +class Foo + +typealias A = MapLike +typealias A2 = Foo> +typealias A3 = ListLike> + +fun main1(x: A<Int?>) {} +fun main2(x: A2<Int?>) {} +fun main3(x: A3) {} +fun main3() { + val x = A3() // TODO: support reporting errors on typealias constructor calls + val x2 = A() + val y: A3 = A3() +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.kt b/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.kt index 2f4a92ce002..2b1481db01e 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.kt @@ -1,17 +1,37 @@ // !LANGUAGE: +ImprovementsAroundTypeEnhancement -// !DIAGNOSTICS: -UNUSED_PARAMETER +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE // FULL_JDK // FILE: MapLike.java import java.util.Map; -public interface MapLike<@org.jetbrains.annotations.NotNull K, V> { +public class MapLike<@org.jetbrains.annotations.NotNull K, V> { void putAll(Map map); } +// FILE: ListLike.java +import java.util.Collection; + +public class ListLike> {} + // FILE: main.kt fun test0(map : MapLike<Int?, Int>) {} fun test11(map : MapLike<K, K>) {} fun test12(map : MapLike<K?, K>) {} fun test13(map : MapLike) {} fun test14(map : MapLike<K?, K>) {} + +class Foo + +typealias A = MapLike +typealias A2 = Foo> +typealias A3 = ListLike> + +fun main1(x: A<Int?>) {} +fun main2(x: A2<Int?>) {} +fun main3(x: A3) {} +fun main3() { + val x = A3() // TODO: support reporting errors on typealias constructor calls + val x2 = A<Int?>() + val y: A3 = A3() +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.txt b/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.txt index bc80a549fca..89322e6d6db 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.txt +++ b/compiler/testData/diagnostics/testsWithStdLib/java/checkEnhancedUpperBoundsWithEnabledImprovements.txt @@ -1,14 +1,37 @@ package +public fun main1(/*0*/ x: A /* = MapLike */): kotlin.Unit +public fun main2(/*0*/ x: A2 /* = Foo> */): kotlin.Unit +public fun main3(): kotlin.Unit +public fun main3(/*0*/ x: A3 /* = ListLike> */): kotlin.Unit public fun test0(/*0*/ map: MapLike): kotlin.Unit public fun test11(/*0*/ map: MapLike): kotlin.Unit public fun test12(/*0*/ map: MapLike): kotlin.Unit public fun test13(/*0*/ map: MapLike): kotlin.Unit public fun test14(/*0*/ map: MapLike): kotlin.Unit -public interface MapLike { +public final class Foo { + public constructor Foo() public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int - public abstract fun putAll(/*0*/ map: kotlin.collections.(Mutable)Map!): kotlin.Unit public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } + +public open class ListLike!> { + public constructor ListLike!>() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open class MapLike { + public constructor MapLike() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public/*package*/ open fun putAll(/*0*/ map: kotlin.collections.(Mutable)Map!): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} +public typealias A = MapLike +public typealias A2 = Foo> +public typealias A3 = ListLike> + diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeAliasExpander.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeAliasExpander.kt index 436d736e516..aef0505dd38 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeAliasExpander.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeAliasExpander.kt @@ -250,12 +250,11 @@ class TypeAliasExpander( val unsubstitutedArgument = unsubstitutedType.arguments[i] val typeParameter = unsubstitutedType.constructor.parameters[i] if (shouldCheckBounds) { - checkBoundsInTypeAlias( - reportStrategy, + reportStrategy.boundsViolationInSubstitution( + typeSubstitutor, unsubstitutedArgument.type, substitutedArgument.type, - typeParameter, - typeSubstitutor + typeParameter ) } } @@ -265,26 +264,6 @@ class TypeAliasExpander( companion object { private const val MAX_RECURSION_DEPTH = 100 - fun checkBoundsInTypeAlias( - reportStrategy: TypeAliasExpansionReportStrategy, - unsubstitutedArgument: KotlinType, - typeArgument: KotlinType, - typeParameterDescriptor: TypeParameterDescriptor, - substitutor: TypeSubstitutor - ) { - for (bound in typeParameterDescriptor.upperBounds) { - val substitutedBound = substitutor.safeSubstitute(bound, Variance.INVARIANT) - if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(typeArgument, substitutedBound)) { - reportStrategy.boundsViolationInSubstitution( - substitutedBound, - unsubstitutedArgument, - typeArgument, - typeParameterDescriptor - ) - } - } - } - private fun assertRecursionDepth(recursionDepth: Int, typeAliasDescriptor: TypeAliasDescriptor) { if (recursionDepth > MAX_RECURSION_DEPTH) { throw AssertionError("Too deep recursion while expanding type alias ${typeAliasDescriptor.name}") diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeAliasExpansionReportStrategy.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeAliasExpansionReportStrategy.kt index 9b029b47990..b6b0f4a26d4 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeAliasExpansionReportStrategy.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeAliasExpansionReportStrategy.kt @@ -14,7 +14,7 @@ interface TypeAliasExpansionReportStrategy { fun conflictingProjection(typeAlias: TypeAliasDescriptor, typeParameter: TypeParameterDescriptor?, substitutedArgument: KotlinType) fun recursiveTypeAlias(typeAlias: TypeAliasDescriptor) fun boundsViolationInSubstitution( - bound: KotlinType, + substitutor: TypeSubstitutor, unsubstitutedArgument: KotlinType, argument: KotlinType, typeParameter: TypeParameterDescriptor @@ -33,7 +33,7 @@ interface TypeAliasExpansionReportStrategy { override fun recursiveTypeAlias(typeAlias: TypeAliasDescriptor) {} override fun boundsViolationInSubstitution( - bound: KotlinType, + substitutor: TypeSubstitutor, unsubstitutedArgument: KotlinType, argument: KotlinType, typeParameter: TypeParameterDescriptor diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeWithEnhancement.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeWithEnhancement.kt index 236de1041a2..b5f179ad799 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeWithEnhancement.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeWithEnhancement.kt @@ -88,6 +88,40 @@ fun KotlinType.getEnhancement(): KotlinType? = when (this) { else -> null } +private fun List.enhanceTypeArguments(depth: Int) = + map { argument -> + // TODO: think about star projections with enhancement (e.g. came from Java: Foo<@NotNull ?>) + if (argument.isStarProjection) { + return@map argument + } + val argumentType = argument.type + val enhancedArgumentType = if (argumentType is TypeWithEnhancement) argumentType.enhancement else argumentType + val enhancedDeeplyArgumentType = enhancedArgumentType.getEnhancementDeeply(depth + 1) + + argument.replaceType(enhancedDeeplyArgumentType) + } + +private fun KotlinType.getEnhancementDeeply(depth: Int): KotlinType { + val newArguments = arguments.enhanceTypeArguments(depth) + val newArgumentsForUpperBound = if (this is FlexibleType) upperBound.arguments.enhanceTypeArguments(depth) else newArguments + val enhancedType = if (this is TypeWithEnhancement) enhancement else this + + return enhancedType.replace( + newArguments = newArguments, + newArgumentsForUpperBound = newArgumentsForUpperBound + ) +} + +fun KotlinType.getEnhancementDeeply(): KotlinType? { + val enhancedTypeWithArguments = getEnhancementDeeply(depth = 0) + + if (enhancedTypeWithArguments === this) return null + + return enhancedTypeWithArguments +} + +fun KotlinType.unwrapEnhancementDeeply() = getEnhancementDeeply() ?: this + fun KotlinType.unwrapEnhancement(): KotlinType = getEnhancement() ?: this fun UnwrappedType.inheritEnhancement(origin: KotlinType): UnwrappedType = wrapEnhancement(origin.getEnhancement()) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CompositeResolverForModuleFactory.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CompositeResolverForModuleFactory.kt index ad6528edc56..3a3d7cefa66 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CompositeResolverForModuleFactory.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CompositeResolverForModuleFactory.kt @@ -266,5 +266,6 @@ class CompositePlatformConigurator(private val componentConfigurators: List() + container.useImpl() } } \ No newline at end of file