diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java index a19850db5ef..45d18e320e9 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java @@ -24882,6 +24882,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation.kt"); } + @Test + @TestMetadata("delegation2.kt") + public void testDelegation2() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation2.kt"); + } + @Test @TestMetadata("disabledFeature.kt") public void testDisabledFeature() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java index 5d4012b2ecb..74c27b7fe2c 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java @@ -24882,6 +24882,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation.kt"); } + @Test + @TestMetadata("delegation2.kt") + public void testDelegation2() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation2.kt"); + } + @Test @TestMetadata("disabledFeature.kt") public void testDisabledFeature() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java index 5171b29f0b4..644295e0d31 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java @@ -1243,6 +1243,12 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst runTest("compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation.kt"); } + @Test + @TestMetadata("delegation2.kt") + public void testDelegation2() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation2.kt"); + } + @Test @TestMetadata("disabledFeature.kt") public void testDisabledFeature() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java index 6bf64a99c1a..926923aa758 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java @@ -1243,6 +1243,12 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi runTest("compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation.kt"); } + @Test + @TestMetadata("delegation2.kt") + public void testDelegation2() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation2.kt"); + } + @Test @TestMetadata("disabledFeature.kt") public void testDisabledFeature() throws Exception { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt index 178d93723cc..d201eff39d0 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt @@ -142,7 +142,6 @@ object CommonDeclarationCheckers : DeclarationCheckers() { FirMultipleDefaultsInheritedFromSupertypesChecker, FirFiniteBoundRestrictionChecker, FirNonExpansiveInheritanceRestrictionChecker, - FirDefaultArgumentsInExpectActualizedByFakeOverrideChecker, ) override val constructorCheckers: Set diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDefaultArgumentsInExpectActualizedByFakeOverrideChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDefaultArgumentsInExpectActualizedByFakeOverrideChecker.kt deleted file mode 100644 index 035efa7a59d..00000000000 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDefaultArgumentsInExpectActualizedByFakeOverrideChecker.kt +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. - */ - -package org.jetbrains.kotlin.fir.analysis.checkers.declaration - -import org.jetbrains.kotlin.config.LanguageFeature -import org.jetbrains.kotlin.diagnostics.DiagnosticReporter -import org.jetbrains.kotlin.diagnostics.reportOn -import org.jetbrains.kotlin.fir.FirExpectActualMatchingContext -import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext -import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors -import org.jetbrains.kotlin.fir.declarations.FirRegularClass -import org.jetbrains.kotlin.fir.declarations.getSingleMatchedExpectForActualOrNull -import org.jetbrains.kotlin.fir.declarations.utils.isActual -import org.jetbrains.kotlin.fir.expectActualMatchingContextFactory -import org.jetbrains.kotlin.fir.isDelegated -import org.jetbrains.kotlin.fir.isSubstitutionOrIntersectionOverride -import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol -import org.jetbrains.kotlin.fir.types.classId -import org.jetbrains.kotlin.mpp.DeclarationSymbolMarker -import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualMatcher -import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility - -// TODO KT-62913 create one more ExpectActualCheckingCompatibility incompatibility, and replace this checker with this incompatibility -internal object FirDefaultArgumentsInExpectActualizedByFakeOverrideChecker : FirRegularClassChecker() { - override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) { - if (!context.languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects) || - !context.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitDefaultArgumentsInExpectActualizedByFakeOverride)) { - return - } - if (!declaration.isActual) { - return - } - val actualClassSymbol = declaration.symbol - // We want to report errors even if a candidate is incompatible, but it's single - val expectedSingleCandidate = actualClassSymbol.getSingleMatchedExpectForActualOrNull() ?: return - val expectClassSymbol = expectedSingleCandidate as FirRegularClassSymbol - - val expectActualMatchingContext = context.session.expectActualMatchingContextFactory.create( - context.session, context.scopeSession, - allowedWritingMemberExpectForActualMapping = true, - ) - AbstractExpectActualMatcher.recursivelyMatchClassScopes(expectClassSymbol, actualClassSymbol, expectActualMatchingContext) - - val matchingContext = context.session.expectActualMatchingContextFactory.create(context.session, context.scopeSession) - val problematicExpectMembers = with(matchingContext) { findProblematicExpectMembers(expectClassSymbol, actualClassSymbol) } - if (problematicExpectMembers.isNotEmpty()) { - reporter.reportOn( - declaration.source, FirErrors.DEFAULT_ARGUMENTS_IN_EXPECT_ACTUALIZED_BY_FAKE_OVERRIDE, - expectClassSymbol, problematicExpectMembers, context - ) - } - } - - private fun FirExpectActualMatchingContext.findProblematicExpectMembers( - expectClassSymbol: FirRegularClassSymbol, actualClassSymbol: FirRegularClassSymbol, - ): List { - val actualFakeOverrideMembers = actualClassSymbol.collectAllMembers(isActualDeclaration = true) - .filterIsInstance() - .filter { it.isFakeOverride(actualClassSymbol) || it.isDelegated } - - return actualFakeOverrideMembers - .mapNotNull { getSingleMatchingExpect(it, expectClassSymbol, actualClassSymbol) } - .filter(::hasDefaultArgumentValues) - } - - private fun FirExpectActualMatchingContext.getSingleMatchingExpect( - actualMember: FirNamedFunctionSymbol, - expectSymbol: FirRegularClassSymbol, - actualSymbol: FirRegularClassSymbol - ): FirNamedFunctionSymbol? { - val potentialExpects = findPotentialExpectClassMembersForActual( - expectSymbol, actualSymbol, actualMember, - ) - val expectMember: DeclarationSymbolMarker = potentialExpects.entries - .singleOrNull { it.value == ExpectActualMatchingCompatibility.MatchedSuccessfully }?.key - ?: potentialExpects.keys.singleOrNull() - ?: return null - return expectMember as FirNamedFunctionSymbol - } - - private fun hasDefaultArgumentValues(function: FirFunctionSymbol<*>): Boolean { - return function.valueParameterSymbols.any { it.hasDefaultValue } - } -} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt index c565948bac7..2948ac3013c 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration import org.jetbrains.kotlin.KtFakeSourceElementKind +import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.diagnostics.DiagnosticReporter @@ -25,10 +26,7 @@ import org.jetbrains.kotlin.fir.expectActualMatchingContextFactory import org.jetbrains.kotlin.fir.languageVersionSettings import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.SymbolInternals -import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol +import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.mpp.RegularClassSymbolMarker import org.jetbrains.kotlin.name.StandardClassIds @@ -157,7 +155,7 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() { when { checkingCompatibility is ExpectActualCheckingCompatibility.ClassScopes -> { - require(symbol is FirRegularClassSymbol || symbol is FirTypeAliasSymbol) { + require((symbol is FirRegularClassSymbol || symbol is FirTypeAliasSymbol) && expectedSingleCandidate is FirRegularClassSymbol) { "Incompatible.ClassScopes is only possible for a class or a typealias: $declaration" } @@ -178,10 +176,35 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() { val nonTrivialIncompatibleMembers = checkingCompatibility.incompatibleMembers.filterNot(::hasSingleActualSuspect) if (nonTrivialIncompatibleMembers.isNotEmpty()) { - reporter.reportOn(source, FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, symbol, nonTrivialIncompatibleMembers, context) + val (defaultArgsIncompatibleMembers, otherIncompatibleMembers) = + nonTrivialIncompatibleMembers.partition { it.second.contains(ExpectActualCheckingCompatibility.DefaultArgumentsInExpectActualizedByFakeOverride) } + + if (defaultArgsIncompatibleMembers.isNotEmpty()) { // report a nicer diagnostic for DefaultArgumentsInExpectActualizedByFakeOverride + val problematicExpectMembers = defaultArgsIncompatibleMembers + .map { + it.first as? FirNamedFunctionSymbol + ?: error("${ExpectActualCheckingCompatibility.DefaultArgumentsInExpectActualizedByFakeOverride} can be reported only for ${FirNamedFunctionSymbol::class}") + } + reporter.reportOn( + source, + FirErrors.DEFAULT_ARGUMENTS_IN_EXPECT_ACTUALIZED_BY_FAKE_OVERRIDE, + expectedSingleCandidate, + problematicExpectMembers, + context + ) + } + if (otherIncompatibleMembers.isNotEmpty()) { + reporter.reportOn(source, FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, symbol, otherIncompatibleMembers, context) + } } if (checkingCompatibility.mismatchedMembers.isNotEmpty()) { - reporter.reportOn(source, FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, symbol, checkingCompatibility.mismatchedMembers, context) + reporter.reportOn( + source, + FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, + symbol, + checkingCompatibility.mismatchedMembers, + context + ) } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt index ad838911c0f..74089901fb0 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt @@ -43,7 +43,7 @@ class FirExpectActualMatchingContextImpl private constructor( private val actualScopeSession: ScopeSession, private val allowedWritingMemberExpectForActualMapping: Boolean, ) : FirExpectActualMatchingContext, TypeSystemContext by actualSession.typeContext { - override val shouldCheckAbsenceOfDefaultParamsInActual: Boolean + override val shouldCheckDefaultParams: Boolean get() = true override val allowClassActualizationWithWiderVisibility: Boolean @@ -257,7 +257,7 @@ class FirExpectActualMatchingContextImpl private constructor( override val CallableSymbolMarker.typeParameters: List get() = asSymbol().typeParameterSymbols - override fun FunctionSymbolMarker.allOverriddenDeclarationsRecursive(): Sequence { + override fun FunctionSymbolMarker.allRecursivelyOverriddenDeclarationsIncludingSelf(): Sequence { return when (val symbol = asSymbol()) { is FirConstructorSymbol, is FirFunctionWithoutNameSymbol -> sequenceOf(this) is FirNamedFunctionSymbol -> { @@ -285,6 +285,9 @@ class FirExpectActualMatchingContextImpl private constructor( override val ValueParameterSymbolMarker.hasDefaultValue: Boolean get() = asSymbol().hasDefaultValue + override val ValueParameterSymbolMarker.hasDefaultValueNonRecursive: Boolean + get() = asSymbol().hasDefaultValue + override fun CallableSymbolMarker.isAnnotationConstructor(): Boolean { val symbol = asSymbol() return symbol.isAnnotationConstructor(symbol.moduleData.session) @@ -375,6 +378,9 @@ class FirExpectActualMatchingContextImpl private constructor( return symbol.isSubstitutionOrIntersectionOverride } + override val CallableSymbolMarker.isDelegatedMember: Boolean + get() = asSymbol().isDelegated + override val CallableSymbolMarker.hasStableParameterNames: Boolean get() = asSymbol().rawStatus.hasStableParameterNames diff --git a/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt b/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt index a6c8d0e16c9..4ca417f5fb4 100644 --- a/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt +++ b/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt @@ -50,7 +50,7 @@ internal abstract class IrExpectActualMatchingContext( // This incompatibility is often suppressed in the source code (e.g. in kotlin-stdlib). // The backend must be able to do expect-actual matching to emit bytecode // That's why we disable the checker here. Probably, this checker can be enabled once KT-60426 is fixed - override val shouldCheckAbsenceOfDefaultParamsInActual: Boolean + override val shouldCheckDefaultParams: Boolean get() = false private inline fun CallableSymbolMarker.processIr( @@ -298,8 +298,16 @@ internal abstract class IrExpectActualMatchingContext( onEnumEntry = { emptyList() } ) - override fun FunctionSymbolMarker.allOverriddenDeclarationsRecursive(): Sequence = - throw NotImplementedError("Not implemented because it's unused") + override fun FunctionSymbolMarker.allRecursivelyOverriddenDeclarationsIncludingSelf(): Sequence = + when (val node = asIr()) { + is IrConstructor -> sequenceOf(this) + is IrSimpleFunction -> (sequenceOf(this) + node.overriddenSymbols) + // Tests work even if you don't filter out fake-overrides. Filtering fake-overrides is needed because + // the returned descriptors are compared by `equals`. And `equals` for fake-overrides is weird. + // I didn't manage to invent a test that would check this condition + .filter { !it.asIr().isFakeOverride } + else -> error("Unknown IR node: $node") + } override val FunctionSymbolMarker.valueParameters: List get() = asIr().valueParameters.map { it.symbol } @@ -313,6 +321,9 @@ internal abstract class IrExpectActualMatchingContext( override val ValueParameterSymbolMarker.hasDefaultValue: Boolean get() = asIr().hasDefaultValue() + override val ValueParameterSymbolMarker.hasDefaultValueNonRecursive: Boolean + get() = asIr().defaultValue != null + override fun CallableSymbolMarker.isAnnotationConstructor(): Boolean { val irConstructor = safeAsIr() ?: return false return irConstructor.constructedClass.isAnnotationClass @@ -458,6 +469,9 @@ internal abstract class IrExpectActualMatchingContext( return asIr().isFakeOverride } + override val CallableSymbolMarker.isDelegatedMember: Boolean + get() = asIr().origin == IrDeclarationOrigin.DELEGATED_MEMBER + override val CallableSymbolMarker.hasStableParameterNames: Boolean get() { var ir = asIr() diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualChecker.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualChecker.kt index 9bb15e1a30d..78fc1abef5d 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualChecker.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualChecker.kt @@ -369,19 +369,31 @@ object AbstractExpectActualChecker { getTypeParametersVarianceOrReifiedIncompatibility(expectedTypeParameters, actualTypeParameters)?.let { return it } - if (shouldCheckAbsenceOfDefaultParamsInActual) { - // "Default parameters in actual" check is required only for functions, because only functions can have parameters - if (actualDeclaration is FunctionSymbolMarker && expectDeclaration is FunctionSymbolMarker) { - // Actual annotation constructors can have default argument values; their consistency with arguments in the expected annotation - // is checked in ExpectedActualDeclarationChecker.checkAnnotationConstructors - if (!actualDeclaration.isAnnotationConstructor() && - // If default params came from common supertypes of actual class and expect class then it's a valid code. - // Here we filter out such default params. - (actualDeclaration.allOverriddenDeclarationsRecursive() - expectDeclaration.allOverriddenDeclarationsRecursive().toSet()) - .flatMap { it.valueParameters }.any { it.hasDefaultValue } - ) { - return ExpectActualCheckingCompatibility.ActualFunctionWithDefaultParameters - } + if (shouldCheckDefaultParams && + // "parameters" checks are required only for functions, because only functions can have parameters + actualDeclaration is FunctionSymbolMarker && expectDeclaration is FunctionSymbolMarker + ) { + if (languageVersionSettings.supportsFeature(LanguageFeature.ProhibitDefaultArgumentsInExpectActualizedByFakeOverride) && + (actualDeclaration.isFakeOverride(actualContainingClass) || actualDeclaration.isDelegatedMember) && + // If default params came from common supertypes of actual class and expect class then it's a valid code. + // Here we filter out such default params. + (expectDeclaration.allRecursivelyOverriddenDeclarationsIncludingSelf() - + actualDeclaration.allRecursivelyOverriddenDeclarationsIncludingSelf().toSet()) + .flatMap { it.valueParameters }.any { it.hasDefaultValueNonRecursive } + ) { + return ExpectActualCheckingCompatibility.DefaultArgumentsInExpectActualizedByFakeOverride + } + + // Actual annotation constructors can have default argument values; their consistency with arguments in the expected annotation + // is checked in ExpectedActualDeclarationChecker.checkAnnotationConstructors + if (!actualDeclaration.isAnnotationConstructor() && + // If default params came from common supertypes of actual class and expect class then it's a valid code. + // Here we filter out such default params. + (actualDeclaration.allRecursivelyOverriddenDeclarationsIncludingSelf() - + expectDeclaration.allRecursivelyOverriddenDeclarationsIncludingSelf().toSet()) + .flatMap { it.valueParameters }.any { it.hasDefaultValue } + ) { + return ExpectActualCheckingCompatibility.ActualFunctionWithDefaultParameters } } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualMatcher.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualMatcher.kt index 3a023156c47..8503bbabfa5 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualMatcher.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualMatcher.kt @@ -74,36 +74,6 @@ object AbstractExpectActualMatcher { ExpectActualMatchingCompatibility.MatchedSuccessfully } - fun recursivelyMatchClassScopes( // todo drop KT-62913 - expectClassSymbol: RegularClassSymbolMarker, - actualClassSymbol: RegularClassSymbolMarker, - context: ExpectActualMatchingContext<*>, - ): Unit = with(context) { - val expectTypeParameterSymbols = expectClassSymbol.typeParameters - val actualTypeParameterSymbols = actualClassSymbol.typeParameters - val substitutor = createExpectActualTypeParameterSubstitutor( - (expectTypeParameterSymbols zipIfSizesAreEqual actualTypeParameterSymbols) ?: return, - parentSubstitutor = null, - ) - - val actualMembersByName = actualClassSymbol.collectAllMembers(isActualDeclaration = true).groupBy { it.name } - - outer@ for (expectMember in expectClassSymbol.collectAllMembers(isActualDeclaration = false)) { - val actualMembers = getPossibleActualsByExpectName(expectMember, actualMembersByName) - - matchSingleExpectAgainstPotentialActuals( - expectMember, - actualMembers, - substitutor, - expectClassSymbol, - actualClassSymbol, - mismatchedMembers = null, - ) - } - - // TODO: check static scope? - } - /** * Besides returning the matched declaration * diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt index 294a186c385..8cd445cdaa5 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt @@ -40,7 +40,7 @@ interface ExpectActualMatchingContext : TypeSystemC // Known clients that do suppress: // - stdlib // - coroutines - val shouldCheckAbsenceOfDefaultParamsInActual: Boolean + val shouldCheckDefaultParams: Boolean /** * This flag determines, how visibilities for classes/typealiases will be matched @@ -123,9 +123,9 @@ interface ExpectActualMatchingContext : TypeSystemC val FunctionSymbolMarker.valueParameters: List /** - * Returns all symbols that are overridden by [this] symbol + * Returns all symbols that are overridden by [this] symbol, including self */ - fun FunctionSymbolMarker.allOverriddenDeclarationsRecursive(): Sequence + fun FunctionSymbolMarker.allRecursivelyOverriddenDeclarationsIncludingSelf(): Sequence val CallableSymbolMarker.valueParameters: List get() = (this as? FunctionSymbolMarker)?.valueParameters ?: emptyList() @@ -134,6 +134,7 @@ interface ExpectActualMatchingContext : TypeSystemC val ValueParameterSymbolMarker.isNoinline: Boolean val ValueParameterSymbolMarker.isCrossinline: Boolean val ValueParameterSymbolMarker.hasDefaultValue: Boolean + val ValueParameterSymbolMarker.hasDefaultValueNonRecursive: Boolean fun CallableSymbolMarker.isAnnotationConstructor(): Boolean @@ -158,6 +159,8 @@ interface ExpectActualMatchingContext : TypeSystemC fun CallableSymbolMarker.isFakeOverride(containingExpectClass: RegularClassSymbolMarker?): Boolean + val CallableSymbolMarker.isDelegatedMember: Boolean + val CallableSymbolMarker.hasStableParameterNames: Boolean fun onMatchedMembers( diff --git a/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_paramsAreCompatibleViaSharedMethodWithDefaultParams.kt b/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_paramsAreCompatibleViaSharedMethodWithDefaultParams.kt index 438163540ab..102586f57f6 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_paramsAreCompatibleViaSharedMethodWithDefaultParams.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_paramsAreCompatibleViaSharedMethodWithDefaultParams.kt @@ -9,4 +9,4 @@ expect class Foo : Shared // MODULE: m2-jvm()()(m1-common) // FILE: jvm.kt -actual class Foo : Shared +actual class Foo : Shared diff --git a/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation.fir.kt index b0bb1f98550..e68794405b0 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation.fir.kt @@ -14,4 +14,4 @@ object BaseImpl : Base { override fun foo(p: Int) {} } -actual class Foo : Base by BaseImpl +actual class Foo : Base by BaseImpl diff --git a/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation.ll.kt b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation.ll.kt index 8d7c0b99932..8a4b762763e 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation.ll.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation.ll.kt @@ -14,4 +14,4 @@ object BaseImpl : Base { override fun foo(p: Int) {} } -actual class Foo : Base by BaseImpl +actual class Foo : Base by BaseImpl diff --git a/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation2.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation2.fir.kt new file mode 100644 index 00000000000..5073964e819 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation2.fir.kt @@ -0,0 +1,17 @@ +// MODULE: m1-common +// FILE: common.kt +expect class Foo { + fun foo(p: Int = 1) +} + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt +interface Base { + fun foo(p: Int) +} + +object BaseImpl : Base { + override fun foo(p: Int) {} +} + +actual class Foo : Base by BaseImpl diff --git a/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation2.kt b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation2.kt new file mode 100644 index 00000000000..230cc3c28bc --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation2.kt @@ -0,0 +1,17 @@ +// MODULE: m1-common +// FILE: common.kt +expect class Foo { + fun foo(p: Int = 1) +} + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt +interface Base { + fun foo(p: Int) +} + +object BaseImpl : Base { + override fun foo(p: Int) {} +} + +actual class Foo : Base by BaseImpl diff --git a/compiler/testData/diagnostics/tests/multiplatform/java/inheritedJavaMembers.kt b/compiler/testData/diagnostics/tests/multiplatform/java/inheritedJavaMembers.kt index aed93a1dccd..c06cca89d53 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/java/inheritedJavaMembers.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/java/inheritedJavaMembers.kt @@ -51,7 +51,7 @@ public actual open class FastArrayList internal constructor( var array: Array, var _size: Int = array.size, var arrayCapacity: Int = array.size, -) : AbstractMutableList(), MutableListEx, RandomAccess { +) : AbstractMutableList(), MutableListEx, RandomAccess { public actual constructor() : this(arrayOfNulls(16), 0) {} public actual constructor(initialCapacity: Int) : this(arrayOfNulls(initialCapacity), 0) {} public actual constructor(elements: Collection) : this(elements.toTypedArray()) {} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index eecbc14dfc2..7c6e46ba920 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -24882,6 +24882,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation.kt"); } + @Test + @TestMetadata("delegation2.kt") + public void testDelegation2() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation2.kt"); + } + @Test @TestMetadata("disabledFeature.kt") public void testDisabledFeature() throws Exception { diff --git a/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt b/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt index 7f1185ea5e1..24d03362251 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt @@ -69,6 +69,8 @@ sealed class ExpectActualCheckingCompatibility : ExpectActualCompatibilit Incompatible("some modifiers on expected declaration are missing on the actual one (infix, inline, operator)") object ActualFunctionWithDefaultParameters : Incompatible("actual function cannot have default argument values, they should be declared in the expected function") + object DefaultArgumentsInExpectActualizedByFakeOverride : + Incompatible("default argument values inside expect declaration are not allowed for methods actualized via fake override") // Properties object PropertyKind : Incompatible("property kinds are different (val vs var)")