diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConstructorProcessing.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConstructorProcessing.kt index c3e1f494ff0..1cd8ac4d507 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConstructorProcessing.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConstructorProcessing.kt @@ -11,11 +11,8 @@ import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.isInner import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor -import org.jetbrains.kotlin.fir.scopes.CallableCopyTypeCalculator -import org.jetbrains.kotlin.fir.scopes.FirScope +import org.jetbrains.kotlin.fir.scopes.* import org.jetbrains.kotlin.fir.scopes.impl.TypeAliasConstructorsSubstitutingScope -import org.jetbrains.kotlin.fir.scopes.processClassifiersByName -import org.jetbrains.kotlin.fir.scopes.scopeForClass import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.types.ConeClassLikeType @@ -86,7 +83,12 @@ fun FirScope.getSingleVisibleClassifier( if (result == null) { result = classifierSymbol } else { - isAmbiguousResult = true + val checkResult = checkUnambiguousClassifiers(result!!, classifierSymbol, session) + if (checkResult.shouldReplaceResult) { + result = classifierSymbol + } else { + isAmbiguousResult = checkResult.isAmbiguousResult + } } } } @@ -137,8 +139,12 @@ private fun FirScope.getFirstClassifierOrNull( } result != null -> { if (isSuccessResult == isSuccessCandidate) { - // results are similar => ambiguity - isAmbiguousResult = true + val checkResult = checkUnambiguousClassifiers(result!!.symbol, symbol, session) + if (checkResult.shouldReplaceResult) { + result = SymbolWithSubstitutor(symbol, substitutor) + } else { + isAmbiguousResult = checkResult.isAmbiguousResult + } } else { // ignore unsuccessful result if we have successful one } @@ -154,6 +160,41 @@ private fun FirScope.getFirstClassifierOrNull( return result.takeUnless { isAmbiguousResult } } +private data class CheckUnambiguousClassifiersResult(val shouldReplaceResult: Boolean, val isAmbiguousResult: Boolean) + +/** + * Handle special cases when classifiers don't cause ambiguity (`Throws`) + * + * The following output options are possible: + * * `shouldReplaceResult = true, isAmbiguousResult = false` means successful disambiguation + * but the previous result should be replaced with the new one (typically class symbol wins typealias) + * * `shouldReplaceResult = false, isAmbiguousResult = false` means successful disambiguation + * but the new result should be discarded + * * `shouldReplaceResult = false, isAmbiguousResult = true` means unsuccessful disambiguation + * and both results become irrelevant + */ +private fun checkUnambiguousClassifiers( + foundClassifierSymbol: FirClassifierSymbol<*>, + newClassifierSymbol: FirClassifierSymbol<*>, + session: FirSession, +): CheckUnambiguousClassifiersResult { + val classTypealiasesThatDontCauseAmbiguity = session.platformClassMapper.classTypealiasesThatDontCauseAmbiguity + + if (foundClassifierSymbol is FirTypeAliasSymbol && newClassifierSymbol is FirRegularClassSymbol && + classTypealiasesThatDontCauseAmbiguity[newClassifierSymbol.classId] == foundClassifierSymbol.classId + ) { + return CheckUnambiguousClassifiersResult(shouldReplaceResult = true, isAmbiguousResult = false) + } + + if (newClassifierSymbol is FirTypeAliasSymbol && foundClassifierSymbol is FirRegularClassSymbol && + classTypealiasesThatDontCauseAmbiguity[foundClassifierSymbol.classId] == newClassifierSymbol.classId + ) { + return CheckUnambiguousClassifiersResult(shouldReplaceResult = false, isAmbiguousResult = false) + } + + return CheckUnambiguousClassifiersResult(shouldReplaceResult = false, isAmbiguousResult = true) +} + private fun processSyntheticConstructors( matchedSymbol: FirClassLikeSymbol<*>, processor: (FirFunctionSymbol<*>) -> Unit, diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/throws.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/throws.kt index fb974ebb414..4ec5db58c80 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/annotations/throws.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/throws.kt @@ -1,4 +1,5 @@ // FIR_IDENTICAL +// ISSUE: KT-63794 // !DIAGNOSTICS: -UNUSED_PARAMETER // FILE: main1.kt package abc1 @@ -163,3 +164,10 @@ fun foo3() {} fun foo5(x: Throws) {} fun foo6(x: kotlin.Throws) {} fun foo7(x: kotlin.jvm.Throws) {} + +// FILE: main10.kt + +val x: Throws? = null +val y = Throws() +val z = Throws::exceptionClasses +val w = Throws::class \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/throws.txt b/compiler/testData/diagnostics/testsWithStdLib/annotations/throws.txt index b1ce81a9877..096c67fd0f9 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/annotations/throws.txt +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/throws.txt @@ -1,5 +1,10 @@ package +public val w: kotlin.reflect.KClass +public val x: kotlin.Throws? /* = kotlin.jvm.Throws? */ = null +public val y: kotlin.Throws /* = kotlin.jvm.Throws */ +public val z: kotlin.reflect.KProperty1>> + package abc1 { @kotlin.Throws /* = kotlin.jvm.Throws */(exceptionClasses = {java.lang.Exception::class}) public fun foo1(): kotlin.Unit @kotlin.Throws /* = kotlin.jvm.Throws */(exceptionClasses = {java.lang.Exception::class}) public fun foo2(): kotlin.Unit