Prohibit type parameters in class literals in annotation arguments

#KT-27799 Fixed
This commit is contained in:
Alexander Udalov
2018-12-27 15:59:50 +01:00
parent c633f7568f
commit 736ac12374
11 changed files with 84 additions and 18 deletions
@@ -231,6 +231,8 @@ public interface Errors {
DiagnosticFactory0<KtExpression> ANNOTATION_ARGUMENT_MUST_BE_CONST = DiagnosticFactory0.create(ERROR); DiagnosticFactory0<KtExpression> ANNOTATION_ARGUMENT_MUST_BE_CONST = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtExpression> ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL = DiagnosticFactory0.create(ERROR); DiagnosticFactory0<KtExpression> ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtExpression> ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST = DiagnosticFactory0.create(ERROR); DiagnosticFactory0<KtExpression> ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtExpression> ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<KtExpression> ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtExpression> ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT = DiagnosticFactory0.create(ERROR); DiagnosticFactory0<KtExpression> ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtAnnotatedExpression> ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE = DiagnosticFactory0.create(WARNING); DiagnosticFactory0<KtAnnotatedExpression> ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<KtAnnotationEntry> ANNOTATION_USED_AS_ANNOTATION_ARGUMENT = DiagnosticFactory0.create(ERROR); DiagnosticFactory0<KtAnnotationEntry> ANNOTATION_USED_AS_ANNOTATION_ARGUMENT = DiagnosticFactory0.create(ERROR);
@@ -866,6 +866,8 @@ public class DefaultErrorMessages {
MAP.put(ANNOTATION_ARGUMENT_MUST_BE_CONST, "An annotation argument must be a compile-time constant"); MAP.put(ANNOTATION_ARGUMENT_MUST_BE_CONST, "An annotation argument must be a compile-time constant");
MAP.put(ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST, "An enum annotation argument must be a enum constant"); MAP.put(ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST, "An enum annotation argument must be a enum constant");
MAP.put(ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL, "An annotation argument must be a class literal (T::class)"); MAP.put(ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL, "An annotation argument must be a class literal (T::class)");
MAP.put(ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER, "Type parameter in a class literal is not allowed in an annotation argument");
MAP.put(ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR, "Type parameter in a class literal is deprecated in an annotation argument");
MAP.put(ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT, "Default value of annotation parameter must be a compile-time constant"); MAP.put(ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT, "Default value of annotation parameter must be a compile-time constant");
MAP.put(ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE, MAP.put(ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE,
@@ -127,7 +127,13 @@ class ConstantExpressionEvaluator(
val descriptor = expressionType.constructor.declarationDescriptor val descriptor = expressionType.constructor.declarationDescriptor
val diagnosticFactory = when { val diagnosticFactory = when {
DescriptorUtils.isEnumClass(descriptor) -> Errors.ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST DescriptorUtils.isEnumClass(descriptor) -> Errors.ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST
descriptor is ClassDescriptor && KotlinBuiltIns.isKClass(descriptor) -> Errors.ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL descriptor is ClassDescriptor && KotlinBuiltIns.isKClass(descriptor) -> {
if (isTypeParameterOrArrayOfTypeParameter(expressionType.arguments.singleOrNull()?.type)) {
Errors.ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR
} else {
Errors.ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL
}
}
else -> Errors.ANNOTATION_ARGUMENT_MUST_BE_CONST else -> Errors.ANNOTATION_ARGUMENT_MUST_BE_CONST
} }
@@ -177,6 +183,8 @@ class ConstantExpressionEvaluator(
} else { } else {
trace.report(Errors.ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL.on(argumentExpression)) trace.report(Errors.ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL.on(argumentExpression))
} }
} else if (doubleColonLhs is DoubleColonLHS.Type && isTypeParameterOrArrayOfTypeParameter(doubleColonLhs.type)) {
trace.report(Errors.ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER.on(argumentExpression))
} }
} }
} }
@@ -324,6 +332,13 @@ class ConstantExpressionEvaluator(
fun getPossiblyErrorConstant(expression: KtExpression, bindingContext: BindingContext): CompileTimeConstant<*>? { fun getPossiblyErrorConstant(expression: KtExpression, bindingContext: BindingContext): CompileTimeConstant<*>? {
return bindingContext.get(BindingContext.COMPILE_TIME_VALUE, expression) return bindingContext.get(BindingContext.COMPILE_TIME_VALUE, expression)
} }
internal fun isTypeParameterOrArrayOfTypeParameter(type: KotlinType?): Boolean =
when {
type == null -> false
KotlinBuiltIns.isArray(type) -> isTypeParameterOrArrayOfTypeParameter(type.arguments.singleOrNull()?.type)
else -> type.constructor.declarationDescriptor is TypeParameterDescriptor
}
} }
} }
@@ -336,6 +351,7 @@ private class ConstantExpressionEvaluatorVisitor(
private val constantExpressionEvaluator: ConstantExpressionEvaluator, private val constantExpressionEvaluator: ConstantExpressionEvaluator,
private val trace: BindingTrace private val trace: BindingTrace
) : KtVisitor<CompileTimeConstant<*>?, KotlinType>() { ) : KtVisitor<CompileTimeConstant<*>?, KotlinType>() {
private val languageVersionSettings = constantExpressionEvaluator.languageVersionSettings
private val builtIns = constantExpressionEvaluator.module.builtIns private val builtIns = constantExpressionEvaluator.module.builtIns
fun evaluate(expression: KtExpression, expectedType: KotlinType?): CompileTimeConstant<*>? { fun evaluate(expression: KtExpression, expectedType: KotlinType?): CompileTimeConstant<*>? {
@@ -613,7 +629,8 @@ private class ConstantExpressionEvaluatorVisitor(
trace.report(Errors.DIVISION_BY_ZERO.on(parentExpression)) trace.report(Errors.DIVISION_BY_ZERO.on(parentExpression))
if ((isIntegerType(argumentForReceiver.value) && isIntegerType(argumentForParameter.value)) || if ((isIntegerType(argumentForReceiver.value) && isIntegerType(argumentForParameter.value)) ||
!constantExpressionEvaluator.languageVersionSettings.supportsFeature(LanguageFeature.DivisionByZeroInConstantExpressions)) { !languageVersionSettings.supportsFeature(LanguageFeature.DivisionByZeroInConstantExpressions)
) {
return ErrorValue.create("Division by zero").wrap() return ErrorValue.create("Division by zero").wrap()
} }
} }
@@ -883,11 +900,19 @@ private class ConstantExpressionEvaluatorVisitor(
} }
override fun visitClassLiteralExpression(expression: KtClassLiteralExpression, expectedType: KotlinType?): CompileTimeConstant<*>? { override fun visitClassLiteralExpression(expression: KtClassLiteralExpression, expectedType: KotlinType?): CompileTimeConstant<*>? {
val type = trace.getType(expression)!! val kClassType = trace.getType(expression)!!
if (type.isError) return null if (kClassType.isError) return null
val descriptor = type.constructor.declarationDescriptor val descriptor = kClassType.constructor.declarationDescriptor
if (descriptor !is ClassDescriptor || !KotlinBuiltIns.isKClass(descriptor)) return null if (descriptor !is ClassDescriptor || !KotlinBuiltIns.isKClass(descriptor)) return null
return KClassValue.create(type.arguments.first().type)?.wrap()
val type = kClassType.arguments.singleOrNull()?.type ?: return null
if (languageVersionSettings.supportsFeature(LanguageFeature.ProhibitTypeParametersInClassLiteralsInAnnotationArguments) &&
ConstantExpressionEvaluator.isTypeParameterOrArrayOfTypeParameter(type)
) {
return null
}
return KClassValue.create(type)?.wrap()
} }
private fun resolveArguments(valueArguments: List<ValueArgument>, expectedType: KotlinType): List<CompileTimeConstant<*>?> { private fun resolveArguments(valueArguments: List<ValueArgument>, expectedType: KotlinType): List<CompileTimeConstant<*>?> {
@@ -983,12 +1008,7 @@ private class ConstantExpressionEvaluatorVisitor(
parameters: CompileTimeConstant.Parameters, parameters: CompileTimeConstant.Parameters,
expectedType: KotlinType expectedType: KotlinType
): CompileTimeConstant<*>? { ): CompileTimeConstant<*>? {
if (parameters.isUnsignedNumberLiteral && if (parameters.isUnsignedNumberLiteral && !checkAccessibilityOfUnsignedTypes()) {
!checkAccessibilityOfUnsignedTypes(
constantExpressionEvaluator.module,
constantExpressionEvaluator.languageVersionSettings
)
) {
return UnsignedErrorValueTypeConstant(value, parameters) return UnsignedErrorValueTypeConstant(value, parameters)
} }
@@ -1019,8 +1039,8 @@ private class ConstantExpressionEvaluatorVisitor(
}.wrap(parameters) }.wrap(parameters)
} }
private fun checkAccessibilityOfUnsignedTypes(module: ModuleDescriptor, languageVersionSettings: LanguageVersionSettings): Boolean { private fun checkAccessibilityOfUnsignedTypes(): Boolean {
val uInt = module.findClassAcrossModuleDependencies(KotlinBuiltIns.FQ_NAMES.uInt) ?: return false val uInt = constantExpressionEvaluator.module.findClassAcrossModuleDependencies(KotlinBuiltIns.FQ_NAMES.uInt) ?: return false
val accessibility = uInt.checkSinceKotlinVersionAccessibility(languageVersionSettings) val accessibility = uInt.checkSinceKotlinVersionAccessibility(languageVersionSettings)
// Case `NotAccessibleButWasExperimental` will be checked later in `checkExperimentalityOfConstantLiteral` // Case `NotAccessibleButWasExperimental` will be checked later in `checkExperimentalityOfConstantLiteral`
return accessibility is SinceKotlinAccessibility.Accessible return accessibility is SinceKotlinAccessibility.Accessible
@@ -1,4 +1,4 @@
// WITH_REFLECT // !LANGUAGE: +ProhibitTypeParametersInClassLiteralsInAnnotationArguments
import kotlin.reflect.KClass import kotlin.reflect.KClass
@@ -35,4 +35,11 @@ fun test7() {}
fun test8() {} fun test8() {}
inline val <reified T> T.test9 inline val <reified T> T.test9
get() = @Ann(T::class) object {} get() = @AnnArray(<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>arrayOf(
<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR!>T::class<!>,
<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR!>Array<T>::class<!>,
<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR!>Array<Array<Array<T>>>::class<!>
)<!>) object {}
inline val <reified T> T.test10
get() = @AnnArray(<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>[<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR!>T::class<!>]<!>) object {}
@@ -1,5 +1,6 @@
package package
public val </*0*/ reified T> T.test10: kotlin.Any
public val </*0*/ reified T> T.test9: kotlin.Any public val </*0*/ reified T> T.test9: kotlin.Any
public fun foo(): kotlin.String public fun foo(): kotlin.String
@Ann(k = kotlin.String::class) public fun test1(): kotlin.Unit @Ann(k = kotlin.String::class) public fun test1(): kotlin.Unit
@@ -0,0 +1,12 @@
// !LANGUAGE: -ProhibitTypeParametersInClassLiteralsInAnnotationArguments
import kotlin.reflect.KClass
annotation class Ann(vararg val k: KClass<*>)
inline val <reified T> T.test
get() = @Ann(
<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER!>T::class<!>,
<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER!>Array<T>::class<!>,
<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER!>Array<Array<Array<T>>>::class<!>
) object {}
@@ -0,0 +1,11 @@
package
public val </*0*/ reified T> T.test: kotlin.Any
public final annotation class Ann : kotlin.Annotation {
public constructor Ann(/*0*/ vararg k: kotlin.reflect.KClass<*> /*kotlin.Array<out kotlin.reflect.KClass<*>>*/)
public final val k: kotlin.Array<out kotlin.reflect.KClass<*>>
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
}
@@ -2991,6 +2991,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
runTest("compiler/testData/diagnostics/tests/classLiteral/inAnnotationArguments.kt"); runTest("compiler/testData/diagnostics/tests/classLiteral/inAnnotationArguments.kt");
} }
@TestMetadata("inAnnotationArguments_noTypeParams.kt")
public void testInAnnotationArguments_noTypeParams() throws Exception {
runTest("compiler/testData/diagnostics/tests/classLiteral/inAnnotationArguments_noTypeParams.kt");
}
@TestMetadata("integerValueType.kt") @TestMetadata("integerValueType.kt")
public void testIntegerValueType() throws Exception { public void testIntegerValueType() throws Exception {
runTest("compiler/testData/diagnostics/tests/classLiteral/integerValueType.kt"); runTest("compiler/testData/diagnostics/tests/classLiteral/integerValueType.kt");
@@ -2986,6 +2986,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
runTest("compiler/testData/diagnostics/tests/classLiteral/inAnnotationArguments.kt"); runTest("compiler/testData/diagnostics/tests/classLiteral/inAnnotationArguments.kt");
} }
@TestMetadata("inAnnotationArguments_noTypeParams.kt")
public void testInAnnotationArguments_noTypeParams() throws Exception {
runTest("compiler/testData/diagnostics/tests/classLiteral/inAnnotationArguments_noTypeParams.kt");
}
@TestMetadata("integerValueType.kt") @TestMetadata("integerValueType.kt")
public void testIntegerValueType() throws Exception { public void testIntegerValueType() throws Exception {
runTest("compiler/testData/diagnostics/tests/classLiteral/integerValueType.kt"); runTest("compiler/testData/diagnostics/tests/classLiteral/integerValueType.kt");
@@ -100,6 +100,7 @@ enum class LanguageFeature(
ProperInlineFromHigherPlatformDiagnostic(KOTLIN_1_4, kind = BUG_FIX), ProperInlineFromHigherPlatformDiagnostic(KOTLIN_1_4, kind = BUG_FIX),
ProhibitRepeatedUseSiteTargetAnnotations(KOTLIN_1_4, kind = BUG_FIX), ProhibitRepeatedUseSiteTargetAnnotations(KOTLIN_1_4, kind = BUG_FIX),
ProhibitUseSiteTargetAnnotationsOnSuperTypes(KOTLIN_1_4, kind = BUG_FIX), ProhibitUseSiteTargetAnnotationsOnSuperTypes(KOTLIN_1_4, kind = BUG_FIX),
ProhibitTypeParametersInClassLiteralsInAnnotationArguments(KOTLIN_1_4, kind = BUG_FIX),
ProperVisibilityForCompanionObjectInstanceField(sinceVersion = null, kind = BUG_FIX), ProperVisibilityForCompanionObjectInstanceField(sinceVersion = null, kind = BUG_FIX),
// Temporarily disabled, see KT-27084/KT-22379 // Temporarily disabled, see KT-27084/KT-22379
@@ -214,10 +214,10 @@ class KClassValue(value: Value) : ConstantValue<KClassValue.Value>(value) {
KClassValue(classId, arrayDimensions) KClassValue(classId, arrayDimensions)
} }
is TypeParameterDescriptor -> { is TypeParameterDescriptor -> {
// This is possible if a reified type parameter is used in annotation on a local class / anonymous object. // This is possible before 1.4 if a reified type parameter is used in annotation on a local class / anonymous object.
// In JVM class file, we can't represent such literal properly, so we're writing java.lang.Object instead. // In JVM class file, we can't represent such literal properly, so we're writing java.lang.Object instead.
// This has no effect on the compiler front-end or other back-ends, so we use kotlin.Any for simplicity here. // This has no effect on the compiler front-end or other back-ends, so we use kotlin.Any for simplicity here.
// Overall, it looks like such code should be disallowed: https://youtrack.jetbrains.com/issue/KT-27799 // See LanguageFeature.ProhibitTypeParametersInClassLiteralsInAnnotationArguments
KClassValue(ClassId.topLevel(KotlinBuiltIns.FQ_NAMES.any.toSafe()), 0) KClassValue(ClassId.topLevel(KotlinBuiltIns.FQ_NAMES.any.toSafe()), 0)
} }
else -> null else -> null