diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt index 1789e7d162b..74bab690f46 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt @@ -25,7 +25,6 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.VariableDescriptorImpl import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors.* -import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* @@ -36,10 +35,12 @@ import org.jetbrains.kotlin.resolve.PossiblyBareType.type import org.jetbrains.kotlin.resolve.bindingContextUtil.recordScope import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallableDescriptors import org.jetbrains.kotlin.resolve.calls.util.createFunctionType +import org.jetbrains.kotlin.resolve.descriptorUtil.findImplicitOuterClassArguments import org.jetbrains.kotlin.resolve.scopes.LazyScopeAdapter import org.jetbrains.kotlin.resolve.scopes.LexicalScope +import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind import org.jetbrains.kotlin.resolve.scopes.MemberScope -import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier +import org.jetbrains.kotlin.resolve.scopes.utils.findFirstFromMeAndParent import org.jetbrains.kotlin.resolve.source.toSourceElement import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.StorageManager @@ -379,7 +380,7 @@ class TypeResolver( return createErrorTypeForTypeConstructor(c, projectionFromAllQualifierParts, typeConstructor) } - val collectedArgumentAsTypeProjections = + val (collectedArgumentAsTypeProjections, argumentsForOuterClass) = collectArgumentsForClassifierTypeConstructor(c, classDescriptor, qualifierResolutionResult.qualifierParts) ?: return createErrorTypeForTypeConstructor(c, projectionFromAllQualifierParts, typeConstructor) @@ -389,7 +390,7 @@ class TypeResolver( } val argumentsFromUserType = resolveTypeProjections(c, typeConstructor, collectedArgumentAsTypeProjections) - val arguments = argumentsFromUserType + appendDefaultArgumentsForInnerScope(argumentsFromUserType.size, parameters) + val arguments = buildFinalArgumentList(argumentsFromUserType, argumentsForOuterClass, parameters) assert(arguments.size == parameters.size) { "Collected arguments count should be equal to parameters count," + @@ -423,6 +424,15 @@ class TypeResolver( return type(resultingType) } + private fun buildFinalArgumentList( + argumentsFromUserType: List, + argumentsForOuterClass: List?, + parameters: List + ): List { + return argumentsFromUserType + + (argumentsForOuterClass ?: appendDefaultArgumentsForLocalClassifier(argumentsFromUserType.size, parameters)) + } + private fun shouldCheckBounds(c: TypeResolutionContext, inType: KotlinType): Boolean { if (!c.checkBounds) return false if (inType.containsTypeAliasParameters()) return false @@ -455,13 +465,13 @@ class TypeResolver( qualifierResolutionResult.qualifierParts.lastOrNull() ?: return createErrorTypeForTypeConstructor(c, projectionFromAllQualifierParts, typeConstructor) - val argumentElementsFromUserType = + val (argumentElementsFromUserType, argumentsForOuterClass) = collectArgumentsForClassifierTypeConstructor(c, descriptor, qualifierResolutionResult.qualifierParts) ?: return createErrorTypeForTypeConstructor(c, projectionFromAllQualifierParts, typeConstructor) val argumentsFromUserType = resolveTypeProjections(c, typeConstructor, argumentElementsFromUserType) - val arguments = argumentsFromUserType + appendDefaultArgumentsForInnerScope(argumentsFromUserType.size, parameters) + val arguments = buildFinalArgumentList(argumentsFromUserType, argumentsForOuterClass, parameters) val reportStrategy = TracingTypeAliasExpansionReportStrategy( c.trace, @@ -562,11 +572,17 @@ class TypeResolver( return firstTypeParameter.original.containingDeclaration is ClassDescriptor } + /** + * @return yet unresolved KtTypeProjection arguments and already resolved ones relevant to an outer class + * @return null if error was reported + * + * If second component is null then rest of the arguments should be appended using default types of relevant parameters + */ private fun collectArgumentsForClassifierTypeConstructor( c: TypeResolutionContext, classifierDescriptor: ClassifierDescriptorWithTypeParameters, qualifierParts: List - ): List? { + ): Pair, List?>? { val classifierDescriptorChain = classifierDescriptor.classifierDescriptorsFromInnerToOuter() val reversedQualifierParts = qualifierParts.asReversed() @@ -615,15 +631,42 @@ class TypeResolver( val parameters = classifierDescriptor.typeConstructor.parameters if (result.size < parameters.size) { - 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, classifierDescriptor)) + val nextParameterOwner = + parameters[result.size].original.containingDeclaration as? ClassDescriptor + // If next parameter is captured from the enclosing function, default arguments must be used + // (see appendDefaultArgumentsForLocalClassifier) + ?: return Pair(result, null) + + val restArguments = c.scope.findImplicitOuterClassArguments(nextParameterOwner) + val restParameters = parameters.subList(result.size, parameters.size) + + val typeArgumentsCanBeSpecifiedCount = + classifierDescriptor.classifierDescriptorsFromInnerToOuter().sumBy { it.declaredTypeParameters.size } + + if (restArguments == null && typeArgumentsCanBeSpecifiedCount > result.size) { + c.trace.report( + WRONG_NUMBER_OF_TYPE_ARGUMENTS.on( + qualifierParts.last().expression, typeArgumentsCanBeSpecifiedCount, classifierDescriptor)) return null } + else if (restArguments == null) { + assert(typeArgumentsCanBeSpecifiedCount == result.size) { + "Number of type arguments that can be specified ($typeArgumentsCanBeSpecifiedCount) " + + "should be equal to actual arguments number ${result.size}, (classifier: $classifierDescriptor)" + } + return Pair(result, null) + } + else { + assert(restParameters.size == restArguments.size) { + "Number of type of restParameters should be equal to ${restArguments.size}, " + + "but ${restArguments.size} were found for $classifierDescriptor/$nextParameterOwner" + } + + return Pair(result, restArguments) + } } - return result + return Pair(result, null) } private fun ClassifierDescriptor?.classifierDescriptorsFromInnerToOuter(): List = @@ -632,42 +675,22 @@ class TypeResolver( { it.containingDeclaration as? ClassifierDescriptorWithTypeParameters } ).toList() - private fun TypeParameterDescriptor.isDeclaredInScope(c: TypeResolutionContext): Boolean { - assert(containingDeclaration is ClassifierDescriptorWithTypeParameters) { - "This function is implemented for ClassifierDescriptorWithTypeParameters only, but $containingDeclaration was given" - } - - // This function checks whether this@TypeParameterDescriptor ()is reachable from current scope by it's name - // The only way it can be is that we are within class that contains it - // We could just walk through containing declarations as it's done on last return, but it can be rather slow, so we at first look into scope. - // Latter can fail if we found some other classifier with same name, but parameter can still be reachable, so - // in case of fail we fall back into slow but exact computation. - - val contributedClassifier = c.scope.findClassifier(name, NoLookupLocation.WHEN_RESOLVING_DEFAULT_TYPE_ARGUMENTS) ?: return false - if (contributedClassifier.typeConstructor == typeConstructor) return true - - return c.scope.ownerDescriptor.isInsideOfClass(original.containingDeclaration as ClassifierDescriptorWithTypeParameters) - } - - private fun DeclarationDescriptor.isInsideOfClass(classifierDescriptor: ClassifierDescriptorWithTypeParameters) - = generateSequence(this, { it.containingDeclaration }).any { it.original == classifierDescriptor } - - private fun resolveTypeProjectionsWithErrorConstructor( c: TypeResolutionContext, argumentElements: List, message: String = "Error type for resolving type projections" ) = resolveTypeProjections(c, ErrorUtils.createErrorTypeConstructor(message), argumentElements) - // In cases like - // class Outer { - // inner class Inner - // val inner: Inner - // } - // - // FQ type of 'inner' val is Outer.Inner (saying strictly it's Outer.Inner), but 'F' is implicitly came from scope - // So we just add it explicitly to make type complete, in a sense of having arguments count equal to parameters one. - private fun appendDefaultArgumentsForInnerScope( + /** + * For cases like: + * fun foo() { + * class Local + * val x: Local <-- resolve this type + * } + * + * type constructor for `Local` captures type parameter E from containing outer function + */ + private fun appendDefaultArgumentsForLocalClassifier( fromIndex: Int, constructorParameters: List ) = constructorParameters.subList(fromIndex, constructorParameters.size).map { @@ -712,6 +735,19 @@ class TypeResolver( } } + private fun LexicalScope.findImplicitOuterClassArguments( + outerClass: ClassDescriptor + ): List? { + val enclosingClass = findFirstFromMeAndParent { scope -> + if (scope is LexicalScope && scope.kind == LexicalScopeKind.CLASS_MEMBER_SCOPE) + scope.ownerDescriptor as ClassDescriptor + else + null + } ?: return null + + return findImplicitOuterClassArguments(enclosingClass, outerClass) + } + fun resolveClass( scope: LexicalScope, userType: KtUserType, trace: BindingTrace, isDebuggerContext: Boolean ): ClassifierDescriptor? = resolveDescriptorForType(scope, userType, trace, isDebuggerContext).classifierDescriptor diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromCompanionObject.kt b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromCompanionObject.kt new file mode 100644 index 00000000000..e8868755fee --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromCompanionObject.kt @@ -0,0 +1,21 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE +open class Outer { + inner class Inner + +} + +class Derived : Outer() { + // Inner here means Outer.Inner + fun foo(x: Inner) {} +} + +class A { + companion object : Outer() + + // Does not work, could be Outer.Inner + // TODO: Should work? + fun foo(x: Inner) { + // Inner() call use companion as implicit receiver + val y: Outer.Inner = Inner() + } +} diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromCompanionObject.txt b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromCompanionObject.txt new file mode 100644 index 00000000000..dd4e61a1fae --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromCompanionObject.txt @@ -0,0 +1,38 @@ +package + +public final class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(/*0*/ x: [ERROR : Inner]): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public companion object Companion : Outer { + private constructor Companion() + 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 Derived : Outer { + public constructor Derived() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(/*0*/ x: Outer.Inner): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open 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 + + public final inner class Inner /*captured type parameters: /*1*/ E*/ { + public constructor Inner() + 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 + } +} diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromOuterClassInObjectLiteral.kt b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromOuterClassInObjectLiteral.kt new file mode 100644 index 00000000000..22600b275a6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromOuterClassInObjectLiteral.kt @@ -0,0 +1,15 @@ +class A { + fun foo() { + val q = object { + open inner class B + inner class C : B() + + // No WRONG_NUMBER_OF_TYPE_ARGUMENTS should be reported on these types + val x: B = B() + val y: C = C() + } + + q.x + q.y + } +} diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromOuterClassInObjectLiteral.txt b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromOuterClassInObjectLiteral.txt new file mode 100644 index 00000000000..293544b3986 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromOuterClassInObjectLiteral.txt @@ -0,0 +1,9 @@ +package + +public final class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(): kotlin.Unit + 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/innerClasses/implicitArguments/fromSuperClasses.kt b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClasses.kt new file mode 100644 index 00000000000..e18c775114d --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClasses.kt @@ -0,0 +1,25 @@ +// !CHECK_TYPE +open class Outer { + inner class Inner + typealias Alias = Map +} + +class Derived : Outer() { + fun foo(): Inner = null!! + fun baz(): Alias = null!! +} + + +class A : Outer() { + class B : Outer() { + fun bar(): Inner = null!! + fun x(): Alias = null!! + } +} + +fun foo() { + Derived().foo() checkType { _.Inner>() } + Derived().baz() checkType { _>() } + A.B().bar() checkType { _.Inner>() } + A.B().x() checkType { _>() } +} diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClasses.txt b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClasses.txt new file mode 100644 index 00000000000..5b81fecbd57 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClasses.txt @@ -0,0 +1,43 @@ +package + +public fun foo(): kotlin.Unit + +public final class A : Outer { + public constructor A() + 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 B : Outer { + public constructor B() + public final fun bar(): Outer.Inner + 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 fun x(): Outer.Alias /* = kotlin.collections.Map */ + } +} + +public final class Derived : Outer { + public constructor Derived() + public final fun baz(): Outer.Alias /* = kotlin.collections.Map */ + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(): Outer.Inner + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open 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 + + public final inner class Inner /*captured type parameters: /*1*/ X, /*2*/ Y*/ { + public constructor Inner() + 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 Alias /*captured type parameters: /*1*/ X, /*2*/ Y*/ = kotlin.collections.Map +} diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesLocal.kt b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesLocal.kt new file mode 100644 index 00000000000..842ef7c9f31 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesLocal.kt @@ -0,0 +1,54 @@ +// !CHECK_TYPE +// !DIAGNOSTICS: -UNUSED_VALUE -VARIABLE_WITH_REDUNDANT_INITIALIZER + +class A + +private fun foobar() = { + open class LocalOuter { + inner class LocalInner { + fun a() = A() + } + + typealias LocalAlias = A + } + + class Derived : LocalOuter() { + fun foo(): LocalInner = null!! + fun bar(): LocalAlias = null!! + } + + Derived() +} + +private fun noParameters() = { + open class LocalOuter2 { + inner class LocalInner2 { + fun a() = A() + } + + typealias LocalAlias2 = A + } + + class Derived2 : LocalOuter2() { + fun foo(): LocalInner2 = null!! + fun bar(): LocalAlias2 = null!! + } + + Derived2() +} + +fun test() { + var x = foobar() + x = foobar() + + x().foo().a() checkType { _>() } + x().bar() checkType { _>() } + + x = foobar() + + var y = noParameters() + y = noParameters() + + y().foo().a() checkType { _>() } + y().bar() checkType { _>() } +} diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesLocal.txt b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesLocal.txt new file mode 100644 index 00000000000..a6f9fd28648 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesLocal.txt @@ -0,0 +1,12 @@ +package + +private fun foobar(): () -> foobar..Derived +private fun noParameters(): () -> noParameters..Derived2 +public fun test(): kotlin.Unit + +public final class A { + public constructor A() + 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 +} diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesLocalInsideInner.kt b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesLocalInsideInner.kt new file mode 100644 index 00000000000..567138ce181 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesLocalInsideInner.kt @@ -0,0 +1,58 @@ +// !CHECK_TYPE +// !DIAGNOSTICS: -UNUSED_VALUE -VARIABLE_WITH_REDUNDANT_INITIALIZER + +class A + +class Outer { + inner class Inner { + private fun foobar() = { + open class LocalOuter { + inner class LocalInner { + fun a() = A() + } + + typealias LocalAlias = A + } + + class Derived : LocalOuter() { + fun foo(): LocalInner = null!! + fun bar(): LocalAlias = null!! + } + + Derived() + } + + private fun noParameters() = { + open class LocalOuter2 { + inner class LocalInner2 { + fun a() = A() + } + + typealias LocalAlias2 = A + } + + class Derived2 : LocalOuter2() { + fun foo(): LocalInner2 = null!! + fun bar(): LocalAlias2 = null!! + } + Derived2() + } + + fun test(z: Outer.Inner) { + var x = foobar() + x = foobar() + + x().foo().a() checkType { _>() } + x().bar() checkType { _>() } + + x = foobar() + x = z.foobar() + + var y = noParameters() + y = noParameters() + + y().foo().a() checkType { _>() } + y().bar() checkType { _>() } + } + } +} diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesLocalInsideInner.txt b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesLocalInsideInner.txt new file mode 100644 index 00000000000..97e88386989 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesLocalInsideInner.txt @@ -0,0 +1,25 @@ +package + +public final class A { + public constructor A() + 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 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 + + public final inner class Inner /*captured type parameters: /*1*/ T*/ { + public constructor Inner() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + private final fun foobar(): () -> Outer.Inner.foobar..Derived + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + private final fun noParameters(): () -> Outer.Inner.noParameters..Derived2 + public final fun test(/*0*/ z: Outer.Inner): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesTransitive.kt b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesTransitive.kt new file mode 100644 index 00000000000..999d7b35805 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesTransitive.kt @@ -0,0 +1,18 @@ +// !CHECK_TYPE +open class Outer { + inner class Inner + typealias Alias = Map +} + +open class BaseDerived1 : Outer() +open class BaseDerived2 : BaseDerived1() + +class Derived : BaseDerived2() { + fun foo(): Inner = null!! + fun baz(): Alias = null!! +} + +fun foo() { + Derived().foo() checkType { _.Inner>() } + Derived().baz() checkType { _>() } +} diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesTransitive.txt b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesTransitive.txt new file mode 100644 index 00000000000..91780e3ab8d --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesTransitive.txt @@ -0,0 +1,41 @@ +package + +public fun foo(): kotlin.Unit + +public open class BaseDerived1 : Outer { + public constructor BaseDerived1() + 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 open class BaseDerived2 : BaseDerived1 { + public constructor BaseDerived2() + 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 Derived : BaseDerived2 { + public constructor Derived() + public final fun baz(): Outer.Alias /* = kotlin.collections.Map */ + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(): Outer.Inner + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open 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 + + public final inner class Inner /*captured type parameters: /*1*/ X, /*2*/ Y*/ { + public constructor Inner() + 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 Alias /*captured type parameters: /*1*/ X, /*2*/ Y*/ = kotlin.collections.Map +} diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/inStaticScope.kt b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/inStaticScope.kt new file mode 100644 index 00000000000..0fcae058ddf --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/inStaticScope.kt @@ -0,0 +1,7 @@ +interface Inv +class Outer { + inner class Inner + + class Nested : Inv<Inner> + object Obj : Inv<Inner> +} diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/inStaticScope.txt b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/inStaticScope.txt new file mode 100644 index 00000000000..7c8c334c59f --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/inStaticScope.txt @@ -0,0 +1,35 @@ +package + +public interface Inv { + 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 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 + + public final inner class Inner /*captured type parameters: /*0*/ E*/ { + public constructor Inner() + 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 Nested : Inv<[ERROR : Inner]> { + public constructor Nested() + 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 object Obj : Inv<[ERROR : Inner]> { + private constructor Obj() + 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 + } +} diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/secondLevelDepth.kt b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/secondLevelDepth.kt new file mode 100644 index 00000000000..0f232098c81 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/secondLevelDepth.kt @@ -0,0 +1,18 @@ +// !CHECK_TYPE +open class Outer { + inner open class Inner { + inner class Inner2 { + + } + } +} + +class DerivedOuter : Outer() { + inner class DerivedInner : Inner() { + fun foo(): Inner2 = null!! + } +} + +fun foo() { + DerivedOuter().DerivedInner().foo() checkType { _.Inner.Inner2>() } +} diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/secondLevelDepth.txt b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/secondLevelDepth.txt new file mode 100644 index 00000000000..72c51c08653 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/secondLevelDepth.txt @@ -0,0 +1,39 @@ +package + +public fun foo(): kotlin.Unit + +public final class DerivedOuter : Outer { + public constructor DerivedOuter() + 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 inner class DerivedInner : Outer.Inner { + public constructor DerivedInner() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(): Outer.Inner.Inner2 + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public open 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 + + public open inner class Inner /*captured type parameters: /*1*/ E*/ { + public constructor Inner() + 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 inner class Inner2 /*captured type parameters: /*1*/ F, /*2*/ E*/ { + public constructor Inner2() + 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 + } + } +} diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/importedInner.kt b/compiler/testData/diagnostics/tests/generics/innerClasses/importedInner.kt index 37f695783e2..30c88512308 100644 --- a/compiler/testData/diagnostics/tests/generics/innerClasses/importedInner.kt +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/importedInner.kt @@ -9,8 +9,12 @@ class Outer { class E val x: Inner = Inner() } + + class Nested { + fun bar(x: Inner) {} + } } class E -fun bar(x: Inner) {} \ No newline at end of file +fun bar(x: Inner) {} diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/importedInner.txt b/compiler/testData/diagnostics/tests/generics/innerClasses/importedInner.txt index 47977b9df52..6f86f9c92e0 100644 --- a/compiler/testData/diagnostics/tests/generics/innerClasses/importedInner.txt +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/importedInner.txt @@ -22,4 +22,12 @@ public final class Outer { public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } + + public final class Nested { + public constructor Nested() + public final fun bar(/*0*/ x: [ERROR : Inner]): kotlin.Unit + 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 + } } diff --git a/compiler/testData/diagnostics/tests/generics/kt5508.kt b/compiler/testData/diagnostics/tests/generics/kt5508.kt index ff91db9e69b..f1bbe71ad06 100644 --- a/compiler/testData/diagnostics/tests/generics/kt5508.kt +++ b/compiler/testData/diagnostics/tests/generics/kt5508.kt @@ -1,14 +1,23 @@ // KT-5508 Stackoverflow in type substitution abstract class A { - public abstract fun Foo(x: T) + public abstract fun foo(x: T) + public abstract fun bar(x: T) public inner abstract class B : A>() { - public inner class C : B>() + public inner class C : B>() { - override fun Foo(x: B>) { + // Here B> means A.B>.B.B.C>>.B.B>.B.B.C>.C> + // while for being a correct override it should be A.B>.B.B.C> + // It happens because at the beginning we search implicit arguments for an outer classes through supertypes + // See TypeResolver.computeImplicitOuterClassArguments for clarifications + override fun foo(x: B>) { + throw UnsupportedOperationException() + } + + override fun bar(x: A.B>.B.B.C>) { throw UnsupportedOperationException() } } } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/generics/kt5508.txt b/compiler/testData/diagnostics/tests/generics/kt5508.txt index 06a9a868cbd..a91bed43623 100644 --- a/compiler/testData/diagnostics/tests/generics/kt5508.txt +++ b/compiler/testData/diagnostics/tests/generics/kt5508.txt @@ -2,22 +2,26 @@ package public abstract class A { public constructor A() - public abstract fun Foo(/*0*/ x: T): kotlin.Unit + public abstract fun bar(/*0*/ x: T): kotlin.Unit public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract fun foo(/*0*/ x: T): kotlin.Unit public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String public abstract inner class B /*captured type parameters: /*1*/ T*/ : A.B> { public constructor B() - public abstract override /*1*/ /*fake_override*/ fun Foo(/*0*/ x: A.B): kotlin.Unit + public abstract override /*1*/ /*fake_override*/ fun bar(/*0*/ x: A.B): kotlin.Unit public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun foo(/*0*/ x: A.B): kotlin.Unit public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String - public final inner class C /*captured type parameters: /*1*/ S, /*2*/ T*/ : A.B.B.C> { + public final inner class C /*captured type parameters: /*1*/ S, /*2*/ T*/ : A.B>.B.B.C> { public constructor C() - public open override /*1*/ fun Foo(/*0*/ x: A.B.B.C>): kotlin.Unit + public open override /*1*/ fun bar(/*0*/ x: A.B>.B.B.C>): kotlin.Unit public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun foo(/*0*/ x: A.B>.B.B.C>>.B.B>.B.B.C>.C>): kotlin.Unit + public abstract override /*1*/ /*fake_override*/ fun foo(/*0*/ x: A.B>.B.B.C>): kotlin.Unit 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/typealias/inheritedNestedTypeAlias.kt b/compiler/testData/diagnostics/tests/typealias/inheritedNestedTypeAlias.kt index e0f1aae317d..2aec76fb48d 100644 --- a/compiler/testData/diagnostics/tests/typealias/inheritedNestedTypeAlias.kt +++ b/compiler/testData/diagnostics/tests/typealias/inheritedNestedTypeAlias.kt @@ -10,11 +10,9 @@ open class Base { } class Derived : Base() { - // TODO KT-11123 - - val x1: InnerCell = InnerCell(42) + val x1: InnerCell = InnerCell(42) val x2: Base.InnerCell = InnerCell(42) - val test1: CT = Cell(42) + val test1: CT = Cell(42) val test2: Base.CT = Cell(42) } diff --git a/compiler/testData/diagnostics/tests/typealias/inheritedNestedTypeAlias.txt b/compiler/testData/diagnostics/tests/typealias/inheritedNestedTypeAlias.txt index 045d9d58f65..781818d1db2 100644 --- a/compiler/testData/diagnostics/tests/typealias/inheritedNestedTypeAlias.txt +++ b/compiler/testData/diagnostics/tests/typealias/inheritedNestedTypeAlias.txt @@ -26,9 +26,9 @@ public final class Cell : ICell { public final class Derived : Base { public constructor Derived() - public final val test1: [ERROR : CT] + public final val test1: Base.CT /* = Cell */ public final val test2: Base.CT /* = Cell */ - public final val x1: [ERROR : InnerCell] + public final val x1: Base.InnerCell public final val x2: Base.InnerCell public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 6bd1aa925cc..e9c89b7e161 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -7981,6 +7981,63 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/innerClasses/substitutedMemberScope.kt"); doTest(fileName); } + + @TestMetadata("compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ImplicitArguments extends AbstractDiagnosticsTest { + public void testAllFilesPresentInImplicitArguments() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("fromCompanionObject.kt") + public void testFromCompanionObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromCompanionObject.kt"); + doTest(fileName); + } + + @TestMetadata("fromOuterClassInObjectLiteral.kt") + public void testFromOuterClassInObjectLiteral() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromOuterClassInObjectLiteral.kt"); + doTest(fileName); + } + + @TestMetadata("fromSuperClasses.kt") + public void testFromSuperClasses() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClasses.kt"); + doTest(fileName); + } + + @TestMetadata("fromSuperClassesLocal.kt") + public void testFromSuperClassesLocal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesLocal.kt"); + doTest(fileName); + } + + @TestMetadata("fromSuperClassesLocalInsideInner.kt") + public void testFromSuperClassesLocalInsideInner() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesLocalInsideInner.kt"); + doTest(fileName); + } + + @TestMetadata("fromSuperClassesTransitive.kt") + public void testFromSuperClassesTransitive() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/fromSuperClassesTransitive.kt"); + doTest(fileName); + } + + @TestMetadata("inStaticScope.kt") + public void testInStaticScope() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/inStaticScope.kt"); + doTest(fileName); + } + + @TestMetadata("secondLevelDepth.kt") + public void testSecondLevelDepth() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/innerClasses/implicitArguments/secondLevelDepth.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/diagnostics/tests/generics/multipleBoundsMemberScope") diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt index c6a6574702a..ff07937c713 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt @@ -29,6 +29,10 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.constants.EnumValue import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeProjection +import org.jetbrains.kotlin.types.TypeSubstitutor +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny import org.jetbrains.kotlin.utils.DFS import org.jetbrains.kotlin.utils.addToStdlib.check @@ -264,3 +268,54 @@ fun getConstructorByParamsMap(classDescriptor: ClassDescriptor, params: List.Inner` + * However, it's not necessary inside Outer's members, only the last one should be specified there. + * So this function return a list of arguments that should be used if relevant arguments weren't specified explicitly inside the [scopeOwner]. + * + * Examples: + * for `Outer` class the map will contain: Outer -> (X, Y) (i.e. defaultType mapping) + * for `Derived` class the map will contain: Derived -> (E), Outer -> (E, String) + * for `A.B` class the map will contain: B -> (), Outer -> (Int, CharSequence), A -> () + * + * open class Outer { + * inner class Inner + * } + * + * class Derived : Outer() + * + * class A : Outer() { + * inner class B : Outer() + * } + */ +fun findImplicitOuterClassArguments(scopeOwner: ClassDescriptor, outerClass: ClassDescriptor): List? { + for (current in scopeOwner.classesFromInnerToOuter()) { + for (supertype in current.getAllSuperClassesTypesIncludeItself()) { + val classDescriptor = supertype.constructor.declarationDescriptor as ClassDescriptor + if (classDescriptor == outerClass) return supertype.arguments + } + } + + return null +} + +private fun ClassDescriptor.classesFromInnerToOuter() = generateSequence(this) { + if (it.isInner) + it.containingDeclaration.original as? ClassDescriptor + else + null +} + +private fun ClassDescriptor.getAllSuperClassesTypesIncludeItself(): List { + val result = arrayListOf() + var current: KotlinType = defaultType + + while (!current.isAnyOrNullableAny()) { + result.add(current) + val next = DescriptorUtils.getSuperClassType(current.constructor.declarationDescriptor as ClassDescriptor) + current = TypeSubstitutor.create(current).substitute(next, Variance.INVARIANT) ?: break + } + + return result +}