From ca8da225698a1d93e12bcb3fa7579270de30f7e4 Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Wed, 28 Aug 2019 01:23:18 +0300 Subject: [PATCH] [NI] Improve CST algorithm to handle non-fixed variables #KT-32456 Fixed #KT-32423 Fixed #KT-32818 Fixed #KT-33197 Fixed --- .../org/jetbrains/kotlin/fir/SessionUtils.kt | 7 +- .../fir/resolve/calls/ConeInferenceContext.kt | 10 ++- .../kotlin/fir/types/ConeTypeContext.kt | 14 ++- .../types/FirCorrespondingSupertypesCache.kt | 2 +- .../jetbrains/kotlin/fir/types/TypeUtils.kt | 2 +- .../fir/FirDiagnosticsSmokeTestGenerated.java | 25 ++++++ .../kotlin/ir/types/IrTypeCheckerContext.kt | 13 +-- .../calls/NewCommonSuperTypeCalculator.kt | 58 +++++++++--- .../components/PostponedArgumentsAnalyzer.kt | 1 + .../resolve/calls/inference/InferenceUtils.kt | 10 ++- ...ctTypeCheckerContextForConstraintSystem.kt | 3 + .../components/ConstraintInjector.kt | 3 +- .../components/NewTypeSubstitutor.kt | 45 ++++++---- .../components/ResultTypeResolver.kt | 36 +++++++- .../model/NewConstraintSystemImpl.kt | 5 ++ .../elvis/genericElvisWithMoreSpecificLHS.kt | 18 ++++ .../box/elvis/genericElvisWithNullLHS.kt | 23 +++++ .../controlStructures/ifElseIntersection.kt | 6 +- .../cstWithTypeContainingNonFixedVariable.kt | 90 +++++++++++++++++++ .../cstWithTypeContainingNonFixedVariable.txt | 23 +++++ .../tests/inference/commonSystem/kt32818.kt | 5 ++ .../tests/inference/commonSystem/kt32818.txt | 4 + .../tests/inference/commonSystem/kt33197.kt | 19 ++++ .../tests/inference/commonSystem/kt33197.txt | 5 ++ .../nonFixedVariableInsideFlexibleType.kt | 30 +++++++ .../nonFixedVariableInsideFlexibleType.txt | 22 +++++ .../outProjectedTypeToOutProjected.kt | 12 +++ .../outProjectedTypeToOutProjected.txt | 12 +++ ...stponedCompletionWithExactAnnotation_ni.kt | 4 +- .../checkers/DiagnosticsTestGenerated.java | 25 ++++++ .../DiagnosticsUsingJavacTestGenerated.java | 25 ++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 10 +++ .../LightAnalysisModeTestGenerated.java | 10 +++ .../ir/IrBlackBoxCodegenTestGenerated.java | 10 +++ .../checker/ClassicTypeCheckerContext.kt | 4 + .../types/checker/ClassicTypeSystemContext.kt | 7 +- .../types/checker/NewKotlinTypeChecker.kt | 2 +- .../kotlin/types/AbstractTypeChecker.kt | 52 ++++++++--- .../kotlin/types/model/TypeSystemContext.kt | 14 ++- .../IrJsCodegenBoxTestGenerated.java | 10 +++ .../semantics/JsCodegenBoxTestGenerated.java | 10 +++ 41 files changed, 605 insertions(+), 81 deletions(-) create mode 100644 compiler/testData/codegen/box/elvis/genericElvisWithMoreSpecificLHS.kt create mode 100644 compiler/testData/codegen/box/elvis/genericElvisWithNullLHS.kt create mode 100644 compiler/testData/diagnostics/tests/inference/commonSystem/cstWithTypeContainingNonFixedVariable.kt create mode 100644 compiler/testData/diagnostics/tests/inference/commonSystem/cstWithTypeContainingNonFixedVariable.txt create mode 100644 compiler/testData/diagnostics/tests/inference/commonSystem/kt32818.kt create mode 100644 compiler/testData/diagnostics/tests/inference/commonSystem/kt32818.txt create mode 100644 compiler/testData/diagnostics/tests/inference/commonSystem/kt33197.kt create mode 100644 compiler/testData/diagnostics/tests/inference/commonSystem/kt33197.txt create mode 100644 compiler/testData/diagnostics/tests/inference/commonSystem/nonFixedVariableInsideFlexibleType.kt create mode 100644 compiler/testData/diagnostics/tests/inference/commonSystem/nonFixedVariableInsideFlexibleType.txt create mode 100644 compiler/testData/diagnostics/tests/inference/commonSystem/outProjectedTypeToOutProjected.kt create mode 100644 compiler/testData/diagnostics/tests/inference/commonSystem/outProjectedTypeToOutProjected.txt diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/SessionUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/SessionUtils.kt index 91f67a96965..6becf48253a 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/SessionUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/SessionUtils.kt @@ -10,8 +10,11 @@ import org.jetbrains.kotlin.fir.types.ConeTypeContext import org.jetbrains.kotlin.types.AbstractTypeCheckerContext private class SessionBasedTypeContext(override val session: FirSession) : ConeTypeContext { - override fun newBaseTypeCheckerContext(errorTypesEqualToAnything: Boolean): AbstractTypeCheckerContext { - return ConeTypeCheckerContext(errorTypesEqualToAnything, session) + override fun newBaseTypeCheckerContext( + errorTypesEqualToAnything: Boolean, + stubTypesEqualToAnything: Boolean + ): AbstractTypeCheckerContext { + return ConeTypeCheckerContext(errorTypesEqualToAnything, stubTypesEqualToAnything, session) } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeInferenceContext.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeInferenceContext.kt index ad2829099bb..a204cfe6730 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeInferenceContext.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeInferenceContext.kt @@ -85,9 +85,11 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, return ConeStarProjection } - override fun newBaseTypeCheckerContext(errorTypesEqualToAnything: Boolean): AbstractTypeCheckerContext { - return ConeTypeCheckerContext(errorTypesEqualToAnything, session) - } + override fun newBaseTypeCheckerContext( + errorTypesEqualToAnything: Boolean, + stubTypesEqualToAnything: Boolean + ): AbstractTypeCheckerContext = + ConeTypeCheckerContext(errorTypesEqualToAnything, stubTypesEqualToAnything, session) override fun KotlinTypeMarker.canHaveUndefinedNullability(): Boolean { require(this is ConeKotlinType) @@ -167,7 +169,7 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, override fun Collection.singleBestRepresentative(): KotlinTypeMarker? { if (this.size == 1) return this.first() - val context = newBaseTypeCheckerContext(true) + val context = newBaseTypeCheckerContext(errorTypesEqualToAnything = true, stubTypesEqualToAnything = true) return this.firstOrNull { candidate -> this.all { other -> // We consider error types equal to anything here, so that intersections like diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt index 31d655b26e2..b39fab638ed 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt @@ -498,8 +498,11 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty } } -class ConeTypeCheckerContext(override val isErrorTypeEqualsToAnything: Boolean, override val session: FirSession) : - AbstractTypeCheckerContext(), ConeTypeContext { +class ConeTypeCheckerContext( + override val isErrorTypeEqualsToAnything: Boolean, + override val isStubTypeEqualsToAnything: Boolean, + override val session: FirSession +) : AbstractTypeCheckerContext(), ConeTypeContext { override fun substitutionSupertypePolicy(type: SimpleTypeMarker): SupertypesPolicy { if (type.argumentsCount() == 0) return SupertypesPolicy.LowerIfFlexible require(type is ConeKotlinType) @@ -539,10 +542,13 @@ class ConeTypeCheckerContext(override val isErrorTypeEqualsToAnything: Boolean, override val KotlinTypeMarker.isAllowedTypeVariable: Boolean get() = this is ConeKotlinType && this is ConeTypeVariableType - override fun newBaseTypeCheckerContext(errorTypesEqualToAnything: Boolean): AbstractTypeCheckerContext = + override fun newBaseTypeCheckerContext( + errorTypesEqualToAnything: Boolean, + stubTypesEqualToAnything: Boolean + ): AbstractTypeCheckerContext = if (this.isErrorTypeEqualsToAnything == errorTypesEqualToAnything) this else - ConeTypeCheckerContext(errorTypesEqualToAnything, session) + ConeTypeCheckerContext(errorTypesEqualToAnything, stubTypesEqualToAnything, session) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/FirCorrespondingSupertypesCache.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/FirCorrespondingSupertypesCache.kt index 6e2dd84d5a2..8fd497b22b1 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/FirCorrespondingSupertypesCache.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/FirCorrespondingSupertypesCache.kt @@ -19,7 +19,7 @@ import org.jetbrains.kotlin.types.model.SimpleTypeMarker import org.jetbrains.kotlin.types.model.TypeConstructorMarker class FirCorrespondingSupertypesCache(private val session: FirSession) : FirSessionComponent { - private val context = ConeTypeCheckerContext(false, session) + private val context = ConeTypeCheckerContext(isErrorTypeEqualsToAnything = false, isStubTypeEqualsToAnything = true, session = session) private val cache = HashMap, Map, List>?>(1000, 0.5f) fun getCorrespondingSupertypes( diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt index ce2aedcb618..6ebfd5cc264 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt @@ -12,7 +12,7 @@ object ConeNullabilityChecker { fun isSubtypeOfAny(context: ConeTypeContext, type: ConeKotlinType): Boolean { val actualType = with(context) { type.lowerBoundIfFlexible() } return with(AbstractNullabilityChecker) { - context.newBaseTypeCheckerContext(false) + context.newBaseTypeCheckerContext(errorTypesEqualToAnything = false, stubTypesEqualToAnything = true) .hasNotNullSupertype(actualType, AbstractTypeCheckerContext.SupertypesPolicy.LowerIfFlexible) } } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java index 67e4226daf5..806372c7746 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsSmokeTestGenerated.java @@ -10176,6 +10176,11 @@ public class FirDiagnosticsSmokeTestGenerated extends AbstractFirDiagnosticsSmok runTest("compiler/testData/diagnostics/tests/inference/commonSystem/cstFromNullableChildAndNonParameterizedType.kt"); } + @TestMetadata("cstWithTypeContainingNonFixedVariable.kt") + public void testCstWithTypeContainingNonFixedVariable() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/cstWithTypeContainingNonFixedVariable.kt"); + } + @TestMetadata("dontCaptureTypeVariable.kt") public void testDontCaptureTypeVariable() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/commonSystem/dontCaptureTypeVariable.kt"); @@ -10206,6 +10211,16 @@ public class FirDiagnosticsSmokeTestGenerated extends AbstractFirDiagnosticsSmok runTest("compiler/testData/diagnostics/tests/inference/commonSystem/kt31969.kt"); } + @TestMetadata("kt32818.kt") + public void testKt32818() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/kt32818.kt"); + } + + @TestMetadata("kt33197.kt") + public void testKt33197() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/kt33197.kt"); + } + @TestMetadata("kt3372toCollection.kt") public void testKt3372toCollection() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/commonSystem/kt3372toCollection.kt"); @@ -10231,6 +10246,16 @@ public class FirDiagnosticsSmokeTestGenerated extends AbstractFirDiagnosticsSmok runTest("compiler/testData/diagnostics/tests/inference/commonSystem/nestedLambdas.kt"); } + @TestMetadata("nonFixedVariableInsideFlexibleType.kt") + public void testNonFixedVariableInsideFlexibleType() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/nonFixedVariableInsideFlexibleType.kt"); + } + + @TestMetadata("outProjectedTypeToOutProjected.kt") + public void testOutProjectedTypeToOutProjected() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/outProjectedTypeToOutProjected.kt"); + } + @TestMetadata("postponedCompletionWithExactAnnotation.kt") public void testPostponedCompletionWithExactAnnotation() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/commonSystem/postponedCompletionWithExactAnnotation.kt"); diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeCheckerContext.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeCheckerContext.kt index 5c9edd478db..b153ec07842 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeCheckerContext.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeCheckerContext.kt @@ -7,8 +7,6 @@ package org.jetbrains.kotlin.ir.types import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.symbols.IrClassSymbol -import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl -import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection import org.jetbrains.kotlin.types.AbstractTypeCheckerContext import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.SimpleTypeMarker @@ -31,13 +29,16 @@ class IrTypeCheckerContext(override val irBuiltIns: IrBuiltIns) : IrTypeSystemCo override fun areEqualTypeConstructors(a: TypeConstructorMarker, b: TypeConstructorMarker) = super.isEqualTypeConstructors(a, b) - override val isErrorTypeEqualsToAnything = false + override val isErrorTypeEqualsToAnything get() = false + override val isStubTypeEqualsToAnything get() = false + override val KotlinTypeMarker.isAllowedTypeVariable: Boolean get() = false - override fun newBaseTypeCheckerContext(errorTypesEqualToAnything: Boolean): AbstractTypeCheckerContext { - return IrTypeCheckerContext(irBuiltIns) - } + override fun newBaseTypeCheckerContext( + errorTypesEqualToAnything: Boolean, + stubTypesEqualToAnything: Boolean + ): AbstractTypeCheckerContext = IrTypeCheckerContext(irBuiltIns) override fun KotlinTypeMarker.isUninferredParameter(): Boolean = false diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/NewCommonSuperTypeCalculator.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/NewCommonSuperTypeCalculator.kt index 87fff026f3c..9dd627bec20 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/NewCommonSuperTypeCalculator.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/NewCommonSuperTypeCalculator.kt @@ -68,7 +68,8 @@ object NewCommonSuperTypeCalculator { depth: Int ): SimpleTypeMarker { // i.e. result type also should be marked nullable - val notAllNotNull = types.any { !AbstractNullabilityChecker.isSubtypeOfAny(this, it) } + val notAllNotNull = + types.any { !isStubRelatedType(it) && !AbstractNullabilityChecker.isSubtypeOfAny(contextStubTypesEqualToAnything, it) } val notNullTypes = if (notAllNotNull) types.map { it.withNullability(false) } else types val commonSuperType = commonSuperTypeForNotNullTypes(notNullTypes, depth) @@ -94,7 +95,8 @@ object NewCommonSuperTypeCalculator { val uniqueTypes = arrayListOf() for (type in types) { val isNewUniqueType = uniqueTypes.all { - !AbstractTypeChecker.equalTypes(this, it, type) || it.typeConstructor().isIntegerLiteralTypeConstructor() + !AbstractTypeChecker.equalTypes(this, it, type, stubTypesEqualToAnything = false) || + it.typeConstructor().isIntegerLiteralTypeConstructor() } if (isNewUniqueType) { uniqueTypes += type @@ -111,7 +113,9 @@ object NewCommonSuperTypeCalculator { while (iterator.hasNext()) { val potentialSubtype = iterator.next() val isSubtype = supertypes.any { supertype -> - supertype !== potentialSubtype && AbstractTypeChecker.isSubtypeOf(this, potentialSubtype, supertype) + supertype !== potentialSubtype && AbstractTypeChecker.isSubtypeOf( + this, potentialSubtype, supertype, stubTypesEqualToAnything = false + ) } if (isSubtype) iterator.remove() @@ -120,13 +124,26 @@ object NewCommonSuperTypeCalculator { return supertypes } + /* + * Common Supertype calculator works with proper types and stub types (which is a replacement for non-proper types) + * Also, there are two invariant related to stub types: + * - resulting type should be only proper type + * - one of the input types is definitely proper type + * */ private fun TypeSystemCommonSuperTypesContext.commonSuperTypeForNotNullTypes( types: List, depth: Int ): SimpleTypeMarker { if (types.size == 1) return types.single() - val uniqueTypes = uniquify(types) + val nonStubTypes = types.filter { !isStubRelatedType(it) } + if (nonStubTypes.size == 1) return nonStubTypes.single() + + assert(nonStubTypes.isNotEmpty()) { + "There should be at least one non-stub type to compute common supertype but there are: $types" + } + + val uniqueTypes = uniquify(nonStubTypes) if (uniqueTypes.size == 1) return uniqueTypes.single() val explicitSupertypes = filterSupertypes(uniqueTypes) @@ -134,12 +151,23 @@ object NewCommonSuperTypeCalculator { findErrorTypeInSupertypesIfItIsNeeded(explicitSupertypes)?.let { return it } findCommonIntegerLiteralTypesSuperType(explicitSupertypes)?.let { return it } -// IntegerLiteralTypeConstructor.findCommonSuperType(explicitSupertypes)?.let { return it } return findSuperTypeConstructorsAndIntersectResult(explicitSupertypes, depth) } - private fun TypeSystemCommonSuperTypesContext.findErrorTypeInSupertypesIfItIsNeeded(types: List): SimpleTypeMarker? { + private fun TypeSystemCommonSuperTypesContext.isStubRelatedType(type: SimpleTypeMarker): Boolean { + return type.isStubType() || isCapturedStubType(type) + } + + private fun TypeSystemCommonSuperTypesContext.isCapturedStubType(type: SimpleTypeMarker): Boolean { + val projectedType = type.asCapturedType()?.typeConstructor()?.projection()?.getType() ?: return false + return projectedType.asSimpleType()?.isStubType() == true + } + + private fun TypeSystemCommonSuperTypesContext.findErrorTypeInSupertypesIfItIsNeeded( + types: List, + contextStubTypesEqualToAnything: AbstractTypeCheckerContext + ): SimpleTypeMarker? { if (isErrorTypeAllowed) return null for (type in types) { collectAllSupertypes(type).firstOrNull { it.isError() }?.let { return it.toErrorType() } @@ -187,7 +215,7 @@ object NewCommonSuperTypeCalculator { nullable = false ) - val typeCheckerContext = newBaseTypeCheckerContext(false) + val typeCheckerContext = newBaseTypeCheckerContext(errorTypesEqualToAnything = false, stubTypesEqualToAnything = true) /** * Sometimes one type can have several supertypes with given type constructor, suppose A <: List and A <: List. @@ -206,11 +234,17 @@ object NewCommonSuperTypeCalculator { val parameter = constructor.getParameter(index) var thereIsStar = false val typeProjections = correspondingSuperTypes.mapNotNull { - it.getArgumentOrNull(index)?.let { - if (it.isStarProjection()) { - thereIsStar = true - null - } else it + it.getArgumentOrNull(index)?.let { typeArgument -> + when { + typeArgument.isStarProjection() -> { + thereIsStar = true + null + } + + typeArgument.getType().lowerBoundIfFlexible().isStubType() -> null + + else -> typeArgument + } } } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzer.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzer.kt index 1e0d287c35c..9876e0a75fb 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzer.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/PostponedArgumentsAnalyzer.kt @@ -23,6 +23,7 @@ class PostponedArgumentsAnalyzer( ) { interface Context : TypeSystemInferenceExtensionContext { fun buildCurrentSubstitutor(additionalBindings: Map): TypeSubstitutorMarker + fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker fun bindingStubsForPostponedVariables(): Map // type can be proper if it not contains not fixed type variables diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt index 68f4f4bd653..94e0689174b 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt @@ -21,10 +21,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstituto import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.types.* -import org.jetbrains.kotlin.types.model.StubTypeMarker -import org.jetbrains.kotlin.types.model.TypeConstructorMarker -import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker -import org.jetbrains.kotlin.types.model.TypeSystemInferenceExtensionContext +import org.jetbrains.kotlin.types.model.* fun ConstraintStorage.buildCurrentSubstitutor( context: TypeSystemInferenceExtensionContext, @@ -50,6 +47,11 @@ fun ConstraintStorage.buildAbstractResultingSubstitutor(context: TypeSystemInfer return context.typeSubstitutorByTypeConstructor(currentSubstitutorMap + uninferredSubstitutorMap) } +fun ConstraintStorage.buildNotFixedVariablesToNonSubtypableTypesSubstitutor(context: TypeSystemInferenceExtensionContext) = + context.typeSubstitutorByTypeConstructor( + notFixedTypeVariables.mapValues { context.createStubType(it.value.typeVariable) } + ) + fun ConstraintStorage.buildResultingSubstitutor(context: TypeSystemInferenceExtensionContext): NewTypeSubstitutor { return buildAbstractResultingSubstitutor(context) as NewTypeSubstitutor } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/AbstractTypeCheckerContextForConstraintSystem.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/AbstractTypeCheckerContextForConstraintSystem.kt index a9a920cc65a..287e2509ea5 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/AbstractTypeCheckerContextForConstraintSystem.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/AbstractTypeCheckerContextForConstraintSystem.kt @@ -18,6 +18,9 @@ abstract class AbstractTypeCheckerContextForConstraintSystem : AbstractTypeCheck override val isErrorTypeEqualsToAnything: Boolean get() = true + override val isStubTypeEqualsToAnything: Boolean + get() = true + abstract fun isMyTypeVariable(type: SimpleTypeMarker): Boolean // super and sub type isSingleClassifierType diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt index a6a3dd752d8..4dad5c63232 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt @@ -65,7 +65,6 @@ class ConstraintInjector( addSubTypeConstraintAndIncorporateIt(c, b, a, incorporationPosition) } - private fun addSubTypeConstraintAndIncorporateIt( c: Context, lowerType: KotlinTypeMarker, @@ -126,7 +125,7 @@ class ConstraintInjector( val possibleNewConstraints: MutableList> ) : AbstractTypeCheckerContextForConstraintSystem(), ConstraintIncorporator.Context, TypeSystemInferenceExtensionContext by c { - val baseContext: AbstractTypeCheckerContext = newBaseTypeCheckerContext(isErrorTypeEqualsToAnything) + val baseContext: AbstractTypeCheckerContext = newBaseTypeCheckerContext(isErrorTypeEqualsToAnything, isStubTypeEqualsToAnything) override fun substitutionSupertypePolicy(type: SimpleTypeMarker): SupertypesPolicy { return baseContext.substitutionSupertypePolicy(type) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/NewTypeSubstitutor.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/NewTypeSubstitutor.kt index c30bf22f83b..2f53bd7538e 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/NewTypeSubstitutor.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/NewTypeSubstitutor.kt @@ -73,29 +73,26 @@ interface NewTypeSubstitutor: TypeSubstitutorMarker { "and class: ${type::class.java.canonicalName}. type.toString() = $type" } val capturedType = if (type is DefinitelyNotNullType) type.original as NewCapturedType else type as NewCapturedType - val lower = capturedType.lowerType?.let { substitute(it, keepAnnotation, runCapturedChecks = false) } - if (lower != null && capturedType.lowerType is StubType) { - return NewCapturedType( - capturedType.captureStatus, - NewCapturedTypeConstructor(TypeProjectionImpl(typeConstructor.projection.projectionKind, lower)), - lower - ) - } - if (lower != null) throw IllegalStateException( - "Illegal type substitutor: $this, " + - "because for captured type '$type' lower type approximation should be null, but it is: '$lower'," + - "original lower type: '${capturedType.lowerType}" - ) + val innerType = capturedType.lowerType ?: capturedType.constructor.projection.type.unwrap() + val substitutedInnerType = substitute(innerType, keepAnnotation, runCapturedChecks = false) + + if (substitutedInnerType != null) { + if (innerType is StubType || substitutedInnerType is StubType) { + return NewCapturedType( + capturedType.captureStatus, + NewCapturedTypeConstructor(TypeProjectionImpl(typeConstructor.projection.projectionKind, substitutedInnerType)), + lowerType = if (capturedType.lowerType != null) substitutedInnerType else null + ) + } else { + throwExceptionAboutInvalidCapturedSubstitution(capturedType, innerType, substitutedInnerType) + } + } if (AbstractTypeChecker.RUN_SLOW_ASSERTIONS) { typeConstructor.supertypes.forEach { supertype -> substitute(supertype, keepAnnotation, runCapturedChecks = false)?.let { - throw IllegalStateException( - "Illegal type substitutor: $this, " + - "because for captured type '$type' supertype approximation should be null, but it is: '$supertype'," + - "original supertype: '$supertype'" - ) + throwExceptionAboutInvalidCapturedSubstitution(capturedType, supertype, it) } } } @@ -130,6 +127,18 @@ interface NewTypeSubstitutor: TypeSubstitutorMarker { return replacement } + private fun throwExceptionAboutInvalidCapturedSubstitution( + capturedType: SimpleType, + innerType: UnwrappedType, + substitutedInnerType: UnwrappedType + ): Nothing = + throw IllegalStateException( + "Illegal type substitutor: $this, " + + "because for captured type '$capturedType' supertype approximation should be null, but it is: '$innerType'," + + "original supertype: '$substitutedInnerType'" + ) + + private fun substituteParametrizedType( type: SimpleType, keepAnnotation: Boolean, diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt index 71b89c94e4b..97e2cb4827a 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.* import org.jetbrains.kotlin.resolve.constants.IntegerLiteralTypeConstructor import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.model.KotlinTypeMarker +import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker import org.jetbrains.kotlin.types.model.TypeSystemInferenceExtensionContext class ResultTypeResolver( @@ -30,6 +31,7 @@ class ResultTypeResolver( ) { interface Context : TypeSystemInferenceExtensionContext { fun isProperType(type: KotlinTypeMarker): Boolean + fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker } fun findResultType(c: Context, variableWithConstraints: VariableWithConstraints, direction: ResolveDirection): KotlinTypeMarker { @@ -83,9 +85,10 @@ class ResultTypeResolver( } private fun Context.findSubType(variableWithConstraints: VariableWithConstraints): KotlinTypeMarker? { - val lowerConstraints = variableWithConstraints.constraints.filter { it.kind == ConstraintKind.LOWER && isProperType(it.type) } - if (lowerConstraints.isNotEmpty()) { - val types = sinkIntegerLiteralTypes(lowerConstraints.map { it.type }) + val lowerConstraintTypes = prepareLowerConstraints(variableWithConstraints.constraints) + + if (lowerConstraintTypes.isNotEmpty()) { + val types = sinkIntegerLiteralTypes(lowerConstraintTypes) val commonSuperType = with(NewCommonSuperTypeCalculator) { this@findSubType.commonSuperType(types) } @@ -116,6 +119,33 @@ class ResultTypeResolver( return null } + private fun Context.prepareLowerConstraints(constraints: List): List { + var atLeastOneProper = false + var atLeastOneNonProper = false + + val lowerConstraintTypes = mutableListOf() + + for (constraint in constraints) { + if (constraint.kind != ConstraintKind.LOWER) continue + + val type = constraint.type + lowerConstraintTypes.add(type) + + if (isProperType(type)) { + atLeastOneProper = true + } else { + atLeastOneNonProper = true + } + } + + if (!atLeastOneProper) return emptyList() + if (!atLeastOneNonProper) return lowerConstraintTypes + + val notFixedToStubTypesSubstitutor = buildNotFixedVariablesToStubTypesSubstitutor() + + return lowerConstraintTypes.map { if (isProperType(it)) it else notFixedToStubTypesSubstitutor.safeSubstitute(it) } + } + private fun Context.sinkIntegerLiteralTypes(types: List): List { return types.sortedBy { type -> diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt index e5702f2d271..6bd4583bb74 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt @@ -307,6 +307,11 @@ class NewConstraintSystemImpl( return storage.buildCurrentSubstitutor(this, additionalBindings) } + override fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker { + checkState(State.BUILDING, State.COMPLETION) + return storage.buildNotFixedVariablesToNonSubtypableTypesSubstitutor(this) + } + override fun bindingStubsForPostponedVariables(): Map { checkState(State.BUILDING, State.COMPLETION) // TODO: SUB diff --git a/compiler/testData/codegen/box/elvis/genericElvisWithMoreSpecificLHS.kt b/compiler/testData/codegen/box/elvis/genericElvisWithMoreSpecificLHS.kt new file mode 100644 index 00000000000..9743fab842c --- /dev/null +++ b/compiler/testData/codegen/box/elvis/genericElvisWithMoreSpecificLHS.kt @@ -0,0 +1,18 @@ +// !LANGUAGE: +NewInference +// WITH_RUNTIME +// IGNORE_BACKEND: JS_IR + +fun test(foo: MutableList?): List { + val bar = foo ?: listOf() + return bar +} + +fun box(): String { + val a = test(null) + if (a.isNotEmpty()) return "Fail 1" + + val b = test(mutableListOf("a")) + if (b.size != 1) return "Fail 2" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/elvis/genericElvisWithNullLHS.kt b/compiler/testData/codegen/box/elvis/genericElvisWithNullLHS.kt new file mode 100644 index 00000000000..7b56c0dca31 --- /dev/null +++ b/compiler/testData/codegen/box/elvis/genericElvisWithNullLHS.kt @@ -0,0 +1,23 @@ +// WITH_RUNTIME +// IGNORE_BACKEND: JS_IR + +fun test() { + fun returnMutableList(): MutableList? = null + fun returnsList(): List? = null + + var mutableList: MutableList? = null + var list: List? = null + + mutableListOf().addAll(returnMutableList() ?: emptyList()) + mutableListOf().addAll(returnsList() ?: emptyList()) + mutableListOf().addAll(list ?: emptyList()) + + mutableListOf().addAll(returnMutableList() ?: emptyList()) + mutableListOf().addAll(mutableList ?: emptyList()) + mutableListOf().addAll(null ?: emptyList()) +} + +fun box(): String { + test() + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/controlStructures/ifElseIntersection.kt b/compiler/testData/diagnostics/tests/controlStructures/ifElseIntersection.kt index dd65c299a0c..30c36b0c9a1 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/ifElseIntersection.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/ifElseIntersection.kt @@ -9,9 +9,9 @@ class None : Option fun bind(r: Option): Option { return if (r is Some) { // Ideally we should infer Option here (see KT-10896) - (if (true) None() else r) checkType { _>() } + (if (true) None() else r) checkType { _>() } // Works correctly - if (true) None() else r + if (true) None() else r } else r } @@ -36,7 +36,7 @@ fun bindWhen(r: Option): Option { return when (r) { is Some -> { // Works correctly - if (true) None() else r + if (true) None() else r } else -> r } diff --git a/compiler/testData/diagnostics/tests/inference/commonSystem/cstWithTypeContainingNonFixedVariable.kt b/compiler/testData/diagnostics/tests/inference/commonSystem/cstWithTypeContainingNonFixedVariable.kt new file mode 100644 index 00000000000..6a1f1dfb9d5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/cstWithTypeContainingNonFixedVariable.kt @@ -0,0 +1,90 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION + +fun select(x: T, y: T): T = x +open class Inv +class SubInv : Inv() + +fun testSimple() { + val a0 = select(Inv(), SubInv()) + + ")!>a0 + + val a1 = select(SubInv(), Inv()) + + ")!>a1 +} + +fun testNullability() { + val n1 = select(Inv(), SubInv()) + + ")!>n1 + + val n2 = select(SubInv(), Inv()) + + ")!>n2 +} + +fun testNested() { + val n1 = select(Inv>(), SubInv()) + + >")!>n1 + + val n2 = select(SubInv>(), Inv()) + + >")!>n2 + + fun createInvInv(): Inv> = TODO() + + val n3 = select(SubInv>(), createInvInv()) + + >")!>n3 +} + +fun testCaptured(cSub: SubInv, cInv: Inv) { + val c1 = select(cInv, SubInv()) + + ")!>c1 + + val c2 = select(cSub, Inv()) + + ")!>c2 +} + +fun testVariableWithBound() { + fun createWithNumberBound(): Inv = TODO() + fun Int> createWithIntBound(): Inv = TODO() + + val c1 = select(SubInv(), createWithNumberBound()) + + ")!>c1 + + // should be an error after variable fixation + val c2 = select(SubInv(), createWithNumberBound()) + + ")!>c2 + + // should be an error after variable fixation + val c3 = select(SubInv(), createWithIntBound()) + + ")!>c3 +} + +fun testCapturedVariable() { + fun createInvOut(): Inv = TODO() + fun createSubInvOut(): SubInv = TODO() + + fun createInvIn(): Inv = TODO() + + val c1 = select(SubInv(), createInvOut()) + + ")!>c1 + + val c2 = select(createSubInvOut(), createInvOut()) + + ")!>c2 + + val c3 = select(SubInv(), createInvIn()) + + ")!>c3 +} diff --git a/compiler/testData/diagnostics/tests/inference/commonSystem/cstWithTypeContainingNonFixedVariable.txt b/compiler/testData/diagnostics/tests/inference/commonSystem/cstWithTypeContainingNonFixedVariable.txt new file mode 100644 index 00000000000..f82801605ea --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/cstWithTypeContainingNonFixedVariable.txt @@ -0,0 +1,23 @@ +package + +public fun select(/*0*/ x: T, /*1*/ y: T): T +public fun testCaptured(/*0*/ cSub: SubInv, /*1*/ cInv: Inv): kotlin.Unit +public fun testCapturedVariable(): kotlin.Unit +public fun testNested(): kotlin.Unit +public fun testNullability(): kotlin.Unit +public fun testSimple(): kotlin.Unit +public fun testVariableWithBound(): kotlin.Unit + +public open class Inv { + public constructor Inv() + 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 final class SubInv : Inv { + public constructor SubInv() + 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 +} diff --git a/compiler/testData/diagnostics/tests/inference/commonSystem/kt32818.kt b/compiler/testData/diagnostics/tests/inference/commonSystem/kt32818.kt new file mode 100644 index 00000000000..9ac95c8770a --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/kt32818.kt @@ -0,0 +1,5 @@ +// !WITH_NEW_INFERENCE + +fun nullable(): T? = null + +val value = nullable() ?: nullable() \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/commonSystem/kt32818.txt b/compiler/testData/diagnostics/tests/inference/commonSystem/kt32818.txt new file mode 100644 index 00000000000..f0e764ecab4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/kt32818.txt @@ -0,0 +1,4 @@ +package + +public val value: kotlin.Int +public fun nullable(): T? diff --git a/compiler/testData/diagnostics/tests/inference/commonSystem/kt33197.kt b/compiler/testData/diagnostics/tests/inference/commonSystem/kt33197.kt new file mode 100644 index 00000000000..bd2dcf71e39 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/kt33197.kt @@ -0,0 +1,19 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_EXPRESSION + +fun test(condition: Boolean) { + val list1 = + if (condition) mutableListOf() + else emptyList() + + ")!>list1 + + val list2 = + if (condition) mutableListOf() + else emptyList() + + ")!>list2 +} + +fun mutableListOf(): MutableList = TODO() +fun emptyList(): List = TODO() \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/commonSystem/kt33197.txt b/compiler/testData/diagnostics/tests/inference/commonSystem/kt33197.txt new file mode 100644 index 00000000000..38abb620f5f --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/kt33197.txt @@ -0,0 +1,5 @@ +package + +public fun emptyList(): kotlin.collections.List +public fun mutableListOf(): kotlin.collections.MutableList +public fun test(/*0*/ condition: kotlin.Boolean): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/inference/commonSystem/nonFixedVariableInsideFlexibleType.kt b/compiler/testData/diagnostics/tests/inference/commonSystem/nonFixedVariableInsideFlexibleType.kt new file mode 100644 index 00000000000..48f47310336 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/nonFixedVariableInsideFlexibleType.kt @@ -0,0 +1,30 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION + +// FILE: Inv2.java + +public class Inv2 {} + +// FILE: JavaSet.java + +public class JavaSet { + public static java.util.Set newIdentityHashSet() { return null; } + + public static V get(Inv2 slice, K key) { return null; } +} + +// FILE: test.kt + +fun select(x: K, y: K): K = x + +fun addElementToSlice( + slice: Inv2>, + key: K, + element: T +) { + val a = select(JavaSet.get(slice, key), JavaSet.newIdentityHashSet()) + + ..kotlin.collections.Collection?)")!>a + + a.add(element) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/commonSystem/nonFixedVariableInsideFlexibleType.txt b/compiler/testData/diagnostics/tests/inference/commonSystem/nonFixedVariableInsideFlexibleType.txt new file mode 100644 index 00000000000..6c1c2b5cd4d --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/nonFixedVariableInsideFlexibleType.txt @@ -0,0 +1,22 @@ +package + +public fun addElementToSlice(/*0*/ slice: Inv2>, /*1*/ key: K, /*2*/ element: T): kotlin.Unit +public fun select(/*0*/ x: K, /*1*/ y: K): K + +public open class Inv2 { + public constructor Inv2() + 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 JavaSet { + public constructor JavaSet() + 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 + + // Static members + public open fun get(/*0*/ slice: Inv2!, /*1*/ key: K!): V! + public open fun newIdentityHashSet(): kotlin.collections.(Mutable)Set! +} diff --git a/compiler/testData/diagnostics/tests/inference/commonSystem/outProjectedTypeToOutProjected.kt b/compiler/testData/diagnostics/tests/inference/commonSystem/outProjectedTypeToOutProjected.kt new file mode 100644 index 00000000000..fdc80f2c532 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/outProjectedTypeToOutProjected.kt @@ -0,0 +1,12 @@ +// !LANGUAGE: +NewInference +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER + +class Inv + +fun select(x: K, y: K): K = x + +fun outToOut(x: Inv): Inv = TODO() + +fun test(invOutAny: Inv, invAny: Inv) { + val a: Inv = select(invAny, outToOut(invOutAny)) +} diff --git a/compiler/testData/diagnostics/tests/inference/commonSystem/outProjectedTypeToOutProjected.txt b/compiler/testData/diagnostics/tests/inference/commonSystem/outProjectedTypeToOutProjected.txt new file mode 100644 index 00000000000..008b156f1c4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/outProjectedTypeToOutProjected.txt @@ -0,0 +1,12 @@ +package + +public fun outToOut(/*0*/ x: Inv): Inv +public fun select(/*0*/ x: K, /*1*/ y: K): K +public fun test(/*0*/ invOutAny: Inv, /*1*/ invAny: Inv): kotlin.Unit + +public final class Inv { + public constructor Inv() + 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 +} diff --git a/compiler/testData/diagnostics/tests/inference/commonSystem/postponedCompletionWithExactAnnotation_ni.kt b/compiler/testData/diagnostics/tests/inference/commonSystem/postponedCompletionWithExactAnnotation_ni.kt index 756fa69c457..14a2970acda 100644 --- a/compiler/testData/diagnostics/tests/inference/commonSystem/postponedCompletionWithExactAnnotation_ni.kt +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/postponedCompletionWithExactAnnotation_ni.kt @@ -11,13 +11,13 @@ fun elvisExact(x: K?, y: K): @kotlin.internal.Exact K = y fun materialize(): T? = null fun test(nullableSample: ISample, any: Any) { - elvisSimple( + elvisSimple( nullableSample, materialize() ) elvisSimple( - elvisSimple(nullableSample, materialize()), + elvisSimple(nullableSample, materialize()), any ) diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index e0adfdd8632..eb80149e0a7 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -10183,6 +10183,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { runTest("compiler/testData/diagnostics/tests/inference/commonSystem/cstFromNullableChildAndNonParameterizedType.kt"); } + @TestMetadata("cstWithTypeContainingNonFixedVariable.kt") + public void testCstWithTypeContainingNonFixedVariable() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/cstWithTypeContainingNonFixedVariable.kt"); + } + @TestMetadata("dontCaptureTypeVariable.kt") public void testDontCaptureTypeVariable() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/commonSystem/dontCaptureTypeVariable.kt"); @@ -10213,6 +10218,16 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { runTest("compiler/testData/diagnostics/tests/inference/commonSystem/kt31969.kt"); } + @TestMetadata("kt32818.kt") + public void testKt32818() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/kt32818.kt"); + } + + @TestMetadata("kt33197.kt") + public void testKt33197() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/kt33197.kt"); + } + @TestMetadata("kt3372toCollection.kt") public void testKt3372toCollection() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/commonSystem/kt3372toCollection.kt"); @@ -10238,6 +10253,16 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { runTest("compiler/testData/diagnostics/tests/inference/commonSystem/nestedLambdas.kt"); } + @TestMetadata("nonFixedVariableInsideFlexibleType.kt") + public void testNonFixedVariableInsideFlexibleType() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/nonFixedVariableInsideFlexibleType.kt"); + } + + @TestMetadata("outProjectedTypeToOutProjected.kt") + public void testOutProjectedTypeToOutProjected() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/outProjectedTypeToOutProjected.kt"); + } + @TestMetadata("postponedCompletionWithExactAnnotation.kt") public void testPostponedCompletionWithExactAnnotation() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/commonSystem/postponedCompletionWithExactAnnotation.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 34d1cf0e3dc..6d130390a7b 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -10178,6 +10178,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/inference/commonSystem/cstFromNullableChildAndNonParameterizedType.kt"); } + @TestMetadata("cstWithTypeContainingNonFixedVariable.kt") + public void testCstWithTypeContainingNonFixedVariable() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/cstWithTypeContainingNonFixedVariable.kt"); + } + @TestMetadata("dontCaptureTypeVariable.kt") public void testDontCaptureTypeVariable() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/commonSystem/dontCaptureTypeVariable.kt"); @@ -10208,6 +10213,16 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/inference/commonSystem/kt31969.kt"); } + @TestMetadata("kt32818.kt") + public void testKt32818() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/kt32818.kt"); + } + + @TestMetadata("kt33197.kt") + public void testKt33197() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/kt33197.kt"); + } + @TestMetadata("kt3372toCollection.kt") public void testKt3372toCollection() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/commonSystem/kt3372toCollection.kt"); @@ -10233,6 +10248,16 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/inference/commonSystem/nestedLambdas.kt"); } + @TestMetadata("nonFixedVariableInsideFlexibleType.kt") + public void testNonFixedVariableInsideFlexibleType() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/nonFixedVariableInsideFlexibleType.kt"); + } + + @TestMetadata("outProjectedTypeToOutProjected.kt") + public void testOutProjectedTypeToOutProjected() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/commonSystem/outProjectedTypeToOutProjected.kt"); + } + @TestMetadata("postponedCompletionWithExactAnnotation.kt") public void testPostponedCompletionWithExactAnnotation() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/commonSystem/postponedCompletionWithExactAnnotation.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 95e1d728707..7f4918c805e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -10334,6 +10334,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/elvis"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("genericElvisWithMoreSpecificLHS.kt") + public void testGenericElvisWithMoreSpecificLHS() throws Exception { + runTest("compiler/testData/codegen/box/elvis/genericElvisWithMoreSpecificLHS.kt"); + } + + @TestMetadata("genericElvisWithNullLHS.kt") + public void testGenericElvisWithNullLHS() throws Exception { + runTest("compiler/testData/codegen/box/elvis/genericElvisWithNullLHS.kt"); + } + @TestMetadata("genericNull.kt") public void testGenericNull() throws Exception { runTest("compiler/testData/codegen/box/elvis/genericNull.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 0fec85b4c8b..a2f5bcf5380 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -10334,6 +10334,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/elvis"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("genericElvisWithMoreSpecificLHS.kt") + public void testGenericElvisWithMoreSpecificLHS() throws Exception { + runTest("compiler/testData/codegen/box/elvis/genericElvisWithMoreSpecificLHS.kt"); + } + + @TestMetadata("genericElvisWithNullLHS.kt") + public void testGenericElvisWithNullLHS() throws Exception { + runTest("compiler/testData/codegen/box/elvis/genericElvisWithNullLHS.kt"); + } + @TestMetadata("genericNull.kt") public void testGenericNull() throws Exception { runTest("compiler/testData/codegen/box/elvis/genericNull.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index ca6af2cf39c..73873d9157e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -9214,6 +9214,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/elvis"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true); } + @TestMetadata("genericElvisWithMoreSpecificLHS.kt") + public void testGenericElvisWithMoreSpecificLHS() throws Exception { + runTest("compiler/testData/codegen/box/elvis/genericElvisWithMoreSpecificLHS.kt"); + } + + @TestMetadata("genericElvisWithNullLHS.kt") + public void testGenericElvisWithNullLHS() throws Exception { + runTest("compiler/testData/codegen/box/elvis/genericElvisWithNullLHS.kt"); + } + @TestMetadata("genericNull.kt") public void testGenericNull() throws Exception { runTest("compiler/testData/codegen/box/elvis/genericNull.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeCheckerContext.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeCheckerContext.kt index 7071c71755c..2f9c48c9a43 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeCheckerContext.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeCheckerContext.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.types.refinement.TypeRefinement open class ClassicTypeCheckerContext( val errorTypeEqualsToAnything: Boolean, + val stubTypeEqualsToAnything: Boolean = true, val allowedTypeVariable: Boolean = true, val kotlinTypeRefiner: KotlinTypeRefiner = KotlinTypeRefiner.Default ) : ClassicTypeSystemContext, AbstractTypeCheckerContext() { @@ -43,6 +44,9 @@ open class ClassicTypeCheckerContext( override val isErrorTypeEqualsToAnything: Boolean get() = errorTypeEqualsToAnything + override val isStubTypeEqualsToAnything: Boolean + get() = stubTypeEqualsToAnything + override fun areEqualTypeConstructors(a: TypeConstructorMarker, b: TypeConstructorMarker): Boolean { require(a is TypeConstructor, a::errorMessage) require(b is TypeConstructor, b::errorMessage) diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt index f3270484151..1499fd4b743 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt @@ -321,8 +321,11 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy } - override fun newBaseTypeCheckerContext(errorTypesEqualToAnything: Boolean): AbstractTypeCheckerContext { - return ClassicTypeCheckerContext(errorTypesEqualToAnything) + override fun newBaseTypeCheckerContext( + errorTypesEqualToAnything: Boolean, + stubTypesEqualToAnything: Boolean + ): AbstractTypeCheckerContext { + return ClassicTypeCheckerContext(errorTypesEqualToAnything, stubTypesEqualToAnything) } override fun nullableNothingType(): SimpleTypeMarker { diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt index e94cb892f80..a0e0acaf50a 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt @@ -148,7 +148,7 @@ object NullabilityChecker { fun isSubtypeOfAny(type: UnwrappedType): Boolean = SimpleClassicTypeSystemContext - .newBaseTypeCheckerContext(false) + .newBaseTypeCheckerContext(errorTypesEqualToAnything = false, stubTypesEqualToAnything = true) .hasNotNullSupertype(type.lowerIfFlexible(), SupertypesPolicy.LowerIfFlexible) } diff --git a/core/type-system/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt b/core/type-system/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt index 5b88636fe83..74c7cacb942 100644 --- a/core/type-system/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt +++ b/core/type-system/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt @@ -31,6 +31,8 @@ abstract class AbstractTypeCheckerContext : TypeSystemContext { abstract val isErrorTypeEqualsToAnything: Boolean + abstract val isStubTypeEqualsToAnything: Boolean + protected var argumentsDepth = 0 @@ -151,12 +153,22 @@ object AbstractTypeChecker { @JvmField var RUN_SLOW_ASSERTIONS = false - fun isSubtypeOf(context: TypeCheckerProviderContext, subType: KotlinTypeMarker, superType: KotlinTypeMarker): Boolean { - return AbstractTypeChecker.isSubtypeOf(context.newBaseTypeCheckerContext(true), subType, superType) + fun isSubtypeOf( + context: TypeCheckerProviderContext, + subType: KotlinTypeMarker, + superType: KotlinTypeMarker, + stubTypesEqualToAnything: Boolean = true + ): Boolean { + return AbstractTypeChecker.isSubtypeOf(context.newBaseTypeCheckerContext(true, stubTypesEqualToAnything), subType, superType) } - fun equalTypes(context: TypeCheckerProviderContext, a: KotlinTypeMarker, b: KotlinTypeMarker): Boolean { - return AbstractTypeChecker.equalTypes(context.newBaseTypeCheckerContext(false), a, b) + fun equalTypes( + context: TypeCheckerProviderContext, + a: KotlinTypeMarker, + b: KotlinTypeMarker, + stubTypesEqualToAnything: Boolean = true + ): Boolean { + return AbstractTypeChecker.equalTypes(context.newBaseTypeCheckerContext(false, stubTypesEqualToAnything), a, b) } fun isSubtypeOf(context: AbstractTypeCheckerContext, subType: KotlinTypeMarker, superType: KotlinTypeMarker): Boolean { @@ -358,7 +370,7 @@ object AbstractTypeChecker { ) } - if (subType.isStubType() || superType.isStubType()) return true + if (subType.isStubType() || superType.isStubType()) return isStubTypeEqualsToAnything val superTypeCaptured = superType.asCapturedType() val lowerType = superTypeCaptured?.lowerType() @@ -482,7 +494,13 @@ object AbstractNullabilityChecker { context.runIsPossibleSubtype(subType, superType) fun isSubtypeOfAny(context: TypeCheckerProviderContext, type: KotlinTypeMarker): Boolean = - AbstractNullabilityChecker.isSubtypeOfAny(context.newBaseTypeCheckerContext(false), type) + AbstractNullabilityChecker.isSubtypeOfAny( + context.newBaseTypeCheckerContext( + errorTypesEqualToAnything = false, + stubTypesEqualToAnything = true + ), + type + ) fun isSubtypeOfAny(context: AbstractTypeCheckerContext, type: KotlinTypeMarker): Boolean = with(context) { @@ -540,12 +558,22 @@ object AbstractNullabilityChecker { } fun TypeCheckerProviderContext.hasPathByNotMarkedNullableNodes(start: SimpleTypeMarker, end: TypeConstructorMarker) = - newBaseTypeCheckerContext(false).hasPathByNotMarkedNullableNodes(start, end) + newBaseTypeCheckerContext(errorTypesEqualToAnything = false, stubTypesEqualToAnything = true) + .hasPathByNotMarkedNullableNodes(start, end) fun AbstractTypeCheckerContext.hasPathByNotMarkedNullableNodes(start: SimpleTypeMarker, end: TypeConstructorMarker) = - anySupertype(start, { - it.isNothing() || (!it.isMarkedNullable() && isEqualTypeConstructors(it.typeConstructor(), end)) - }) { - if (it.isMarkedNullable()) SupertypesPolicy.None else SupertypesPolicy.LowerIfFlexible - } + anySupertype( + start, + { isApplicableAsEndNode(it, end) }, + { if (it.isMarkedNullable()) SupertypesPolicy.None else SupertypesPolicy.LowerIfFlexible } + ) + + private fun AbstractTypeCheckerContext.isApplicableAsEndNode(type: SimpleTypeMarker, end: TypeConstructorMarker): Boolean { + if (type.isNothing()) return true + if (type.isMarkedNullable()) return false + + if (isStubTypeEqualsToAnything && type.isStubType()) return true + + return isEqualTypeConstructors(type.typeConstructor(), end) + } } diff --git a/core/type-system/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt b/core/type-system/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt index 00700c46038..7f821af9b6c 100644 --- a/core/type-system/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt +++ b/core/type-system/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt @@ -63,7 +63,10 @@ interface TypeSystemTypeFactoryContext { interface TypeCheckerProviderContext { - fun newBaseTypeCheckerContext(errorTypesEqualToAnything: Boolean): AbstractTypeCheckerContext + fun newBaseTypeCheckerContext( + errorTypesEqualToAnything: Boolean, + stubTypesEqualToAnything: Boolean + ): AbstractTypeCheckerContext } interface TypeSystemCommonSuperTypesContext : TypeSystemContext, TypeSystemTypeFactoryContext, TypeCheckerProviderContext { @@ -74,9 +77,12 @@ interface TypeSystemCommonSuperTypesContext : TypeSystemContext, TypeSystemTypeF val isErrorTypeAllowed: Boolean fun KotlinTypeMarker.anySuperTypeConstructor(predicate: (TypeConstructorMarker) -> Boolean) = - newBaseTypeCheckerContext(false).anySupertype(lowerBoundIfFlexible(), { - predicate(it.typeConstructor()) - }, { AbstractTypeCheckerContext.SupertypesPolicy.LowerIfFlexible }) + newBaseTypeCheckerContext(errorTypesEqualToAnything = false, stubTypesEqualToAnything = true) + .anySupertype( + lowerBoundIfFlexible(), + { predicate(it.typeConstructor()) }, + { AbstractTypeCheckerContext.SupertypesPolicy.LowerIfFlexible } + ) fun KotlinTypeMarker.canHaveUndefinedNullability(): Boolean diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index d30d4489136..e16836089a7 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -7954,6 +7954,16 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/elvis"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS_IR, true); } + @TestMetadata("genericElvisWithMoreSpecificLHS.kt") + public void testGenericElvisWithMoreSpecificLHS() throws Exception { + runTest("compiler/testData/codegen/box/elvis/genericElvisWithMoreSpecificLHS.kt"); + } + + @TestMetadata("genericElvisWithNullLHS.kt") + public void testGenericElvisWithNullLHS() throws Exception { + runTest("compiler/testData/codegen/box/elvis/genericElvisWithNullLHS.kt"); + } + @TestMetadata("genericNull.kt") public void testGenericNull() throws Exception { runTest("compiler/testData/codegen/box/elvis/genericNull.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 1c6f30ae625..fcf0450d1b0 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -9039,6 +9039,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/elvis"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("genericElvisWithMoreSpecificLHS.kt") + public void testGenericElvisWithMoreSpecificLHS() throws Exception { + runTest("compiler/testData/codegen/box/elvis/genericElvisWithMoreSpecificLHS.kt"); + } + + @TestMetadata("genericElvisWithNullLHS.kt") + public void testGenericElvisWithNullLHS() throws Exception { + runTest("compiler/testData/codegen/box/elvis/genericElvisWithNullLHS.kt"); + } + @TestMetadata("genericNull.kt") public void testGenericNull() throws Exception { runTest("compiler/testData/codegen/box/elvis/genericNull.kt");