Support type aliases as bare types after 'is/as'.

This commit is contained in:
Dmitry Petrov
2016-11-10 14:52:41 +03:00
parent 21f2febf82
commit 6876f0e4d2
8 changed files with 139 additions and 1 deletions
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.codeFragmentUtil.debugTypeInfo
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
import org.jetbrains.kotlin.psi.debugText.getDebugText
import org.jetbrains.kotlin.resolve.PossiblyBareType.bare
import org.jetbrains.kotlin.resolve.PossiblyBareType.type
import org.jetbrains.kotlin.resolve.bindingContextUtil.recordScope
import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallableDescriptors
@@ -467,6 +468,13 @@ class TypeResolver(
val parameters = typeConstructor.parameters
if (c.allowBareTypes && projectionFromAllQualifierParts.isEmpty() && isPossibleToSpecifyTypeArgumentsFor(descriptor)) {
val classDescriptor = descriptor.classDescriptor
if (classDescriptor != null && canBeUsedAsBareType(descriptor)) {
return bare(descriptor.classDescriptor!!.typeConstructor, TypeUtils.isNullableType(descriptor.expandedType))
}
}
val typeAliasQualifierPart =
qualifierResolutionResult.qualifierParts.lastOrNull()
?: return createErrorTypeForTypeConstructor(c, projectionFromAllQualifierParts, typeConstructor)
@@ -502,6 +510,44 @@ class TypeResolver(
}
}
/**
* Type alias can be used as bare type (after is/as, e.g., 'x is List')
* iff all type arguments of the corresponding expanded type are either star projections
* or type parameters of the given type alias in invariant projection,
* and each of the type parameters is mentioned no more than once.
*
* E.g.:
* ```
* typealias HashMap<K, V> = java.util.HashMap<K, V> // can be used as bare type
* typealias MyList<T, X> = List<X> // can be used as bare type
* typealias StarMap<T> = Map<T, *> // can be used as bare type
* typealias MyMap<T> = Map<T, T> // CAN NOT be used as bare type: type parameter 'T' is used twice
* typealias StringMap<T> = Map<String, T> // CAN NOT be used as bare type: type argument 'String' is not a type parameter
* ```
*/
private fun canBeUsedAsBareType(descriptor: TypeAliasDescriptor): Boolean {
val expandedType = descriptor.expandedType
if (expandedType.isError) return false
val classDescriptor = descriptor.classDescriptor ?: return false
if (!isPossibleToSpecifyTypeArgumentsFor(classDescriptor)) return false
val usedTypeParameters = linkedSetOf<TypeParameterDescriptor>()
for (argument in expandedType.arguments) {
if (argument.isStarProjection) continue
if (argument.projectionKind != INVARIANT) return false
val argumentTypeDescriptor = argument.type.constructor.declarationDescriptor as? TypeParameterDescriptor ?: return false
if (argumentTypeDescriptor.containingDeclaration != descriptor) return false
if (usedTypeParameters.contains(argumentTypeDescriptor)) return false
usedTypeParameters.add(argumentTypeDescriptor)
}
return true
}
private class TracingTypeAliasExpansionReportStrategy(
val trace: BindingTrace,
val type: KtElement?,
@@ -578,7 +624,7 @@ class TypeResolver(
// First parameter relates to the innermost declaration
// If it's declared in function there
val firstTypeParameter = classifierDescriptor.typeConstructor.parameters.firstOrNull() ?: return false
return firstTypeParameter.original.containingDeclaration is ClassDescriptor
return firstTypeParameter.original.containingDeclaration is ClassifierDescriptorWithTypeParameters
}
/**
@@ -0,0 +1,11 @@
// WITH_RUNTIME
typealias L<T> = List<T>
fun box(): String {
val test: Collection<Int> = listOf(1, 2, 3)
if (test !is L) return "test !is L"
val test2 = test as L
if (test.toList() != test2) return "test.toList() != test2"
return "OK"
}
@@ -0,0 +1,33 @@
typealias L<T> = List<T>
typealias NL<T> = List<T>?
typealias LStar<<!UNUSED_TYPEALIAS_PARAMETER!>T<!>> = List<*>
typealias MyList<<!UNUSED_TYPEALIAS_PARAMETER!>T<!>, X> = List<X>
fun testL1(x: Collection<Any>) = x is L
fun testL2(x: Collection<Int>): List<Int> = x as L
fun testL3(x: Collection<Int>?): List<Int>? = x as L?
fun testL4(x: Collection<Int>?): List<Int>? = x as? L
fun testNL1(x: Collection<Int>?): Boolean = x is NL
fun testNL2(x: Collection<Int>?): List<Int>? = x as NL
fun testNL3(x: Collection<Int>?): List<Int>? = x as NL<!REDUNDANT_NULLABLE!>?<!>
fun testLStar(x: Collection<Int>): List<Int> = x as LStar
fun testMyList(x: Collection<Int>): List<Int> = x as MyList
typealias MMTT<T> = MutableMap<T, T>
typealias Dictionary<T> = MutableMap<String, T>
typealias WriteableMap<K, V> = MutableMap<in K, V>
typealias ReadableList<T> = MutableList<out T>
fun testWrong1(x: Map<Any, Any>) = x is <!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>MMTT<!>
fun testWrong2(x: Map<Any, Any>) = x is <!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>Dictionary<!>
fun testWrong3(x: Map<Any, Any>) = x is <!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>WriteableMap<!>
fun testWrong4(x: List<Any>) = x is <!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>ReadableList<!>
fun <T> testLocal(x: Any) {
class C
typealias CA = C
if (x is <!CANNOT_CHECK_FOR_ERASED!>C<!>) {}
if (x is <!CANNOT_CHECK_FOR_ERASED!>CA<!>) {}
}
@@ -0,0 +1,24 @@
package
public fun testL1(/*0*/ x: kotlin.collections.Collection<kotlin.Any>): kotlin.Boolean
public fun testL2(/*0*/ x: kotlin.collections.Collection<kotlin.Int>): kotlin.collections.List<kotlin.Int>
public fun testL3(/*0*/ x: kotlin.collections.Collection<kotlin.Int>?): kotlin.collections.List<kotlin.Int>?
public fun testL4(/*0*/ x: kotlin.collections.Collection<kotlin.Int>?): kotlin.collections.List<kotlin.Int>?
public fun testLStar(/*0*/ x: kotlin.collections.Collection<kotlin.Int>): kotlin.collections.List<kotlin.Int>
public fun </*0*/ T> testLocal(/*0*/ x: kotlin.Any): kotlin.Unit
public fun testMyList(/*0*/ x: kotlin.collections.Collection<kotlin.Int>): kotlin.collections.List<kotlin.Int>
public fun testNL1(/*0*/ x: kotlin.collections.Collection<kotlin.Int>?): kotlin.Boolean
public fun testNL2(/*0*/ x: kotlin.collections.Collection<kotlin.Int>?): kotlin.collections.List<kotlin.Int>?
public fun testNL3(/*0*/ x: kotlin.collections.Collection<kotlin.Int>?): kotlin.collections.List<kotlin.Int>?
public fun testWrong1(/*0*/ x: kotlin.collections.Map<kotlin.Any, kotlin.Any>): kotlin.Boolean
public fun testWrong2(/*0*/ x: kotlin.collections.Map<kotlin.Any, kotlin.Any>): kotlin.Boolean
public fun testWrong3(/*0*/ x: kotlin.collections.Map<kotlin.Any, kotlin.Any>): kotlin.Boolean
public fun testWrong4(/*0*/ x: kotlin.collections.List<kotlin.Any>): kotlin.Boolean
public typealias Dictionary</*0*/ T> = kotlin.collections.MutableMap<kotlin.String, T>
public typealias L</*0*/ T> = kotlin.collections.List<T>
public typealias LStar</*0*/ T> = kotlin.collections.List<*>
public typealias MMTT</*0*/ T> = kotlin.collections.MutableMap<T, T>
public typealias MyList</*0*/ T, /*1*/ X> = kotlin.collections.List<X>
public typealias NL</*0*/ T> = kotlin.collections.List<T>?
public typealias ReadableList</*0*/ T> = kotlin.collections.MutableList<out T>
public typealias WriteableMap</*0*/ K, /*1*/ V> = kotlin.collections.MutableMap<in K, V>
@@ -16049,6 +16049,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("typeAliasAsBareType.kt")
public void testTypeAliasAsBareType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/typealias/typeAliasAsBareType.kt");
doTest(fileName);
}
@TestMetadata("typeAliasCompanion.kt")
public void testTypeAliasCompanion() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/typealias/typeAliasCompanion.kt");
@@ -21022,6 +21022,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("typeAliasAsBareType.kt")
public void testTypeAliasAsBareType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typealias/typeAliasAsBareType.kt");
doTest(fileName);
}
@TestMetadata("typeAliasAsQualifier.kt")
public void testTypeAliasAsQualifier() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typealias/typeAliasAsQualifier.kt");
@@ -16049,6 +16049,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("typeAliasAsBareType.kt")
public void testTypeAliasAsBareType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/typealias/typeAliasAsBareType.kt");
doTest(fileName);
}
@TestMetadata("typeAliasCompanion.kt")
public void testTypeAliasCompanion() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/typealias/typeAliasCompanion.kt");
@@ -20829,6 +20829,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
doTest(fileName);
}
@TestMetadata("typeAliasAsBareType.kt")
public void testTypeAliasAsBareType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/typealias/typeAliasAsBareType.kt");
doTest(fileName);
}
@TestMetadata("typeAliasCompanion.kt")
public void testTypeAliasCompanion() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/typealias/typeAliasCompanion.kt");