Prohibit unsafe covariant conversion for collections invariant in Java
This commit is contained in:
+2
@@ -81,6 +81,8 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
|
||||
"Please use the more clear ''::class.java'' syntax to avoid confusion",
|
||||
Renderers.RENDER_TYPE, Renderers.RENDER_TYPE
|
||||
);
|
||||
MAP.put(ErrorsJvm.JAVA_TYPE_MISMATCH,
|
||||
"Java type mismatch expected {1} but found {0}. Use explicit cast", Renderers.RENDER_TYPE, Renderers.RENDER_TYPE);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -67,6 +67,7 @@ public interface ErrorsJvm {
|
||||
DiagnosticFactory0<JetElement> NO_REFLECTION_IN_CLASS_PATH = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
DiagnosticFactory2<JetElement, JetType, JetType> JAVA_CLASS_ON_COMPANION = DiagnosticFactory2.create(WARNING);
|
||||
DiagnosticFactory2<JetExpression, JetType, JetType> JAVA_TYPE_MISMATCH = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
enum NullabilityInformationSource {
|
||||
KOTLIN {
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.jvm.platform
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
|
||||
import org.jetbrains.kotlin.load.java.lazy.types.RawTypeTag
|
||||
import org.jetbrains.kotlin.psi.JetExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||
import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure
|
||||
|
||||
public object JavaGenericVarianceViolationTypeChecker : AdditionalTypeChecker {
|
||||
// Prohibits covariant type argument conversions `List<String> -> (MutableList<Any>..List<Any>)` when expected type's lower bound is invariant.
|
||||
// It's needed to prevent accident unsafe covariant conversions of mutable collections.
|
||||
//
|
||||
// Example:
|
||||
// class JavaClass { static void fillWithDefaultObjects(List<Object> list); // add Object's to list }
|
||||
//
|
||||
// val x: MutableList<String>
|
||||
// JavaClass.fillWithDefaultObjects(x) // using `x` after this call may lead to CCE
|
||||
override fun checkType(
|
||||
expression: JetExpression,
|
||||
expressionType: JetType,
|
||||
expressionTypeWithSmartCast: JetType,
|
||||
c: ResolutionContext<*>
|
||||
) {
|
||||
val expectedType = c.expectedType
|
||||
if (TypeUtils.noExpectedType(expectedType) || ErrorUtils.containsErrorType(expectedType) || ErrorUtils.containsUninferredParameter(expectedType)) return
|
||||
|
||||
// optimization: if no arguments or flexibility, everything is OK
|
||||
if (expectedType.arguments.isEmpty() || !expectedType.isFlexible()) return
|
||||
|
||||
val lowerBound = expectedType.flexibility().lowerBound
|
||||
val upperBound = expectedType.flexibility().upperBound
|
||||
|
||||
// Use site variance projection is always the same for flexible types
|
||||
if (lowerBound.constructor == upperBound.constructor) return
|
||||
// Anything is acceptable for raw types
|
||||
if (expectedType.getCapability<RawTypeTag>() != null) return
|
||||
|
||||
val correspondingSubType = TypeCheckingProcedure.findCorrespondingSupertype(expressionTypeWithSmartCast, lowerBound) ?: return
|
||||
|
||||
assert(lowerBound.arguments.size() == upperBound.arguments.size()) {
|
||||
"Different arguments count in flexible bounds: " +
|
||||
"($lowerBound(${lowerBound.arguments.size()})..$upperBound(${upperBound.arguments.size()})"
|
||||
}
|
||||
|
||||
assert(lowerBound.arguments.size() == correspondingSubType.arguments.size()) {
|
||||
"Different arguments count in corresponding subtype and supertype: " +
|
||||
"($lowerBound(${lowerBound.arguments.size()})..$correspondingSubType(${correspondingSubType.arguments.size()})"
|
||||
}
|
||||
|
||||
|
||||
val lowerParameters = lowerBound.constructor.parameters
|
||||
val upperParameters = upperBound.constructor.parameters
|
||||
val lowerArguments = lowerBound.arguments
|
||||
|
||||
correspondingSubType.arguments.indices.forEach {
|
||||
index ->
|
||||
val lowerArgument = lowerArguments[index]
|
||||
// Currently we don't have flexible types with different constructors with contravariant arguments
|
||||
// So check just covariant case
|
||||
if (lowerParameters[index].variance == Variance.INVARIANT
|
||||
&& upperParameters[index].variance == Variance.OUT_VARIANCE
|
||||
&& lowerArgument.projectionKind != Variance.OUT_VARIANCE
|
||||
&& !JetTypeChecker.DEFAULT.equalTypes(correspondingSubType.arguments[index].type, lowerArgument.type)
|
||||
) {
|
||||
c.trace.report(ErrorsJvm.JAVA_TYPE_MISMATCH.on(expression, expressionTypeWithSmartCast, expectedType))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun checkReceiver(
|
||||
receiverParameter: ReceiverParameterDescriptor,
|
||||
receiverArgument: ReceiverValue,
|
||||
safeAccess: Boolean, c: CallResolutionContext<*>) { }
|
||||
}
|
||||
+3
-1
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.jvm.RuntimeAssertionsTypeChecker
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import org.jetbrains.kotlin.load.java.lazy.types.RawTypeTag
|
||||
import org.jetbrains.kotlin.load.java.lazy.types.isMarkedNotNull
|
||||
import org.jetbrains.kotlin.load.java.lazy.types.isMarkedNullable
|
||||
import org.jetbrains.kotlin.load.kotlin.JavaAnnotationCallChecker
|
||||
@@ -83,7 +84,8 @@ public object JvmPlatformConfigurator : PlatformConfigurator(
|
||||
|
||||
additionalTypeCheckers = listOf(
|
||||
JavaNullabilityWarningsChecker(),
|
||||
RuntimeAssertionsTypeChecker
|
||||
RuntimeAssertionsTypeChecker,
|
||||
JavaGenericVarianceViolationTypeChecker
|
||||
),
|
||||
|
||||
additionalSymbolUsageValidators = listOf(),
|
||||
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
// FILE: A.java
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class A {
|
||||
void foo(List<Object> x) {}
|
||||
}
|
||||
// FILE: main.kt
|
||||
|
||||
abstract class B : MutableList<String>
|
||||
|
||||
fun main(a: A, b: B) {
|
||||
a.foo(<!JAVA_TYPE_MISMATCH!>b<!>)
|
||||
a.foo(b as List<Any>)
|
||||
}
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
package
|
||||
|
||||
internal fun main(/*0*/ a: A, /*1*/ b: B): kotlin.Unit
|
||||
|
||||
public open class A {
|
||||
public constructor A()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public/*package*/ open fun foo(/*0*/ x: kotlin.(Mutable)List<kotlin.Any!>!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
internal abstract class B : kotlin.MutableList<kotlin.String> {
|
||||
public constructor B()
|
||||
public abstract override /*1*/ /*fake_override*/ fun add(/*0*/ index: kotlin.Int, /*1*/ element: kotlin.String): kotlin.Unit
|
||||
public abstract override /*1*/ /*fake_override*/ fun add(/*0*/ e: kotlin.String): kotlin.Boolean
|
||||
public abstract override /*1*/ /*fake_override*/ fun addAll(/*0*/ c: kotlin.Collection<kotlin.String>): kotlin.Boolean
|
||||
public abstract override /*1*/ /*fake_override*/ fun addAll(/*0*/ index: kotlin.Int, /*1*/ c: kotlin.Collection<kotlin.String>): kotlin.Boolean
|
||||
public abstract override /*1*/ /*fake_override*/ fun clear(): kotlin.Unit
|
||||
public abstract override /*1*/ /*fake_override*/ fun contains(/*0*/ o: kotlin.Any?): kotlin.Boolean
|
||||
public abstract override /*1*/ /*fake_override*/ fun containsAll(/*0*/ c: kotlin.Collection<kotlin.Any?>): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public abstract override /*1*/ /*fake_override*/ fun get(/*0*/ index: kotlin.Int): kotlin.String
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public abstract override /*1*/ /*fake_override*/ fun indexOf(/*0*/ o: kotlin.Any?): kotlin.Int
|
||||
public abstract override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean
|
||||
public abstract override /*1*/ /*fake_override*/ fun iterator(): kotlin.MutableIterator<kotlin.String>
|
||||
public abstract override /*1*/ /*fake_override*/ fun lastIndexOf(/*0*/ o: kotlin.Any?): kotlin.Int
|
||||
public abstract override /*1*/ /*fake_override*/ fun listIterator(): kotlin.MutableListIterator<kotlin.String>
|
||||
public abstract override /*1*/ /*fake_override*/ fun listIterator(/*0*/ index: kotlin.Int): kotlin.MutableListIterator<kotlin.String>
|
||||
public abstract override /*1*/ /*fake_override*/ fun remove(/*0*/ o: kotlin.Any?): kotlin.Boolean
|
||||
public abstract override /*1*/ /*fake_override*/ fun remove(/*0*/ index: kotlin.Int): kotlin.String
|
||||
public abstract override /*1*/ /*fake_override*/ fun removeAll(/*0*/ c: kotlin.Collection<kotlin.Any?>): kotlin.Boolean
|
||||
public abstract override /*1*/ /*fake_override*/ fun retainAll(/*0*/ c: kotlin.Collection<kotlin.Any?>): kotlin.Boolean
|
||||
public abstract override /*1*/ /*fake_override*/ fun set(/*0*/ index: kotlin.Int, /*1*/ element: kotlin.String): kotlin.String
|
||||
public abstract override /*1*/ /*fake_override*/ fun size(): kotlin.Int
|
||||
public abstract override /*1*/ /*fake_override*/ fun subList(/*0*/ fromIndex: kotlin.Int, /*1*/ toIndex: kotlin.Int): kotlin.MutableList<kotlin.String>
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
// FILE: A.java
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class A {
|
||||
void foo(List x) {}
|
||||
}
|
||||
// FILE: main.kt
|
||||
|
||||
fun main(a: A, ml: MutableList<String>, l: List<String>) {
|
||||
a.foo(ml)
|
||||
a.foo(l)
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package
|
||||
|
||||
internal fun main(/*0*/ a: A, /*1*/ ml: kotlin.MutableList<kotlin.String>, /*2*/ l: kotlin.List<kotlin.String>): kotlin.Unit
|
||||
|
||||
public open class A {
|
||||
public constructor A()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public/*package*/ open fun foo(/*0*/ x: kotlin.(Mutable)List<(raw) kotlin.Any?>!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
// FILE: A.java
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class A {
|
||||
void foo(List<Object> x) {}
|
||||
void foo(Iterable<Object> x) {}
|
||||
void foo(Iterator<Object> x) {}
|
||||
void foo(Set<Object> x) {}
|
||||
void foo(Map<Object, Object> x) {}
|
||||
void foo(Map.Entry<Object, Object> x) {}
|
||||
|
||||
void foo(List<List<Object>> x) {}
|
||||
}
|
||||
|
||||
// FILE: main.kt
|
||||
|
||||
fun main(
|
||||
a: A,
|
||||
ml: MutableList<String>, l: List<String>,
|
||||
ms: MutableSet<String>, s: Set<String>,
|
||||
mm: MutableMap<Any, String>, m: Map<Any, String>,
|
||||
mme: MutableMap.MutableEntry<Any, String>, me: Map.Entry<Any, String>,
|
||||
mll: MutableList<MutableList<String>>, ll: List<List<String>>
|
||||
) {
|
||||
// Lists
|
||||
a.foo(<!JAVA_TYPE_MISMATCH!>ml<!>)
|
||||
a.foo(l)
|
||||
a.foo(<!UNCHECKED_CAST!>ml as MutableList<Any><!>)
|
||||
a.foo(l as List<Any>)
|
||||
|
||||
// Iterables
|
||||
val mit: MutableIterable<String> = ml
|
||||
val it: Iterable<String> = ml
|
||||
a.foo(mit)
|
||||
a.foo(it)
|
||||
|
||||
// Iterators
|
||||
a.foo(ml.iterator())
|
||||
a.foo(l.iterator())
|
||||
|
||||
// Sets
|
||||
a.foo(<!JAVA_TYPE_MISMATCH!>ms<!>)
|
||||
a.foo(s)
|
||||
a.foo(<!UNCHECKED_CAST!>ms as MutableSet<Any><!>)
|
||||
a.foo(s as Set<Any>)
|
||||
|
||||
// Maps
|
||||
a.foo(<!JAVA_TYPE_MISMATCH!>mm<!>)
|
||||
a.foo(m)
|
||||
a.foo(<!UNCHECKED_CAST!>mm as MutableMap<Any, Any><!>)
|
||||
a.foo(m as Map<Any, Any>)
|
||||
|
||||
// Map entries
|
||||
a.foo(<!JAVA_TYPE_MISMATCH!>mme<!>)
|
||||
a.foo(me)
|
||||
a.foo(<!UNCHECKED_CAST!>mme as MutableMap.MutableEntry<Any, Any><!>)
|
||||
a.foo(me as Map.Entry<Any, Any>)
|
||||
|
||||
// Lists of lists
|
||||
a.foo(<!JAVA_TYPE_MISMATCH!>mll<!>)
|
||||
a.foo(ll)
|
||||
a.foo(<!UNCHECKED_CAST!>mll as MutableList<MutableList<Any>><!>)
|
||||
a.foo(ll as List<List<Any>>)
|
||||
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package
|
||||
|
||||
internal fun main(/*0*/ a: A, /*1*/ ml: kotlin.MutableList<kotlin.String>, /*2*/ l: kotlin.List<kotlin.String>, /*3*/ ms: kotlin.MutableSet<kotlin.String>, /*4*/ s: kotlin.Set<kotlin.String>, /*5*/ mm: kotlin.MutableMap<kotlin.Any, kotlin.String>, /*6*/ m: kotlin.Map<kotlin.Any, kotlin.String>, /*7*/ mme: kotlin.MutableMap.MutableEntry<kotlin.Any, kotlin.String>, /*8*/ me: kotlin.Map.Entry<kotlin.Any, kotlin.String>, /*9*/ mll: kotlin.MutableList<kotlin.MutableList<kotlin.String>>, /*10*/ ll: kotlin.List<kotlin.List<kotlin.String>>): kotlin.Unit
|
||||
|
||||
public open class A {
|
||||
public constructor A()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public/*package*/ open fun foo(/*0*/ x: kotlin.(Mutable)Iterable<kotlin.Any!>!): kotlin.Unit
|
||||
public/*package*/ open fun foo(/*0*/ x: kotlin.(Mutable)Iterator<kotlin.Any!>!): kotlin.Unit
|
||||
public/*package*/ open fun foo(/*0*/ x: kotlin.(Mutable)List<kotlin.(Mutable)List<kotlin.Any!>!>!): kotlin.Unit
|
||||
public/*package*/ open fun foo(/*0*/ x: kotlin.(Mutable)List<kotlin.Any!>!): kotlin.Unit
|
||||
public/*package*/ open fun foo(/*0*/ x: kotlin.(Mutable)Map.(Mutable)Entry<kotlin.Any!, kotlin.Any!>!): kotlin.Unit
|
||||
public/*package*/ open fun foo(/*0*/ x: kotlin.(Mutable)Map<kotlin.Any!, kotlin.Any!>!): kotlin.Unit
|
||||
public/*package*/ open fun foo(/*0*/ x: kotlin.(Mutable)Set<kotlin.Any!>!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
// FILE: A.java
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class A {
|
||||
void foo(List<Object> x) {}
|
||||
}
|
||||
// FILE: main.kt
|
||||
|
||||
fun main(a: A, ml: Any) {
|
||||
if (ml is <!CANNOT_CHECK_FOR_ERASED!>MutableList<String><!>) {
|
||||
a.foo(<!JAVA_TYPE_MISMATCH, DEBUG_INFO_SMARTCAST!>ml<!>)
|
||||
a.foo(<!UNCHECKED_CAST!>ml as List<Any><!>)
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package
|
||||
|
||||
internal fun main(/*0*/ a: A, /*1*/ ml: kotlin.Any): kotlin.Unit
|
||||
|
||||
public open class A {
|
||||
public constructor A()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public/*package*/ open fun foo(/*0*/ x: kotlin.(Mutable)List<kotlin.Any!>!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
// FILE: A.java
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class A {
|
||||
void foo(List<? super String> x) {}
|
||||
void bar(List<? super Object> x) {}
|
||||
}
|
||||
// FILE: main.kt
|
||||
|
||||
fun main(a: A, ml: MutableList<String>, l: List<String>) {
|
||||
a.foo(ml)
|
||||
a.foo(l)
|
||||
|
||||
a.bar(<!JAVA_TYPE_MISMATCH!>ml<!>)
|
||||
a.bar(l)
|
||||
}
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
package
|
||||
|
||||
internal fun main(/*0*/ a: A, /*1*/ ml: kotlin.MutableList<kotlin.String>, /*2*/ l: kotlin.List<kotlin.String>): kotlin.Unit
|
||||
|
||||
public open class A {
|
||||
public constructor A()
|
||||
public/*package*/ open fun bar(/*0*/ x: kotlin.(Mutable)List<in kotlin.Any!>!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public/*package*/ open fun foo(/*0*/ x: kotlin.(Mutable)List<in kotlin.String!>!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
// FILE: A.java
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class A {
|
||||
void foo(Out<Object> x) {}
|
||||
void bar(Out<? extends Object> x) {}
|
||||
}
|
||||
// FILE: main.kt
|
||||
|
||||
public class Out<out T>()
|
||||
|
||||
fun main(a: A, o: Out<String>) {
|
||||
a.foo(o)
|
||||
a.bar(o)
|
||||
}
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
package
|
||||
|
||||
internal fun main(/*0*/ a: A, /*1*/ o: Out<kotlin.String>): kotlin.Unit
|
||||
|
||||
public open class A {
|
||||
public constructor A()
|
||||
public/*package*/ open fun bar(/*0*/ x: Out<kotlin.Any!>!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public/*package*/ open fun foo(/*0*/ x: Out<kotlin.Any!>!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final class Out</*0*/ out T> {
|
||||
public constructor Out</*0*/ out T>()
|
||||
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
|
||||
}
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
// FILE: A.java
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class A {
|
||||
void foo(List<Object> x) {}
|
||||
List<String> bar() {}
|
||||
}
|
||||
// FILE: main.kt
|
||||
|
||||
fun main(a: A) {
|
||||
a.foo(<!JAVA_TYPE_MISMATCH!>a.bar()<!>)
|
||||
}
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
package
|
||||
|
||||
internal fun main(/*0*/ a: A): kotlin.Unit
|
||||
|
||||
public open class A {
|
||||
public constructor A()
|
||||
public/*package*/ open fun bar(): kotlin.(Mutable)List<kotlin.String!>!
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public/*package*/ open fun foo(/*0*/ x: kotlin.(Mutable)List<kotlin.Any!>!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
// FILE: A.java
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class A {
|
||||
void foo(List<?> x) {}
|
||||
void foo(Iterable<?> x) {}
|
||||
void foo(Iterator<?> x) {}
|
||||
void foo(Set<?> x) {}
|
||||
void foo(Map<?, ?> x) {}
|
||||
void foo(Map.Entry<?, ?> x) {}
|
||||
}
|
||||
|
||||
// FILE: main.kt
|
||||
|
||||
fun main(
|
||||
a: A,
|
||||
ml: MutableList<String>, l: List<String>,
|
||||
ms: MutableSet<String>, s: Set<String>,
|
||||
mm: MutableMap<Any, String>, m: Map<Any, String>,
|
||||
mme: MutableMap.MutableEntry<Any, String>, me: Map.Entry<Any, String>
|
||||
) {
|
||||
// Lists
|
||||
a.foo(ml)
|
||||
a.foo(l)
|
||||
|
||||
// Iterables
|
||||
val mit: MutableIterable<String> = ml
|
||||
val it: Iterable<String> = ml
|
||||
a.foo(mit)
|
||||
a.foo(it)
|
||||
|
||||
// Iterators
|
||||
a.foo(ml.iterator())
|
||||
a.foo(l.iterator())
|
||||
|
||||
// Sets
|
||||
a.foo(ms)
|
||||
a.foo(s)
|
||||
|
||||
// Maps
|
||||
a.foo(mm)
|
||||
a.foo(m)
|
||||
|
||||
// Map entries
|
||||
a.foo(mme)
|
||||
a.foo(me)
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package
|
||||
|
||||
internal fun main(/*0*/ a: A, /*1*/ ml: kotlin.MutableList<kotlin.String>, /*2*/ l: kotlin.List<kotlin.String>, /*3*/ ms: kotlin.MutableSet<kotlin.String>, /*4*/ s: kotlin.Set<kotlin.String>, /*5*/ mm: kotlin.MutableMap<kotlin.Any, kotlin.String>, /*6*/ m: kotlin.Map<kotlin.Any, kotlin.String>, /*7*/ mme: kotlin.MutableMap.MutableEntry<kotlin.Any, kotlin.String>, /*8*/ me: kotlin.Map.Entry<kotlin.Any, kotlin.String>): kotlin.Unit
|
||||
|
||||
public open class A {
|
||||
public constructor A()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public/*package*/ open fun foo(/*0*/ x: kotlin.(Mutable)Iterable<*>!): kotlin.Unit
|
||||
public/*package*/ open fun foo(/*0*/ x: kotlin.(Mutable)Iterator<*>!): kotlin.Unit
|
||||
public/*package*/ open fun foo(/*0*/ x: kotlin.(Mutable)List<*>!): kotlin.Unit
|
||||
public/*package*/ open fun foo(/*0*/ x: kotlin.(Mutable)Map.(Mutable)Entry<*, *>!): kotlin.Unit
|
||||
public/*package*/ open fun foo(/*0*/ x: kotlin.(Mutable)Map<*, *>!): kotlin.Unit
|
||||
public/*package*/ open fun foo(/*0*/ x: kotlin.(Mutable)Set<*>!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -10395,6 +10395,63 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class GenericVarianceViolation extends AbstractJetDiagnosticsTest {
|
||||
public void testAllFilesPresentInGenericVarianceViolation() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("listSuperType.kt")
|
||||
public void testListSuperType() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/listSuperType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("rawTypes.kt")
|
||||
public void testRawTypes() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/rawTypes.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/simple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("smartCast.kt")
|
||||
public void testSmartCast() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/smartCast.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("strangeVariance.kt")
|
||||
public void testStrangeVariance() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/strangeVariance.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("userDefinedOut.kt")
|
||||
public void testUserDefinedOut() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/userDefinedOut.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("valueFromJava.kt")
|
||||
public void testValueFromJava() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/valueFromJava.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("wildcards.kt")
|
||||
public void testWildcards() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/wildcards.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/platformTypes/intersection")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -24,6 +24,8 @@ import org.jetbrains.kotlin.renderer.CustomFlexibleRendering
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.types.*
|
||||
|
||||
public object RawTypeTag : TypeCapability
|
||||
|
||||
public object RawTypeCapabilities : TypeCapabilities {
|
||||
private object RawSubstitutionCapability : CustomSubstitutionCapability {
|
||||
override val substitution = RawSubstitution
|
||||
@@ -72,6 +74,7 @@ public object RawTypeCapabilities : TypeCapabilities {
|
||||
return when(capabilityClass) {
|
||||
javaClass<CustomSubstitutionCapability>() -> RawSubstitutionCapability as T
|
||||
javaClass<CustomFlexibleRendering>() -> RawFlexibleRendering as T
|
||||
javaClass<RawTypeTag>() -> RawTypeTag as T
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +116,39 @@ Erase(A) = Raw(A) // `A` is a type constructor without parameters
|
||||
// then it becomes `Foo<(raw) Any!>` inside Erase(A)
|
||||
```
|
||||
|
||||
## Unsafe covariant conversions
|
||||
|
||||
In case of platform collections their upper bound contains covariant parameter, which means they may behave covariantly even it doesn't meant to do so.
|
||||
|
||||
Example:
|
||||
```java
|
||||
class JavaClass {
|
||||
void addObject(List<Object> x) {
|
||||
x.add(new Object());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```kotlin
|
||||
val x: MutableList<String> = arrayListOf()
|
||||
JavaClass.addObject(x) // Ok
|
||||
x[0].length() // ClassCastException
|
||||
```
|
||||
|
||||
This happens because `MutableList<String>` <: `List<String>` <: `List<Any>` and by subtyping rule for flexible types `MutableList<String>` <: `(Mutable)Collection<Any!>!` follows.
|
||||
|
||||
While it's legal from point of view of type system, in most cases such conversion is unintended and must be prohibited when being made implicitly.
|
||||
|
||||
So implicit covariant conversion by i-th argument from type `source` to `target` is prohibited when:
|
||||
- `target` is flexible type with invariant i-th *parameter* of lower bound (when same parameter in upper bound may be covariant)
|
||||
- i-th *argument* of `target`'s lower bound is invariant (which means it declared as invariant in Java)
|
||||
- type of i-th argument of `source` is not *equal* to same argument in `target`'s lower bound.
|
||||
|
||||
NOTE: Such conversion still may be done explicitly, with covariant upcast. E.g. for upper case:
|
||||
```
|
||||
JavaClass.addObject(x as List<Any>) // No unchecked cast warning
|
||||
```
|
||||
|
||||
## Overriding
|
||||
|
||||
When overriding a method from a Java class, one can not use flexible type, only replace them with denotable Kotlin types:
|
||||
|
||||
Reference in New Issue
Block a user