Fix type parameter list for local classes
Add captured parameters from enclosing functions #KT-9584 Fixed
This commit is contained in:
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.resolve.PossiblyBareType.type
|
||||
import org.jetbrains.kotlin.resolve.TypeResolver.FlexibleTypeCapabilitiesProvider
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.recordScope
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallableDescriptors
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
|
||||
import org.jetbrains.kotlin.resolve.lazy.LazyEntity
|
||||
import org.jetbrains.kotlin.resolve.scopes.LazyScopeAdapter
|
||||
@@ -299,7 +300,7 @@ public class TypeResolver(
|
||||
|
||||
val projectionFromAllQualifierParts = qualifierResolutionResult.allProjections
|
||||
val parameters = typeConstructor.parameters
|
||||
if (c.allowBareTypes && projectionFromAllQualifierParts.isEmpty() && parameters.isNotEmpty()) {
|
||||
if (c.allowBareTypes && projectionFromAllQualifierParts.isEmpty() && isPossibleToSpecifyTypeArgumentsFor(classDescriptor)) {
|
||||
// See docs for PossiblyBareType
|
||||
return PossiblyBareType.bare(typeConstructor, false)
|
||||
}
|
||||
@@ -357,6 +358,24 @@ public class TypeResolver(
|
||||
return type(resultingType)
|
||||
}
|
||||
|
||||
// Returns true in case when at least one argument for this class could be specified
|
||||
// It could be always equal to 'typeConstructor.parameters.isNotEmpty()' unless local classes could captured type parameters
|
||||
// from enclosing functions. In such cases you can not specify any argument:
|
||||
// fun <E> foo(x: Any?) {
|
||||
// class C
|
||||
// if (x is C) { // 'C' should not be treated as bare type here
|
||||
// ...
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// It's needed to determine whether this particular type could be bare
|
||||
private fun isPossibleToSpecifyTypeArgumentsFor(classDescriptor: ClassDescriptor): Boolean {
|
||||
// First parameter relates to the innermost declaration
|
||||
// If it's declared in function there
|
||||
val firstTypeParameter = classDescriptor.typeConstructor.parameters.firstOrNull() ?: return false
|
||||
return firstTypeParameter.original.containingDeclaration is ClassDescriptor
|
||||
}
|
||||
|
||||
private fun collectArgumentsForClassTypeConstructor(
|
||||
c: TypeResolutionContext,
|
||||
classDescriptor: ClassDescriptor,
|
||||
@@ -406,7 +425,9 @@ public class TypeResolver(
|
||||
|
||||
val parameters = classDescriptor.typeConstructor.parameters
|
||||
if (result.size < parameters.size) {
|
||||
if (parameters.subList(result.size, parameters.size).any { parameter -> !parameter.isDeclaredInScope(c) }) {
|
||||
val typeParametersToSpecify =
|
||||
parameters.subList(result.size, parameters.size).takeWhile { it.original.containingDeclaration is ClassDescriptor }
|
||||
if (typeParametersToSpecify.any { parameter -> !parameter.isDeclaredInScope(c) }) {
|
||||
c.trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(qualifierParts.last().expression, parameters.size))
|
||||
return null
|
||||
}
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// !CHECK_TYPE
|
||||
private class Outer<E> {
|
||||
private inner class Inner<out F> {
|
||||
private fun <G> foo() = {
|
||||
fun baz() = {
|
||||
class Local {
|
||||
val e: E = magic()
|
||||
val f: F = magic()
|
||||
val g: G = magic()
|
||||
}
|
||||
Local()
|
||||
}
|
||||
baz()()
|
||||
}
|
||||
|
||||
private var doubleCharSequenceInt = Outer<Double>().Inner<CharSequence>().foo<Int>()()
|
||||
private var doubleStringNumber = Outer<Double>().Inner<String>().foo<Number>()()
|
||||
private var doubleStringInt = Outer<Double>().Inner<String>().foo<Int>()()
|
||||
|
||||
private fun bar() {
|
||||
doubleCharSequenceInt = <!TYPE_MISMATCH!>doubleStringNumber<!>
|
||||
doubleCharSequenceInt = doubleStringInt
|
||||
|
||||
doubleStringInt = Outer<Double>().Inner<String>().foo<Int>()()
|
||||
|
||||
doubleStringInt.e.checkType { _<Double>() }
|
||||
doubleStringInt.f.checkType { _<String>() }
|
||||
doubleStringInt.g.checkType { _<Int>() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> magic(): T = null!!
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package
|
||||
|
||||
public fun </*0*/ T> magic(): T
|
||||
|
||||
private final class Outer</*0*/ E> {
|
||||
public constructor Outer</*0*/ E>()
|
||||
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
|
||||
|
||||
private final inner class Inner</*0*/ out F> /*captured type parameters: /*1*/ E*/ {
|
||||
public constructor Inner</*0*/ out F>()
|
||||
private final var doubleCharSequenceInt: Outer.Inner.foo.<anonymous>.baz.<anonymous>.Local<kotlin.Int, kotlin.CharSequence, kotlin.Double>
|
||||
private final var doubleStringInt: Outer.Inner.foo.<anonymous>.baz.<anonymous>.Local<kotlin.Int, kotlin.String, kotlin.Double>
|
||||
private final var doubleStringNumber: Outer.Inner.foo.<anonymous>.baz.<anonymous>.Local<kotlin.Number, kotlin.String, kotlin.Double>
|
||||
private final fun bar(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
private final fun </*0*/ G> foo(): () -> Outer.Inner.foo.<anonymous>.baz.<anonymous>.Local<G, F, E>
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// !CHECK_TYPE
|
||||
|
||||
fun <T> magic(): T = null!!
|
||||
|
||||
class Q {
|
||||
private fun <E> foo() = {
|
||||
class C {
|
||||
val prop: E = magic()
|
||||
}
|
||||
C()
|
||||
}
|
||||
|
||||
private var x = foo<CharSequence>()()
|
||||
private var y = foo<String>()()
|
||||
|
||||
fun bar() {
|
||||
x = <!TYPE_MISMATCH!>y<!>
|
||||
x = foo<CharSequence>()()
|
||||
y = foo<String>()()
|
||||
|
||||
x.prop.checkType { _<CharSequence>() }
|
||||
y.prop.checkType { _<String>() }
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package
|
||||
|
||||
public fun </*0*/ T> magic(): T
|
||||
|
||||
public final class Q {
|
||||
public constructor Q()
|
||||
private final var x: Q.foo.<anonymous>.C<kotlin.CharSequence>
|
||||
private final var y: Q.foo.<anonymous>.C<kotlin.String>
|
||||
public final fun bar(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
private final fun </*0*/ E> foo(): () -> Q.foo.<anonymous>.C<E>
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// !CHECK_TYPE
|
||||
|
||||
fun <T> magic(): T = null!!
|
||||
|
||||
class Q {
|
||||
private fun <E, F> foo() = {
|
||||
class C<G> {
|
||||
val e: E = magic()
|
||||
val f: F = magic()
|
||||
val g: G = magic()
|
||||
}
|
||||
C<F>()
|
||||
}
|
||||
|
||||
private var x = foo<CharSequence, Number>()()
|
||||
|
||||
fun bar() {
|
||||
x.e.checkType { _<CharSequence>() }
|
||||
x.f.checkType { _<Number>() }
|
||||
x.g.checkType { _<Number>() }
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package
|
||||
|
||||
public fun </*0*/ T> magic(): T
|
||||
|
||||
public final class Q {
|
||||
public constructor Q()
|
||||
private final var x: Q.foo.<anonymous>.C<kotlin.Number, kotlin.CharSequence, kotlin.Number>
|
||||
public final fun bar(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
private final fun </*0*/ E, /*1*/ F> foo(): () -> Q.foo.<anonymous>.C<F, E, F>
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// !CHECK_TYPE
|
||||
|
||||
fun <T> magic(): T = null!!
|
||||
|
||||
class Q {
|
||||
private fun <E> foo() =
|
||||
object {
|
||||
val prop: E = magic()
|
||||
}
|
||||
|
||||
private var x = foo<CharSequence>()
|
||||
private var y = foo<String>()
|
||||
|
||||
fun bar() {
|
||||
x = <!TYPE_MISMATCH!>y<!>
|
||||
x = foo<CharSequence>()
|
||||
y = foo<String>()
|
||||
|
||||
x.prop.checkType { _<CharSequence>() }
|
||||
y.prop.checkType { _<String>() }
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package
|
||||
|
||||
public fun </*0*/ T> magic(): T
|
||||
|
||||
public final class Q {
|
||||
public constructor Q()
|
||||
private final var x: Q.foo.<no name provided><kotlin.CharSequence>
|
||||
private final var y: Q.foo.<no name provided><kotlin.String>
|
||||
public final fun bar(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
private final fun </*0*/ E> foo(): Q.foo.<no name provided><E>
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
fun <E> foo(x: Any, y: Any) : Any {
|
||||
class C
|
||||
// without E?
|
||||
if(x is <!CANNOT_CHECK_FOR_ERASED!>C<!>) {
|
||||
return x
|
||||
}
|
||||
|
||||
if (1 == 2) {
|
||||
<!UNCHECKED_CAST!>x as C<!>
|
||||
}
|
||||
|
||||
if (2 == 3) {
|
||||
<!UNCHECKED_CAST!>x as? C<!>
|
||||
}
|
||||
|
||||
class Outer<F> {
|
||||
inner class Inner
|
||||
}
|
||||
|
||||
// bare type
|
||||
if (y is <!NO_TYPE_ARGUMENTS_ON_RHS!>Outer.Inner<!>) {
|
||||
return y
|
||||
}
|
||||
|
||||
<!UNCHECKED_CAST!>y as Outer<*>.Inner<!>
|
||||
|
||||
return C()
|
||||
}
|
||||
|
||||
fun noTypeParameters(x: Any) : Any {
|
||||
class C
|
||||
if(x is C) {
|
||||
return x
|
||||
}
|
||||
|
||||
return C()
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun </*0*/ E> foo(/*0*/ x: kotlin.Any, /*1*/ y: kotlin.Any): kotlin.Any
|
||||
public fun noTypeParameters(/*0*/ x: kotlin.Any): kotlin.Any
|
||||
Vendored
+2
-2
@@ -2,7 +2,7 @@ package
|
||||
|
||||
public final class Foo</*0*/ R> {
|
||||
public constructor Foo</*0*/ R>()
|
||||
private final fun </*0*/ T> bar(): Foo.bar.<no name provided>
|
||||
private final fun </*0*/ T> bar(): Foo.bar.<no name provided><T, R>
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public final fun getR(/*0*/ r: R): R
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
@@ -16,5 +16,5 @@ public final class Test {
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public final fun test(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
private final fun </*0*/ T : kotlin.Any> T.self(): Test.self.<no name provided>
|
||||
private final fun </*0*/ T : kotlin.Any> T.self(): Test.self.<no name provided><T>
|
||||
}
|
||||
|
||||
@@ -6795,6 +6795,45 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/generics/capturedParameters")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class CapturedParameters extends AbstractDiagnosticsTest {
|
||||
public void testAllFilesPresentInCapturedParameters() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/generics/capturedParameters"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("innerLocalClass.kt")
|
||||
public void testInnerLocalClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/capturedParameters/innerLocalClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("localClass.kt")
|
||||
public void testLocalClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/capturedParameters/localClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("localWithTypeParameter.kt")
|
||||
public void testLocalWithTypeParameter() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/capturedParameters/localWithTypeParameter.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("objectLiteral.kt")
|
||||
public void testObjectLiteral() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/capturedParameters/objectLiteral.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("uncheckedCast.kt")
|
||||
public void testUncheckedCast() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/capturedParameters/uncheckedCast.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/generics/cyclicBounds")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -17,18 +17,26 @@
|
||||
package org.jetbrains.kotlin.descriptors
|
||||
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.parents
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.error.MissingDependencyErrorClass
|
||||
import java.util.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
|
||||
fun ClassDescriptor.computeConstructorTypeParameters(): List<TypeParameterDescriptor> {
|
||||
val declaredParameters = declaredTypeParameters
|
||||
|
||||
if (!isInner) return declaredParameters
|
||||
val containingTypeConstructor = (containingDeclaration as? ClassDescriptor)?.typeConstructor ?: return emptyList()
|
||||
if (!isInner && containingDeclaration !is CallableDescriptor) return declaredParameters
|
||||
|
||||
val additional = containingTypeConstructor.parameters.map { it.capturedCopyForInnerDeclaration(this, declaredParameters.size) }
|
||||
if (additional.isEmpty()) return declaredParameters
|
||||
val parametersFromContainingFunctions =
|
||||
parents.takeWhile { it is CallableDescriptor }
|
||||
.flatMap { (it as CallableDescriptor).typeParameters.asSequence() }.toList()
|
||||
|
||||
val containingClassTypeConstructorParameters = parents.firstIsInstanceOrNull<ClassDescriptor>()?.typeConstructor?.parameters.orEmpty()
|
||||
if (parametersFromContainingFunctions.isEmpty() && containingClassTypeConstructorParameters.isEmpty()) return declaredTypeParameters
|
||||
|
||||
val additional =
|
||||
(parametersFromContainingFunctions + containingClassTypeConstructorParameters)
|
||||
.map { it.capturedCopyForInnerDeclaration(this, declaredParameters.size) }
|
||||
|
||||
return declaredParameters + additional
|
||||
}
|
||||
@@ -69,14 +77,15 @@ private fun KotlinType.buildPossiblyInnerType(classDescriptor: ClassDescriptor?,
|
||||
if (classDescriptor == null || ErrorUtils.isError(classDescriptor)) return null
|
||||
|
||||
val toIndex = classDescriptor.declaredTypeParameters.size + index
|
||||
val argumentsSubList = arguments.subList(index, toIndex)
|
||||
|
||||
if (!classDescriptor.isInner) {
|
||||
assert(toIndex == arguments.size) { "${arguments.size - toIndex} trailing arguments were found in $this type" }
|
||||
assert(toIndex == arguments.size || DescriptorUtils.isLocal(classDescriptor)) {
|
||||
"${arguments.size - toIndex} trailing arguments were found in $this type"
|
||||
}
|
||||
|
||||
return PossiblyInnerType(classDescriptor, argumentsSubList, null)
|
||||
return PossiblyInnerType(classDescriptor, arguments.subList(index, arguments.size), null)
|
||||
}
|
||||
|
||||
val argumentsSubList = arguments.subList(index, toIndex)
|
||||
return PossiblyInnerType(
|
||||
classDescriptor, argumentsSubList,
|
||||
buildPossiblyInnerType(classDescriptor.containingDeclaration as? ClassDescriptor, toIndex))
|
||||
|
||||
Reference in New Issue
Block a user