Fixed rendering of error message for 'cannot capture type' error

Type variable should be chosen correctly
This commit is contained in:
Svetlana Isakova
2015-01-21 22:25:25 +03:00
parent 971ad53bcc
commit 6cac6350a6
6 changed files with 47 additions and 13 deletions
@@ -394,18 +394,26 @@ public class Renderers {
@NotNull InferenceErrorData inferenceErrorData, @NotNull InferenceErrorData inferenceErrorData,
@NotNull TabledDescriptorRenderer result @NotNull TabledDescriptorRenderer result
) { ) {
ConstraintSystem constraintSystem = inferenceErrorData.constraintSystem; ConstraintSystemImpl constraintSystem = (ConstraintSystemImpl) inferenceErrorData.constraintSystem;
List<ConstraintError> errors = constraintSystem.getConstraintErrors();
TypeParameterDescriptor typeParameterWithCapturedConstraint = null; TypeParameterDescriptor typeParameterWithCapturedConstraint = null;
for (ConstraintError error : errors) {
if (error instanceof CannotCapture) {
typeParameterWithCapturedConstraint = ((CannotCapture) error).getTypeVariable();
}
}
if (typeParameterWithCapturedConstraint == null) {
LOG.error(renderDebugMessage("An error 'cannot capture type parameter' is not found in errors", inferenceErrorData));
return result;
}
CapturedTypeConstructor capturedTypeConstructor = null; CapturedTypeConstructor capturedTypeConstructor = null;
for (TypeParameterDescriptor typeParameter : constraintSystem.getTypeVariables()) { TypeBounds typeBounds = constraintSystem.getTypeBounds(typeParameterWithCapturedConstraint);
TypeBounds typeBounds = constraintSystem.getTypeBounds(typeParameter);
for (TypeBounds.Bound bound : typeBounds.getBounds()) { for (TypeBounds.Bound bound : typeBounds.getBounds()) {
TypeConstructor constructor = bound.getConstrainingType().getConstructor(); TypeConstructor constructor = bound.getConstrainingType().getConstructor();
if (constructor instanceof CapturedTypeConstructor) { if (constructor instanceof CapturedTypeConstructor) {
typeParameterWithCapturedConstraint = typeParameter;
capturedTypeConstructor = (CapturedTypeConstructor) constructor; capturedTypeConstructor = (CapturedTypeConstructor) constructor;
} }
}
} }
if (capturedTypeConstructor == null) { if (capturedTypeConstructor == null) {
LOG.error(renderDebugMessage("There is no captured type in bounds, but there is an error 'cannot capture type parameter'", LOG.error(renderDebugMessage("There is no captured type in bounds, but there is an error 'cannot capture type parameter'",
@@ -26,3 +26,6 @@ class TypeConstructorMismatch(constraintPosition: ConstraintPosition): Constrain
class ErrorInConstrainingType(constraintPosition: ConstraintPosition): ConstraintError(constraintPosition) class ErrorInConstrainingType(constraintPosition: ConstraintPosition): ConstraintError(constraintPosition)
class CannotCapture(constraintPosition: ConstraintPosition, val typeVariable: TypeParameterDescriptor): ConstraintError(constraintPosition) class CannotCapture(constraintPosition: ConstraintPosition, val typeVariable: TypeParameterDescriptor): ConstraintError(constraintPosition)
fun ConstraintError.substituteTypeVariable(substitution: (TypeParameterDescriptor) -> TypeParameterDescriptor) =
if (this is CannotCapture) CannotCapture(constraintPosition, substitution(typeVariable)) else this
@@ -51,7 +51,10 @@ public class ConstraintSystemImpl : ConstraintSystem {
} }
private val typeParameterBounds = LinkedHashMap<TypeParameterDescriptor, TypeBoundsImpl>() private val typeParameterBounds = LinkedHashMap<TypeParameterDescriptor, TypeBoundsImpl>()
private val errors = ArrayList<ConstraintError>() private val errors = ArrayList<ConstraintError>()
public val constraintErrors: List<ConstraintError>
get() = errors
private val constraintSystemStatus = object : ConstraintSystemStatus { private val constraintSystemStatus = object : ConstraintSystemStatus {
// for debug ConstraintsUtil.getDebugMessageForStatus might be used // for debug ConstraintsUtil.getDebugMessageForStatus might be used
@@ -134,7 +137,7 @@ public class ConstraintSystemImpl : ConstraintSystem {
public fun copy(): ConstraintSystem = createNewConstraintSystemFromThis({ it }, { it.copy() }, { true }) public fun copy(): ConstraintSystem = createNewConstraintSystemFromThis({ it }, { it.copy() }, { true })
public fun substituteTypeVariables(typeVariablesMap: (TypeParameterDescriptor) -> TypeParameterDescriptor?): ConstraintSystem { public fun substituteTypeVariables(typeVariablesMap: (TypeParameterDescriptor) -> TypeParameterDescriptor): ConstraintSystem {
// type bounds are proper types and don't contain other variables // type bounds are proper types and don't contain other variables
return createNewConstraintSystemFromThis(typeVariablesMap, { it }, { true }) return createNewConstraintSystemFromThis(typeVariablesMap, { it }, { true })
} }
@@ -163,16 +166,16 @@ public class ConstraintSystemImpl : ConstraintSystem {
} }
private fun createNewConstraintSystemFromThis( private fun createNewConstraintSystemFromThis(
substituteTypeVariable: (TypeParameterDescriptor) -> TypeParameterDescriptor?, substituteTypeVariable: (TypeParameterDescriptor) -> TypeParameterDescriptor,
replaceTypeBounds: (TypeBoundsImpl) -> TypeBoundsImpl, replaceTypeBounds: (TypeBoundsImpl) -> TypeBoundsImpl,
filterConstraintPosition: (ConstraintPosition) -> Boolean filterConstraintPosition: (ConstraintPosition) -> Boolean
): ConstraintSystem { ): ConstraintSystem {
val newSystem = ConstraintSystemImpl() val newSystem = ConstraintSystemImpl()
for ((typeParameter, typeBounds) in typeParameterBounds) { for ((typeParameter, typeBounds) in typeParameterBounds) {
val newTypeParameter = substituteTypeVariable(typeParameter) val newTypeParameter = substituteTypeVariable(typeParameter)
newSystem.typeParameterBounds.put(newTypeParameter!!, replaceTypeBounds(typeBounds)) newSystem.typeParameterBounds.put(newTypeParameter, replaceTypeBounds(typeBounds))
} }
newSystem.errors.addAll(errors.filter { filterConstraintPosition(it.constraintPosition) }) newSystem.errors.addAll(errors.filter { filterConstraintPosition(it.constraintPosition) }.map { it.substituteTypeVariable(substituteTypeVariable) })
return newSystem return newSystem
} }
@@ -188,11 +191,12 @@ public class ConstraintSystemImpl : ConstraintSystem {
private fun addConstraint(constraintKind: ConstraintKind, subType: JetType?, superType: JetType?, constraintPosition: ConstraintPosition) { private fun addConstraint(constraintKind: ConstraintKind, subType: JetType?, superType: JetType?, constraintPosition: ConstraintPosition) {
val typeCheckingProcedure = TypeCheckingProcedure(object : TypeCheckingProcedureCallbacks { val typeCheckingProcedure = TypeCheckingProcedure(object : TypeCheckingProcedureCallbacks {
private var isTopLevel = true private var depth = 0
override fun assertEqualTypes(a: JetType, b: JetType, typeCheckingProcedure: TypeCheckingProcedure): Boolean { override fun assertEqualTypes(a: JetType, b: JetType, typeCheckingProcedure: TypeCheckingProcedure): Boolean {
isTopLevel = false depth++
doAddConstraint(EQUAL, a, b, constraintPosition, typeCheckingProcedure) doAddConstraint(EQUAL, a, b, constraintPosition, typeCheckingProcedure)
depth--
return true return true
} }
@@ -202,15 +206,16 @@ public class ConstraintSystemImpl : ConstraintSystem {
} }
override fun assertSubtype(subtype: JetType, supertype: JetType, typeCheckingProcedure: TypeCheckingProcedure): Boolean { override fun assertSubtype(subtype: JetType, supertype: JetType, typeCheckingProcedure: TypeCheckingProcedure): Boolean {
isTopLevel = false depth++
doAddConstraint(SUB_TYPE, subtype, supertype, constraintPosition, typeCheckingProcedure) doAddConstraint(SUB_TYPE, subtype, supertype, constraintPosition, typeCheckingProcedure)
depth--
return true return true
} }
override fun capture(typeVariable: JetType, typeProjection: TypeProjection): Boolean { override fun capture(typeVariable: JetType, typeProjection: TypeProjection): Boolean {
val myTypeVariable = getMyTypeVariable(typeVariable) val myTypeVariable = getMyTypeVariable(typeVariable)
if (myTypeVariable != null && constraintPosition.isCaptureAllowed()) { if (myTypeVariable != null && constraintPosition.isCaptureAllowed()) {
if (!isTopLevel) { if (depth > 0) {
errors.add(CannotCapture(constraintPosition, myTypeVariable)) errors.add(CannotCapture(constraintPosition, myTypeVariable))
} }
generateTypeParameterCaptureConstraint(typeVariable, typeProjection, constraintPosition) generateTypeParameterCaptureConstraint(typeVariable, typeProjection, constraintPosition)
@@ -0,0 +1,10 @@
// !DIAGNOSTICS_NUMBER: 1
// !DIAGNOSTICS: TYPE_INFERENCE_CANNOT_CAPTURE_TYPES
class My<R, T>
fun <R, T> foo(my: My<Array<R>, T>): My<Array<R>, T> = my
fun test11(my: My<Array<out Int>, out Int>) {
foo(my)
}
@@ -0,0 +1,2 @@
<!-- typeInferenceCannotCaptureTypes1 -->
Type inference failed: 'R' cannot capture 'out Int'. Only top-level type projections can be captured
@@ -150,6 +150,12 @@ public class DiagnosticMessageTestGenerated extends AbstractDiagnosticMessageTes
doTest(fileName); doTest(fileName);
} }
@TestMetadata("typeInferenceCannotCaptureTypes.kt")
public void testTypeInferenceCannotCaptureTypes() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/diagnosticMessage/typeInferenceCannotCaptureTypes.kt");
doTest(fileName);
}
@TestMetadata("typeInferenceExpectedTypeMismatch.kt") @TestMetadata("typeInferenceExpectedTypeMismatch.kt")
public void testTypeInferenceExpectedTypeMismatch() throws Exception { public void testTypeInferenceExpectedTypeMismatch() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/diagnosticMessage/typeInferenceExpectedTypeMismatch.kt"); String fileName = JetTestUtils.navigationMetadata("idea/testData/diagnosticMessage/typeInferenceExpectedTypeMismatch.kt");