[NI] Support Exact and NoInfer annotations
This commit is contained in:
committed by
Stanislav Erokhin
parent
d1e52e76f9
commit
98eae4e7ee
+1
-1
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-18
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-8
@@ -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()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user