Fixed rendering of error message for 'cannot capture type' error
Type variable should be chosen correctly
This commit is contained in:
@@ -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
|
||||||
+13
-8
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user