Add call to type variable and mapping descriptor -> variable

Since type parameter descriptor represents a single declaration of a type
parameter, multiple variables in one system may correspond to one type
parameter (e.g. in case of nested calls). Hence it's not possible to map type
parameters uniquely onto type variables and additional information is required
This commit is contained in:
Alexander Udalov
2015-11-11 20:24:13 +03:00
parent fc73e8c620
commit 178ca3daae
14 changed files with 83 additions and 36 deletions
@@ -251,7 +251,7 @@ public object Renderers {
val systemWithoutWeakConstraints = constraintSystem.filterConstraintsOut(ConstraintPositionKind.TYPE_BOUND_POSITION)
val typeParameterDescriptor = inferenceErrorData.descriptor.typeParameters.firstOrNull {
!ConstraintsUtil.checkUpperBoundIsSatisfied(systemWithoutWeakConstraints, it, true)
!ConstraintsUtil.checkUpperBoundIsSatisfied(systemWithoutWeakConstraints, it, inferenceErrorData.call, true)
}
if (typeParameterDescriptor == null && status.hasConflictingConstraints()) {
return renderConflictingSubstitutionsInferenceError(inferenceErrorData, result)
@@ -261,8 +261,8 @@ public object Renderers {
return result
}
val inferredValueForTypeParameter =
systemWithoutWeakConstraints.getTypeBounds(systemWithoutWeakConstraints.descriptorToVariable(typeParameterDescriptor)).value
val typeVariable = systemWithoutWeakConstraints.descriptorToVariable(inferenceErrorData.call.toHandle(), typeParameterDescriptor)
val inferredValueForTypeParameter = systemWithoutWeakConstraints.getTypeBounds(typeVariable).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))
@@ -204,7 +204,8 @@ public class CallCompleter(
val errorData = InferenceErrorData.create(
getCandidateDescriptor(), getConstraintSystem()!!, valueArgumentsCheckingResult.argumentTypes,
receiverType, context.expectedType)
receiverType, context.expectedType, context.call
)
tracing.typeInferenceFailed(context.trace, errorData)
addStatus(ResolutionStatus.OTHER_ERROR)
@@ -32,13 +32,10 @@ import org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT
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.ConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilderImpl
import org.jetbrains.kotlin.resolve.calls.inference.*
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.VALUE_PARAMETER_POSITION
import org.jetbrains.kotlin.resolve.calls.inference.getNestedTypeParameters
import org.jetbrains.kotlin.resolve.calls.inference.getNestedTypeVariables
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.INCOMPLETE_TYPE_INFERENCE
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.OTHER_ERROR
@@ -56,7 +53,7 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
val candidate = candidateCall.candidateDescriptor
val builder = ConstraintSystemBuilderImpl()
builder.registerTypeVariables(candidate.typeParameters)
builder.registerTypeVariables(candidateCall.call.toHandle(), candidate.typeParameters)
val substituteDontCare = makeConstantSubstitutor(candidate.typeParameters, DONT_CARE)
@@ -158,7 +155,7 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
val conversion = candidateDescriptor.typeParameters.zip(candidateWithFreshVariables.typeParameters).toMap()
val freshVariables = returnType.getNestedTypeParameters().map { conversion[it] }.filterNotNull()
builder.registerTypeVariables(freshVariables, external = true)
builder.registerTypeVariables(resultingCall.call.toHandle(), freshVariables, external = true)
builder.addSubtypeConstraint(candidateWithFreshVariables.returnType, effectiveExpectedType, constraintPosition)
return true
@@ -34,7 +34,7 @@ interface ConstraintSystem {
*/
val typeVariables: Set<TypeVariable>
fun descriptorToVariable(descriptor: TypeParameterDescriptor): TypeVariable
fun descriptorToVariable(call: CallHandle, descriptor: TypeParameterDescriptor): TypeVariable
/**
* Returns the resulting type constraints of solving the constraint system for specific type parameter descriptor.
@@ -66,7 +66,9 @@ interface ConstraintSystem {
* Registers variables in a constraint system. Returns a substitutor which maps type parameter descriptors given as parameters
* to the corresponding type variables of the system. Use that substitutor to provide constraints to the system
*/
fun registerTypeVariables(typeParameters: Collection<TypeParameterDescriptor>, external: Boolean = false): TypeSubstitutor
fun registerTypeVariables(
call: CallHandle, typeParameters: Collection<TypeParameterDescriptor>, external: Boolean = false
): TypeSubstitutor
/**
* Adds a constraint that the constraining type is a subtype of the subject type.
@@ -64,19 +64,25 @@ class ConstraintSystemBuilderImpl : ConstraintSystem.Builder {
})
}
override fun registerTypeVariables(typeParameters: Collection<TypeParameterDescriptor>, external: Boolean): TypeSubstitutor {
override fun registerTypeVariables(
call: CallHandle, typeParameters: Collection<TypeParameterDescriptor>, external: Boolean
): TypeSubstitutor {
if (typeParameters.isEmpty()) return TypeSubstitutor.EMPTY
val typeVariables = (if (external) {
typeParameters.toList()
val typeVariables = if (external) {
typeParameters.map {
TypeVariable(call, it, it, true)
}
}
else ArrayList<TypeParameterDescriptor>(typeParameters.size).apply {
else {
val freshTypeParameters = ArrayList<TypeParameterDescriptor>(typeParameters.size)
DescriptorSubstitutor.substituteTypeParameters(
typeParameters.toList(), TypeSubstitution.EMPTY, typeParameters.first().containingDeclaration, this
typeParameters.toList(), TypeSubstitution.EMPTY, typeParameters.first().containingDeclaration, freshTypeParameters
)
}).zip(typeParameters).map {
val (fresh, original) = it
TypeVariable(fresh, original, external)
freshTypeParameters.zip(typeParameters).map {
val (fresh, original) = it
TypeVariable(call, fresh, original, external)
}
}
for ((descriptor, typeVariable) in typeParameters.zip(typeVariables)) {
@@ -111,8 +111,8 @@ internal class ConstraintSystemImpl(
override val typeVariables: Set<TypeVariable>
get() = allTypeParameterBounds.keys
override fun descriptorToVariable(descriptor: TypeParameterDescriptor): TypeVariable =
descriptorToVariable[descriptor] ?: throw IllegalArgumentException("Unknown descriptor: $descriptor")
override fun descriptorToVariable(call: CallHandle, descriptor: TypeParameterDescriptor): TypeVariable =
descriptorToVariable[descriptor] ?: throw IllegalArgumentException("Unknown descriptor: $descriptor, call: $call")
override fun getTypeBounds(typeVariable: TypeVariable): TypeBoundsImpl {
return allTypeParameterBounds[typeVariable] ?:
@@ -21,6 +21,7 @@ import com.google.common.collect.Maps;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
import org.jetbrains.kotlin.psi.Call;
import org.jetbrains.kotlin.types.*;
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
@@ -84,9 +85,11 @@ public class ConstraintsUtil {
public static boolean checkUpperBoundIsSatisfied(
@NotNull ConstraintSystem constraintSystem,
@NotNull TypeParameterDescriptor typeParameter,
@NotNull Call call,
boolean substituteOtherTypeParametersInBound
) {
KotlinType type = constraintSystem.getTypeBounds(constraintSystem.descriptorToVariable(typeParameter)).getValue();
TypeVariable typeVariable = constraintSystem.descriptorToVariable(TypeVariableKt.toHandle(call), typeParameter);
KotlinType type = constraintSystem.getTypeBounds(typeVariable).getValue();
if (type == null) return true;
for (KotlinType upperBound : typeParameter.getUpperBounds()) {
if (!substituteOtherTypeParametersInBound &&
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve.calls.inference;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
import org.jetbrains.kotlin.psi.Call;
import org.jetbrains.kotlin.types.KotlinType;
import java.util.List;
@@ -34,22 +35,34 @@ public class InferenceErrorData {
public final KotlinType expectedType;
@NotNull
public final List<KotlinType> valueArgumentsTypes;
@NotNull
public final Call call;
private InferenceErrorData(
@NotNull CallableDescriptor descriptor, @NotNull ConstraintSystem constraintSystem,
@NotNull List<KotlinType> valueArgumentsTypes, @Nullable KotlinType receiverArgumentType, @NotNull KotlinType expectedType
@NotNull CallableDescriptor descriptor,
@NotNull ConstraintSystem constraintSystem,
@NotNull List<KotlinType> valueArgumentsTypes,
@Nullable KotlinType receiverArgumentType,
@NotNull KotlinType expectedType,
@NotNull Call call
) {
this.descriptor = descriptor;
this.constraintSystem = constraintSystem;
this.receiverArgumentType = receiverArgumentType;
this.valueArgumentsTypes = valueArgumentsTypes;
this.expectedType = expectedType;
this.call = call;
}
@NotNull
public static InferenceErrorData create(@NotNull CallableDescriptor descriptor, @NotNull ConstraintSystem constraintSystem,
@NotNull List<KotlinType> valueArgumentsTypes, @Nullable KotlinType receiverArgumentType, @NotNull KotlinType expectedType) {
return new InferenceErrorData(descriptor, constraintSystem, valueArgumentsTypes, receiverArgumentType, expectedType);
public static InferenceErrorData create(
@NotNull CallableDescriptor descriptor,
@NotNull ConstraintSystem constraintSystem,
@NotNull List<KotlinType> valueArgumentsTypes,
@Nullable KotlinType receiverArgumentType,
@NotNull KotlinType expectedType,
@NotNull Call call
) {
return new InferenceErrorData(descriptor, constraintSystem, valueArgumentsTypes, receiverArgumentType, expectedType, call);
}
}
@@ -19,13 +19,15 @@ package org.jetbrains.kotlin.resolve.calls.inference
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.Call
import org.jetbrains.kotlin.resolve.descriptorUtil.hasOnlyInputTypesAnnotation
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.KotlinTypeImpl
class TypeVariable(
val freshTypeParameter: TypeParameterDescriptor,
val call: CallHandle,
internal val freshTypeParameter: TypeParameterDescriptor,
val originalTypeParameter: TypeParameterDescriptor,
val isExternal: Boolean
) {
@@ -36,3 +38,20 @@ class TypeVariable(
fun hasOnlyInputTypesAnnotation(): Boolean =
originalTypeParameter.hasOnlyInputTypesAnnotation()
}
interface CallHandle {
object NONE : CallHandle
}
class CallBasedCallHandle(val call: Call): CallHandle {
override fun equals(other: Any?) =
other is CallBasedCallHandle && call === other.call
override fun hashCode() =
System.identityHashCode(call)
override fun toString() =
call.toString()
}
fun Call.toHandle(): CallHandle = CallBasedCallHandle(this)
@@ -24,8 +24,10 @@ import org.jetbrains.kotlin.types.TypeProjection
import org.jetbrains.kotlin.types.TypeProjectionImpl
import java.util.*
fun ConstraintSystem.getNestedTypeVariables(type: KotlinType): List<TypeVariable> =
type.getNestedTypeParameters().filter { it in typeParameterDescriptors }.map { descriptorToVariable(it) }
fun ConstraintSystem.getNestedTypeVariables(type: KotlinType): List<TypeVariable> {
val nestedTypeParameters = type.getNestedTypeParameters().toSet()
return typeVariables.filter { it.originalTypeParameter in nestedTypeParameters }
}
fun ConstraintSystem.filterConstraintsOut(excludePositionKind: ConstraintPositionKind): ConstraintSystem {
return toBuilder { !it.derivedFrom(excludePositionKind) }.build()
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.resolve.calls.inference.CallHandle;
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem;
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilderImpl;
import org.jetbrains.kotlin.resolve.scopes.ChainedScope;
@@ -185,7 +186,7 @@ public class TypeIntersector {
processAllTypeParameters(withParameters, Variance.INVARIANT, processor);
processAllTypeParameters(expected, Variance.INVARIANT, processor);
ConstraintSystem.Builder constraintSystem = new ConstraintSystemBuilderImpl();
constraintSystem.registerTypeVariables(parameters.keySet(), false);
constraintSystem.registerTypeVariables(CallHandle.NONE.INSTANCE, parameters.keySet(), false);
constraintSystem.addSubtypeConstraint(withParameters, expected, SPECIAL.position());
return constraintSystem.build().getStatus().isSuccessful();
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.TypeResolver
import org.jetbrains.kotlin.resolve.calls.inference.CallHandle
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintContext
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilderImpl
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.SPECIAL
@@ -81,7 +82,7 @@ abstract public class AbstractConstraintSystemTest() : KotlinLiteFixture() {
val variables = parseVariables(constraintsFileText)
val fixVariables = constraintsFileText.contains("FIX_VARIABLES")
val typeParameterDescriptors = variables.map { testDeclarations.getParameterDescriptor(it) }
val substitutor = builder.registerTypeVariables(typeParameterDescriptors)
val substitutor = builder.registerTypeVariables(CallHandle.NONE, typeParameterDescriptors)
val constraints = parseConstraints(constraintsFileText)
@@ -20,6 +20,7 @@ package org.jetbrains.kotlin.idea.util
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.resolve.calls.inference.CallHandle
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilderImpl
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind
import org.jetbrains.kotlin.types.*
@@ -115,7 +116,7 @@ class FuzzyType(
}
val builder = ConstraintSystemBuilderImpl()
val typeVariableSubstitutor = builder.registerTypeVariables(freeParameters + otherType.freeParameters)
val typeVariableSubstitutor = builder.registerTypeVariables(CallHandle.NONE, freeParameters + otherType.freeParameters)
when (matchKind) {
MatchKind.IS_SUBTYPE -> builder.addSubtypeConstraint(
@@ -87,7 +87,8 @@ public class AddGenericUpperBoundFix(
typeParameterDescriptor ->
if (ConstraintsUtil.checkUpperBoundIsSatisfied(
successfulConstraintSystem, typeParameterDescriptor, /* substituteOtherTypeParametersInBound */ true
successfulConstraintSystem, typeParameterDescriptor, inferenceData.call,
/* substituteOtherTypeParametersInBound */ true
)) return@factory null
val upperBound = typeParameterDescriptor.upperBounds.singleOrNull() ?: return@factory null