JVM: refactor JvmDefaultMode, remove/rename some entries

- remove ENABLE/COMPATIBILITY because they can no longer be used
- remove forAllMethodsWithBody because its behavior is now equivalent to
  isEnabled
- inline isCompatibility
- inline DEFAULT
- rename ALL_INCOMPATIBLE -> ALL
This commit is contained in:
Alexander Udalov
2024-02-01 13:22:48 +01:00
committed by Space Team
parent 6219806ab9
commit 28797a31b4
22 changed files with 58 additions and 85 deletions
@@ -155,7 +155,7 @@ internal class KtFirMetadataCalculator(override val analysisSession: KtFirAnalys
metadataVersion = metadataVersion, metadataVersion = metadataVersion,
// Technically we should use JvmDefaultMode.ALL_INCOMPATIBLE because Kapt4 always uses `-Xjvm-default=all`, but it would make // Technically we should use JvmDefaultMode.ALL_INCOMPATIBLE because Kapt4 always uses `-Xjvm-default=all`, but it would make
// the majority of tests fail because metadata of each interface will have a flag set, compared to Kapt3. // the majority of tests fail because metadata of each interface will have a flag set, compared to Kapt3.
jvmDefaultMode = JvmDefaultMode.DEFAULT, jvmDefaultMode = JvmDefaultMode.DISABLE,
stringTable, stringTable,
null, null,
null null
@@ -37,11 +37,7 @@ import org.jetbrains.kotlin.config.JvmDefaultMode
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.lexer.KtTokens.* import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.light.classes.symbol.annotations.hasJvmNameAnnotation import org.jetbrains.kotlin.light.classes.symbol.annotations.*
import org.jetbrains.kotlin.light.classes.symbol.annotations.hasJvmOverloadsAnnotation
import org.jetbrains.kotlin.light.classes.symbol.annotations.hasJvmStaticAnnotation
import org.jetbrains.kotlin.light.classes.symbol.annotations.isHiddenOrSynthetic
import org.jetbrains.kotlin.light.classes.symbol.annotations.toOptionalFilter
import org.jetbrains.kotlin.light.classes.symbol.copy import org.jetbrains.kotlin.light.classes.symbol.copy
import org.jetbrains.kotlin.light.classes.symbol.fields.SymbolLightField import org.jetbrains.kotlin.light.classes.symbol.fields.SymbolLightField
import org.jetbrains.kotlin.light.classes.symbol.fields.SymbolLightFieldForEnumEntry import org.jetbrains.kotlin.light.classes.symbol.fields.SymbolLightFieldForEnumEntry
@@ -560,11 +556,11 @@ internal fun KtSymbolWithMembers.createInnerClasses(
?.let { getModule(it) as? KtSourceModule } ?.let { getModule(it) as? KtSourceModule }
?.languageVersionSettings ?.languageVersionSettings
?.getFlag(JvmAnalysisFlags.jvmDefaultMode) ?.getFlag(JvmAnalysisFlags.jvmDefaultMode)
?: JvmDefaultMode.DEFAULT ?: JvmDefaultMode.DISABLE
if (containingClass is SymbolLightClassForInterface && if (containingClass is SymbolLightClassForInterface &&
classOrObject?.hasInterfaceDefaultImpls == true && classOrObject?.hasInterfaceDefaultImpls == true &&
jvmDefaultMode != JvmDefaultMode.ALL_INCOMPATIBLE jvmDefaultMode != JvmDefaultMode.ALL
) { ) {
result.add(SymbolLightClassForInterfaceDefaultImpls(containingClass)) result.add(SymbolLightClassForInterfaceDefaultImpls(containingClass))
} }
@@ -715,9 +715,9 @@ public class FunctionCodegen {
} }
private static boolean isCompiledInCompatibilityMode(JvmDefaultMode mode, CallableMemberDescriptor descriptor) { private static boolean isCompiledInCompatibilityMode(JvmDefaultMode mode, CallableMemberDescriptor descriptor) {
return mode.isCompatibility() || return mode == JvmDefaultMode.ALL_COMPATIBILITY ||
(mode == JvmDefaultMode.ALL_INCOMPATIBLE && (mode == JvmDefaultMode.ALL &&
JvmAnnotationUtilKt.hasJvmDefaultWithCompatibilityAnnotation(descriptor.getContainingDeclaration())); JvmAnnotationUtilKt.hasJvmDefaultWithCompatibilityAnnotation(descriptor.getContainingDeclaration()));
} }
private static void generateLocalVariableTable( private static void generateLocalVariableTable(
@@ -1681,7 +1681,7 @@ public class FunctionCodegen {
// Fake overrides in interfaces should be expanded to implementation to make proper default check // Fake overrides in interfaces should be expanded to implementation to make proper default check
if (JvmAnnotationUtilKt.checkIsImplementationCompiledToJvmDefault(memberDescriptor, mode)) { if (JvmAnnotationUtilKt.checkIsImplementationCompiledToJvmDefault(memberDescriptor, mode)) {
boolean isCompatibilityMode = isCompiledInCompatibilityMode(mode, memberDescriptor); boolean isCompatibilityMode = isCompiledInCompatibilityMode(mode, memberDescriptor);
boolean isSyntheticInCompatibilityOrJvmDefault = isSynthetic && (isCompatibilityMode || mode == JvmDefaultMode.ENABLE); boolean isSyntheticInCompatibilityOrJvmDefault = isSynthetic && isCompatibilityMode;
return (kind != OwnerKind.DEFAULT_IMPLS && !isSyntheticInCompatibilityOrJvmDefault) || return (kind != OwnerKind.DEFAULT_IMPLS && !isSyntheticInCompatibilityOrJvmDefault) ||
(kind == OwnerKind.DEFAULT_IMPLS && (kind == OwnerKind.DEFAULT_IMPLS &&
(isSyntheticInCompatibilityOrJvmDefault || (isSyntheticInCompatibilityOrJvmDefault ||
@@ -591,7 +591,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
} }
return AsmUtil.asmTypeByClassId( return AsmUtil.asmTypeByClassId(
DescriptorUtils.isInterface(descriptor) && !jvmDefaultMode.getForAllMethodsWithBody() DescriptorUtils.isInterface(descriptor) && !jvmDefaultMode.isEnabled()
? classId.createNestedClassId(Name.identifier(JvmAbi.DEFAULT_IMPLS_CLASS_NAME)) ? classId.createNestedClassId(Name.identifier(JvmAbi.DEFAULT_IMPLS_CLASS_NAME))
: classId : classId
); );
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.codegen.*;
import org.jetbrains.kotlin.codegen.binding.MutableClosure; import org.jetbrains.kotlin.codegen.binding.MutableClosure;
import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper; import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.config.JvmDefaultMode;
import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities; import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities;
import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.name.Name;
@@ -652,13 +651,6 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
return descriptor; return descriptor;
} }
//in other default modes there shouldn't be any accessors form DefaultImpls to Interface cause all compiled inside interface
if (getState().getJvmDefaultMode() == JvmDefaultMode.ENABLE &&
JvmAnnotationUtilKt.hasJvmDefaultAnnotation(descriptor) &&
descriptorContext instanceof DefaultImplsClassContext) {
descriptorContext = ((DefaultImplsClassContext) descriptorContext).getInterfaceContext();
}
if (descriptor instanceof PropertyDescriptor) { if (descriptor instanceof PropertyDescriptor) {
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor; PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
int propertyAccessFlag = getVisibilityAccessFlag(descriptor); int propertyAccessFlag = getVisibilityAccessFlag(descriptor);
@@ -75,17 +75,17 @@ class JvmSerializerExtension @JvmOverloads constructor(
} }
//TODO: support local delegated properties in new defaults scheme //TODO: support local delegated properties in new defaults scheme
val containerAsmType = val containerAsmType =
if (isInterface(descriptor) && !jvmDefaultMode.forAllMethodsWithBody) typeMapper.mapDefaultImpls(descriptor) else typeMapper.mapClass(descriptor) if (isInterface(descriptor) && !jvmDefaultMode.isEnabled) typeMapper.mapDefaultImpls(descriptor) else typeMapper.mapClass(descriptor)
writeLocalProperties(proto, containerAsmType, JvmProtoBuf.classLocalVariable) writeLocalProperties(proto, containerAsmType, JvmProtoBuf.classLocalVariable)
writeVersionRequirementForJvmDefaultIfNeeded(descriptor, proto, versionRequirementTable) writeVersionRequirementForJvmDefaultIfNeeded(descriptor, proto, versionRequirementTable)
if (jvmDefaultMode.forAllMethodsWithBody && isInterface(descriptor)) { if (jvmDefaultMode.isEnabled && isInterface(descriptor)) {
proto.setExtension( proto.setExtension(
JvmProtoBuf.jvmClassFlags, JvmProtoBuf.jvmClassFlags,
JvmFlags.getClassFlags( JvmFlags.getClassFlags(
jvmDefaultMode.forAllMethodsWithBody, true,
(JvmDefaultMode.ALL_COMPATIBILITY == jvmDefaultMode && !descriptor.hasJvmDefaultNoCompatibilityAnnotation()) || (JvmDefaultMode.ALL_COMPATIBILITY == jvmDefaultMode && !descriptor.hasJvmDefaultNoCompatibilityAnnotation()) ||
(JvmDefaultMode.ALL_INCOMPATIBLE == jvmDefaultMode && descriptor.hasJvmDefaultWithCompatibilityAnnotation()) (JvmDefaultMode.ALL == jvmDefaultMode && descriptor.hasJvmDefaultWithCompatibilityAnnotation())
) )
) )
} }
@@ -99,7 +99,7 @@ class JvmSerializerExtension @JvmOverloads constructor(
versionRequirementTable: MutableVersionRequirementTable versionRequirementTable: MutableVersionRequirementTable
) { ) {
if (isInterface(classDescriptor)) { if (isInterface(classDescriptor)) {
if (jvmDefaultMode == JvmDefaultMode.ALL_INCOMPATIBLE) { if (jvmDefaultMode == JvmDefaultMode.ALL) {
builder.addVersionRequirement( builder.addVersionRequirement(
writeVersionRequirement(1, 4, 0, ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION, versionRequirementTable) writeVersionRequirement(1, 4, 0, ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION, versionRequirementTable)
) )
@@ -466,7 +466,7 @@ The default value is 'warn'."""
and the class is not annotated with '@JvmDefaultWithoutCompatibility' (see KT-39603 for more details). and the class is not annotated with '@JvmDefaultWithoutCompatibility' (see KT-39603 for more details).
-Xjvm-default=disable Default behavior. Do not generate JVM default methods.""" -Xjvm-default=disable Default behavior. Do not generate JVM default methods."""
) )
var jvmDefault: String = JvmDefaultMode.DEFAULT.description var jvmDefault: String = JvmDefaultMode.DISABLE.description
set(value) { set(value) {
checkFrozen() checkFrozen()
field = value field = value
@@ -846,11 +846,7 @@ This option is deprecated and will be deleted in future versions."""
result[JvmAnalysisFlags.jvmDefaultMode] = it result[JvmAnalysisFlags.jvmDefaultMode] = it
} ?: collector.report( } ?: collector.report(
CompilerMessageSeverity.ERROR, CompilerMessageSeverity.ERROR,
"Unknown -Xjvm-default mode: $jvmDefault, supported modes: ${ "Unknown -Xjvm-default mode: $jvmDefault, supported modes: ${JvmDefaultMode.values().map(JvmDefaultMode::description)}"
JvmDefaultMode.values().mapNotNull { mode ->
mode.description.takeIf { JvmDefaultMode.fromStringOrNull(it) != null }
}
}"
) )
result[JvmAnalysisFlags.inheritMultifileParts] = inheritMultifileParts result[JvmAnalysisFlags.inheritMultifileParts] = inheritMultifileParts
result[JvmAnalysisFlags.sanitizeParentheses] = sanitizeParentheses result[JvmAnalysisFlags.sanitizeParentheses] = sanitizeParentheses
@@ -869,7 +865,7 @@ This option is deprecated and will be deleted in future versions."""
if (enhanceTypeParameterTypesToDefNotNull) { if (enhanceTypeParameterTypesToDefNotNull) {
result[LanguageFeature.ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated] = LanguageFeature.State.ENABLED result[LanguageFeature.ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated] = LanguageFeature.State.ENABLED
} }
if (JvmDefaultMode.fromStringOrNull(jvmDefault)?.forAllMethodsWithBody == true) { if (JvmDefaultMode.fromStringOrNull(jvmDefault)?.isEnabled == true) {
result[LanguageFeature.ForbidSuperDelegationToAbstractFakeOverride] = LanguageFeature.State.ENABLED result[LanguageFeature.ForbidSuperDelegationToAbstractFakeOverride] = LanguageFeature.State.ENABLED
result[LanguageFeature.AbstractClassMemberNotImplementedWithIntermediateAbstractClass] = LanguageFeature.State.ENABLED result[LanguageFeature.AbstractClassMemberNotImplementedWithIntermediateAbstractClass] = LanguageFeature.State.ENABLED
} }
@@ -75,16 +75,8 @@ fun CompilerConfiguration.setupJvmSpecificArguments(arguments: K2JVMCompilerArgu
} }
} }
val jvmDefaultMode = languageVersionSettings.getFlag(JvmAnalysisFlags.jvmDefaultMode)
val jvmTarget = get(JVMConfigurationKeys.JVM_TARGET) ?: JvmTarget.DEFAULT val jvmTarget = get(JVMConfigurationKeys.JVM_TARGET) ?: JvmTarget.DEFAULT
if (jvmDefaultMode == JvmDefaultMode.ENABLE || jvmDefaultMode == JvmDefaultMode.ENABLE_WITH_DEFAULT_IMPLS) {
messageCollector.report(
WARNING,
"'-Xjvm-default=${jvmDefaultMode.description}' is deprecated, please use '-Xjvm-default=all|all-compatibility'"
)
}
val stringConcat = arguments.stringConcat val stringConcat = arguments.stringConcat
if (stringConcat != null) { if (stringConcat != null) {
val runtimeStringConcat = JvmStringConcat.fromString(stringConcat) val runtimeStringConcat = JvmStringConcat.fromString(stringConcat)
@@ -24,7 +24,7 @@ fun <D> FirBasedSymbol<out D>.isCompiledToJvmDefault(
if (getAnnotationByClassId(JvmStandardClassIds.Annotations.JvmDefault, session) != null) return true if (getAnnotationByClassId(JvmStandardClassIds.Annotations.JvmDefault, session) != null) return true
val container = getContainingClassSymbol(session) val container = getContainingClassSymbol(session)
if (container !is FirRegularClassSymbol || container.origin.fromSource) return jvmDefaultMode.forAllMethodsWithBody if (container !is FirRegularClassSymbol || container.origin.fromSource) return jvmDefaultMode.isEnabled
// Opt-in is fine here because this flag is only possible for deserialized declarations, and it's set during deserialization. // Opt-in is fine here because this flag is only possible for deserialized declarations, and it's set during deserialization.
@OptIn(SymbolInternals::class) @OptIn(SymbolInternals::class)
@@ -36,7 +36,7 @@ object FirJvmDefaultChecker : FirBasicDeclarationChecker(MppCheckerKind.Common)
if (annotationWithCompatibility != null) { if (annotationWithCompatibility != null) {
val source = annotationWithCompatibility.source val source = annotationWithCompatibility.source
when { when {
jvmDefaultMode != JvmDefaultMode.ALL_INCOMPATIBLE -> { jvmDefaultMode != JvmDefaultMode.ALL -> {
reporter.reportOn(source, FirJvmErrors.JVM_DEFAULT_WITH_COMPATIBILITY_IN_DECLARATION, context) reporter.reportOn(source, FirJvmErrors.JVM_DEFAULT_WITH_COMPATIBILITY_IN_DECLARATION, context)
return return
} }
@@ -27,7 +27,10 @@ import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.providers.getRegularClassSymbolByClassId import org.jetbrains.kotlin.fir.resolve.providers.getRegularClassSymbolByClassId
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
import org.jetbrains.kotlin.fir.serialization.* import org.jetbrains.kotlin.fir.serialization.FirAdditionalMetadataProvider
import org.jetbrains.kotlin.fir.serialization.FirElementAwareStringTable
import org.jetbrains.kotlin.fir.serialization.FirElementSerializer
import org.jetbrains.kotlin.fir.serialization.FirSerializerExtension
import org.jetbrains.kotlin.fir.serialization.constant.ConstValueProvider import org.jetbrains.kotlin.fir.serialization.constant.ConstValueProvider
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.ir.declarations.MetadataSource import org.jetbrains.kotlin.ir.declarations.MetadataSource
@@ -100,14 +103,14 @@ class FirJvmSerializerExtension(
writeLocalProperties(proto, JvmProtoBuf.classLocalVariable) writeLocalProperties(proto, JvmProtoBuf.classLocalVariable)
writeVersionRequirementForJvmDefaultIfNeeded(klass, proto, versionRequirementTable) writeVersionRequirementForJvmDefaultIfNeeded(klass, proto, versionRequirementTable)
if (jvmDefaultMode.forAllMethodsWithBody && klass is FirRegularClass && klass.classKind == ClassKind.INTERFACE) { if (jvmDefaultMode.isEnabled && klass is FirRegularClass && klass.classKind == ClassKind.INTERFACE) {
proto.setExtension( proto.setExtension(
JvmProtoBuf.jvmClassFlags, JvmProtoBuf.jvmClassFlags,
JvmFlags.getClassFlags( JvmFlags.getClassFlags(
jvmDefaultMode.forAllMethodsWithBody, true,
(JvmDefaultMode.ALL_COMPATIBILITY == jvmDefaultMode && (JvmDefaultMode.ALL_COMPATIBILITY == jvmDefaultMode &&
!klass.hasAnnotation(JVM_DEFAULT_NO_COMPATIBILITY_CLASS_ID, session)) || !klass.hasAnnotation(JVM_DEFAULT_NO_COMPATIBILITY_CLASS_ID, session)) ||
(JvmDefaultMode.ALL_INCOMPATIBLE == jvmDefaultMode && (JvmDefaultMode.ALL == jvmDefaultMode &&
klass.hasAnnotation(JVM_DEFAULT_WITH_COMPATIBILITY_CLASS_ID, session)) klass.hasAnnotation(JVM_DEFAULT_WITH_COMPATIBILITY_CLASS_ID, session))
) )
) )
@@ -135,7 +138,7 @@ class FirJvmSerializerExtension(
versionRequirementTable: MutableVersionRequirementTable versionRequirementTable: MutableVersionRequirementTable
) { ) {
if (klass is FirRegularClass && klass.classKind == ClassKind.INTERFACE) { if (klass is FirRegularClass && klass.classKind == ClassKind.INTERFACE) {
if (jvmDefaultMode == JvmDefaultMode.ALL_INCOMPATIBLE) { if (jvmDefaultMode == JvmDefaultMode.ALL) {
builder.addVersionRequirement( builder.addVersionRequirement(
DescriptorSerializer.writeVersionRequirement( DescriptorSerializer.writeVersionRequirement(
1, 1,
@@ -6,7 +6,10 @@
package org.jetbrains.kotlin.resolve.jvm.annotations package org.jetbrains.kotlin.resolve.jvm.annotations
import org.jetbrains.kotlin.config.JvmDefaultMode import org.jetbrains.kotlin.config.JvmDefaultMode
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME
@@ -68,7 +71,7 @@ fun CallableMemberDescriptor.isCompiledToJvmDefault(jvmDefault: JvmDefaultMode):
// } // }
if (directMember.annotations.hasAnnotation(JVM_DEFAULT_FQ_NAME)) return true if (directMember.annotations.hasAnnotation(JVM_DEFAULT_FQ_NAME)) return true
if (clazz !is DeserializedClassDescriptor) return jvmDefault.forAllMethodsWithBody if (clazz !is DeserializedClassDescriptor) return jvmDefault.isEnabled
return JvmProtoBufUtil.isNewPlaceForBodyGeneration(clazz.classProto) return JvmProtoBufUtil.isNewPlaceForBodyGeneration(clazz.classProto)
} }
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.resolve.jvm.checkers
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.config.JvmAnalysisFlags import org.jetbrains.kotlin.config.JvmAnalysisFlags
import org.jetbrains.kotlin.config.JvmDefaultMode import org.jetbrains.kotlin.config.JvmDefaultMode
import org.jetbrains.kotlin.config.JvmDefaultMode.ALL_COMPATIBILITY
import org.jetbrains.kotlin.config.JvmTarget import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
@@ -44,9 +45,10 @@ class JvmDefaultChecker(private val jvmTarget: JvmTarget, project: Project) : De
// report error because absent of it's can affect library ABI // report error because absent of it's can affect library ABI
// 2. If it's mixed hierarchy with implicit override in base class and override one in inherited derived interface report error. // 2. If it's mixed hierarchy with implicit override in base class and override one in inherited derived interface report error.
// Otherwise the implicit class override would be used for dispatching method calls (but not more specialized) // Otherwise the implicit class override would be used for dispatching method calls (but not more specialized)
val performSpecializationCheck = jvmDefaultMode.isCompatibility && !descriptor.hasJvmDefaultNoCompatibilityAnnotation() && val performSpecializationCheck =
//TODO: maybe remove this check for JVM compatibility jvmDefaultMode == JvmDefaultMode.ALL_COMPATIBILITY && !descriptor.hasJvmDefaultNoCompatibilityAnnotation() &&
!(descriptor.modality !== Modality.OPEN && descriptor.modality !== Modality.ABSTRACT || descriptor.isEffectivelyPrivateApi) //TODO: maybe remove this check for JVM compatibility
!(descriptor.modality !== Modality.OPEN && descriptor.modality !== Modality.ABSTRACT || descriptor.isEffectivelyPrivateApi)
//Should we check clash with implicit class member (that comes from old compilation scheme) and specialization for compatibility mode //Should we check clash with implicit class member (that comes from old compilation scheme) and specialization for compatibility mode
// If specialization check is reported clash one shouldn't be reported // If specialization check is reported clash one shouldn't be reported
@@ -71,7 +73,7 @@ class JvmDefaultChecker(private val jvmTarget: JvmTarget, project: Project) : De
} else if (actualImplementation is PropertyDescriptor && inheritedMember is PropertyDescriptor) { } else if (actualImplementation is PropertyDescriptor && inheritedMember is PropertyDescriptor) {
val getterImpl = actualImplementation.getter val getterImpl = actualImplementation.getter
val getterInherited = inheritedMember.getter val getterInherited = inheritedMember.getter
if (getterImpl == null || getterInherited == null || !jvmDefaultMode.isCompatibility || if (getterImpl == null || getterInherited == null || jvmDefaultMode != ALL_COMPATIBILITY ||
checkSpecializationInCompatibilityMode( checkSpecializationInCompatibilityMode(
getterInherited, getterInherited,
getterImpl, getterImpl,
@@ -117,7 +119,7 @@ class JvmDefaultChecker(private val jvmTarget: JvmTarget, project: Project) : De
descriptor.annotations.findAnnotation(JVM_DEFAULT_WITH_COMPATIBILITY_FQ_NAME)?.let { annotationDescriptor -> descriptor.annotations.findAnnotation(JVM_DEFAULT_WITH_COMPATIBILITY_FQ_NAME)?.let { annotationDescriptor ->
val reportOn = DescriptorToSourceUtils.getSourceFromAnnotation(annotationDescriptor) ?: declaration val reportOn = DescriptorToSourceUtils.getSourceFromAnnotation(annotationDescriptor) ?: declaration
if (jvmDefaultMode != JvmDefaultMode.ALL_INCOMPATIBLE) { if (jvmDefaultMode != JvmDefaultMode.ALL) {
context.trace.report(ErrorsJvm.JVM_DEFAULT_WITH_COMPATIBILITY_IN_DECLARATION.on(reportOn)) context.trace.report(ErrorsJvm.JVM_DEFAULT_WITH_COMPATIBILITY_IN_DECLARATION.on(reportOn))
return true return true
} else if (!isInterface(descriptor)) { } else if (!isInterface(descriptor)) {
@@ -19,7 +19,10 @@ import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.expressions.IrReturn import org.jetbrains.kotlin.ir.expressions.IrReturn
import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.defaultType import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.util.functions import org.jetbrains.kotlin.ir.util.functions
@@ -65,8 +68,8 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
private fun handleInterface(irClass: IrClass) { private fun handleInterface(irClass: IrClass) {
val jvmDefaultMode = context.config.jvmDefaultMode val jvmDefaultMode = context.config.jvmDefaultMode
val isCompatibilityMode = val isCompatibilityMode =
(jvmDefaultMode.isCompatibility && !irClass.hasJvmDefaultNoCompatibilityAnnotation()) || (jvmDefaultMode == JvmDefaultMode.ALL_COMPATIBILITY && !irClass.hasJvmDefaultNoCompatibilityAnnotation()) ||
(jvmDefaultMode == JvmDefaultMode.ALL_INCOMPATIBLE && irClass.hasJvmDefaultWithCompatibilityAnnotation()) (jvmDefaultMode == JvmDefaultMode.ALL && irClass.hasJvmDefaultWithCompatibilityAnnotation())
// There are 6 cases for functions on interfaces: // There are 6 cases for functions on interfaces:
for (function in irClass.functions) { for (function in irClass.functions) {
when { when {
@@ -130,8 +133,7 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
|| function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER || function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER
|| function.origin == JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_OR_TYPEALIAS_ANNOTATIONS)) || || function.origin == JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_OR_TYPEALIAS_ANNOTATIONS)) ||
(function.origin == JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_OR_TYPEALIAS_ANNOTATIONS && (function.origin == JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_OR_TYPEALIAS_ANNOTATIONS &&
(isCompatibilityMode || jvmDefaultMode == JvmDefaultMode.ENABLE) && isCompatibilityMode && function.isCompiledToJvmDefault(jvmDefaultMode)) -> {
function.isCompiledToJvmDefault(jvmDefaultMode)) -> {
if (function.origin == JvmLoweredDeclarationOrigin.INLINE_LAMBDA) { if (function.origin == JvmLoweredDeclarationOrigin.INLINE_LAMBDA) {
//move as is //move as is
val defaultImplsClass = context.cachedDeclarations.getDefaultImplsClass(irClass) val defaultImplsClass = context.cachedDeclarations.getDefaultImplsClass(irClass)
@@ -177,7 +179,7 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
// Move $$delegatedProperties array and $assertionsDisabled field // Move $$delegatedProperties array and $assertionsDisabled field
for (field in irClass.declarations.filterIsInstance<IrField>()) { for (field in irClass.declarations.filterIsInstance<IrField>()) {
if ((jvmDefaultMode.forAllMethodsWithBody || field.origin != JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE) && if ((jvmDefaultMode.isEnabled || field.origin != JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE) &&
field.origin != JvmLoweredDeclarationOrigin.GENERATED_ASSERTION_ENABLED_FIELD field.origin != JvmLoweredDeclarationOrigin.GENERATED_ASSERTION_ENABLED_FIELD
) )
continue continue
@@ -218,7 +218,8 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : IrEle
isFinal = true isFinal = true
isStatic = true isStatic = true
visibility = visibility =
if (irClass.isInterface && context.config.jvmDefaultMode.forAllMethodsWithBody) DescriptorVisibilities.PUBLIC else JavaDescriptorVisibilities.PACKAGE_VISIBILITY if (irClass.isInterface && context.config.jvmDefaultMode.isEnabled) DescriptorVisibilities.PUBLIC
else JavaDescriptorVisibilities.PACKAGE_VISIBILITY
} }
val localProperties = mutableListOf<IrLocalDelegatedPropertySymbol>() val localProperties = mutableListOf<IrLocalDelegatedPropertySymbol>()
@@ -98,7 +98,7 @@ fun IrSimpleFunction.isCompiledToJvmDefault(jvmDefaultMode: JvmDefaultMode): Boo
} }
is IrMaybeDeserializedClass -> return klass.isNewPlaceForBodyGeneration is IrMaybeDeserializedClass -> return klass.isNewPlaceForBodyGeneration
} }
return jvmDefaultMode.forAllMethodsWithBody return jvmDefaultMode.isEnabled
} }
fun IrFunction.hasJvmDefault(): Boolean = propertyIfAccessor.hasAnnotation(JVM_DEFAULT_FQ_NAME) fun IrFunction.hasJvmDefault(): Boolean = propertyIfAccessor.hasAnnotation(JVM_DEFAULT_FQ_NAME)
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.codegen.createFreeFakeLambdaDescriptor import org.jetbrains.kotlin.codegen.createFreeFakeLambdaDescriptor
import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.ir.declarations.DescriptorMetadataSource import org.jetbrains.kotlin.ir.declarations.DescriptorMetadataSource
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
@@ -59,10 +58,8 @@ class DescriptorMetadataSerializer(
context.state.bindingTrace.record( context.state.bindingTrace.record(
CodegenBinding.DELEGATED_PROPERTIES_WITH_METADATA, CodegenBinding.DELEGATED_PROPERTIES_WITH_METADATA,
// key for local delegated properties metadata in interfaces depends on jvmDefaultMode // key for local delegated properties metadata in interfaces depends on jvmDefaultMode
if (irClass.isInterface && !context.config.jvmDefaultMode.forAllMethodsWithBody) context.defaultTypeMapper.mapClass( if (irClass.isInterface && !context.config.jvmDefaultMode.isEnabled) context.defaultTypeMapper.mapClass(
context.cachedDeclarations.getDefaultImplsClass( context.cachedDeclarations.getDefaultImplsClass(irClass)
irClass
)
) else type, ) else type,
localDelegatedProperties.mapNotNull { (it.owner.metadata as? DescriptorMetadataSource.LocalDelegatedProperty)?.descriptor } localDelegatedProperties.mapNotNull { (it.owner.metadata as? DescriptorMetadataSource.LocalDelegatedProperty)?.descriptor }
) )
@@ -171,7 +171,7 @@ abstract class KtLightClassImpl(
.filter { it.name != null } .filter { it.name != null }
.mapNotNullTo(result, KtClassOrObject::toLightClass) .mapNotNullTo(result, KtClassOrObject::toLightClass)
if (classOrObject.hasInterfaceDefaultImpls && jvmDefaultMode != JvmDefaultMode.ALL_INCOMPATIBLE) { if (classOrObject.hasInterfaceDefaultImpls && jvmDefaultMode != JvmDefaultMode.ALL) {
result.add(createClassForInterfaceDefaultImpls()) result.add(createClassForInterfaceDefaultImpls())
} }
@@ -66,7 +66,7 @@ class JvmVersionRequirementTest : AbstractVersionRequirementTest() {
fun testAllJvmDefault() { fun testAllJvmDefault() {
doTest( doTest(
VersionRequirement.Version(1, 4, 0), DeprecationLevel.ERROR, null, COMPILER_VERSION, null, VersionRequirement.Version(1, 4, 0), DeprecationLevel.ERROR, null, COMPILER_VERSION, null,
analysisFlags = mapOf(JvmAnalysisFlags.jvmDefaultMode to JvmDefaultMode.ALL_INCOMPATIBLE), analysisFlags = mapOf(JvmAnalysisFlags.jvmDefaultMode to JvmDefaultMode.ALL),
fqNamesWithRequirements = listOf( fqNamesWithRequirements = listOf(
"test.Base", "test.Base",
"test.Derived", "test.Derived",
@@ -7,29 +7,18 @@ package org.jetbrains.kotlin.config
enum class JvmDefaultMode(val description: String) { enum class JvmDefaultMode(val description: String) {
DISABLE("disable"), DISABLE("disable"),
ENABLE("enable"),
ENABLE_WITH_DEFAULT_IMPLS("compatibility"),
ALL_COMPATIBILITY("all-compatibility"), ALL_COMPATIBILITY("all-compatibility"),
ALL_INCOMPATIBLE("all"); ALL("all");
val isEnabled: Boolean val isEnabled: Boolean
get() = this != DISABLE get() = this != DISABLE
val isCompatibility: Boolean
get() = this == ENABLE_WITH_DEFAULT_IMPLS || this == ALL_COMPATIBILITY
val forAllMethodsWithBody: Boolean
get() = this == ALL_COMPATIBILITY || this == ALL_INCOMPATIBLE
companion object { companion object {
@JvmField
val DEFAULT = DISABLE
@JvmStatic @JvmStatic
fun fromStringOrNull(string: String?): JvmDefaultMode? = when (string) { fun fromStringOrNull(string: String?): JvmDefaultMode? = when (string) {
DISABLE.description -> DISABLE DISABLE.description -> DISABLE
ALL_COMPATIBILITY.description -> ALL_COMPATIBILITY ALL_COMPATIBILITY.description -> ALL_COMPATIBILITY
ALL_INCOMPATIBLE.description -> ALL_INCOMPATIBLE ALL.description -> ALL
else -> null else -> null
} }
} }
@@ -896,7 +896,7 @@ open class KotlinJpsBuildTest : KotlinJpsBuildTestBase() {
val facet = KotlinFacetSettings() val facet = KotlinFacetSettings()
facet.useProjectSettings = false facet.useProjectSettings = false
facet.compilerArguments = K2JVMCompilerArguments() facet.compilerArguments = K2JVMCompilerArguments()
(facet.compilerArguments as K2JVMCompilerArguments).jvmDefault = JvmDefaultMode.DEFAULT.description (facet.compilerArguments as K2JVMCompilerArguments).jvmDefault = JvmDefaultMode.DISABLE.description
it.container.setChild( it.container.setChild(
JpsKotlinFacetModuleExtension.KIND, JpsKotlinFacetModuleExtension.KIND,
@@ -20,7 +20,7 @@ class Kapt4EnvironmentConfigurator(testServices: TestServices) : EnvironmentConf
languageVersion: LanguageVersion, languageVersion: LanguageVersion,
): Map<AnalysisFlag<*>, Any?> = mapOf( ): Map<AnalysisFlag<*>, Any?> = mapOf(
JvmAnalysisFlags.generatePropertyAnnotationsMethods to true, JvmAnalysisFlags.generatePropertyAnnotationsMethods to true,
JvmAnalysisFlags.jvmDefaultMode to JvmDefaultMode.ALL_INCOMPATIBLE JvmAnalysisFlags.jvmDefaultMode to JvmDefaultMode.ALL
) )
override val directiveContainers: List<DirectivesContainer> = listOf(Kapt4TestDirectives) override val directiveContainers: List<DirectivesContainer> = listOf(Kapt4TestDirectives)