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:
Dmitry Petrov
2015-08-06 14:44:04 +03:00
parent 3b03cfaf4f
commit b59bf6227f
13 changed files with 301 additions and 30 deletions
@@ -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);
@@ -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"
}
}
@@ -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<!>()
}
@@ -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
}
@@ -0,0 +1,12 @@
interface A {
override fun toString(): String {
return "OK"
}
}
interface B : A
class C : B {
override fun toString(): String =
super.toString()
}
@@ -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
}
@@ -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()
}
@@ -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
}
@@ -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()
}
@@ -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);
}
}
}
@@ -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")