Infer type arguments of type alias constructors.

This commit is contained in:
Dmitry Petrov
2016-11-08 15:38:03 +03:00
parent 9b55ad82a0
commit 03d8aa272b
14 changed files with 265 additions and 18 deletions
@@ -38,10 +38,11 @@ import org.jetbrains.kotlin.resolve.calls.inference.*
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.Bound
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.LOWER_BOUND
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.UPPER_BOUND
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ValidityConstraintForConstituentType
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.RECEIVER_POSITION
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.VALUE_PARAMETER_POSITION
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.*
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.getValidityConstraintForConstituentType
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeIntersector
@@ -52,6 +53,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.io.PrintWriter
import java.io.StringWriter
import java.lang.AssertionError
import java.util.*
object Renderers {
@@ -273,12 +275,20 @@ object Renderers {
val typeParameterDescriptor = inferenceErrorData.descriptor.typeParameters.firstOrNull {
!ConstraintsUtil.checkUpperBoundIsSatisfied(systemWithoutWeakConstraints, it, inferenceErrorData.call, true)
}
if (typeParameterDescriptor == null && status.hasConflictingConstraints()) {
return renderConflictingSubstitutionsInferenceError(inferenceErrorData, result)
}
if (typeParameterDescriptor == null) {
LOG.error(debugMessage("There is no type parameter with violated upper bound for 'upper bound violated' error", inferenceErrorData))
return result
if (inferenceErrorData.descriptor is TypeAliasConstructorDescriptor) {
renderUpperBoundViolatedInferenceErrorForTypeAliasConstructor(inferenceErrorData, result, systemWithoutWeakConstraints)?.let {
return it
}
}
return if (status.hasConflictingConstraints())
renderConflictingSubstitutionsInferenceError(inferenceErrorData, result)
else {
LOG.error(debugMessage("There is no type parameter with violated upper bound for 'upper bound violated' error", inferenceErrorData))
result
}
}
val typeVariable = systemWithoutWeakConstraints.descriptorToVariable(inferenceErrorData.call.toHandle(), typeParameterDescriptor)
@@ -322,6 +332,42 @@ object Renderers {
return result
}
private fun renderUpperBoundViolatedInferenceErrorForTypeAliasConstructor(
inferenceErrorData: InferenceErrorData,
result: TabledDescriptorRenderer,
systemWithoutWeakConstraints: ConstraintSystem
): TabledDescriptorRenderer? {
val descriptor = inferenceErrorData.descriptor
if (descriptor !is TypeAliasConstructorDescriptor) {
LOG.error("Type alias constructor descriptor expected: $descriptor")
return result
}
val inferredTypeSubstitutor = systemWithoutWeakConstraints.resultingSubstitutor
for (constraintError in inferenceErrorData.constraintSystem.status.constraintErrors) {
val constraintInfo = constraintError.constraintPosition.getValidityConstraintForConstituentType() ?: continue
if (constraintInfo.typeParameter.variance == Variance.IN_VARIANCE) continue
val violatedUpperBound = inferredTypeSubstitutor.safeSubstitute(constraintInfo.bound, Variance.INVARIANT)
val violatingInferredType = inferredTypeSubstitutor.safeSubstitute(constraintInfo.typeArgument, Variance.INVARIANT)
val context = RenderingContext.of(violatingInferredType, violatedUpperBound)
val typeRenderer = result.typeRenderer
result.text(newText().normal("Type parameter bound for ").strong(constraintInfo.typeParameter.name)
.normal(" in type inferred from type alias expansion for "))
.table(newTable().descriptor(inferenceErrorData.descriptor))
result.text(newText().normal(" is not satisfied: inferred type ").error(typeRenderer.render(violatingInferredType, context))
.normal(" is not a subtype of ").strong(typeRenderer.render(violatedUpperBound, context)))
return result
}
return null
}
@JvmStatic fun renderCannotCaptureTypeParameterError(
inferenceErrorData: InferenceErrorData, result: TabledDescriptorRenderer
): TabledDescriptorRenderer {
@@ -115,7 +115,7 @@ class CandidateResolver(
if (candidateCall.knownTypeParametersSubstitutor != null) {
candidateCall.setResultingSubstitutor(candidateCall.knownTypeParametersSubstitutor!!)
}
else if (ktTypeArguments.isNotEmpty() || candidateDescriptor is TypeAliasConstructorDescriptor) {
else if (ktTypeArguments.isNotEmpty()) {
// Explicit type arguments passed
val typeArguments = ArrayList<KotlinType>()
@@ -18,9 +18,8 @@ package org.jetbrains.kotlin.resolve.calls
import org.jetbrains.kotlin.builtins.ReflectionTypes
import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.*
@@ -34,8 +33,10 @@ import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
import org.jetbrains.kotlin.resolve.calls.context.ResolutionResultsCache
import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache
import org.jetbrains.kotlin.resolve.calls.inference.*
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ValidityConstraintForConstituentType
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.RECEIVER_POSITION
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.TYPE_BOUND_POSITION
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.VALUE_PARAMETER_POSITION
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.makeNullableTypeIfSafeReceiver
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus
@@ -74,6 +75,11 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
}
}
if (candidate is TypeAliasConstructorDescriptor) {
val substitutedReturnType = builder.compositeSubstitutor().safeSubstitute(candidate.returnType, Variance.INVARIANT)
addValidityConstraintsForConstituentTypes(builder, substitutedReturnType)
}
// Receiver
// Error is already reported if something is missing
val receiverArgument = candidateCall.extensionReceiver
@@ -105,6 +111,34 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
return OTHER_ERROR
}
private fun addValidityConstraintsForConstituentTypes(builder: ConstraintSystem.Builder, type: KotlinType) {
val typeConstructor = type.constructor
if (typeConstructor.declarationDescriptor is TypeParameterDescriptor) return
val boundsSubstitutor = TypeSubstitutor.create(type)
type.arguments.forEachIndexed forEachArgument@{ i, typeProjection ->
if (typeProjection.isStarProjection) return@forEachArgument // continue
val typeParameter = typeConstructor.parameters[i]
addValidityConstraintsForTypeArgument(builder, typeProjection, typeParameter, boundsSubstitutor)
}
}
private fun addValidityConstraintsForTypeArgument(
builder: ConstraintSystem.Builder,
typeProjection: TypeProjection,
typeParameter: TypeParameterDescriptor,
boundsSubstitutor: TypeSubstitutor
) {
val typeArgument = typeProjection.type
for (upperBound in typeParameter.upperBounds) {
val substitutedUpperBound = boundsSubstitutor.safeSubstitute(upperBound, Variance.INVARIANT)
val constraintPosition = ValidityConstraintForConstituentType(typeArgument, typeParameter, substitutedUpperBound)
builder.addSubtypeConstraint(typeArgument, substitutedUpperBound, constraintPosition)
}
}
// Creates a substitutor which maps types to their representation in the constraint system.
// In case when some type parameter descriptor is represented by more than one variable in the system, the behavior is undefined.
private fun ConstraintSystem.Builder.compositeSubstitutor(): TypeSubstitutor {
@@ -16,7 +16,9 @@
package org.jetbrains.kotlin.resolve.calls.inference.constraintPosition
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.*
import org.jetbrains.kotlin.types.KotlinType
enum class ConstraintPositionKind {
RECEIVER_POSITION,
@@ -71,4 +73,22 @@ class CompoundConstraintPosition(vararg positions: ConstraintPosition) : Constra
fun ConstraintPosition.derivedFrom(kind: ConstraintPositionKind): Boolean {
return if (this !is CompoundConstraintPosition) this.kind == kind else positions.any { it.kind == kind }
}
}
class ValidityConstraintForConstituentType(
val typeArgument: KotlinType,
val typeParameter: TypeParameterDescriptor,
val bound: KotlinType
) : ConstraintPosition {
override val kind: ConstraintPositionKind get() = TYPE_BOUND_POSITION
}
fun ConstraintPosition.getValidityConstraintForConstituentType(): ValidityConstraintForConstituentType? =
when (this) {
is ValidityConstraintForConstituentType ->
this
is CompoundConstraintPosition ->
positions.asSequence().map { it.getValidityConstraintForConstituentType() }.firstOrNull { it != null }
else ->
null
}