[FIR] Fix substitution of Java arrays enhanced for warning
... w.r.t the variance of their type arguments. Because `Object[]` is represented as `Array<Any>..Array<out Any>?`, when we substitute, we can't just take the upper bound because it changes the variance of the type agument. Instead, we construct a flexible type and use both bounds, like with collections with flexible mutability. #KT-65246 Fixed
This commit is contained in:
committed by
Space Team
parent
20f6b0cb0c
commit
eae72eac54
+15
-2
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.java.enhancement
|
|||||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||||
import org.jetbrains.kotlin.fir.resolve.substitution.AbstractConeSubstitutor
|
import org.jetbrains.kotlin.fir.resolve.substitution.AbstractConeSubstitutor
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
|
import org.jetbrains.kotlin.name.StandardClassIds
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
data class EnhancedTypeForWarningAttribute(
|
data class EnhancedTypeForWarningAttribute(
|
||||||
@@ -45,8 +46,10 @@ val ConeKotlinType.isEnhancedTypeForWarningDeprecation: Boolean
|
|||||||
class EnhancedForWarningConeSubstitutor(typeContext: ConeTypeContext) : AbstractConeSubstitutor(typeContext) {
|
class EnhancedForWarningConeSubstitutor(typeContext: ConeTypeContext) : AbstractConeSubstitutor(typeContext) {
|
||||||
override fun substituteType(type: ConeKotlinType): ConeKotlinType? {
|
override fun substituteType(type: ConeKotlinType): ConeKotlinType? {
|
||||||
// The attribute is usually taken from the lower bound of flexible types.
|
// The attribute is usually taken from the lower bound of flexible types.
|
||||||
// However, if the type has flexible mutability, we don't want to accidentally enhance the mutability to the lower bound.
|
// However, if the type has flexible mutability or type argument variance,
|
||||||
if (type is ConeFlexibleType && type.hasFlexibleMutability()) {
|
// we can't just use the lower bound, otherwise we can get false positive mismatches
|
||||||
|
// because of the mutability or the type argument variance.
|
||||||
|
if (type is ConeFlexibleType && (type.hasFlexibleMutability() || type.isArrayWithFlexibleVariance())) {
|
||||||
val lowerSubstituted = substituteOrNull(type.lowerBound)
|
val lowerSubstituted = substituteOrNull(type.lowerBound)
|
||||||
|
|
||||||
return if (lowerSubstituted is ConeSimpleKotlinType) {
|
return if (lowerSubstituted is ConeSimpleKotlinType) {
|
||||||
@@ -67,7 +70,17 @@ class EnhancedForWarningConeSubstitutor(typeContext: ConeTypeContext) : Abstract
|
|||||||
return enhancedTopLevel?.let(::substituteOrSelf)
|
return enhancedTopLevel?.let(::substituteOrSelf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `List<Object>` is represented as `MutableList<Any!>..List<Any!>?`.
|
||||||
|
*/
|
||||||
private fun ConeFlexibleType.hasFlexibleMutability(): Boolean {
|
private fun ConeFlexibleType.hasFlexibleMutability(): Boolean {
|
||||||
return JavaToKotlinClassMap.isMutable(lowerBound.classId) && JavaToKotlinClassMap.isReadOnly(upperBound.classId)
|
return JavaToKotlinClassMap.isMutable(lowerBound.classId) && JavaToKotlinClassMap.isReadOnly(upperBound.classId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `Object[]` is represented as `Array<Any>..Array<out Any>?`.
|
||||||
|
*/
|
||||||
|
private fun ConeFlexibleType.isArrayWithFlexibleVariance(): Boolean {
|
||||||
|
return lowerBound.classId == StandardClassIds.Array && lowerBound.typeArguments.firstOrNull()?.kind != upperBound.typeArguments.firstOrNull()?.kind
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+4
@@ -18,6 +18,8 @@ public class BaseClass {
|
|||||||
|
|
||||||
public Foo explicitlyNullnessUnspecified(@NullnessUnspecified Foo x) { return null; }
|
public Foo explicitlyNullnessUnspecified(@NullnessUnspecified Foo x) { return null; }
|
||||||
|
|
||||||
|
public void withVararg(Object... p) { }
|
||||||
|
|
||||||
public static Foo foo() { return null; }
|
public static Foo foo() { return null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +56,8 @@ class Correct : IntermediateClass() {
|
|||||||
override fun intermediateNotNull(): Foo {
|
override fun intermediateNotNull(): Foo {
|
||||||
return FOO
|
return FOO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun withVararg(vararg p: Any) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WrongReturnTypes : IntermediateClass() {
|
class WrongReturnTypes : IntermediateClass() {
|
||||||
|
|||||||
+4
@@ -18,6 +18,8 @@ public class BaseClass {
|
|||||||
|
|
||||||
public Foo explicitlyNullnessUnspecified(@NullnessUnspecified Foo x) { return null; }
|
public Foo explicitlyNullnessUnspecified(@NullnessUnspecified Foo x) { return null; }
|
||||||
|
|
||||||
|
public void withVararg(Object... p) { }
|
||||||
|
|
||||||
public static Foo foo() { return null; }
|
public static Foo foo() { return null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +56,8 @@ class Correct : IntermediateClass() {
|
|||||||
override fun intermediateNotNull(): Foo {
|
override fun intermediateNotNull(): Foo {
|
||||||
return FOO
|
return FOO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun withVararg(vararg p: Any) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WrongReturnTypes : IntermediateClass() {
|
class WrongReturnTypes : IntermediateClass() {
|
||||||
|
|||||||
+5
@@ -12,6 +12,7 @@ private val FOO: FOO.`<no name provided>`
|
|||||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
@org.jspecify.annotations.Nullable public open fun mixed(/*0*/ x: Foo!): @org.jspecify.annotations.Nullable Foo!
|
@org.jspecify.annotations.Nullable public open fun mixed(/*0*/ x: Foo!): @org.jspecify.annotations.Nullable Foo!
|
||||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
public open fun withVararg(/*0*/ vararg p: kotlin.Any! /*kotlin.Array<(out) kotlin.Any!>!*/): kotlin.Unit
|
||||||
|
|
||||||
// Static members
|
// Static members
|
||||||
public open fun foo(): Foo!
|
public open fun foo(): Foo!
|
||||||
@@ -28,6 +29,7 @@ public final class Correct : IntermediateClass {
|
|||||||
public open override /*1*/ fun intermediateNotNull(): Foo
|
public open override /*1*/ fun intermediateNotNull(): Foo
|
||||||
public open override /*1*/ fun mixed(/*0*/ x: Foo): Foo?
|
public open override /*1*/ fun mixed(/*0*/ x: Foo): Foo?
|
||||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
public open override /*1*/ fun withVararg(/*0*/ vararg p: kotlin.Any /*kotlin.Array<out kotlin.Any>*/): kotlin.Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Foo {
|
public interface Foo {
|
||||||
@@ -47,6 +49,7 @@ public open class IntermediateClass : BaseClass {
|
|||||||
public open fun intermediateNotNull(): Foo!
|
public open fun intermediateNotNull(): Foo!
|
||||||
@org.jspecify.annotations.Nullable public open override /*1*/ /*fake_override*/ fun mixed(/*0*/ x: Foo!): @org.jspecify.annotations.Nullable Foo!
|
@org.jspecify.annotations.Nullable public open override /*1*/ /*fake_override*/ fun mixed(/*0*/ x: Foo!): @org.jspecify.annotations.Nullable Foo!
|
||||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
public open override /*1*/ /*fake_override*/ fun withVararg(/*0*/ vararg p: kotlin.Any! /*kotlin.Array<(out) kotlin.Any!>!*/): kotlin.Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class WrongParameter : IntermediateClass {
|
public final class WrongParameter : IntermediateClass {
|
||||||
@@ -60,6 +63,7 @@ public final class WrongParameter : IntermediateClass {
|
|||||||
public open override /*1*/ /*fake_override*/ fun intermediateNotNull(): Foo!
|
public open override /*1*/ /*fake_override*/ fun intermediateNotNull(): Foo!
|
||||||
public open override /*1*/ fun mixed(/*0*/ x: Foo?): Foo?
|
public open override /*1*/ fun mixed(/*0*/ x: Foo?): Foo?
|
||||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
public open override /*1*/ /*fake_override*/ fun withVararg(/*0*/ vararg p: kotlin.Any! /*kotlin.Array<(out) kotlin.Any!>!*/): kotlin.Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class WrongReturnTypes : IntermediateClass {
|
public final class WrongReturnTypes : IntermediateClass {
|
||||||
@@ -73,5 +77,6 @@ public final class WrongReturnTypes : IntermediateClass {
|
|||||||
public open override /*1*/ fun intermediateNotNull(): Foo?
|
public open override /*1*/ fun intermediateNotNull(): Foo?
|
||||||
@org.jspecify.annotations.Nullable public open override /*1*/ /*fake_override*/ fun mixed(/*0*/ x: Foo!): @org.jspecify.annotations.Nullable Foo!
|
@org.jspecify.annotations.Nullable public open override /*1*/ /*fake_override*/ fun mixed(/*0*/ x: Foo!): @org.jspecify.annotations.Nullable Foo!
|
||||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
public open override /*1*/ /*fake_override*/ fun withVararg(/*0*/ vararg p: kotlin.Any! /*kotlin.Array<(out) kotlin.Any!>!*/): kotlin.Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user