diff --git a/compiler/fir/analysis-tests/testData/resolve/inference/nullableIntegerLiteralType.fir.txt b/compiler/fir/analysis-tests/testData/resolve/inference/nullableIntegerLiteralType.fir.txt index 5c4218a0dd5..60c1c692117 100644 --- a/compiler/fir/analysis-tests/testData/resolve/inference/nullableIntegerLiteralType.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/inference/nullableIntegerLiteralType.fir.txt @@ -11,7 +11,7 @@ FILE: nullableIntegerLiteralType.kt } } - #(R|/x|) + #(R|/x|) } public final fun test_2(b: R|kotlin/Boolean|, y: R|kotlin/Int|): R|kotlin/Unit| { lval x: R|kotlin/Int?| = when () { @@ -23,5 +23,5 @@ FILE: nullableIntegerLiteralType.kt } } - #(R|/x|) + #(R|/x|) } diff --git a/compiler/fir/analysis-tests/testData/resolve/kt41984.fir.txt b/compiler/fir/analysis-tests/testData/resolve/kt41984.fir.txt index 0de2d198e4f..733d8d6dfd2 100644 --- a/compiler/fir/analysis-tests/testData/resolve/kt41984.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/kt41984.fir.txt @@ -21,6 +21,6 @@ FILE: main.kt } public final fun test_1(b: R|B|, x: R|kotlin/Int|, inv: R|Inv|): R|kotlin/Unit| { R|/b|.R|SubstitutionOverride|(R|/x|) - R|/b|.#(Null(null)) + R|/b|.#(Null(null)) R|/b|.R|SubstitutionOverride|(R|/inv|) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt index 77bdd3df6e9..cb495ab2c3b 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintPosition import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.model.CaptureStatus @@ -71,6 +72,7 @@ fun Candidate.resolveArgumentExpression( // Assignment checkApplicabilityForArgumentType( csBuilder, + argument, StandardClassIds.Unit.constructClassLikeType(emptyArray(), isNullable = false), expectedType?.type, SimpleConstraintSystemConstraintPosition, @@ -136,6 +138,7 @@ private fun Candidate.resolveBlockArgument( if (returnArguments.isEmpty()) { checkApplicabilityForArgumentType( csBuilder, + block, block.typeRef.coneType, expectedType?.type, SimpleConstraintSystemConstraintPosition, @@ -170,9 +173,10 @@ fun Candidate.resolveSubCallArgument( isDispatch: Boolean, useNullableArgumentType: Boolean = false ) { + require(argument is FirExpression) val candidate = argument.candidate() ?: return resolvePlainExpressionArgument( csBuilder, - argument as FirExpression, + argument, expectedType, sink, context, @@ -186,7 +190,17 @@ fun Candidate.resolveSubCallArgument( */ val type: ConeKotlinType = context.returnTypeCalculator.tryCalculateReturnType(candidate.symbol.firUnsafe()).type val argumentType = candidate.substitutor.substituteOrSelf(type) - resolvePlainArgumentType(csBuilder, argumentType, expectedType, sink, context, isReceiver, isDispatch, useNullableArgumentType) + resolvePlainArgumentType( + csBuilder, + argument, + argumentType, + expectedType, + sink, + context, + isReceiver, + isDispatch, + useNullableArgumentType + ) } fun Candidate.resolvePlainExpressionArgument( @@ -201,11 +215,22 @@ fun Candidate.resolvePlainExpressionArgument( ) { if (expectedType == null) return val argumentType = argument.typeRef.coneTypeSafe() ?: return - resolvePlainArgumentType(csBuilder, argumentType, expectedType, sink, context, isReceiver, isDispatch, useNullableArgumentType) + resolvePlainArgumentType( + csBuilder, + argument, + argumentType, + expectedType, + sink, + context, + isReceiver, + isDispatch, + useNullableArgumentType + ) } fun Candidate.resolvePlainArgumentType( csBuilder: ConstraintSystemBuilder, + argument: FirExpression, argumentType: ConeKotlinType, expectedType: ConeKotlinType?, sink: CheckerSink, @@ -237,7 +262,7 @@ fun Candidate.resolvePlainArgumentType( } checkApplicabilityForArgumentType( - csBuilder, argumentTypeForApplicabilityCheck, expectedType, position, isReceiver, isDispatch, sink, context + csBuilder, argument, argumentTypeForApplicabilityCheck, expectedType, position, isReceiver, isDispatch, sink, context ) } @@ -299,6 +324,7 @@ private fun Candidate.captureTypeFromExpressionOrNull(argumentType: ConeKotlinTy private fun checkApplicabilityForArgumentType( csBuilder: ConstraintSystemBuilder, + argument: FirExpression, argumentType: ConeKotlinType, expectedType: ConeKotlinType?, position: SimpleConstraintSystemConstraintPosition, @@ -308,15 +334,48 @@ private fun checkApplicabilityForArgumentType( context: ResolutionContext ) { if (expectedType == null) return + + fun unstableSmartCastOrSubtypeError( + unstableType: ConeKotlinType?, + actualExpectedType: ConeKotlinType, + position: ConstraintPosition + ): ResolutionDiagnostic? { + if (unstableType != null) { + if (csBuilder.addSubtypeConstraintIfCompatible(unstableType, actualExpectedType, position)) { + return UnstableSmartCast.ResolutionError(argument, unstableType) + } + } + if (argumentType.isMarkedNullable) { + if (csBuilder.addSubtypeConstraintIfCompatible(argumentType, actualExpectedType, position)) return null + if (csBuilder.addSubtypeConstraintIfCompatible( + argumentType.withNullability(ConeNullability.NOT_NULL), + actualExpectedType, + position + ) + ) return ArgumentTypeMismatch(actualExpectedType, argumentType, argument) + } + + csBuilder.addSubtypeConstraint(argumentType, actualExpectedType, position) + return null + } + + if (isReceiver && isDispatch) { if (!expectedType.isNullable && argumentType.isMarkedNullable) { sink.reportDiagnostic(InapplicableWrongReceiver(expectedType, argumentType)) } return } + if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedType, position)) { if (!isReceiver) { - csBuilder.addSubtypeConstraint(argumentType, expectedType, position) + sink.reportDiagnosticIfNotNull( + unstableSmartCastOrSubtypeError( + unstableType = null, // TODO: handle unstable smartcasts + expectedType, + position + ) + ) return } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CheckerSink.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CheckerSink.kt index fd0f9f3d006..812106efe59 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CheckerSink.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CheckerSink.kt @@ -49,3 +49,9 @@ class CheckerSinkImpl( override val needYielding: Boolean get() = stopOnFirstError && !candidate.isSuccessful } + +fun CheckerSink.reportDiagnosticIfNotNull(diagnostic: ResolutionDiagnostic?) { + if (diagnostic != null) { + reportDiagnostic(diagnostic) + } +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt index 7d1773cc098..696167ab019 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt @@ -11,10 +11,11 @@ import org.jetbrains.kotlin.fir.declarations.FirValueParameter import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability +import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability.* abstract class ResolutionDiagnostic(val applicability: CandidateApplicability) -abstract class InapplicableArgumentDiagnostic : ResolutionDiagnostic(CandidateApplicability.INAPPLICABLE) { +abstract class InapplicableArgumentDiagnostic : ResolutionDiagnostic(INAPPLICABLE) { abstract val argument: FirExpression } @@ -23,7 +24,7 @@ class MixingNamedAndPositionArguments(override val argument: FirExpression) : In class TooManyArguments( val argument: FirExpression, val function: FirFunction<*> -) : ResolutionDiagnostic(CandidateApplicability.INAPPLICABLE_ARGUMENTS_MAPPING_ERROR) +) : ResolutionDiagnostic(INAPPLICABLE_ARGUMENTS_MAPPING_ERROR) class NamedArgumentNotAllowed( override val argument: FirExpression, @@ -47,24 +48,46 @@ class NonVarargSpread(override val argument: FirExpression) : InapplicableArgume class NoValueForParameter( val valueParameter: FirValueParameter, val function: FirFunction<*> -) : ResolutionDiagnostic(CandidateApplicability.INAPPLICABLE_ARGUMENTS_MAPPING_ERROR) +) : ResolutionDiagnostic(INAPPLICABLE_ARGUMENTS_MAPPING_ERROR) class NameNotFound( override val argument: FirExpression, val function: FirFunction<*> ) : InapplicableArgumentDiagnostic() -object InapplicableCandidate : ResolutionDiagnostic(CandidateApplicability.INAPPLICABLE) +object InapplicableCandidate : ResolutionDiagnostic(INAPPLICABLE) -object HiddenCandidate : ResolutionDiagnostic(CandidateApplicability.HIDDEN) +object HiddenCandidate : ResolutionDiagnostic(HIDDEN) -object ResolvedWithLowPriority : ResolutionDiagnostic(CandidateApplicability.RESOLVED_LOW_PRIORITY) +object ResolvedWithLowPriority : ResolutionDiagnostic(RESOLVED_LOW_PRIORITY) class InapplicableWrongReceiver( val expectedType: ConeKotlinType? = null, val actualType: ConeKotlinType? = null, -) : ResolutionDiagnostic(CandidateApplicability.INAPPLICABLE_WRONG_RECEIVER) +) : ResolutionDiagnostic(INAPPLICABLE_WRONG_RECEIVER) -object LowerPriorityToPreserveCompatibilityDiagnostic : ResolutionDiagnostic(CandidateApplicability.RESOLVED_NEED_PRESERVE_COMPATIBILITY) +object LowerPriorityToPreserveCompatibilityDiagnostic : ResolutionDiagnostic(RESOLVED_NEED_PRESERVE_COMPATIBILITY) -object CandidateChosenUsingOverloadResolutionByLambdaAnnotation : ResolutionDiagnostic(CandidateApplicability.RESOLVED) +object CandidateChosenUsingOverloadResolutionByLambdaAnnotation : ResolutionDiagnostic(RESOLVED) + +sealed class UnstableSmartCast( + val argument: FirExpression, + val targetType: ConeKotlinType, + applicability: CandidateApplicability +) : ResolutionDiagnostic(applicability) { + class ResolutionError( + argument: FirExpression, + targetType: ConeKotlinType, + ) : UnstableSmartCast(argument, targetType, MAY_THROW_RUNTIME_ERROR) + + class DiagnosticError( + argument: FirExpression, + targetType: ConeKotlinType, + ) : UnstableSmartCast(argument, targetType, RESOLVED_WITH_ERROR) +} + +class ArgumentTypeMismatch( + val expectedType: ConeKotlinType, + val actualType: ConeKotlinType, + val argument: FirExpression +) : ResolutionDiagnostic(MAY_THROW_RUNTIME_ERROR) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt index 1adedf0db7c..e7231ac0283 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt @@ -70,6 +70,7 @@ object CheckExtensionReceiver : ResolutionStage() { ) candidate.resolvePlainArgumentType( candidate.csBuilder, + argumentExtensionReceiverValue.receiverExpression, argumentType = argumentType, expectedType = expectedType, sink = sink, diff --git a/compiler/testData/diagnostics/tests/nullableTypes/nullableArgumentToNonNullParameterPlatform.fir.kt b/compiler/testData/diagnostics/tests/nullableTypes/nullableArgumentToNonNullParameterPlatform.fir.kt index 13ee4dbdf08..93e05a43a71 100644 --- a/compiler/testData/diagnostics/tests/nullableTypes/nullableArgumentToNonNullParameterPlatform.fir.kt +++ b/compiler/testData/diagnostics/tests/nullableTypes/nullableArgumentToNonNullParameterPlatform.fir.kt @@ -13,6 +13,6 @@ public class J { fun test(j: J, nullStr: String?, nullByte: Byte?, nullDouble: Double?) { j.foo(nullStr) - j.foo(nullDouble) + j.foo(nullDouble) j.foo(nullByte) } diff --git a/compiler/testData/diagnostics/tests/nullableTypes/nullableArgumentToNonNullParameterSimple.fir.kt b/compiler/testData/diagnostics/tests/nullableTypes/nullableArgumentToNonNullParameterSimple.fir.kt index 9c07471393f..abbe5c5e7c1 100644 --- a/compiler/testData/diagnostics/tests/nullableTypes/nullableArgumentToNonNullParameterSimple.fir.kt +++ b/compiler/testData/diagnostics/tests/nullableTypes/nullableArgumentToNonNullParameterSimple.fir.kt @@ -6,7 +6,7 @@ fun foo(x: Int) {} fun foo(x: Int, y: String) {} fun bar(nullX: Int?, nullY: String?, notNullY: String) { - foo(nullX) + foo(nullX) foo(nullX, notNullY) foo(nullX, nullY) foo()