[NI] Support Exact and NoInfer annotations

This commit is contained in:
Stanislav Erokhin
2017-08-18 06:41:53 +03:00
committed by Stanislav Erokhin
parent d1e52e76f9
commit 98eae4e7ee
4 changed files with 62 additions and 28 deletions
@@ -111,7 +111,7 @@ class KotlinCallCompleter(
// true if we should complete this call // true if we should complete this call
private fun KotlinResolutionCandidate.prepareForCompletion(expectedType: UnwrappedType?): ConstraintSystemCompletionMode { private fun KotlinResolutionCandidate.prepareForCompletion(expectedType: UnwrappedType?): ConstraintSystemCompletionMode {
val unsubstitutedReturnType = resolvedCall.candidateDescriptor.returnType?.unwrap() ?: return ConstraintSystemCompletionMode.PARTIAL 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)) { if (expectedType != null && !TypeUtils.noExpectedType(expectedType)) {
csBuilder.addSubtypeConstraint(returnType, expectedType, ExpectedTypeConstraintPosition(resolvedCall.atom)) csBuilder.addSubtypeConstraint(returnType, expectedType, ExpectedTypeConstraintPosition(resolvedCall.atom))
} }
@@ -212,7 +212,7 @@ private fun KotlinResolutionCandidate.resolveKotlinArgument(
isReceiver: Boolean isReceiver: Boolean
) { ) {
val expectedType = candidateParameter?.let { val expectedType = candidateParameter?.let {
resolvedCall.substitutor.safeSubstitute(argument.getExpectedType(candidateParameter)) resolvedCall.substitutor.substituteKeepAnnotations(argument.getExpectedType(candidateParameter))
} }
addResolvedKtPrimitive(resolveKtPrimitive(csBuilder, argument, expectedType, this, isReceiver)) addResolvedKtPrimitive(resolveKtPrimitive(csBuilder, argument, expectedType, this, isReceiver))
} }
@@ -26,20 +26,20 @@ import org.jetbrains.kotlin.types.checker.intersectTypes
interface NewTypeSubstitutor { interface NewTypeSubstitutor {
fun substituteNotNullTypeWithConstructor(constructor: TypeConstructor): UnwrappedType? 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 substituteKeepAnnotations(type: UnwrappedType): UnwrappedType =
fun substitute(type: UnwrappedType): UnwrappedType? = substitute(type, runCapturedChecks = true) 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) { when (type) {
is SimpleType -> substitute(type, runCapturedChecks) is SimpleType -> substitute(type, keepAnnotation, runCapturedChecks)
is FlexibleType -> if (type is DynamicType || type is RawType) { is FlexibleType -> if (type is DynamicType || type is RawType) {
null null
} }
else { else {
val lowerBound = substitute(type.lowerBound, runCapturedChecks) val lowerBound = substitute(type.lowerBound, keepAnnotation, runCapturedChecks)
val upperBound = substitute(type.upperBound, runCapturedChecks) val upperBound = substitute(type.upperBound, keepAnnotation, runCapturedChecks)
if (lowerBound == null && upperBound == null) { if (lowerBound == null && upperBound == null) {
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.isError) return null
if (type is AbbreviatedType) { if (type is AbbreviatedType) {
val substitutedExpandedType = substitute(type.expandedType, runCapturedChecks) val substitutedExpandedType = substitute(type.expandedType, keepAnnotation, runCapturedChecks)
val substitutedAbbreviation = substitute(type.abbreviation, runCapturedChecks) val substitutedAbbreviation = substitute(type.abbreviation, keepAnnotation, runCapturedChecks)
if (substitutedExpandedType is SimpleType? && substitutedAbbreviation is SimpleType?) { if (substitutedExpandedType is SimpleType? && substitutedAbbreviation is SimpleType?) {
return AbbreviatedType(substitutedExpandedType ?: type.expandedType, return AbbreviatedType(substitutedExpandedType ?: type.expandedType,
substitutedAbbreviation ?: type.abbreviation) substitutedAbbreviation ?: type.abbreviation)
@@ -66,7 +66,7 @@ interface NewTypeSubstitutor {
} }
if (type.arguments.isNotEmpty()) { if (type.arguments.isNotEmpty()) {
return substituteParametrizedType(type, runCapturedChecks) return substituteParametrizedType(type, keepAnnotation, runCapturedChecks)
} }
val typeConstructor = type.constructor val typeConstructor = type.constructor
@@ -78,13 +78,13 @@ interface NewTypeSubstitutor {
"Type is inconsistent -- somewhere we create type with typeConstructor = $typeConstructor " + "Type is inconsistent -- somewhere we create type with typeConstructor = $typeConstructor " +
"and class: ${type::class.java.canonicalName}. type.toString() = $type" "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, " + if (lower != null) throw IllegalStateException("Illegal type substitutor: $this, " +
"because for captured type '$type' lower type approximation should be null, but it is: '$lower'," + "because for captured type '$type' lower type approximation should be null, but it is: '$lower'," +
"original lower type: '${type.lowerType}") "original lower type: '${type.lowerType}")
type.constructor.supertypes.forEach { supertype -> type.constructor.supertypes.forEach { supertype ->
substitute(supertype, runCapturedChecks = false)?.let { substitute(supertype, keepAnnotation, runCapturedChecks = false)?.let {
throw IllegalStateException("Illegal type substitutor: $this, " + throw IllegalStateException("Illegal type substitutor: $this, " +
"because for captured type '$type' supertype approximation should be null, but it is: '$supertype'," + "because for captured type '$type' supertype approximation should be null, but it is: '$supertype'," +
"original supertype: '$supertype'") "original supertype: '$supertype'")
@@ -97,19 +97,29 @@ interface NewTypeSubstitutor {
if (typeConstructor is IntersectionTypeConstructor) { if (typeConstructor is IntersectionTypeConstructor) {
var thereIsChanges = false var thereIsChanges = false
val newTypes = typeConstructor.supertypes.map { 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 if (!thereIsChanges) return null
return intersectTypes(newTypes).let { if (type.isMarkedNullable) it.makeNullableAsSpecified(true) else it } return intersectTypes(newTypes).let { if (type.isMarkedNullable) it.makeNullableAsSpecified(true) else it }
} }
// simple classifier type // 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 parameters = type.constructor.parameters
val arguments = type.arguments val arguments = type.arguments
if (parameters.size != arguments.size) { if (parameters.size != arguments.size) {
@@ -122,7 +132,7 @@ interface NewTypeSubstitutor {
val argument = arguments[index] val argument = arguments[index]
if (argument.isStarProjection) continue 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) newArguments[index] = TypeProjectionImpl(argument.projectionKind, substitutedArgumentType)
} }
@@ -16,6 +16,9 @@
package org.jetbrains.kotlin.resolve.calls.inference.components 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.*
import org.jetbrains.kotlin.types.checker.* import org.jetbrains.kotlin.types.checker.*
import org.jetbrains.kotlin.types.typeUtil.builtIns 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 val sameConstructorPolicy get() = SeveralSupertypesWithSameConstructorPolicy.TAKE_FIRST_FOR_SUBTYPING
*/ */
override final fun addSubtypeConstraint(subType: UnwrappedType, superType: UnwrappedType): Boolean? { 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) assertInputTypes(subType, superType)
var answer: Boolean? = null var answer: Boolean? = null
@@ -63,7 +91,7 @@ abstract class TypeCheckerContextForConstraintSystem : TypeCheckerContext(errorT
* Foo <: T? <=> Foo & Any <: T * Foo <: T? <=> Foo & Any <: T
* Foo <: T -- leave as is * Foo <: T -- leave as is
*/ */
fun simplifyLowerConstraint(typeVariable: UnwrappedType, subType: UnwrappedType): Boolean { private fun simplifyLowerConstraint(typeVariable: UnwrappedType, subType: UnwrappedType): Boolean {
@Suppress("NAME_SHADOWING") @Suppress("NAME_SHADOWING")
val typeVariable = typeVariable.upperIfFlexible() val typeVariable = typeVariable.upperIfFlexible()
@@ -82,7 +110,7 @@ abstract class TypeCheckerContextForConstraintSystem : TypeCheckerContext(errorT
* T? <: Foo <=> T <: Foo && Nothing? <: Foo * T? <: Foo <=> T <: Foo && Nothing? <: Foo
* T <: Foo -- leave as is * T <: Foo -- leave as is
*/ */
fun simplifyUpperConstraint(typeVariable: UnwrappedType, superType: UnwrappedType): Boolean { private fun simplifyUpperConstraint(typeVariable: UnwrappedType, superType: UnwrappedType): Boolean {
@Suppress("NAME_SHADOWING") @Suppress("NAME_SHADOWING")
val typeVariable = typeVariable.lowerIfFlexible() val typeVariable = typeVariable.lowerIfFlexible()
@@ -90,18 +118,14 @@ abstract class TypeCheckerContextForConstraintSystem : TypeCheckerContext(errorT
if (typeVariable.isMarkedNullable) { if (typeVariable.isMarkedNullable) {
// here is important that superType is singleClassifierType // here is important that superType is singleClassifierType
return if (superType.anyBound(this::isMyTypeVariable)) { return superType.anyBound(this::isMyTypeVariable) ||
simplifyLowerConstraint(superType, typeVariable)
}
else {
isSubtypeOfByTypeChecker(typeVariable.builtIns.nullableNothingType, superType) isSubtypeOfByTypeChecker(typeVariable.builtIns.nullableNothingType, superType)
}
} }
return true return true
} }
fun simplifyConstraintForPossibleIntersectionSubType(subType: UnwrappedType, superType: UnwrappedType): Boolean? { private fun simplifyConstraintForPossibleIntersectionSubType(subType: UnwrappedType, superType: UnwrappedType): Boolean? {
@Suppress("NAME_SHADOWING") @Suppress("NAME_SHADOWING")
val subType = subType.lowerIfFlexible() val subType = subType.lowerIfFlexible()