Prohibit Array::class, require type arguments for reified parameters

This commit is contained in:
Alexander Udalov
2015-04-08 16:35:39 +03:00
parent a0d2b00287
commit 6182b3ff5c
17 changed files with 137 additions and 39 deletions
@@ -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
@@ -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);
@@ -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"
}
@@ -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 {
@@ -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");
@@ -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
}