From 98eae4e7eeb04354090cfeb37f08187675ba076e Mon Sep 17 00:00:00 2001 From: Stanislav Erokhin Date: Fri, 18 Aug 2017 06:41:53 +0300 Subject: [PATCH] [NI] Support Exact and NoInfer annotations --- .../calls/components/KotlinCallCompleter.kt | 2 +- .../calls/components/ResolutionParts.kt | 2 +- .../components/NewTypeSubstitutor.kt | 46 +++++++++++-------- .../TypeCheckerContextForConstraintSystem.kt | 40 ++++++++++++---- 4 files changed, 62 insertions(+), 28 deletions(-) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt index 4b22b5b571d..70ab1b3d134 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt @@ -111,7 +111,7 @@ class KotlinCallCompleter( // true if we should complete this call private fun KotlinResolutionCandidate.prepareForCompletion(expectedType: UnwrappedType?): ConstraintSystemCompletionMode { val unsubstitutedReturnType = resolvedCall.candidateDescriptor.returnType?.unwrap() ?: return ConstraintSystemCompletionMode.PARTIAL - val returnType = resolvedCall.substitutor.safeSubstitute(unsubstitutedReturnType) + val returnType = resolvedCall.substitutor.substituteKeepAnnotations(unsubstitutedReturnType) if (expectedType != null && !TypeUtils.noExpectedType(expectedType)) { csBuilder.addSubtypeConstraint(returnType, expectedType, ExpectedTypeConstraintPosition(resolvedCall.atom)) } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt index 07d00d05adc..ac09a3cb0e0 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt @@ -212,7 +212,7 @@ private fun KotlinResolutionCandidate.resolveKotlinArgument( isReceiver: Boolean ) { val expectedType = candidateParameter?.let { - resolvedCall.substitutor.safeSubstitute(argument.getExpectedType(candidateParameter)) + resolvedCall.substitutor.substituteKeepAnnotations(argument.getExpectedType(candidateParameter)) } addResolvedKtPrimitive(resolveKtPrimitive(csBuilder, argument, expectedType, this, isReceiver)) } 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 0d24da87257..2aae3d3837d 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 @@ -26,20 +26,20 @@ import org.jetbrains.kotlin.types.checker.intersectTypes interface NewTypeSubstitutor { fun substituteNotNullTypeWithConstructor(constructor: TypeConstructor): UnwrappedType? - fun safeSubstitute(type: UnwrappedType): UnwrappedType = substitute(type) ?: type + fun safeSubstitute(type: UnwrappedType): UnwrappedType = substitute(type, runCapturedChecks = true, keepAnnotation = false) ?: type - // null means that this type isn't changed - fun substitute(type: UnwrappedType): UnwrappedType? = substitute(type, runCapturedChecks = true) + fun substituteKeepAnnotations(type: UnwrappedType): UnwrappedType = + substitute(type, runCapturedChecks = true, keepAnnotation = true) ?: type - private fun substitute(type: UnwrappedType, runCapturedChecks: Boolean): UnwrappedType? = + private fun substitute(type: UnwrappedType, keepAnnotation: Boolean, runCapturedChecks: Boolean): UnwrappedType? = when (type) { - is SimpleType -> substitute(type, runCapturedChecks) + is SimpleType -> substitute(type, keepAnnotation, runCapturedChecks) is FlexibleType -> if (type is DynamicType || type is RawType) { null } else { - val lowerBound = substitute(type.lowerBound, runCapturedChecks) - val upperBound = substitute(type.upperBound, runCapturedChecks) + val lowerBound = substitute(type.lowerBound, keepAnnotation, runCapturedChecks) + val upperBound = substitute(type.upperBound, keepAnnotation, runCapturedChecks) if (lowerBound == null && upperBound == null) { null } @@ -50,12 +50,12 @@ interface NewTypeSubstitutor { } } - private fun substitute(type: SimpleType, runCapturedChecks: Boolean): UnwrappedType? { + private fun substitute(type: SimpleType, keepAnnotation: Boolean, runCapturedChecks: Boolean): UnwrappedType? { if (type.isError) return null if (type is AbbreviatedType) { - val substitutedExpandedType = substitute(type.expandedType, runCapturedChecks) - val substitutedAbbreviation = substitute(type.abbreviation, runCapturedChecks) + val substitutedExpandedType = substitute(type.expandedType, keepAnnotation, runCapturedChecks) + val substitutedAbbreviation = substitute(type.abbreviation, keepAnnotation, runCapturedChecks) if (substitutedExpandedType is SimpleType? && substitutedAbbreviation is SimpleType?) { return AbbreviatedType(substitutedExpandedType ?: type.expandedType, substitutedAbbreviation ?: type.abbreviation) @@ -66,7 +66,7 @@ interface NewTypeSubstitutor { } if (type.arguments.isNotEmpty()) { - return substituteParametrizedType(type, runCapturedChecks) + return substituteParametrizedType(type, keepAnnotation, runCapturedChecks) } val typeConstructor = type.constructor @@ -78,13 +78,13 @@ interface NewTypeSubstitutor { "Type is inconsistent -- somewhere we create type with typeConstructor = $typeConstructor " + "and class: ${type::class.java.canonicalName}. type.toString() = $type" } - val lower = (type as NewCapturedType).lowerType?.let { substitute(it, runCapturedChecks = false) } + val lower = (type as NewCapturedType).lowerType?.let { substitute(it, keepAnnotation, runCapturedChecks = false) } 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: '${type.lowerType}") type.constructor.supertypes.forEach { supertype -> - substitute(supertype, runCapturedChecks = false)?.let { + 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'") @@ -97,19 +97,29 @@ interface NewTypeSubstitutor { if (typeConstructor is IntersectionTypeConstructor) { var thereIsChanges = false val newTypes = typeConstructor.supertypes.map { - substitute(it.unwrap(), runCapturedChecks)?.apply { thereIsChanges = true } ?: it.unwrap() + substitute(it.unwrap(), keepAnnotation, runCapturedChecks)?.apply { thereIsChanges = true } ?: it.unwrap() } if (!thereIsChanges) return null return intersectTypes(newTypes).let { if (type.isMarkedNullable) it.makeNullableAsSpecified(true) else it } } // simple classifier type - val replacement = substituteNotNullTypeWithConstructor(typeConstructor) ?: return null + var replacement = substituteNotNullTypeWithConstructor(typeConstructor) ?: return null + if (keepAnnotation) { + replacement = replacement.replaceAnnotations(type.annotations) + } + if (type.isMarkedNullable) { + replacement = replacement.makeNullableAsSpecified(true) + } - return if (type.isMarkedNullable) replacement.makeNullableAsSpecified(true) else replacement + return replacement } - private fun substituteParametrizedType(type: SimpleType, runCapturedChecks: Boolean): UnwrappedType? { + private fun substituteParametrizedType( + type: SimpleType, + keepAnnotation: Boolean, + runCapturedChecks: Boolean + ): UnwrappedType? { val parameters = type.constructor.parameters val arguments = type.arguments if (parameters.size != arguments.size) { @@ -122,7 +132,7 @@ interface NewTypeSubstitutor { val argument = arguments[index] if (argument.isStarProjection) continue - val substitutedArgumentType = substitute(argument.type.unwrap(), runCapturedChecks) ?: continue + val substitutedArgumentType = substitute(argument.type.unwrap(), keepAnnotation, runCapturedChecks) ?: continue newArguments[index] = TypeProjectionImpl(argument.projectionKind, substitutedArgumentType) } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeCheckerContextForConstraintSystem.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeCheckerContextForConstraintSystem.kt index 973b4d06796..8290d3ac951 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeCheckerContextForConstraintSystem.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeCheckerContextForConstraintSystem.kt @@ -16,6 +16,9 @@ package org.jetbrains.kotlin.resolve.calls.inference.components +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.resolve.descriptorUtil.hasExactAnnotation +import org.jetbrains.kotlin.resolve.descriptorUtil.hasNoInferAnnotation import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.* import org.jetbrains.kotlin.types.typeUtil.builtIns @@ -42,6 +45,31 @@ abstract class TypeCheckerContextForConstraintSystem : TypeCheckerContext(errorT * override val sameConstructorPolicy get() = SeveralSupertypesWithSameConstructorPolicy.TAKE_FIRST_FOR_SUBTYPING */ override final fun addSubtypeConstraint(subType: UnwrappedType, superType: UnwrappedType): Boolean? { + val hasNoInfer = subType.isTypeVariableWithNoInfer() || superType.isTypeVariableWithNoInfer() + if (hasNoInfer) return true + + val hasExact = subType.isTypeVariableWithExact() || superType.isTypeVariableWithExact() + + // we should strip annotation's because we have incorporation operation and they should be not affected + val mySubType = if (hasExact) subType.replaceAnnotations(Annotations.EMPTY) else subType + val mySuperType = if (hasExact) superType.replaceAnnotations(Annotations.EMPTY) else superType + + val result = internalAddSubtypeConstraint(mySubType, mySuperType) + if (!hasExact) return result + + val result2 = internalAddSubtypeConstraint(mySuperType, mySubType) + + if (result == null && result2 == null) return null + return (result ?: true) && (result2 ?: true) + } + + private fun UnwrappedType.isTypeVariableWithExact() = + anyBound(this@TypeCheckerContextForConstraintSystem::isMyTypeVariable) && hasExactAnnotation() + + private fun UnwrappedType.isTypeVariableWithNoInfer() = + anyBound(this@TypeCheckerContextForConstraintSystem::isMyTypeVariable) && hasNoInferAnnotation() + + private fun internalAddSubtypeConstraint(subType: UnwrappedType, superType: UnwrappedType): Boolean? { assertInputTypes(subType, superType) var answer: Boolean? = null @@ -63,7 +91,7 @@ abstract class TypeCheckerContextForConstraintSystem : TypeCheckerContext(errorT * Foo <: T? <=> Foo & Any <: T * Foo <: T -- leave as is */ - fun simplifyLowerConstraint(typeVariable: UnwrappedType, subType: UnwrappedType): Boolean { + private fun simplifyLowerConstraint(typeVariable: UnwrappedType, subType: UnwrappedType): Boolean { @Suppress("NAME_SHADOWING") val typeVariable = typeVariable.upperIfFlexible() @@ -82,7 +110,7 @@ abstract class TypeCheckerContextForConstraintSystem : TypeCheckerContext(errorT * T? <: Foo <=> T <: Foo && Nothing? <: Foo * T <: Foo -- leave as is */ - fun simplifyUpperConstraint(typeVariable: UnwrappedType, superType: UnwrappedType): Boolean { + private fun simplifyUpperConstraint(typeVariable: UnwrappedType, superType: UnwrappedType): Boolean { @Suppress("NAME_SHADOWING") val typeVariable = typeVariable.lowerIfFlexible() @@ -90,18 +118,14 @@ abstract class TypeCheckerContextForConstraintSystem : TypeCheckerContext(errorT if (typeVariable.isMarkedNullable) { // here is important that superType is singleClassifierType - return if (superType.anyBound(this::isMyTypeVariable)) { - simplifyLowerConstraint(superType, typeVariable) - } - else { + return superType.anyBound(this::isMyTypeVariable) || isSubtypeOfByTypeChecker(typeVariable.builtIns.nullableNothingType, superType) - } } return true } - fun simplifyConstraintForPossibleIntersectionSubType(subType: UnwrappedType, superType: UnwrappedType): Boolean? { + private fun simplifyConstraintForPossibleIntersectionSubType(subType: UnwrappedType, superType: UnwrappedType): Boolean? { @Suppress("NAME_SHADOWING") val subType = subType.lowerIfFlexible()