Make ConstraintSystem work with variables, provide API to map to descriptors

This commit is contained in:
Alexander Udalov
2015-11-02 19:11:13 +03:00
parent 13abc265ad
commit dd4a7ac6a9
6 changed files with 67 additions and 60 deletions
@@ -46,6 +46,7 @@ import org.jetbrains.kotlin.types.TypeIntersector
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.io.PrintWriter
import java.io.StringWriter
@@ -162,15 +163,15 @@ public object Renderers {
substitutedDescriptors.add(substitutedDescriptor)
}
val firstConflictingParameter = ConstraintsUtil.getFirstConflictingParameter(inferenceErrorData.constraintSystem)
if (firstConflictingParameter == null) {
val firstConflictingVariable = ConstraintsUtil.getFirstConflictingVariable(inferenceErrorData.constraintSystem)
if (firstConflictingVariable == null) {
LOG.error(renderDebugMessage("There is no conflicting parameter for 'conflicting constraints' error.", inferenceErrorData))
return result
}
result.text(newText()
.normal("Cannot infer type parameter ")
.strong(firstConflictingParameter.getName())
.strong(firstConflictingVariable.name)
.normal(" in "))
val table = newTable()
result.table(table)
@@ -224,21 +225,15 @@ public object Renderers {
public fun renderNoInformationForParameterError(
inferenceErrorData: InferenceErrorData, result: TabledDescriptorRenderer
): TabledDescriptorRenderer {
var firstUnknownParameter: TypeParameterDescriptor? = null
for (typeParameter in inferenceErrorData.constraintSystem.typeParameterDescriptors) {
if (inferenceErrorData.constraintSystem.getTypeBounds(typeParameter).values.isEmpty()) {
firstUnknownParameter = typeParameter
break
}
}
if (firstUnknownParameter == null) {
val firstUnknownVariable = inferenceErrorData.constraintSystem.typeVariables.firstOrNull { variable ->
inferenceErrorData.constraintSystem.getTypeBounds(variable).values.isEmpty()
} ?: return result.apply {
LOG.error(renderDebugMessage("There is no unknown parameter for 'no information for parameter error'.", inferenceErrorData))
return result
}
return result
.text(newText().normal("Not enough information to infer parameter ")
.strong(firstUnknownParameter.getName())
.strong(firstUnknownVariable.name)
.normal(" in "))
.table(newTable()
.descriptor(inferenceErrorData.descriptor)
@@ -266,7 +261,8 @@ public object Renderers {
return result
}
val inferredValueForTypeParameter = systemWithoutWeakConstraints.getTypeBounds(typeParameterDescriptor).value
val inferredValueForTypeParameter =
systemWithoutWeakConstraints.getTypeBounds(systemWithoutWeakConstraints.descriptorToVariable(typeParameterDescriptor)).value
if (inferredValueForTypeParameter == null) {
LOG.error(renderDebugMessage("System without weak constraints is not successful, there is no value for type parameter " +
typeParameterDescriptor.getName() + "\n: " + systemWithoutWeakConstraints, inferenceErrorData))
@@ -308,15 +304,15 @@ public object Renderers {
public fun renderCannotCaptureTypeParameterError(
inferenceErrorData: InferenceErrorData, result: TabledDescriptorRenderer
): TabledDescriptorRenderer {
val constraintSystem = inferenceErrorData.constraintSystem
val errors = constraintSystem.status.constraintErrors
val typeParameterWithCapturedConstraint = (errors.firstOrNull { it is CannotCapture } as? CannotCapture)?.typeVariable
if (typeParameterWithCapturedConstraint == null) {
val system = inferenceErrorData.constraintSystem
val errors = system.status.constraintErrors
val typeVariableWithCapturedConstraint = errors.firstIsInstanceOrNull<CannotCapture>()?.typeVariable
if (typeVariableWithCapturedConstraint == null) {
LOG.error(renderDebugMessage("An error 'cannot capture type parameter' is not found in errors", inferenceErrorData))
return result
}
val typeBounds = constraintSystem.getTypeBounds(typeParameterWithCapturedConstraint)
val typeBounds = system.getTypeBounds(typeVariableWithCapturedConstraint)
val boundWithCapturedType = typeBounds.bounds.firstOrNull { it.constrainingType.isCaptured() }
val capturedTypeConstructor = boundWithCapturedType?.constrainingType?.getConstructor() as? CapturedTypeConstructor
if (capturedTypeConstructor == null) {
@@ -324,8 +320,10 @@ public object Renderers {
return result
}
val typeParameter = system.variableToDescriptor(typeVariableWithCapturedConstraint)
val explanation: String
val upperBound = TypeIntersector.getUpperBoundsAsType(typeParameterWithCapturedConstraint)
val upperBound = TypeIntersector.getUpperBoundsAsType(typeParameter)
if (!KotlinBuiltIns.isNullableAny(upperBound) && capturedTypeConstructor.typeProjection.getProjectionKind() == Variance.IN_VARIANCE) {
explanation = "Type parameter has an upper bound '" + result.getTypeRenderer().render(upperBound) + "'" +
" that cannot be satisfied capturing 'in' projection"
@@ -333,10 +331,12 @@ public object Renderers {
else {
explanation = "Only top-level type projections can be captured"
}
result.text(newText().normal("'" + typeParameterWithCapturedConstraint.getName() + "'" +
" cannot capture " +
"'" + capturedTypeConstructor.typeProjection + "'. " +
explanation))
result.text(newText().normal(
"'" + typeParameter.name + "'" +
" cannot capture " +
"'" + capturedTypeConstructor.typeProjection + "'. " +
explanation
))
return result
}
@@ -362,9 +362,8 @@ public object Renderers {
public val RENDER_COLLECTION_OF_TYPES: Renderer<Collection<KotlinType>> = Renderer { renderTypes(it) }
private fun renderConstraintSystem(constraintSystem: ConstraintSystem, renderTypeBounds: Renderer<TypeBounds>): String {
val typeVariables = constraintSystem.typeParameterDescriptors
val typeBounds = linkedSetOf<TypeBounds>()
for (variable in typeVariables) {
for (variable in constraintSystem.typeVariables) {
typeBounds.add(constraintSystem.getTypeBounds(variable))
}
return "type parameter bounds:\n" +
@@ -165,7 +165,7 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
val candidateWithFreshVariables = FunctionDescriptorUtil.alphaConvertTypeParameters(candidateDescriptor)
val conversion = candidateDescriptor.typeParameters.zip(candidateWithFreshVariables.typeParameters).toMap()
val freshVariables = nestedTypeVariables.map { conversion[it] }.filterNotNull()
val freshVariables = nestedTypeVariables.map { conversion[argumentConstraintSystem.variableToDescriptor(it)] }.filterNotNull()
builder.registerTypeVariables(freshVariables, external = true)
builder.addSubtypeConstraint(candidateWithFreshVariables.returnType, effectiveExpectedType, constraintPosition)
@@ -36,11 +36,15 @@ interface ConstraintSystem {
*/
val typeVariables: Set<TypeParameterDescriptor>
fun descriptorToVariable(descriptor: TypeParameterDescriptor): TypeParameterDescriptor
fun variableToDescriptor(typeVariable: TypeParameterDescriptor): TypeParameterDescriptor
/**
* Returns the resulting type constraints of solving the constraint system for specific type parameter descriptor.
* Throws IllegalArgumentException if the type parameter descriptor is not known to the system.
*/
fun getTypeBounds(descriptor: TypeParameterDescriptor): TypeBounds
fun getTypeBounds(typeVariable: TypeParameterDescriptor): TypeBounds
/**
* Returns the result of solving the constraint system (mapping from the type variable to the resulting type projection).
@@ -149,15 +149,15 @@ class ConstraintSystemBuilderImpl : ConstraintSystem.Builder {
return true
}
override fun capture(typeVariable: KotlinType, typeProjection: TypeProjection): Boolean {
override fun capture(type: KotlinType, typeProjection: TypeProjection): Boolean {
if (isMyTypeVariable(typeProjection.type)) return false
val myTypeVariable = getMyTypeVariable(typeVariable)
val myTypeVariable = getMyTypeVariable(type)
if (myTypeVariable != null && constraintPosition.isParameter()) {
if (depth > 0) {
errors.add(CannotCapture(constraintPosition, myTypeVariable))
}
generateTypeParameterCaptureConstraint(typeVariable, typeProjection, newConstraintContext)
generateTypeParameterCaptureConstraint(myTypeVariable, typeProjection, newConstraintContext, type.isMarkedNullable)
return true
}
return false
@@ -311,16 +311,16 @@ class ConstraintSystemBuilderImpl : ConstraintSystem.Builder {
}
private fun generateTypeParameterCaptureConstraint(
parameterType: KotlinType,
typeVariable: TypeParameterDescriptor,
constrainingTypeProjection: TypeProjection,
constraintContext: ConstraintContext
constraintContext: ConstraintContext,
isTypeMarkedNullable: Boolean
) {
val typeVariable = getMyTypeVariable(parameterType)!!
if (!typeVariable.upperBounds.let { it.size == 1 && it.single().isDefaultBound() } &&
constrainingTypeProjection.projectionKind == Variance.IN_VARIANCE) {
errors.add(CannotCapture(constraintContext.position, typeVariable))
}
val typeProjection = if (parameterType.isMarkedNullable) {
val typeProjection = if (isTypeMarkedNullable) {
TypeProjectionImpl(constrainingTypeProjection.projectionKind, TypeUtils.makeNotNullable(constrainingTypeProjection.type))
}
else {
@@ -112,8 +112,9 @@ internal class ConstraintSystemImpl(
override fun getNestedTypeVariables(type: KotlinType): List<TypeParameterDescriptor> {
return type.getNestedArguments().map { typeProjection ->
typeProjection.type.constructor.declarationDescriptor as? TypeParameterDescriptor
}.filterNotNull().filter { it in typeParameterDescriptors }
val descriptor = typeProjection.type.constructor.declarationDescriptor as? TypeParameterDescriptor
descriptor?.let { descriptorToVariable[it] }
}.filterNotNull()
}
override val typeParameterDescriptors: Set<TypeParameterDescriptor>
@@ -122,13 +123,15 @@ internal class ConstraintSystemImpl(
override val typeVariables: Set<TypeParameterDescriptor>
get() = variableToDescriptor.keys
override fun getTypeBounds(descriptor: TypeParameterDescriptor): TypeBoundsImpl {
val variable = descriptorToVariable[descriptor]
if (variable != null && variable != descriptor) {
return getTypeBounds(variable)
}
return allTypeParameterBounds[descriptor] ?:
throw IllegalArgumentException("TypeParameterDescriptor is not a type variable for constraint system: $descriptor")
override fun descriptorToVariable(descriptor: TypeParameterDescriptor): TypeParameterDescriptor =
descriptorToVariable[descriptor] ?: throw IllegalArgumentException("Unknown descriptor: $descriptor")
override fun variableToDescriptor(typeVariable: TypeParameterDescriptor): TypeParameterDescriptor =
variableToDescriptor[typeVariable] ?: throw IllegalArgumentException("Unknown type variable: $typeVariable")
override fun getTypeBounds(typeVariable: TypeParameterDescriptor): TypeBoundsImpl {
return allTypeParameterBounds[typeVariable] ?:
throw IllegalArgumentException("TypeParameterDescriptor is not a type variable for constraint system: $typeVariable")
}
override val resultingSubstitutor: TypeSubstitutor
@@ -30,11 +30,11 @@ import java.util.*;
public class ConstraintsUtil {
@Nullable
public static TypeParameterDescriptor getFirstConflictingParameter(@NotNull ConstraintSystem constraintSystem) {
for (TypeParameterDescriptor typeParameter : constraintSystem.getTypeParameterDescriptors()) {
TypeBounds constraints = constraintSystem.getTypeBounds(typeParameter);
public static TypeParameterDescriptor getFirstConflictingVariable(@NotNull ConstraintSystem constraintSystem) {
for (TypeParameterDescriptor typeVariable : constraintSystem.getTypeVariables()) {
TypeBounds constraints = constraintSystem.getTypeBounds(typeVariable);
if (constraints.getValues().size() > 1) {
return typeParameter;
return typeVariable;
}
}
return null;
@@ -42,10 +42,11 @@ public class ConstraintsUtil {
@NotNull
public static Collection<TypeSubstitutor> getSubstitutorsForConflictingParameters(@NotNull ConstraintSystem constraintSystem) {
TypeParameterDescriptor firstConflictingParameter = getFirstConflictingParameter(constraintSystem);
if (firstConflictingParameter == null) return Collections.emptyList();
TypeParameterDescriptor firstConflictingVariable = getFirstConflictingVariable(constraintSystem);
if (firstConflictingVariable == null) return Collections.emptyList();
TypeParameterDescriptor firstConflictingParameter = constraintSystem.variableToDescriptor(firstConflictingVariable);
Collection<KotlinType> conflictingTypes = constraintSystem.getTypeBounds(firstConflictingParameter).getValues();
Collection<KotlinType> conflictingTypes = constraintSystem.getTypeBounds(firstConflictingVariable).getValues();
List<Map<TypeConstructor, TypeProjection>> substitutionContexts = Lists.newArrayList();
for (KotlinType type : conflictingTypes) {
@@ -54,16 +55,16 @@ public class ConstraintsUtil {
substitutionContexts.add(context);
}
for (TypeParameterDescriptor typeParameter : constraintSystem.getTypeParameterDescriptors()) {
if (typeParameter == firstConflictingParameter) continue;
for (TypeParameterDescriptor typeVariable : constraintSystem.getTypeVariables()) {
if (typeVariable == firstConflictingVariable) continue;
KotlinType safeType = getSafeValue(constraintSystem, typeParameter);
KotlinType safeType = getSafeValue(constraintSystem, typeVariable);
for (Map<TypeConstructor, TypeProjection> context : substitutionContexts) {
TypeProjection typeProjection = new TypeProjectionImpl(safeType);
context.put(typeParameter.getTypeConstructor(), typeProjection);
context.put(constraintSystem.variableToDescriptor(typeVariable).getTypeConstructor(), typeProjection);
}
}
Collection<TypeSubstitutor> typeSubstitutors = Lists.newArrayList();
Collection<TypeSubstitutor> typeSubstitutors = new ArrayList<TypeSubstitutor>(substitutionContexts.size());
for (Map<TypeConstructor, TypeProjection> context : substitutionContexts) {
typeSubstitutors.add(TypeSubstitutor.create(context));
}
@@ -71,13 +72,13 @@ public class ConstraintsUtil {
}
@NotNull
public static KotlinType getSafeValue(@NotNull ConstraintSystem constraintSystem, @NotNull TypeParameterDescriptor typeParameter) {
KotlinType type = constraintSystem.getTypeBounds(typeParameter).getValue();
private static KotlinType getSafeValue(@NotNull ConstraintSystem constraintSystem, @NotNull TypeParameterDescriptor typeVariable) {
KotlinType type = constraintSystem.getTypeBounds(typeVariable).getValue();
if (type != null) {
return type;
}
//todo may be error type
return TypeIntersector.getUpperBoundsAsType(typeParameter);
return TypeIntersector.getUpperBoundsAsType(constraintSystem.variableToDescriptor(typeVariable));
}
public static boolean checkUpperBoundIsSatisfied(
@@ -85,7 +86,7 @@ public class ConstraintsUtil {
@NotNull TypeParameterDescriptor typeParameter,
boolean substituteOtherTypeParametersInBound
) {
KotlinType type = constraintSystem.getTypeBounds(typeParameter).getValue();
KotlinType type = constraintSystem.getTypeBounds(constraintSystem.descriptorToVariable(typeParameter)).getValue();
if (type == null) return true;
for (KotlinType upperBound : typeParameter.getUpperBounds()) {
if (!substituteOtherTypeParametersInBound &&