diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt index 2ac29920c94..cc1321d5ccb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt @@ -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 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 } diff --git a/compiler/testData/diagnostics/tests/generics/capturedParameters/innerLocalClass.kt b/compiler/testData/diagnostics/tests/generics/capturedParameters/innerLocalClass.kt new file mode 100644 index 00000000000..52b4443fdb8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/capturedParameters/innerLocalClass.kt @@ -0,0 +1,33 @@ +// !CHECK_TYPE +private class Outer { + private inner class Inner { + private fun foo() = { + fun baz() = { + class Local { + val e: E = magic() + val f: F = magic() + val g: G = magic() + } + Local() + } + baz()() + } + + private var doubleCharSequenceInt = Outer().Inner().foo()() + private var doubleStringNumber = Outer().Inner().foo()() + private var doubleStringInt = Outer().Inner().foo()() + + private fun bar() { + doubleCharSequenceInt = doubleStringNumber + doubleCharSequenceInt = doubleStringInt + + doubleStringInt = Outer().Inner().foo()() + + doubleStringInt.e.checkType { _() } + doubleStringInt.f.checkType { _() } + doubleStringInt.g.checkType { _() } + } + } +} + +fun magic(): T = null!! diff --git a/compiler/testData/diagnostics/tests/generics/capturedParameters/innerLocalClass.txt b/compiler/testData/diagnostics/tests/generics/capturedParameters/innerLocalClass.txt new file mode 100644 index 00000000000..420a908ca0f --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/capturedParameters/innerLocalClass.txt @@ -0,0 +1,22 @@ +package + +public fun magic(): T + +private final class Outer { + public constructor Outer() + 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 /*captured type parameters: /*1*/ E*/ { + public constructor Inner() + private final var doubleCharSequenceInt: Outer.Inner.foo..baz..Local + private final var doubleStringInt: Outer.Inner.foo..baz..Local + private final var doubleStringNumber: Outer.Inner.foo..baz..Local + private final fun bar(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + private final fun foo(): () -> Outer.Inner.foo..baz..Local + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/testData/diagnostics/tests/generics/capturedParameters/localClass.kt b/compiler/testData/diagnostics/tests/generics/capturedParameters/localClass.kt new file mode 100644 index 00000000000..71aa61d27c7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/capturedParameters/localClass.kt @@ -0,0 +1,24 @@ +// !CHECK_TYPE + +fun magic(): T = null!! + +class Q { + private fun foo() = { + class C { + val prop: E = magic() + } + C() + } + + private var x = foo()() + private var y = foo()() + + fun bar() { + x = y + x = foo()() + y = foo()() + + x.prop.checkType { _() } + y.prop.checkType { _() } + } +} diff --git a/compiler/testData/diagnostics/tests/generics/capturedParameters/localClass.txt b/compiler/testData/diagnostics/tests/generics/capturedParameters/localClass.txt new file mode 100644 index 00000000000..19c4a5bb2a2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/capturedParameters/localClass.txt @@ -0,0 +1,14 @@ +package + +public fun magic(): T + +public final class Q { + public constructor Q() + private final var x: Q.foo..C + private final var y: Q.foo..C + public final fun bar(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + private final fun foo(): () -> Q.foo..C + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/generics/capturedParameters/localWithTypeParameter.kt b/compiler/testData/diagnostics/tests/generics/capturedParameters/localWithTypeParameter.kt new file mode 100644 index 00000000000..a7ff90eb7b9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/capturedParameters/localWithTypeParameter.kt @@ -0,0 +1,22 @@ +// !CHECK_TYPE + +fun magic(): T = null!! + +class Q { + private fun foo() = { + class C { + val e: E = magic() + val f: F = magic() + val g: G = magic() + } + C() + } + + private var x = foo()() + + fun bar() { + x.e.checkType { _() } + x.f.checkType { _() } + x.g.checkType { _() } + } +} diff --git a/compiler/testData/diagnostics/tests/generics/capturedParameters/localWithTypeParameter.txt b/compiler/testData/diagnostics/tests/generics/capturedParameters/localWithTypeParameter.txt new file mode 100644 index 00000000000..a8123b81aa1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/capturedParameters/localWithTypeParameter.txt @@ -0,0 +1,13 @@ +package + +public fun magic(): T + +public final class Q { + public constructor Q() + private final var x: Q.foo..C + public final fun bar(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + private final fun foo(): () -> Q.foo..C + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/generics/capturedParameters/objectLiteral.kt b/compiler/testData/diagnostics/tests/generics/capturedParameters/objectLiteral.kt new file mode 100644 index 00000000000..5da8b8f602f --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/capturedParameters/objectLiteral.kt @@ -0,0 +1,22 @@ +// !CHECK_TYPE + +fun magic(): T = null!! + +class Q { + private fun foo() = + object { + val prop: E = magic() + } + + private var x = foo() + private var y = foo() + + fun bar() { + x = y + x = foo() + y = foo() + + x.prop.checkType { _() } + y.prop.checkType { _() } + } +} diff --git a/compiler/testData/diagnostics/tests/generics/capturedParameters/objectLiteral.txt b/compiler/testData/diagnostics/tests/generics/capturedParameters/objectLiteral.txt new file mode 100644 index 00000000000..fc221827e71 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/capturedParameters/objectLiteral.txt @@ -0,0 +1,14 @@ +package + +public fun magic(): T + +public final class Q { + public constructor Q() + private final var x: Q.foo. + private final var y: Q.foo. + public final fun bar(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + private final fun foo(): Q.foo. + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/generics/capturedParameters/uncheckedCast.kt b/compiler/testData/diagnostics/tests/generics/capturedParameters/uncheckedCast.kt new file mode 100644 index 00000000000..dea38f0a6b3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/capturedParameters/uncheckedCast.kt @@ -0,0 +1,37 @@ +fun foo(x: Any, y: Any) : Any { + class C + // without E? + if(x is C) { + return x + } + + if (1 == 2) { + x as C + } + + if (2 == 3) { + x as? C + } + + class Outer { + inner class Inner + } + + // bare type + if (y is Outer.Inner) { + return y + } + + y as Outer<*>.Inner + + return C() +} + +fun noTypeParameters(x: Any) : Any { + class C + if(x is C) { + return x + } + + return C() +} diff --git a/compiler/testData/diagnostics/tests/generics/capturedParameters/uncheckedCast.txt b/compiler/testData/diagnostics/tests/generics/capturedParameters/uncheckedCast.txt new file mode 100644 index 00000000000..d8c15c8fd6a --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/capturedParameters/uncheckedCast.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ x: kotlin.Any, /*1*/ y: kotlin.Any): kotlin.Any +public fun noTypeParameters(/*0*/ x: kotlin.Any): kotlin.Any diff --git a/compiler/testData/diagnostics/tests/inference/substitutions/substitutionIntoAnonymousClass.txt b/compiler/testData/diagnostics/tests/inference/substitutions/substitutionIntoAnonymousClass.txt index 3d89067fba2..762b3151846 100644 --- a/compiler/testData/diagnostics/tests/inference/substitutions/substitutionIntoAnonymousClass.txt +++ b/compiler/testData/diagnostics/tests/inference/substitutions/substitutionIntoAnonymousClass.txt @@ -2,7 +2,7 @@ package public final class Foo { public constructor Foo() - private final fun bar(): Foo.bar. + private final fun bar(): Foo.bar. 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 T.self(): Test.self. + private final fun T.self(): Test.self. } diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index cd93ce42f0b..a59dac91724 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -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) diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/typeParameterUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/typeParameterUtils.kt index bc5d6c920fc..35a05bb5fbb 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/typeParameterUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/typeParameterUtils.kt @@ -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 { 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()?.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))