From db500fab94c394ff125ffcca6cd806ba4ddfacf1 Mon Sep 17 00:00:00 2001 From: "Denis.Zharkov" Date: Wed, 19 May 2021 18:23:03 +0300 Subject: [PATCH] FIR: Avoid reporting redundant TYPE_MISMATCH (for assignments) --- .../coneDiagnosticToFirDiagnostic.kt | 27 ++++++++++++++++-- .../kotlin/fir/resolve/ResolutionMode.kt | 16 +++++++++-- .../fir/resolve/inference/FirCallCompleter.kt | 28 +++++++++++++------ .../model/FirConstraintPositionAndErrors.kt | 4 ++- .../FirExpressionsResolveTransformer.kt | 11 ++++++-- 5 files changed, 70 insertions(+), 16 deletions(-) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt index 2662585e73f..da23e5f2743 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.diagnostics.* import org.jetbrains.kotlin.fir.resolve.calls.* import org.jetbrains.kotlin.fir.resolve.diagnostics.* import org.jetbrains.kotlin.fir.resolve.inference.model.ConeArgumentConstraintPosition +import org.jetbrains.kotlin.fir.resolve.inference.model.ConeExpectedTypeConstraintPosition import org.jetbrains.kotlin.fir.resolve.inference.model.ConeExplicitTypeParameterConstraintPosition import org.jetbrains.kotlin.fir.resolve.inference.model.ConeLambdaArgumentConstraintPosition import org.jetbrains.kotlin.fir.symbols.impl.FirBackingFieldSymbol @@ -164,19 +165,36 @@ private fun mapSystemHasContradictionError( source: FirSourceElement, qualifiedAccessSource: FirSourceElement?, ): List> { + val errorsToIgnore = mutableSetOf() return buildList> { for (error in diagnostic.candidate.system.errors) { - addIfNotNull(error.toDiagnostic(source, qualifiedAccessSource, diagnostic.candidate.callInfo.session.typeContext)) + addIfNotNull( + error.toDiagnostic( + source, + qualifiedAccessSource, + diagnostic.candidate.callInfo.session.typeContext, + errorsToIgnore, + ) + ) } }.ifEmpty { listOfNotNull( diagnostic.candidate.system.errors.firstNotNullOfOrNull { + if (it in errorsToIgnore) return@firstNotNullOfOrNull null val message = when (it) { is NewConstraintError -> "NewConstraintError at ${it.position}: ${it.lowerType} return@firstNotNullOfOrNull null else -> "Inference error: ${it::class.simpleName}" } + + if (it is NewConstraintError && it.position.from is FixVariableConstraintPosition<*>) { + val morePreciseDiagnosticExists = diagnostic.candidate.system.errors.any { other -> + other is NewConstraintError && other.position.from !is FixVariableConstraintPosition<*> + } + if (morePreciseDiagnosticExists) return@firstNotNullOfOrNull null + } + FirErrors.NEW_INFERENCE_ERROR.on(qualifiedAccessSource ?: source, message) } ) @@ -187,6 +205,7 @@ private fun ConstraintSystemError.toDiagnostic( source: FirSourceElement, qualifiedAccessSource: FirSourceElement?, typeContext: ConeTypeContext, + errorsToIgnore: MutableSet, ): FirDiagnostic? { return when (this) { is NewConstraintError -> { @@ -204,7 +223,11 @@ private fun ConstraintSystemError.toDiagnostic( } when (position) { - is ExpectedTypeConstraintPosition<*> -> { + is ConeExpectedTypeConstraintPosition -> { + if (position.expectedTypeMismatchIsReportedInChecker) { + errorsToIgnore.add(this) + return null + } val inferredType = if (!lowerConeType.isNullableNothing) lowerConeType diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt index 05d4bceead7..a553e5322e6 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolutionMode.kt @@ -15,8 +15,13 @@ sealed class ResolutionMode { object ContextDependent : ResolutionMode() object ContextDependentDelegate : ResolutionMode() object ContextIndependent : ResolutionMode() + // TODO: it's better not to use WithExpectedType(FirImplicitTypeRef) - class WithExpectedType(val expectedTypeRef: FirTypeRef, val mayBeCoercionToUnitApplied: Boolean = false) : ResolutionMode() + class WithExpectedType( + val expectedTypeRef: FirTypeRef, + val mayBeCoercionToUnitApplied: Boolean = false, + val expectedTypeMismatchIsReportedInChecker: Boolean = false, + ) : ResolutionMode() class WithStatus(val status: FirDeclarationStatus) : ResolutionMode() @@ -29,8 +34,13 @@ fun ResolutionMode.expectedType(components: BodyResolveComponents): FirTypeRef? else -> null } -fun withExpectedType(expectedTypeRef: FirTypeRef?): ResolutionMode = - expectedTypeRef?.let { ResolutionMode.WithExpectedType(it) } ?: ResolutionMode.ContextDependent +fun withExpectedType(expectedTypeRef: FirTypeRef?, expectedTypeMismatchIsReportedInChecker: Boolean = false): ResolutionMode = + expectedTypeRef?.let { + ResolutionMode.WithExpectedType( + it, + expectedTypeMismatchIsReportedInChecker = expectedTypeMismatchIsReportedInChecker + ) + } ?: ResolutionMode.ContextDependent @JvmName("withExpectedTypeNullable") fun withExpectedType(coneType: ConeKotlinType?, mayBeCoercionToUnitApplied: Boolean = false): ResolutionMode { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt index 6b49df6c753..67fb922668c 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt @@ -31,8 +31,6 @@ import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType import org.jetbrains.kotlin.fir.resolve.typeFromCallee import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol import org.jetbrains.kotlin.fir.types.* -import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef -import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl import org.jetbrains.kotlin.fir.visitors.transformSingle import org.jetbrains.kotlin.name.Name @@ -56,13 +54,26 @@ class FirCallCompleter( data class CompletionResult(val result: T, val callCompleted: Boolean) - fun completeCall(call: T, expectedTypeRef: FirTypeRef?): CompletionResult where T : FirResolvable, T : FirStatement = - completeCall(call, expectedTypeRef, mayBeCoercionToUnitApplied = false) + fun completeCall( + call: T, + expectedTypeRef: FirTypeRef?, + expectedTypeMismatchIsReportedInChecker: Boolean = false, + ): CompletionResult where T : FirResolvable, T : FirStatement = + completeCall(call, expectedTypeRef, mayBeCoercionToUnitApplied = false, expectedTypeMismatchIsReportedInChecker) fun completeCall(call: T, data: ResolutionMode): CompletionResult where T : FirResolvable, T : FirStatement = - completeCall(call, data.expectedType(components), (data as? ResolutionMode.WithExpectedType)?.mayBeCoercionToUnitApplied == true) + completeCall( + call, + data.expectedType(components), + (data as? ResolutionMode.WithExpectedType)?.mayBeCoercionToUnitApplied == true, + (data as? ResolutionMode.WithExpectedType)?.expectedTypeMismatchIsReportedInChecker == true, + ) - fun completeCall(call: T, expectedTypeRef: FirTypeRef?, mayBeCoercionToUnitApplied: Boolean): CompletionResult + private fun completeCall( + call: T, expectedTypeRef: FirTypeRef?, + mayBeCoercionToUnitApplied: Boolean, + expectedTypeMismatchIsReportedInChecker: Boolean, + ): CompletionResult where T : FirResolvable, T : FirStatement { val typeRef = components.typeFromCallee(call) @@ -82,14 +93,15 @@ class FirCallCompleter( } if (expectedTypeRef is FirResolvedTypeRef) { + val expectedTypeConstraintPosition = ConeExpectedTypeConstraintPosition(expectedTypeMismatchIsReportedInChecker) if (expectedTypeRef.coneType.isUnitOrFlexibleUnit && mayBeCoercionToUnitApplied) { if (candidate.system.notFixedTypeVariables.isNotEmpty()) { candidate.system.addSubtypeConstraintIfCompatible( - initialType, expectedTypeRef.type, ConeExpectedTypeConstraintPosition() + initialType, expectedTypeRef.type, expectedTypeConstraintPosition ) } } else { - candidate.system.addSubtypeConstraint(initialType, expectedTypeRef.type, ConeExpectedTypeConstraintPosition()) + candidate.system.addSubtypeConstraint(initialType, expectedTypeRef.type, expectedTypeConstraintPosition) } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/model/FirConstraintPositionAndErrors.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/model/FirConstraintPositionAndErrors.kt index 022caad3002..63a64ac05d2 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/model/FirConstraintPositionAndErrors.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/model/FirConstraintPositionAndErrors.kt @@ -17,7 +17,9 @@ class ConeFixVariableConstraintPosition(variable: TypeVariableMarker) : FixVaria class ConeArgumentConstraintPosition(argument: FirElement) : ArgumentConstraintPosition(argument) -class ConeExpectedTypeConstraintPosition : ExpectedTypeConstraintPosition(null) +class ConeExpectedTypeConstraintPosition( + val expectedTypeMismatchIsReportedInChecker: Boolean +) : ExpectedTypeConstraintPosition(null) class ConeExplicitTypeParameterConstraintPosition( typeArgument: FirTypeProjection, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt index 96b9dafd786..083121efb21 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt @@ -423,7 +423,11 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform } fun chooseOperator(): FirStatement { - callCompleter.completeCall(resolvedOperatorCall, lhsVariable?.returnTypeRef ?: noExpectedType) + callCompleter.completeCall( + resolvedOperatorCall, + lhsVariable?.returnTypeRef ?: noExpectedType, + expectedTypeMismatchIsReportedInChecker = true + ) dataFlowAnalyzer.exitFunctionCall(resolvedOperatorCall, callCompleted = true) val assignment = buildVariableAssignment { @@ -684,7 +688,10 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform val resolvedAssignment = callResolver.resolveVariableAccessAndSelectCandidate(variableAssignment) val result = if (resolvedAssignment is FirVariableAssignment) { val completeAssignment = callCompleter.completeCall(resolvedAssignment, noExpectedType).result // TODO: check - completeAssignment.transformRValue(transformer, withExpectedType(variableAssignment.lValueTypeRef)) + completeAssignment.transformRValue( + transformer, + withExpectedType(variableAssignment.lValueTypeRef, expectedTypeMismatchIsReportedInChecker = true), + ) } else { // This can happen in erroneous code only resolvedAssignment