diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/upperBoundViolated.fir.txt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/upperBoundViolated.fir.txt index b472efdcd77..42f7b68fa54 100644 --- a/compiler/fir/analysis-tests/testData/resolve/diagnostics/upperBoundViolated.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/upperBoundViolated.fir.txt @@ -85,3 +85,58 @@ FILE: upperBoundViolated.kt public get(): R|NumberPhile| public final val np2: R|NumberPhile| = #(String(Test)) public get(): R|NumberPhile| + public final class Test1|, K : R|kotlin/Any|> : R|kotlin/Any| { + public constructor|, K : R|kotlin/Any|>(): R|Test1| { + super() + } + + } + public final class Test2|> : R|kotlin/Any| { + public constructor|>(): R|Test2| { + super() + } + + } + public final class Test3|, K : R|kotlin/Any|> : R|kotlin/Any| { + public constructor|, K : R|kotlin/Any|>(): R|Test3| { + super() + } + + } + public final class Test4|> : R|kotlin/Any| { + public constructor|>(): R|Test4| { + super() + } + + } + public final class Test5|, K : R|kotlin/Any|> : R|kotlin/Any| { + public constructor|, K : R|kotlin/Any|>(): R|Test5| { + super() + } + + } + public final class Test6|> : R|kotlin/Any| { + public constructor|>(): R|Test6| { + super() + } + + } + public final class Test7|, K : R|kotlin/CharSequence|> : R|kotlin/Any| { + public constructor|, K : R|kotlin/CharSequence|>(): R|Test7| { + super() + } + + } + public final class Test8|> : R|kotlin/Any| { + public constructor|>(): R|Test8| { + super() + } + + } + public final class Class : R|kotlin/Any| { + public constructor(): R|Class| { + super() + } + + } + public final typealias Alias = R|(Class) -> kotlin/Boolean| diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/upperBoundViolated.kt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/upperBoundViolated.kt index d1904da748e..2266b07eb4b 100644 --- a/compiler/fir/analysis-tests/testData/resolve/diagnostics/upperBoundViolated.kt +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/upperBoundViolated.kt @@ -50,3 +50,19 @@ val test8 = NL<String>() class NumberPhile(x: T) val np1 = NumberPhile(10) val np2 = NumberPhile("Test") + +class Test1, K : Any> +class Test2> + +class Test3, K : Any> +class Test4S4, out Any>> + +class Test5, K : Any> +class Test6> + +class Test7, K : CharSequence> +class Test8in Any>> + +class Class +typealias Alias = (Class) -> Boolean + diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUpperBoundViolatedChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUpperBoundViolatedChecker.kt index 6b38ec805d4..7f92ee6e889 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUpperBoundViolatedChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUpperBoundViolatedChecker.kt @@ -47,7 +47,10 @@ object FirUpperBoundViolatedClassChecker : FirBasicDeclarationChecker() { checkUpperBoundViolated(declaration.expandedTypeRef, context, reporter, isIgnoreTypeParameters = true) } else if (declaration is FirCallableDeclaration<*>) { if (declaration.returnTypeRef.source?.kind !is FirFakeSourceElementKind) { - checkUpperBoundViolated(declaration.returnTypeRef, context, reporter) + checkUpperBoundViolated( + declaration.returnTypeRef, context, reporter, + isIgnoreTypeParameters = context.containingDeclarations.lastOrNull() is FirTypeAlias + ) } } } @@ -163,6 +166,14 @@ private fun checkUpperBoundViolated( substitution[typeParameterSymbol] = typeArgument.typeRef.coneType } else if (typeArgument is ConeKotlinType) { substitution[typeParameterSymbol] = typeArgument.type + } else if (typeArgument is ConeTypeProjection) { + val typeArgumentType = typeArgument.type + if (typeArgumentType != null) { + substitution[typeParameterSymbol] = typeArgumentType + } else { + substitution[typeParameterSymbol] = + ConeStubType(ConeTypeVariable("", typeParameterSymbol.toLookupTag()), ConeNullability.NOT_NULL) + } } } @@ -186,7 +197,12 @@ private fun checkUpperBoundViolated( typeArgumentSource = typeRef.source } } else { - typeArgument = type.typeArguments[index] as? ConeKotlinType + val localTypeArgument = type.typeArguments[index] + if (localTypeArgument is ConeKotlinType) { + typeArgument = localTypeArgument + } else if (localTypeArgument is ConeKotlinTypeProjection) { + typeArgument = localTypeArgument.type + } val typeArgumentRefAndSource = typeArgumentRefsAndSources?.elementAtOrNull(index) if (typeArgumentRefAndSource != null) { typeArgumentTypeRef = typeArgumentRefAndSource.typeRef @@ -202,10 +218,17 @@ private fun checkUpperBoundViolated( if (typeArgument != null && typeArgumentSource != null) { if (!isIgnoreTypeParameters || (typeArgument.typeArguments.isEmpty() && typeArgument !is ConeTypeParameterType)) { - val intersection = typeSystemContext.intersectTypes(typeParameterSymbols[index].fir.bounds.map { it.coneType }) as? ConeKotlinType + val intersection = + typeSystemContext.intersectTypes(typeParameterSymbols[index].fir.bounds.map { it.coneType }) as? ConeKotlinType if (intersection != null) { val upperBound = substitutor.substituteOrSelf(intersection) - if (!AbstractTypeChecker.isSubtypeOf(typeSystemContext, typeArgument.type, upperBound, stubTypesEqualToAnything = false)) { + if (!AbstractTypeChecker.isSubtypeOf( + typeSystemContext, + typeArgument.type, + upperBound, + stubTypesEqualToAnything = true + ) + ) { val factory = if (isTypeAlias) FirErrors.UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION else FirErrors.UPPER_BOUND_VIOLATED reporter.reportOn(typeArgumentSource, factory, upperBound, typeArgument.type, context)