Fix type parameter list for local classes

Add captured parameters from enclosing functions

 #KT-9584 Fixed
This commit is contained in:
Denis Zharkov
2015-12-21 18:37:03 +03:00
parent 73b961e885
commit 3fd35636ad
14 changed files with 287 additions and 13 deletions
@@ -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
}
@@ -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!!
@@ -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
}
}
@@ -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>() }
}
}
@@ -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
}
@@ -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>() }
}
}
@@ -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
}
@@ -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>() }
}
}
@@ -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
}
@@ -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()
}
@@ -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
@@ -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))