From 549ae59562248b41591abd871185550ddd872ede Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Wed, 9 Nov 2016 16:32:19 +0300 Subject: [PATCH] Inference for type alias constructor type arguments: better error reporting. --- .../kotlin/diagnostics/rendering/Renderers.kt | 28 ++++++++++------- .../resolve/calls/GenericCandidateResolver.kt | 17 ++++++----- ...eAliasConstructorTypeArgumentsInference.kt | 22 ++++++++------ ...AliasConstructorTypeArgumentsInference.txt | 17 +++-------- ...orTypeArgumentsInferenceWithNestedCalls.kt | 5 ++++ ...rTypeArgumentsInferenceWithNestedCalls.txt | 14 +++++++++ ...rTypeArgumentsInferenceWithPhantomTypes.kt | 16 ++++++++++ ...TypeArgumentsInferenceWithPhantomTypes.txt | 30 +++++++++++++++++++ .../checkers/DiagnosticsTestGenerated.java | 12 ++++++++ ...undViolatedInTypeAliasConstructorCall1.txt | 2 +- 10 files changed, 123 insertions(+), 40 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithNestedCalls.kt create mode 100644 compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithNestedCalls.txt create mode 100644 compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithPhantomTypes.kt create mode 100644 compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithPhantomTypes.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt index 835a8e3ef12..e13b0418f63 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt @@ -40,14 +40,11 @@ import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.LOWER_B import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.UPPER_BOUND 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.derivedFrom 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 -import org.jetbrains.kotlin.types.TypeUtils -import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import java.io.PrintWriter @@ -270,14 +267,16 @@ object Renderers { LOG.assertTrue(status.hasViolatedUpperBound(), debugMessage("Upper bound violated renderer is applied for incorrect status", inferenceErrorData)) - val systemWithoutWeakConstraints = constraintSystem.filterConstraintsOut(ConstraintPositionKind.TYPE_BOUND_POSITION) + val systemWithoutWeakConstraints = constraintSystem.filterConstraintsOut(TYPE_BOUND_POSITION) val typeParameterDescriptor = inferenceErrorData.descriptor.typeParameters.firstOrNull { !ConstraintsUtil.checkUpperBoundIsSatisfied(systemWithoutWeakConstraints, it, inferenceErrorData.call, true) } if (typeParameterDescriptor == null) { if (inferenceErrorData.descriptor is TypeAliasConstructorDescriptor) { - renderUpperBoundViolatedInferenceErrorForTypeAliasConstructor(inferenceErrorData, result, systemWithoutWeakConstraints)?.let { + renderUpperBoundViolatedInferenceErrorForTypeAliasConstructor( + inferenceErrorData, result, systemWithoutWeakConstraints + )?.let { return it } } @@ -342,11 +341,20 @@ object Renderers { return result } - val inferredTypeSubstitutor = systemWithoutWeakConstraints.resultingSubstitutor + val inferredTypesForTypeParameters = descriptor.typeParameters.map { + val typeVariable = systemWithoutWeakConstraints.descriptorToVariable(inferenceErrorData.call.toHandle(), it) + systemWithoutWeakConstraints.getTypeBounds(typeVariable).value + } + val inferredTypeSubstitutor = TypeSubstitutor.create(object : TypeConstructorSubstitution() { + override fun get(key: TypeConstructor): TypeProjection? { + val typeDescriptor = key.declarationDescriptor as? TypeParameterDescriptor ?: return null + if (typeDescriptor.containingDeclaration != descriptor.typeAliasDescriptor) return null + return inferredTypesForTypeParameters[typeDescriptor.index]?.let { TypeProjectionImpl(it) } + } + }) 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) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt index 49e03c96d40..b9535dde33f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt @@ -18,7 +18,9 @@ package org.jetbrains.kotlin.resolve.calls import org.jetbrains.kotlin.builtins.ReflectionTypes import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype -import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil @@ -33,11 +35,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.inference.constraintPosition.ValidityConstraintForConstituentType import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.makeNullableTypeIfSafeReceiver import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.INCOMPLETE_TYPE_INFERENCE @@ -122,20 +123,22 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes val typeParameter = typeConstructor.parameters[i] addValidityConstraintsForTypeArgument(builder, typeProjection, typeParameter, boundsSubstitutor) + + addValidityConstraintsForConstituentTypes(builder, typeProjection.type) } } private fun addValidityConstraintsForTypeArgument( builder: ConstraintSystem.Builder, - typeProjection: TypeProjection, + substitutedArgument: TypeProjection, typeParameter: TypeParameterDescriptor, boundsSubstitutor: TypeSubstitutor ) { - val typeArgument = typeProjection.type + val substitutedType = substitutedArgument.type for (upperBound in typeParameter.upperBounds) { val substitutedUpperBound = boundsSubstitutor.safeSubstitute(upperBound, Variance.INVARIANT) - val constraintPosition = ValidityConstraintForConstituentType(typeArgument, typeParameter, substitutedUpperBound) - builder.addSubtypeConstraint(typeArgument, substitutedUpperBound, constraintPosition) + val constraintPosition = ValidityConstraintForConstituentType(substitutedType, typeParameter, substitutedUpperBound) + builder.addSubtypeConstraint(substitutedType, substitutedUpperBound, constraintPosition) } } diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInference.kt b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInference.kt index 025132738f4..a2eccd30dd1 100644 --- a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInference.kt +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInference.kt @@ -1,26 +1,30 @@ class Num(val x: Tn) typealias N = Num +val test0 = N(1) +val test1 = \(x: T\): Num + is not satisfied: inferred type String is not a subtype of Number)!>N("1") + + class Cons(val head: T, val tail: Cons?) typealias C = Cons typealias CC = C> +val test2 = C(1, 2) +val test3 = CC(1, 2) +val test4 = CC(C(1, null), null) + + class Pair(val x: X, val y: Y) typealias PL = Pair> +typealias PN = Pair> + +val test5 = PL(1, null) -class Bound(val x: X, val y: Y) -typealias B = Bound class Foo(val p: Pair) typealias F = Foo -val test0 = N(1) -val test1 = N("1") -val test2 = C(1, 2) -val test3 = PL(1, null) -val test4 = CC(1, 2) -val test4a = CC(C(1, null), null) - fun testProjections1(x: Pair) = F(x) fun testProjections2(x: Pair) = F(x) fun testProjections3(x: Pair) = F(x) diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInference.txt b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInference.txt index 4e3ecfd4548..f22d82148a8 100644 --- a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInference.txt +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInference.txt @@ -3,23 +3,14 @@ package public val test0: Num public val test1: [ERROR : Type for N("1")] public val test2: Cons -public val test3: Pair> -public val test4: [ERROR : Type for CC(1, 2)] -public val test4a: Cons> +public val test3: [ERROR : Type for CC(1, 2)] +public val test4: Cons> +public val test5: Pair> public fun testProjections1(/*0*/ x: Pair): [ERROR : Error function type] public fun testProjections2(/*0*/ x: Pair): [ERROR : Error function type] public fun testProjections3(/*0*/ x: Pair): [ERROR : Error function type] public fun testProjections4(/*0*/ x: Pair): [ERROR : Error function type] -public final class Bound { - public constructor Bound(/*0*/ x: X, /*1*/ y: Y) - public final val x: X - public final val y: Y - public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String -} - public final class Cons { public constructor Cons(/*0*/ head: T, /*1*/ tail: Cons?) public final val head: T @@ -53,9 +44,9 @@ public final class Pair { public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } -public typealias B = Bound public typealias C = Cons public typealias CC = C> public typealias F = Foo public typealias N = Num public typealias PL = Pair> +public typealias PN = Pair> diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithNestedCalls.kt b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithNestedCalls.kt new file mode 100644 index 00000000000..e34c2eb132b --- /dev/null +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithNestedCalls.kt @@ -0,0 +1,5 @@ +class Cons(val head: T, val tail: Cons?) +typealias C = Cons + +val test1 = C(1, C(2, null)) +val test2 = C(1, C("", null)) diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithNestedCalls.txt b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithNestedCalls.txt new file mode 100644 index 00000000000..80968c8e006 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithNestedCalls.txt @@ -0,0 +1,14 @@ +package + +public val test1: Cons +public val test2: Cons + +public final class Cons { + public constructor Cons(/*0*/ head: T, /*1*/ tail: Cons?) + public final val head: T + public final val tail: Cons? + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} +public typealias C = Cons diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithPhantomTypes.kt b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithPhantomTypes.kt new file mode 100644 index 00000000000..e5428b12a35 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithPhantomTypes.kt @@ -0,0 +1,16 @@ +class Foo +class Bar + +class Hr(val a: A, val b: B) + +typealias Test = Hr, Bar> + +val test1 = Test(1, "") +val test2 = \(a: A, b: B\): Hr, Bar> + is not satisfied: inferred type Int is not a subtype of CharSequence)!>Test(1, 2) + + +typealias Bas = Hr, Bar> + +val test3 = \(a: T, b: T\): Hr, Bar> + is not satisfied: inferred type Int is not a subtype of CharSequence)!>Bas(1, 1) diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithPhantomTypes.txt b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithPhantomTypes.txt new file mode 100644 index 00000000000..2386061ebe4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithPhantomTypes.txt @@ -0,0 +1,30 @@ +package + +public val test1: Hr, Bar> +public val test2: [ERROR : Type for Test(1, 2)] +public val test3: [ERROR : Type for Bas(1, 1)] + +public final class Bar { + public constructor Bar() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Foo { + public constructor Foo() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Hr { + public constructor Hr(/*0*/ a: A, /*1*/ b: B) + public final val a: A + public final val b: B + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} +public typealias Bas = Hr, Bar> +public typealias Test = Hr, Bar> diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index dd3d3cd6d7f..1e2ebaf7433 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -21058,6 +21058,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("typeAliasConstructorTypeArgumentsInferenceWithNestedCalls.kt") + public void testTypeAliasConstructorTypeArgumentsInferenceWithNestedCalls() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithNestedCalls.kt"); + doTest(fileName); + } + + @TestMetadata("typeAliasConstructorTypeArgumentsInferenceWithPhantomTypes.kt") + public void testTypeAliasConstructorTypeArgumentsInferenceWithPhantomTypes() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typealias/typeAliasConstructorTypeArgumentsInferenceWithPhantomTypes.kt"); + doTest(fileName); + } + @TestMetadata("typeAliasConstructorWrongClass.kt") public void testTypeAliasConstructorWrongClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typealias/typeAliasConstructorWrongClass.kt"); diff --git a/idea/testData/diagnosticMessage/upperBoundViolatedInTypeAliasConstructorCall1.txt b/idea/testData/diagnosticMessage/upperBoundViolatedInTypeAliasConstructorCall1.txt index 89ef6a1e62d..cfa0c19e631 100644 --- a/idea/testData/diagnosticMessage/upperBoundViolatedInTypeAliasConstructorCall1.txt +++ b/idea/testData/diagnosticMessage/upperBoundViolatedInTypeAliasConstructorCall1.txt @@ -1,3 +1,3 @@ Type parameter bound for Tn in type inferred from type alias expansion for fun (x: T): Num - is not satisfied: inferred type T is not a subtype of Number \ No newline at end of file + is not satisfied: inferred type String is not a subtype of Number \ No newline at end of file