Extract and normalize member scope calculation
Basically it's wrong to use original types' member scope as a worker for SubstitutionScope. Member scope should always be determined by type constructor's default one and substitution/arguments #KT-10448 Fixed
This commit is contained in:
+11
@@ -0,0 +1,11 @@
|
||||
// !CHECK_TYPE
|
||||
|
||||
class A<T> {
|
||||
fun foo(): T = null!!
|
||||
}
|
||||
|
||||
fun <E> A<E>.bar(): A<in E> = this
|
||||
|
||||
fun baz(x: A<out CharSequence>) {
|
||||
x.bar().foo() checkType { _<Any?>() } // See KT-10448
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package
|
||||
|
||||
public fun baz(/*0*/ x: A<out kotlin.CharSequence>): kotlin.Unit
|
||||
public fun </*0*/ E> A<E>.bar(): A<in E>
|
||||
|
||||
public final class A</*0*/ T> {
|
||||
public constructor A</*0*/ T>()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public final fun foo(): T
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -7997,6 +7997,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("memberScopeOfCaptured.kt")
|
||||
public void testMemberScopeOfCaptured() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/capturedTypes/memberScopeOfCaptured.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notApproximateWhenCopyDescriptors.kt")
|
||||
public void testNotApproximateWhenCopyDescriptors() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/capturedTypes/notApproximateWhenCopyDescriptors.kt");
|
||||
|
||||
@@ -116,10 +116,8 @@ public fun approximateCapturedTypes(type: KotlinType): ApproximationBounds<Kotli
|
||||
}
|
||||
|
||||
private fun KotlinType.replaceTypeArguments(newTypeArguments: List<TypeArgument>): KotlinType {
|
||||
assert(getArguments().size() == newTypeArguments.size()) { "Incorrect type arguments $newTypeArguments" }
|
||||
return KotlinTypeImpl.create(
|
||||
getAnnotations(), getConstructor(), isMarkedNullable(), newTypeArguments.map { it.toTypeProjection() }, getMemberScope()
|
||||
)
|
||||
assert(arguments.size == newTypeArguments.size) { "Incorrect type arguments $newTypeArguments" }
|
||||
return replace(newTypeArguments.map { it.toTypeProjection() })
|
||||
}
|
||||
|
||||
private fun approximateProjection(typeArgument: TypeArgument): ApproximationBounds<TypeArgument> {
|
||||
|
||||
@@ -114,6 +114,43 @@ public fun KotlinType.computeNewSubstitution(
|
||||
return composedSubstitution
|
||||
}
|
||||
|
||||
public fun KotlinType.replace(
|
||||
newArguments: List<TypeProjection>,
|
||||
annotations: Annotations = this@replace.annotations
|
||||
): KotlinType {
|
||||
if (newArguments.isEmpty() && annotations === this.annotations) return this
|
||||
|
||||
if (newArguments.isEmpty()) {
|
||||
return KotlinTypeImpl.create(
|
||||
annotations,
|
||||
constructor,
|
||||
isMarkedNullable,
|
||||
arguments,
|
||||
substitution,
|
||||
memberScope,
|
||||
capabilities
|
||||
)
|
||||
}
|
||||
|
||||
val newSubstitution = computeNewSubstitution(constructor, newArguments)
|
||||
|
||||
val declarationDescriptor = constructor.declarationDescriptor
|
||||
val newScope =
|
||||
if (declarationDescriptor is ClassDescriptor)
|
||||
declarationDescriptor.getMemberScope(newSubstitution)
|
||||
else ErrorUtils.createErrorScope("Unexpected declaration descriptor for type constructor: $constructor")
|
||||
|
||||
return KotlinTypeImpl.create(
|
||||
annotations,
|
||||
constructor,
|
||||
isMarkedNullable,
|
||||
newArguments,
|
||||
newSubstitution,
|
||||
newScope,
|
||||
capabilities
|
||||
)
|
||||
}
|
||||
|
||||
private class CompositeTypeSubstitution(
|
||||
private val first: TypeSubstitution,
|
||||
private val second: TypeSubstitution
|
||||
|
||||
@@ -26,12 +26,10 @@ import org.jetbrains.kotlin.descriptors.annotations.CompositeAnnotations;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.FilteredAnnotations;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.CapturedTypeConstructorKt;
|
||||
import org.jetbrains.kotlin.resolve.scopes.SubstitutingScope;
|
||||
import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt;
|
||||
import org.jetbrains.kotlin.types.typesApproximation.CapturedTypeApproximationKt;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -217,7 +215,7 @@ public class TypeSubstitutor {
|
||||
TypeProjection originalProjection,
|
||||
int recursionDepth
|
||||
) throws SubstitutionException {
|
||||
final KotlinType type = originalProjection.getType();
|
||||
KotlinType type = originalProjection.getType();
|
||||
Variance projectionKind = originalProjection.getProjectionKind();
|
||||
if (type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) {
|
||||
// substitution can't change type parameter
|
||||
@@ -228,25 +226,8 @@ public class TypeSubstitutor {
|
||||
List<TypeProjection> substitutedArguments = substituteTypeArguments(
|
||||
type.getConstructor().getParameters(), type.getArguments(), recursionDepth);
|
||||
|
||||
// Only type parameters of the corresponding class (or captured type parameters of outer declaration) are substituted
|
||||
// e.g. for return type Foo of 'add(..)' in 'class Foo { fun <R> add(bar: Bar<R>): Foo }' R shouldn't be substituted in the scope
|
||||
TypeSubstitution substitutionFilteringTypeParameters = new DelegatedTypeSubstitution(substitution) {
|
||||
private final Collection<TypeConstructor> containedOrCapturedTypeParameters =
|
||||
TypeUtilsKt.getContainedAndCapturedTypeParameterConstructors(type);
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TypeProjection get(@NotNull KotlinType key) {
|
||||
return containedOrCapturedTypeParameters.contains(key.getConstructor()) ? substitution.get(key) : null;
|
||||
}
|
||||
};
|
||||
KotlinType substitutedType = KotlinTypeImpl.create(substitution.filterAnnotations(type.getAnnotations()), // Old annotations. This is questionable
|
||||
type.getConstructor(), // The same constructor
|
||||
type.isMarkedNullable(), // Same nullability
|
||||
substitutedArguments,
|
||||
substitutionFilteringTypeParameters,
|
||||
new SubstitutingScope(type.getMemberScope(), create(substitutionFilteringTypeParameters)),
|
||||
type.getCapabilities());
|
||||
KotlinType substitutedType =
|
||||
TypeSubstitutionKt.replace(type, substitutedArguments, substitution.filterAnnotations(type.getAnnotations()));
|
||||
return new TypeProjectionImpl(projectionKind, substitutedType);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,15 +17,11 @@
|
||||
package org.jetbrains.kotlin.types.typeUtil
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.utils.toReadOnlyList
|
||||
import java.util.*
|
||||
|
||||
public enum class TypeNullability {
|
||||
@@ -65,39 +61,6 @@ fun KotlinType?.isArrayOfNothing(): Boolean {
|
||||
return typeArg != null && KotlinBuiltIns.isNothingOrNullableNothing(typeArg)
|
||||
}
|
||||
|
||||
private fun KotlinType.getContainedTypeParameters(): Collection<TypeParameterDescriptor> {
|
||||
val declarationDescriptor = getConstructor().getDeclarationDescriptor()
|
||||
if (declarationDescriptor is TypeParameterDescriptor) return listOf(declarationDescriptor)
|
||||
|
||||
val flexibility = getCapability(javaClass<Flexibility>())
|
||||
if (flexibility != null) {
|
||||
return flexibility.lowerBound.getContainedTypeParameters() + flexibility.upperBound.getContainedTypeParameters()
|
||||
}
|
||||
return getArguments().filter { !it.isStarProjection() }.map { it.getType() }.flatMap { it.getContainedTypeParameters() }
|
||||
}
|
||||
|
||||
fun DeclarationDescriptor.getCapturedTypeParameters(): Collection<TypeParameterDescriptor> {
|
||||
val result = LinkedHashSet<TypeParameterDescriptor>()
|
||||
val containingDeclaration = this.getContainingDeclaration()
|
||||
|
||||
if (containingDeclaration is ClassDescriptor) {
|
||||
result.addAll(containingDeclaration.getDefaultType().getContainedTypeParameters())
|
||||
}
|
||||
else if (containingDeclaration is CallableDescriptor) {
|
||||
result.addAll(containingDeclaration.getTypeParameters())
|
||||
}
|
||||
if (containingDeclaration != null) {
|
||||
result.addAll(containingDeclaration.getCapturedTypeParameters())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
public fun KotlinType.getContainedAndCapturedTypeParameterConstructors(): Collection<TypeConstructor> {
|
||||
// todo type arguments (instead of type parameters) of the type of outer class must be considered; KT-6325
|
||||
val capturedTypeParameters = getConstructor().getDeclarationDescriptor()?.getCapturedTypeParameters() ?: emptyList()
|
||||
val typeParameters = getContainedTypeParameters() + capturedTypeParameters
|
||||
return typeParameters.map { it.getTypeConstructor() }.toReadOnlyList()
|
||||
}
|
||||
|
||||
public fun KotlinType.isSubtypeOf(superType: KotlinType): Boolean = KotlinTypeChecker.DEFAULT.isSubtypeOf(this, superType)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user