Prohibit Array::class, require type arguments for reified parameters
This commit is contained in:
@@ -36,7 +36,9 @@ import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
|
||||
import org.jetbrains.kotlin.codegen.context.*;
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension;
|
||||
import org.jetbrains.kotlin.codegen.inline.*;
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.*;
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethod;
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.JavaClassProperty;
|
||||
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
|
||||
@@ -2668,10 +2670,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
assert state.getReflectionTypes().getkClass().getTypeConstructor().equals(type.getConstructor())
|
||||
: "::class expression should be type checked to a KClass: " + type;
|
||||
|
||||
ClassifierDescriptor typeArgument = KotlinPackage.single(type.getArguments()).getType().getConstructor().getDeclarationDescriptor();
|
||||
assert typeArgument instanceof ClassDescriptor : "KClass argument should be a class: " + typeArgument;
|
||||
|
||||
return generateClassLiteralReference((ClassDescriptor) typeArgument);
|
||||
return generateClassLiteralReference(KotlinPackage.single(type.getArguments()).getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2763,7 +2762,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
@Override
|
||||
public Unit invoke(InstructionAdapter v) {
|
||||
v.visitLdcInsn(descriptor.getName().asString());
|
||||
StackValue receiverClass = generateClassLiteralReference(containingClass);
|
||||
StackValue receiverClass = generateClassLiteralReference(containingClass.getDefaultType());
|
||||
receiverClass.put(receiverClass.type, v);
|
||||
v.invokestatic(REFLECTION, factoryMethod.getName(), factoryMethod.getDescriptor(), false);
|
||||
|
||||
@@ -2773,11 +2772,13 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private StackValue generateClassLiteralReference(@NotNull final ClassDescriptor descriptor) {
|
||||
private StackValue generateClassLiteralReference(@NotNull final JetType type) {
|
||||
return StackValue.operation(K_CLASS_TYPE, new Function1<InstructionAdapter, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(InstructionAdapter v) {
|
||||
Type classAsmType = typeMapper.mapClass(descriptor);
|
||||
Type classAsmType = typeMapper.mapType(type);
|
||||
ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
|
||||
//noinspection ConstantConditions
|
||||
ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor);
|
||||
if (descriptor instanceof JavaClassDescriptor || module == module.getBuiltIns().getBuiltInsModule()) {
|
||||
putJavaLangClassInstance(v, classAsmType);
|
||||
|
||||
@@ -440,6 +440,7 @@ public interface Errors {
|
||||
DiagnosticFactory0<JetExpression> CALLABLE_REFERENCE_LHS_NOT_A_CLASS = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<JetExpression> CLASS_LITERAL_LHS_NOT_A_CLASS = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<JetExpression> ARRAY_CLASS_LITERAL_REQUIRES_ARGUMENT = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
// Multi-declarations
|
||||
|
||||
|
||||
+1
@@ -589,6 +589,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(CALLABLE_REFERENCE_LHS_NOT_A_CLASS, "Left hand side of a callable reference cannot be a type parameter");
|
||||
|
||||
MAP.put(CLASS_LITERAL_LHS_NOT_A_CLASS, "Only classes are allowed on the left hand side of a class literal");
|
||||
MAP.put(ARRAY_CLASS_LITERAL_REQUIRES_ARGUMENT, "kotlin.Array class literal requires a type argument, please specify one in angle brackets");
|
||||
|
||||
//Inline
|
||||
MAP.put(INVISIBLE_MEMBER_FROM_INLINE, "Cannot access effectively non-public-api ''{0}'' member from effectively public-api ''{1}''", SHORT_NAMES_IN_TYPES, SHORT_NAMES_IN_TYPES);
|
||||
|
||||
+52
-16
@@ -22,6 +22,7 @@ import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import kotlin.Function0;
|
||||
import kotlin.Function1;
|
||||
import kotlin.KotlinPackage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
@@ -451,11 +452,11 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
|
||||
@Override
|
||||
public JetTypeInfo visitClassLiteralExpression(@NotNull JetClassLiteralExpression expression, ExpressionTypingContext c) {
|
||||
ClassDescriptor descriptor = resolveClassLiteral(expression, c);
|
||||
if (descriptor != null && !ErrorUtils.isError(descriptor)) {
|
||||
JetType type = components.reflectionTypes.getKClassType(Annotations.EMPTY, descriptor);
|
||||
if (!type.isError()) {
|
||||
return JetTypeInfo.create(type, c.dataFlowInfo);
|
||||
JetType type = resolveClassLiteral(expression, c);
|
||||
if (type != null && !type.isError()) {
|
||||
JetType kClassType = components.reflectionTypes.getKClassType(Annotations.EMPTY, type);
|
||||
if (!kClassType.isError()) {
|
||||
return JetTypeInfo.create(kClassType, c.dataFlowInfo);
|
||||
}
|
||||
c.trace.report(REFLECTION_TYPES_NOT_LOADED.on(expression.getDoubleColonTokenReference()));
|
||||
}
|
||||
@@ -464,7 +465,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ClassDescriptor resolveClassLiteral(@NotNull JetClassLiteralExpression expression, ExpressionTypingContext c) {
|
||||
private JetType resolveClassLiteral(@NotNull JetClassLiteralExpression expression, ExpressionTypingContext c) {
|
||||
JetTypeReference typeReference = expression.getTypeReference();
|
||||
|
||||
if (typeReference == null) {
|
||||
@@ -478,30 +479,65 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
PossiblyBareType possiblyBareType =
|
||||
components.expressionTypingServices.getTypeResolver().resolvePossiblyBareType(context, typeReference);
|
||||
|
||||
TypeConstructor typeConstructor = null;
|
||||
JetType type = null;
|
||||
if (possiblyBareType.isBare()) {
|
||||
if (!possiblyBareType.isNullable()) {
|
||||
typeConstructor = possiblyBareType.getBareTypeConstructor();
|
||||
ClassifierDescriptor descriptor = possiblyBareType.getBareTypeConstructor().getDeclarationDescriptor();
|
||||
if (descriptor instanceof ClassDescriptor) {
|
||||
ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
|
||||
if (KotlinBuiltIns.isNonPrimitiveArray(classDescriptor)) {
|
||||
context.trace.report(ARRAY_CLASS_LITERAL_REQUIRES_ARGUMENT.on(expression));
|
||||
return null;
|
||||
}
|
||||
type = substituteWithStarProjections(classDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
JetType type = possiblyBareType.getActualType();
|
||||
if (!type.isMarkedNullable() && type.getArguments().isEmpty()) {
|
||||
typeConstructor = type.getConstructor();
|
||||
JetType actualType = possiblyBareType.getActualType();
|
||||
if (isAllowedInClassLiteral(actualType)) {
|
||||
type = actualType;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeConstructor != null) {
|
||||
ClassifierDescriptor classifier = typeConstructor.getDeclarationDescriptor();
|
||||
if (classifier instanceof ClassDescriptor) {
|
||||
return (ClassDescriptor) classifier;
|
||||
}
|
||||
if (type != null) {
|
||||
return type;
|
||||
}
|
||||
|
||||
context.trace.report(CLASS_LITERAL_LHS_NOT_A_CLASS.on(expression));
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static JetType substituteWithStarProjections(@NotNull ClassDescriptor descriptor) {
|
||||
TypeConstructor typeConstructor = descriptor.getTypeConstructor();
|
||||
List<TypeProjection> arguments =
|
||||
KotlinPackage.map(typeConstructor.getParameters(), new Function1<TypeParameterDescriptor, TypeProjection>() {
|
||||
@Override
|
||||
public TypeProjection invoke(TypeParameterDescriptor descriptor) {
|
||||
return TypeUtils.makeStarProjection(descriptor);
|
||||
}
|
||||
});
|
||||
|
||||
return new JetTypeImpl(Annotations.EMPTY, typeConstructor, false, arguments, descriptor.getMemberScope(arguments));
|
||||
}
|
||||
|
||||
private static boolean isAllowedInClassLiteral(@NotNull JetType type) {
|
||||
if (type.isMarkedNullable()) return false;
|
||||
|
||||
TypeConstructor typeConstructor = type.getConstructor();
|
||||
if (!(typeConstructor.getDeclarationDescriptor() instanceof ClassDescriptor)) return false;
|
||||
|
||||
List<TypeParameterDescriptor> parameters = typeConstructor.getParameters();
|
||||
if (parameters.size() != type.getArguments().size()) return false;
|
||||
|
||||
for (TypeParameterDescriptor parameter : parameters) {
|
||||
if (!parameter.isReified()) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JetTypeInfo visitCallableReferenceExpression(@NotNull JetCallableReferenceExpression expression, ExpressionTypingContext c) {
|
||||
JetTypeReference typeReference = expression.getTypeReference();
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.jetbrains.kotlin.name.isSubpackageOf
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.TypeUtils.makeStarProjection
|
||||
import java.util.ArrayList
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
@@ -61,20 +60,14 @@ public class ReflectionTypes(private val module: ModuleDescriptor) {
|
||||
public val kTopLevelExtensionProperty: ClassDescriptor by ClassLookup
|
||||
public val kMutableTopLevelExtensionProperty: ClassDescriptor by ClassLookup
|
||||
|
||||
public fun getKClassType(annotations: Annotations, classDescriptor: ClassDescriptor): JetType {
|
||||
val kClassDescriptor = kClass
|
||||
if (ErrorUtils.isError(kClassDescriptor)) {
|
||||
return kClassDescriptor.getDefaultType()
|
||||
public fun getKClassType(annotations: Annotations, type: JetType): JetType {
|
||||
val descriptor = kClass
|
||||
if (ErrorUtils.isError(descriptor)) {
|
||||
return descriptor.getDefaultType()
|
||||
}
|
||||
|
||||
val typeConstructor = classDescriptor.getTypeConstructor()
|
||||
val arguments = typeConstructor.getParameters().map(TypeUtils::makeStarProjection)
|
||||
val kClassArguments = listOf(TypeProjectionImpl(
|
||||
Variance.INVARIANT,
|
||||
JetTypeImpl(Annotations.EMPTY, typeConstructor, false, arguments, classDescriptor.getMemberScope(arguments))
|
||||
))
|
||||
return JetTypeImpl(annotations, kClassDescriptor.getTypeConstructor(), false, kClassArguments,
|
||||
kClassDescriptor.getMemberScope(kClassArguments))
|
||||
val arguments = listOf(TypeProjectionImpl(Variance.INVARIANT, type))
|
||||
return JetTypeImpl(annotations, descriptor.getTypeConstructor(), false, arguments, descriptor.getMemberScope(arguments))
|
||||
}
|
||||
|
||||
public fun getKFunctionType(
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
val Array<String>.firstElement: String get() = get(0)
|
||||
|
||||
fun box(): String {
|
||||
val p = Array<String>::firstElement
|
||||
return p.get(array("OK", "Fail"))
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import kotlin.test.*
|
||||
import kotlin.reflect.jvm.*
|
||||
|
||||
fun box(): String {
|
||||
val any = Array<Any>::class
|
||||
val string = Array<String>::class
|
||||
|
||||
assertNotEquals(any, string)
|
||||
assertNotEquals(any.java, string.java)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+3
@@ -8,6 +8,9 @@ fun box(): String {
|
||||
assertEquals("Int", Int::class.simpleName)
|
||||
assertEquals("Long", Long::class.simpleName)
|
||||
|
||||
assertEquals("Array", Array<Any>::class.simpleName)
|
||||
assertEquals("Array", Array<IntArray>::class.simpleName)
|
||||
|
||||
assertEquals("Companion", Int.Companion::class.simpleName)
|
||||
assertEquals("Companion", Double.Companion::class.simpleName)
|
||||
assertEquals("Companion", Char.Companion::class.simpleName)
|
||||
|
||||
@@ -15,11 +15,13 @@ fun box(): String {
|
||||
assertEquals("class kotlin.Int", "${Int::class}")
|
||||
assertEquals("class kotlin.Int\$Companion", "${Int.Companion::class}")
|
||||
assertEquals("class kotlin.IntArray", "${IntArray::class}")
|
||||
// TODO
|
||||
// assertEquals("class kotlin.Array", "${Array<Any>::class}")
|
||||
assertEquals("class kotlin.String", "${String::class}")
|
||||
assertEquals("class kotlin.String", "${java.lang.String::class}")
|
||||
|
||||
assertEquals("class kotlin.Array", "${Array<Any>::class}")
|
||||
assertEquals("class kotlin.Array", "${Array<Int>::class}")
|
||||
assertEquals("class kotlin.Array", "${Array<Array<String>>::class}")
|
||||
|
||||
assertEquals("class java.lang.Runnable", "${Runnable::class}")
|
||||
|
||||
return "OK"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
val a01 = <!ARRAY_CLASS_LITERAL_REQUIRES_ARGUMENT!>Array::class<!>
|
||||
val a02 = Array<<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>Array<!>>::class
|
||||
val a03 = Array<Any?>::class
|
||||
val a04 = Array<Array<Any?>?>::class
|
||||
val a05 = Array<IntArray?>::class
|
||||
@@ -0,0 +1,7 @@
|
||||
package
|
||||
|
||||
internal val a01: [ERROR : Unresolved class]
|
||||
internal val a02: kotlin.reflect.KClass<kotlin.Array<[ERROR : Array]>>
|
||||
internal val a03: kotlin.reflect.KClass<kotlin.Array<kotlin.Any?>>
|
||||
internal val a04: kotlin.reflect.KClass<kotlin.Array<kotlin.Array<kotlin.Any?>?>>
|
||||
internal val a05: kotlin.reflect.KClass<kotlin.Array<kotlin.IntArray?>>
|
||||
@@ -12,3 +12,5 @@ fun foo<T : Any>() {
|
||||
val t1 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>T::class<!>
|
||||
val t2 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>T?::class<!>
|
||||
}
|
||||
|
||||
val m = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>Map<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><String><!>::class<!>
|
||||
|
||||
@@ -4,6 +4,7 @@ internal val a1: [ERROR : Unresolved class]
|
||||
internal val a2: [ERROR : Unresolved class]
|
||||
internal val l1: [ERROR : Unresolved class]
|
||||
internal val l2: [ERROR : Unresolved class]
|
||||
internal val m: [ERROR : Unresolved class]
|
||||
internal fun </*0*/ T : kotlin.Any> foo(): kotlin.Unit
|
||||
|
||||
internal final class A {
|
||||
|
||||
+6
@@ -733,6 +733,12 @@ public class JetDiagnosticsTestWithStdLibGenerated extends AbstractJetDiagnostic
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/classLiteral"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("arrays.kt")
|
||||
public void testArrays() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/classLiteral/arrays.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("genericClasses.kt")
|
||||
public void testGenericClasses() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/classLiteral/genericClasses.kt");
|
||||
|
||||
+12
@@ -758,6 +758,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
|
||||
doTestWithStdlib(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("extensionToArray.kt")
|
||||
public void testExtensionToArray() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/callableReference/property/extensionToArray.kt");
|
||||
doTestWithStdlib(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("genericProperty.kt")
|
||||
public void testGenericProperty() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/callableReference/property/genericProperty.kt");
|
||||
@@ -2709,6 +2715,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/classLiterals"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("arrays.kt")
|
||||
public void testArrays() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/classLiterals/arrays.kt");
|
||||
doTestWithStdlib(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("builtinClassLiterals.kt")
|
||||
public void testBuiltinClassLiterals() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/classLiterals/builtinClassLiterals.kt");
|
||||
|
||||
@@ -853,6 +853,10 @@ public class KotlinBuiltIns {
|
||||
return type != null && isNotNullConstructedFromGivenClass(type, FQ_NAMES.string);
|
||||
}
|
||||
|
||||
public static boolean isNonPrimitiveArray(@NotNull ClassDescriptor descriptor) {
|
||||
return FQ_NAMES.array.equals(DescriptorUtils.getFqName(descriptor));
|
||||
}
|
||||
|
||||
public static boolean isCloneable(@NotNull ClassDescriptor descriptor) {
|
||||
return FQ_NAMES.cloneable.equals(DescriptorUtils.getFqName(descriptor));
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ import org.jetbrains.kotlin.types.TypeUtils
|
||||
import kotlin.reflect.KotlinReflectionInternalError
|
||||
|
||||
object RuntimeTypeMapper : JavaToKotlinClassMapBuilder() {
|
||||
private val kotlinArrayClassId = KotlinBuiltIns.getInstance().getArray().classId
|
||||
|
||||
private val kotlinFqNameToJvmDesc = linkedMapOf<FqName, String>()
|
||||
private val kotlinFqNameToJvmDescNullable = linkedMapOf<FqName, String>()
|
||||
private val jvmDescToKotlinClassId = linkedMapOf<String, ClassId>()
|
||||
@@ -110,6 +112,10 @@ object RuntimeTypeMapper : JavaToKotlinClassMapBuilder() {
|
||||
}
|
||||
|
||||
fun mapJvmClassToKotlinClassId(klass: Class<*>): ClassId {
|
||||
if (klass.isArray() && !klass.getComponentType().isPrimitive()) {
|
||||
return kotlinArrayClassId
|
||||
}
|
||||
|
||||
return jvmDescToKotlinClassId[klass.desc] ?: klass.classId
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user