diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index c536c082aed..152dc3eba27 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -409,6 +409,8 @@ public interface Errors { DiagnosticFactory1 EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED = DiagnosticFactory1.create(ERROR); DiagnosticFactory0 CALLABLE_REFERENCE_LHS_NOT_A_CLASS = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 CLASS_LITERAL_LHS_NOT_A_CLASS = DiagnosticFactory0.create(ERROR); + // Multi-declarations DiagnosticFactory0 INITIALIZER_REQUIRED_FOR_MULTIDECLARATION = DiagnosticFactory0.create(ERROR, DEFAULT); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 35d2f3f1904..3e9976615c0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -594,7 +594,9 @@ public class DefaultErrorMessages { MAP.put(EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED, "''{0}'' is a member and an extension at the same time. References to such elements are not allowed", NAME); - MAP.put(CALLABLE_REFERENCE_LHS_NOT_A_CLASS, "Callable reference left-hand side cannot be a type parameter"); + 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"); //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); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index 1721dba98b6..cd0d1d1b98e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -453,38 +453,72 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } @Override - public JetTypeInfo visitClassLiteralExpression(@NotNull JetClassLiteralExpression expression, ExpressionTypingContext context) { - JetType receiverType = resolveDoubleColonExpressionType(expression, context); - // TODO: forbid type parameters & generic classes - ClassifierDescriptor classifier = receiverType.getConstructor().getDeclarationDescriptor(); - return JetTypeInfo.create( - components.reflectionTypes.getKClassType(Annotations.EMPTY, (ClassDescriptor) classifier), - context.dataFlowInfo - ); - } + public JetTypeInfo visitClassLiteralExpression(@NotNull JetClassLiteralExpression expression, ExpressionTypingContext c) { + ClassDescriptor descriptor = resolveClassLiteral(expression, c); + JetType type = descriptor == null + ? ErrorUtils.createErrorType("Unresolved class") + : components.reflectionTypes.getKClassType(Annotations.EMPTY, descriptor); - @Override - public JetTypeInfo visitCallableReferenceExpression(@NotNull JetCallableReferenceExpression expression, ExpressionTypingContext context) { - JetType receiverType = resolveDoubleColonExpressionType(expression, context); - - JetSimpleNameExpression callableReference = expression.getCallableReference(); - if (callableReference.getReferencedName().isEmpty()) { - context.trace.report(UNRESOLVED_REFERENCE.on(callableReference, callableReference)); - JetType errorType = ErrorUtils.createErrorType("Empty callable reference"); - return DataFlowUtils.checkType(errorType, expression, context, context.dataFlowInfo); - } - - JetType result = getCallableReferenceType(expression, receiverType, context); - return DataFlowUtils.checkType(result, expression, context, context.dataFlowInfo); + return JetTypeInfo.create(type, c.dataFlowInfo); } @Nullable - private JetType resolveDoubleColonExpressionType(@NotNull JetDoubleColonExpression expression, ExpressionTypingContext context) { + private ClassDescriptor resolveClassLiteral(@NotNull JetClassLiteralExpression expression, ExpressionTypingContext c) { JetTypeReference typeReference = expression.getTypeReference(); - return typeReference == null - ? null - : components.expressionTypingServices.getTypeResolver().resolveType(context.scope, typeReference, context.trace, false); + if (typeReference == null) { + // "::class" will mean "this::class", a class of "this" instance + c.trace.report(UNSUPPORTED.on(expression, "Class literals with empty left hand side are not yet supported")); + return null; + } + + TypeResolutionContext context = + new TypeResolutionContext(c.scope, c.trace, /* checkBounds = */ false, /* allowBareTypes = */ true); + PossiblyBareType possiblyBareType = + components.expressionTypingServices.getTypeResolver().resolvePossiblyBareType(context, typeReference); + + TypeConstructor typeConstructor = null; + if (possiblyBareType.isBare()) { + if (!possiblyBareType.isNullable()) { + typeConstructor = possiblyBareType.getBareTypeConstructor(); + } + } + else { + JetType type = possiblyBareType.getActualType(); + if (!type.isMarkedNullable() && type.getArguments().isEmpty()) { + typeConstructor = type.getConstructor(); + } + } + + if (typeConstructor != null) { + ClassifierDescriptor classifier = typeConstructor.getDeclarationDescriptor(); + if (classifier instanceof ClassDescriptor) { + return (ClassDescriptor) classifier; + } + } + + context.trace.report(CLASS_LITERAL_LHS_NOT_A_CLASS.on(expression)); + return null; + } + + @Override + public JetTypeInfo visitCallableReferenceExpression(@NotNull JetCallableReferenceExpression expression, ExpressionTypingContext c) { + JetTypeReference typeReference = expression.getTypeReference(); + + JetType receiverType = + typeReference == null + ? null + : components.expressionTypingServices.getTypeResolver().resolveType(c.scope, typeReference, c.trace, false); + + JetSimpleNameExpression callableReference = expression.getCallableReference(); + if (callableReference.getReferencedName().isEmpty()) { + c.trace.report(UNRESOLVED_REFERENCE.on(callableReference, callableReference)); + JetType errorType = ErrorUtils.createErrorType("Empty callable reference"); + return DataFlowUtils.checkType(errorType, expression, c, c.dataFlowInfo); + } + + JetType result = getCallableReferenceType(expression, receiverType, c); + return DataFlowUtils.checkType(result, expression, c, c.dataFlowInfo); } @Nullable diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/classLiterals/genericClass.kt b/compiler/testData/codegen/boxWithStdlib/reflection/classLiterals/genericClass.kt new file mode 100644 index 00000000000..cf447a2544a --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/classLiterals/genericClass.kt @@ -0,0 +1,9 @@ +import kotlin.test.assertEquals + +class Generic + +fun box(): String { + val g = Generic::class + assertEquals("Generic", g.simpleName) + return "OK" +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/classLiteral/genericClasses.kt b/compiler/testData/diagnostics/testsWithStdLib/classLiteral/genericClasses.kt new file mode 100644 index 00000000000..c179e8f92dd --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/classLiteral/genericClasses.kt @@ -0,0 +1,26 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE + +class A { + class Nested + + inner class Inner +} + +val a1 = A::class +val a2 = A<*>::class +val a3 = A::class +val a4 = A::class + +val n1 = A.Nested::class +val n2 = A.Nested<*>::class + +val i1 = A.Inner::class +val i2 = A<*>.Inner<*>::class +val i3 = A<Int>.Inner::class + +val m1 = Map::class +val m2 = Map::class +val m3 = Map.Entry::class + +val b1 = Int::class +val b2 = Nothing::class diff --git a/compiler/testData/diagnostics/testsWithStdLib/classLiteral/genericClasses.txt b/compiler/testData/diagnostics/testsWithStdLib/classLiteral/genericClasses.txt new file mode 100644 index 00000000000..1f5ff19a419 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/classLiteral/genericClasses.txt @@ -0,0 +1,37 @@ +package + +internal val a1: kotlin.reflect.KClass> +internal val a2: [ERROR : Unresolved class] +internal val a3: [ERROR : Unresolved class] +internal val a4: [ERROR : Unresolved class] +internal val b1: kotlin.reflect.KClass +internal val b2: kotlin.reflect.KClass +internal val i1: kotlin.reflect.KClass> +internal val i2: [ERROR : Unresolved class] +internal val i3: [ERROR : Unresolved class] +internal val m1: kotlin.reflect.KClass> +internal val m2: [ERROR : Unresolved class] +internal val m3: kotlin.reflect.KClass> +internal val n1: kotlin.reflect.KClass> +internal val n2: [ERROR : Unresolved class] + +internal final class A { + public constructor A() + 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 + + internal final inner class Inner { + public constructor Inner() + 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 + } + + internal final class Nested { + public constructor Nested() + 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 + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/classLiteral/nonClassesOnLHS.kt b/compiler/testData/diagnostics/testsWithStdLib/classLiteral/nonClassesOnLHS.kt new file mode 100644 index 00000000000..4d7ae895547 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/classLiteral/nonClassesOnLHS.kt @@ -0,0 +1,14 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE + +class A + +val a1 = A?::class +val a2 = A??::class + +val l1 = List?::class +val l2 = List?::class + +fun foo() { + val t1 = T::class + val t2 = T?::class +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/classLiteral/nonClassesOnLHS.txt b/compiler/testData/diagnostics/testsWithStdLib/classLiteral/nonClassesOnLHS.txt new file mode 100644 index 00000000000..114dfe03165 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/classLiteral/nonClassesOnLHS.txt @@ -0,0 +1,14 @@ +package + +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 fun foo(): kotlin.Unit + +internal final class A { + public constructor A() + 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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java index e40a125ec59..0a158b886ac 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java @@ -607,6 +607,18 @@ public class JetDiagnosticsTestWithStdLibGenerated extends AbstractJetDiagnostic JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/classLiteral"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("genericClasses.kt") + public void testGenericClasses() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/classLiteral/genericClasses.kt"); + doTest(fileName); + } + + @TestMetadata("nonClassesOnLHS.kt") + public void testNonClassesOnLHS() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/classLiteral/nonClassesOnLHS.kt"); + doTest(fileName); + } + @TestMetadata("simpleClassLiteral.kt") public void testSimpleClassLiteral() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/classLiteral/simpleClassLiteral.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index 4d8d17fccfe..44c79341893 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -2557,6 +2557,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode doTestWithStdlib(fileName); } + @TestMetadata("genericClass.kt") + public void testGenericClass() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/classLiterals/genericClass.kt"); + doTestWithStdlib(fileName); + } + @TestMetadata("simpleClassLiteral.kt") public void testSimpleClassLiteral() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/classLiterals/simpleClassLiteral.kt");