[IR] Support user-defined equals for MFVC

Signed-off-by: Evgeniy.Zhelenskiy <Evgeniy.Zhelenskiy@jetbrains.com>

#KT-1179
This commit is contained in:
Evgeniy.Zhelenskiy
2022-10-14 07:17:03 +02:00
committed by Space Team
parent 51f9f31a0a
commit 9f01ccc304
75 changed files with 1686 additions and 315 deletions
@@ -420,8 +420,9 @@ public interface Errors {
DiagnosticFactory0<PsiElement> VALUE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtTypeReference> VALUE_CLASS_CANNOT_EXTEND_CLASSES = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtTypeReference> VALUE_CLASS_CANNOT_BE_RECURSIVE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, String> RESERVED_MEMBER_INSIDE_VALUE_CLASS = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<PsiElement> SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_VALUE_CLASS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, String> RESERVED_MEMBER_INSIDE_VALUE_CLASS = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<KtTypeReference> TYPE_ARGUMENT_ON_TYPED_VALUE_CLASS_EQUALS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> INNER_CLASS_INSIDE_VALUE_CLASS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> VALUE_CLASS_CANNOT_BE_CLONEABLE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> INLINE_CLASS_DEPRECATED = DiagnosticFactory0.create(WARNING);
@@ -795,14 +795,15 @@ public class DefaultErrorMessages {
MAP.put(VALUE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION, "Value class cannot implement an interface by delegation if expression is not a parameter");
MAP.put(VALUE_CLASS_CANNOT_EXTEND_CLASSES, "Value class cannot extend classes");
MAP.put(VALUE_CLASS_CANNOT_BE_RECURSIVE, "Value class cannot be recursive");
MAP.put(RESERVED_MEMBER_INSIDE_VALUE_CLASS, "Member with the name ''{0}'' is reserved for future releases", STRING);
MAP.put(SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_VALUE_CLASS, "Secondary constructors with bodies are reserved for for future releases");
MAP.put(RESERVED_MEMBER_INSIDE_VALUE_CLASS, "Member with the name ''{0}'' is reserved for future releases", STRING);
MAP.put(TYPE_ARGUMENT_ON_TYPED_VALUE_CLASS_EQUALS, "Type arguments for typed value class equals must be only star projections");
MAP.put(INNER_CLASS_INSIDE_VALUE_CLASS, "Value class cannot have inner classes");
MAP.put(VALUE_CLASS_CANNOT_BE_CLONEABLE, "Value class cannot be Cloneable");
MAP.put(INLINE_CLASS_DEPRECATED, "'inline' modifier is deprecated. Use 'value' instead");
MAP.put(VALUE_CLASS_CANNOT_HAVE_CONTEXT_RECEIVERS, "Value classes cannot have context receivers");
MAP.put(INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS,
"Overriding ''equals'' from ''Any'' in inline class without operator ''equals(other: {0}): Boolean'' leads to boxing on every equality comparison",
MAP.put(INEFFICIENT_EQUALS_OVERRIDING_IN_VALUE_CLASS,
"Overriding ''equals'' from ''Any'' in value class without operator ''equals(other: {0}): Boolean'' leads to boxing on every equality comparison",
RENDER_TYPE);
MAP.put(RESULT_CLASS_IN_RETURN_TYPE, "'kotlin.Result' cannot be used as a return type");
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.isTypedEqualsInInlineClass
import org.jetbrains.kotlin.descriptors.isTypedEqualsInValueClass
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.lexer.KtTokens
@@ -52,8 +52,8 @@ object OperatorModifierChecker {
functionDescriptor.name == OperatorNameConventions.PROVIDE_DELEGATE ->
checkSupportsFeature(LanguageFeature.OperatorProvideDelegate, languageVersionSettings, diagnosticHolder, modifier)
functionDescriptor.isTypedEqualsInInlineClass() ->
checkSupportsFeature(LanguageFeature.CustomEqualsInInlineClasses, languageVersionSettings, diagnosticHolder, modifier)
functionDescriptor.isTypedEqualsInValueClass() ->
checkSupportsFeature(LanguageFeature.CustomEqualsInValueClasses, languageVersionSettings, diagnosticHolder, modifier)
}
if (functionDescriptor.name in REM_TO_MOD_OPERATION_NAMES.values &&
@@ -26,7 +26,7 @@ import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
private val javaLangCloneable = FqNameUnsafe("java.lang.Cloneable")
object InlineClassDeclarationChecker : DeclarationChecker {
object ValueClassDeclarationChecker : DeclarationChecker {
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (declaration !is KtClass) return
if (descriptor !is ClassDescriptor || !descriptor.isInline && !descriptor.isValue) return
@@ -166,20 +166,16 @@ object InlineClassDeclarationChecker : DeclarationChecker {
context.trace.bindingContext.get(BindingContext.FUNCTION, declaration)
fun isUntypedEquals(declaration: KtNamedFunction): Boolean = getFunctionDescriptor(declaration)?.overridesEqualsFromAny() ?: false
fun isTypedEquals(declaration: KtNamedFunction): Boolean = getFunctionDescriptor(declaration)?.isTypedEqualsInInlineClass() ?: false
fun isTypedEquals(declaration: KtNamedFunction): Boolean = getFunctionDescriptor(declaration)?.isTypedEqualsInValueClass() ?: false
fun KtClass.namedFunctions() = declarations.filterIsInstance<KtNamedFunction>()
if (context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInInlineClasses)) {
if (context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInValueClasses)) {
val typedEquals = declaration.namedFunctions().firstOrNull { isTypedEquals(it) }
if (typedEquals?.typeParameters?.isNotEmpty() == true) {
trace.report(Errors.TYPE_PARAMETERS_NOT_ALLOWED.on(typedEquals))
}
declaration.namedFunctions().singleOrNull { isUntypedEquals(it) }?.apply {
if (typedEquals == null) {
trace.report(
Errors.INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS.on(
Errors.INEFFICIENT_EQUALS_OVERRIDING_IN_VALUE_CLASS.on(
this@apply,
descriptor.defaultType.replaceArgumentsWithStarProjections()
)
@@ -255,10 +251,18 @@ class ReservedMembersAndConstructsForInlineClass : DeclarationChecker {
val functionName = descriptor.name.asString()
if (functionName in boxAndUnboxNames
|| (functionName in equalsAndHashCodeNames
&& !context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInInlineClasses))
&& !context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInValueClasses))
) {
val nameIdentifier = ktFunction.nameIdentifier ?: return
context.trace.report(Errors.RESERVED_MEMBER_INSIDE_VALUE_CLASS.on(nameIdentifier, functionName))
} else if (descriptor.isTypedEqualsInValueClass()) {
if (descriptor.typeParameters.isNotEmpty()) {
context.trace.report(Errors.TYPE_PARAMETERS_NOT_ALLOWED.on(declaration))
}
val parameterType = descriptor.valueParameters.first()?.type
if (parameterType != null && parameterType.arguments.any { !it.isStarProjection }) {
context.trace.report(Errors.TYPE_ARGUMENT_ON_TYPED_VALUE_CLASS_EQUALS.on(declaration.valueParameters[0].typeReference!!))
}
}
}