unqualified super: should be able to invoke methods of Any without extra hassle
when Any is an implicit immediate superclass
This commit is contained in:
+3
-2
@@ -392,9 +392,10 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (supertypes.size() > 1) {
|
||||
if (UnqualifiedSuperPackage.isPossiblyAmbiguousUnqualifiedSuper(expression, supertypes)) {
|
||||
Collection<JetType> supertypesResolvedFromContext =
|
||||
UnqualifiedSuperPackage.resolveUnqualifiedSuperFromExpressionContext(expression, supertypes);
|
||||
UnqualifiedSuperPackage.resolveUnqualifiedSuperFromExpressionContext(
|
||||
expression, supertypes, components.builtIns.getAnyType());
|
||||
if (supertypesResolvedFromContext.size() == 1) {
|
||||
JetType singleResolvedType = supertypesResolvedFromContext.iterator().next();
|
||||
result = substitutor.substitute(singleResolvedType, Variance.INVARIANT);
|
||||
|
||||
+97
-28
@@ -17,33 +17,47 @@
|
||||
package org.jetbrains.kotlin.types.expressions.unqualifiedSuper
|
||||
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.MemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.JetCallExpression
|
||||
import org.jetbrains.kotlin.psi.JetDotQualifiedExpression
|
||||
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
|
||||
import org.jetbrains.kotlin.psi.JetSuperExpression
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
|
||||
import java.util.*
|
||||
|
||||
public fun resolveUnqualifiedSuperFromExpressionContext(superExpression: JetSuperExpression, supertypes: Collection<JetType>): Collection<JetType> {
|
||||
val parentElement = superExpression.getParent()
|
||||
|
||||
public fun resolveUnqualifiedSuperFromExpressionContext(
|
||||
superExpression: JetSuperExpression,
|
||||
supertypes: Collection<JetType>,
|
||||
anyType: JetType
|
||||
): Collection<JetType> {
|
||||
val parentElement = superExpression.parent
|
||||
|
||||
if (parentElement is JetDotQualifiedExpression) {
|
||||
val selectorExpression = parentElement.getSelectorExpression()
|
||||
val selectorExpression = parentElement.selectorExpression
|
||||
when (selectorExpression) {
|
||||
is JetCallExpression -> {
|
||||
// super.foo(...): foo can be a function or a property of a callable type
|
||||
val calleeExpression = selectorExpression.getCalleeExpression()
|
||||
val calleeExpression = selectorExpression.calleeExpression
|
||||
if (calleeExpression is JetSimpleNameExpression) {
|
||||
return resolveSupertypesByCalleeName(supertypes, calleeExpression.getReferencedNameAsName())
|
||||
val calleeName = calleeExpression.getReferencedNameAsName()
|
||||
if (isCallingMethodOfAny(selectorExpression, calleeName)) {
|
||||
return resolveSupertypesForMethodOfAny(supertypes, calleeName, anyType)
|
||||
}
|
||||
else {
|
||||
return resolveSupertypesByCalleeName(supertypes, calleeName)
|
||||
}
|
||||
}
|
||||
}
|
||||
is JetSimpleNameExpression -> {
|
||||
// super.x: x can be a property only
|
||||
// NB there are no properties in kotlin.Any
|
||||
return resolveSupertypesByPropertyName(supertypes, selectorExpression.getReferencedNameAsName())
|
||||
}
|
||||
}
|
||||
@@ -52,37 +66,92 @@ public fun resolveUnqualifiedSuperFromExpressionContext(superExpression: JetSupe
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
private fun resolveSupertypesByCalleeName(supertypes: Collection<JetType>, calleeName: Name): Collection<JetType> =
|
||||
resolveSupertypesByMembers(supertypes) { getFunctionMembers(it, calleeName) + getPropertyMembers(it, calleeName) }
|
||||
private val ARITY_OF_METHODS_OF_ANY = hashMapOf("hashCode" to 0, "equals" to 1, "toString" to 0)
|
||||
|
||||
private fun resolveSupertypesByPropertyName(supertypes: Collection<JetType>, propertyName: Name): Collection<JetType> =
|
||||
resolveSupertypesByMembers(supertypes) { getPropertyMembers(it, propertyName) }
|
||||
private fun isCallingMethodOfAny(callExpression: JetCallExpression, calleeName: Name): Boolean =
|
||||
ARITY_OF_METHODS_OF_ANY.getOrElse(calleeName.asString(), { -1 }) == callExpression.valueArguments.size()
|
||||
|
||||
private inline fun resolveSupertypesByMembers(
|
||||
supertypes: Collection<JetType>,
|
||||
getMembers: (JetType) -> Collection<MemberDescriptor>
|
||||
): Collection<JetType> {
|
||||
val withConcreteMembers = SmartList<JetType>()
|
||||
val withAnyMembers = SmartList<JetType>()
|
||||
public fun isPossiblyAmbiguousUnqualifiedSuper(superExpression: JetSuperExpression, supertypes: Collection<JetType>): Boolean =
|
||||
supertypes.size() > 1 ||
|
||||
(supertypes.size() == 1 && supertypes.single().isInterface() && isCallingMethodOfAnyWithSuper(superExpression))
|
||||
|
||||
for (supertype in supertypes) {
|
||||
val members = getMembers(supertype)
|
||||
if (members.isNotEmpty()) {
|
||||
withAnyMembers.add(supertype)
|
||||
if (members any ::isConcreteMember) {
|
||||
withConcreteMembers.add(supertype)
|
||||
private fun isCallingMethodOfAnyWithSuper(superExpression: JetSuperExpression): Boolean {
|
||||
val parent = superExpression.parent
|
||||
if (parent is JetDotQualifiedExpression) {
|
||||
val selectorExpression = parent.selectorExpression
|
||||
if (selectorExpression is JetCallExpression) {
|
||||
val calleeExpression = selectorExpression.calleeExpression
|
||||
if (calleeExpression is JetSimpleNameExpression) {
|
||||
val calleeName = calleeExpression.getReferencedNameAsName()
|
||||
return isCallingMethodOfAny(selectorExpression, calleeName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return if (withConcreteMembers.isNotEmpty()) withConcreteMembers else withAnyMembers
|
||||
return false
|
||||
}
|
||||
|
||||
private fun JetType.isInterface(): Boolean =
|
||||
TypeUtils.getClassDescriptor(this)?.kind == ClassKind.INTERFACE
|
||||
|
||||
private fun resolveSupertypesForMethodOfAny(supertypes: Collection<JetType>, calleeName: Name, anyType: JetType): Collection<JetType> {
|
||||
val typesWithConcreteOverride = resolveSupertypesByMembers(supertypes, false) { getFunctionMembers(it, calleeName) }
|
||||
return if (typesWithConcreteOverride.isNotEmpty())
|
||||
typesWithConcreteOverride
|
||||
else
|
||||
anyType.singletonList()
|
||||
}
|
||||
|
||||
private fun resolveSupertypesByCalleeName(supertypes: Collection<JetType>, calleeName: Name): Collection<JetType> =
|
||||
resolveSupertypesByMembers(supertypes, true) { getFunctionMembers(it, calleeName) + getPropertyMembers(it, calleeName) }
|
||||
|
||||
private fun resolveSupertypesByPropertyName(supertypes: Collection<JetType>, propertyName: Name): Collection<JetType> =
|
||||
resolveSupertypesByMembers(supertypes, true) { getPropertyMembers(it, propertyName) }
|
||||
|
||||
private inline fun resolveSupertypesByMembers(
|
||||
supertypes: Collection<JetType>,
|
||||
allowArbitraryMembers: Boolean,
|
||||
getMembers: (JetType) -> Collection<MemberDescriptor>
|
||||
): Collection<JetType> {
|
||||
val typesWithConcreteMembers = SmartList<JetType>()
|
||||
val typesWithArbitraryMembers = SmartList<JetType>()
|
||||
|
||||
for (supertype in supertypes) {
|
||||
val members = getMembers(supertype)
|
||||
if (members.isNotEmpty()) {
|
||||
typesWithArbitraryMembers.add(supertype)
|
||||
if (members.any { isConcreteMember(supertype, it) }) {
|
||||
typesWithConcreteMembers.add(supertype)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return if (typesWithConcreteMembers.isNotEmpty()) typesWithConcreteMembers
|
||||
else if (allowArbitraryMembers) typesWithArbitraryMembers
|
||||
else emptyList<JetType>()
|
||||
}
|
||||
|
||||
private fun getFunctionMembers(type: JetType, name: Name): Collection<MemberDescriptor> =
|
||||
type.getMemberScope().getFunctions(name)
|
||||
type.memberScope.getFunctions(name)
|
||||
|
||||
private fun getPropertyMembers(type: JetType, name: Name): Collection<MemberDescriptor> =
|
||||
type.getMemberScope().getProperties(name).filterIsInstanceTo(SmartList<MemberDescriptor>())
|
||||
type.memberScope.getProperties(name).filterIsInstanceTo(SmartList<MemberDescriptor>())
|
||||
|
||||
private fun isConcreteMember(memberDescriptor: MemberDescriptor): Boolean =
|
||||
memberDescriptor.getModality() != Modality.ABSTRACT
|
||||
private fun isConcreteMember(supertype: JetType, memberDescriptor: MemberDescriptor): Boolean {
|
||||
// "Concrete member" is a function or a property that is not abstract,
|
||||
// and is not an implicit fake override for a method of Any on an interface.
|
||||
|
||||
if (memberDescriptor.modality == Modality.ABSTRACT)
|
||||
return false
|
||||
|
||||
val classDescriptorForSupertype = TypeUtils.getClassDescriptor(supertype)
|
||||
val memberKind = (memberDescriptor as CallableMemberDescriptor).kind
|
||||
if (classDescriptorForSupertype?.kind == ClassKind.INTERFACE && memberKind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
|
||||
// We have a fake override on interface. It should have a dispatch receiver, which should not be Any.
|
||||
val dispatchReceiverType = memberDescriptor.dispatchReceiverParameter?.type ?: return false
|
||||
val dispatchReceiverClass = TypeUtils.getClassDescriptor(dispatchReceiverType) ?: return false
|
||||
return !KotlinBuiltIns.isAny(dispatchReceiverClass)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
interface ISomething
|
||||
|
||||
open class ClassWithToString {
|
||||
override fun toString(): String = "C"
|
||||
}
|
||||
|
||||
interface IWithToString {
|
||||
override fun toString(): String
|
||||
}
|
||||
|
||||
interface IWithDefaultToString {
|
||||
override fun toString(): String = "I"
|
||||
}
|
||||
|
||||
class C1 : ClassWithToString(), ISomething {
|
||||
override fun toString(): String = super.toString()
|
||||
}
|
||||
|
||||
class C2 : ClassWithToString(), IWithToString, ISomething {
|
||||
override fun toString(): String = super.toString()
|
||||
}
|
||||
|
||||
class C3 : IWithDefaultToString, ISomething {
|
||||
override fun toString(): String = super.toString()
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
return when {
|
||||
C1().toString() != "C" -> "Failed #1"
|
||||
C2().toString() != "C" -> "Failed #2"
|
||||
C3().toString() != "I" -> "Failed #3"
|
||||
else -> "OK"
|
||||
}
|
||||
}
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
interface IA {
|
||||
override fun toString(): String = "IA"
|
||||
}
|
||||
|
||||
interface IB {
|
||||
override fun toString(): String = "IB"
|
||||
}
|
||||
|
||||
class C : IA, IB {
|
||||
override fun toString(): String =
|
||||
<!AMBIGUOUS_SUPER!>super<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>toString<!>()
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
package
|
||||
|
||||
internal final class C : IA, IB {
|
||||
public constructor C()
|
||||
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*2*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
internal interface IA {
|
||||
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*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
internal interface IB {
|
||||
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*/ fun toString(): kotlin.String
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
interface A {
|
||||
override fun toString(): String {
|
||||
return "OK"
|
||||
}
|
||||
}
|
||||
|
||||
interface B : A
|
||||
|
||||
class C : B {
|
||||
override fun toString(): String =
|
||||
super.toString()
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package
|
||||
|
||||
internal interface 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*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
internal interface B : 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
|
||||
}
|
||||
|
||||
internal final class C : B {
|
||||
public constructor C()
|
||||
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*/ fun toString(): kotlin.String
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
interface IWithToString {
|
||||
override fun toString(): String
|
||||
}
|
||||
|
||||
class A : IWithToString {
|
||||
// Should be Any#toString(), even though IWithToString defines an abstract toString.
|
||||
override fun toString(): String = super.toString()
|
||||
}
|
||||
|
||||
interface IWithImplementedToString {
|
||||
override fun toString(): String = "Heh"
|
||||
}
|
||||
|
||||
class B : IWithImplementedToString {
|
||||
override fun toString(): String = super.toString()
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package
|
||||
|
||||
internal final class A : IWithToString {
|
||||
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*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
internal final class B : IWithImplementedToString {
|
||||
public constructor 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*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
internal interface IWithImplementedToString {
|
||||
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*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
internal interface IWithToString {
|
||||
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 abstract override /*1*/ fun toString(): kotlin.String
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
interface IFoo
|
||||
|
||||
interface IBar
|
||||
|
||||
class A : IFoo, IBar {
|
||||
// Unqualified 'super' should be resolved to 'Any'.
|
||||
override fun equals(other: Any?): Boolean = super.equals(other)
|
||||
override fun hashCode(): Int = super.hashCode()
|
||||
override fun toString(): String = super.toString()
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package
|
||||
|
||||
internal final class A : IFoo, IBar {
|
||||
public constructor A()
|
||||
public open override /*2*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*2*/ fun hashCode(): kotlin.Int
|
||||
public open override /*2*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
internal interface IBar {
|
||||
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
|
||||
}
|
||||
|
||||
internal interface IFoo {
|
||||
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
|
||||
}
|
||||
@@ -14584,6 +14584,30 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/thisAndSuper/unqualifiedSuper/unqualifiedSuperWithUnresolvedBase.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("withAmbiguousMethodOfAny.kt")
|
||||
public void testWithAmbiguousMethodOfAny() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/thisAndSuper/unqualifiedSuper/withAmbiguousMethodOfAny.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("withMethodOfAnyImplementedInSuperInterface.kt")
|
||||
public void testWithMethodOfAnyImplementedInSuperInterface() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/thisAndSuper/unqualifiedSuper/withMethodOfAnyImplementedInSuperInterface.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("withMethodOfAnyOverridenInInterface.kt")
|
||||
public void testWithMethodOfAnyOverridenInInterface() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/thisAndSuper/unqualifiedSuper/withMethodOfAnyOverridenInInterface.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("withMethodsOfAny.kt")
|
||||
public void testWithMethodsOfAny() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/thisAndSuper/unqualifiedSuper/withMethodsOfAny.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -7185,6 +7185,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/super/unqualifiedSuperWithLocalClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("unqualifiedSuperWithMethodsOfAny.kt")
|
||||
public void testUnqualifiedSuperWithMethodsOfAny() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/super/unqualifiedSuperWithMethodsOfAny.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/superConstructorCall")
|
||||
|
||||
Reference in New Issue
Block a user