FIR: Avoid reporting redundant TYPE_MISMATCH (for assignments)

This commit is contained in:
Denis.Zharkov
2021-05-19 18:23:03 +03:00
committed by teamcityserver
parent 36c9418d55
commit db500fab94
5 changed files with 70 additions and 16 deletions
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.diagnostics.*
import org.jetbrains.kotlin.fir.resolve.calls.* import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.diagnostics.* import org.jetbrains.kotlin.fir.resolve.diagnostics.*
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeArgumentConstraintPosition 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.ConeExplicitTypeParameterConstraintPosition
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeLambdaArgumentConstraintPosition import org.jetbrains.kotlin.fir.resolve.inference.model.ConeLambdaArgumentConstraintPosition
import org.jetbrains.kotlin.fir.symbols.impl.FirBackingFieldSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirBackingFieldSymbol
@@ -164,19 +165,36 @@ private fun mapSystemHasContradictionError(
source: FirSourceElement, source: FirSourceElement,
qualifiedAccessSource: FirSourceElement?, qualifiedAccessSource: FirSourceElement?,
): List<FirDiagnostic<FirSourceElement>> { ): List<FirDiagnostic<FirSourceElement>> {
val errorsToIgnore = mutableSetOf<ConstraintSystemError>()
return buildList<FirDiagnostic<FirSourceElement>> { return buildList<FirDiagnostic<FirSourceElement>> {
for (error in diagnostic.candidate.system.errors) { 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 { }.ifEmpty {
listOfNotNull( listOfNotNull(
diagnostic.candidate.system.errors.firstNotNullOfOrNull { diagnostic.candidate.system.errors.firstNotNullOfOrNull {
if (it in errorsToIgnore) return@firstNotNullOfOrNull null
val message = when (it) { val message = when (it) {
is NewConstraintError -> "NewConstraintError at ${it.position}: ${it.lowerType} <!: ${it.upperType}" is NewConstraintError -> "NewConstraintError at ${it.position}: ${it.lowerType} <!: ${it.upperType}"
// Error should be reported on the error type itself // Error should be reported on the error type itself
is ConstrainingTypeIsError -> return@firstNotNullOfOrNull null is ConstrainingTypeIsError -> return@firstNotNullOfOrNull null
else -> "Inference error: ${it::class.simpleName}" 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) FirErrors.NEW_INFERENCE_ERROR.on(qualifiedAccessSource ?: source, message)
} }
) )
@@ -187,6 +205,7 @@ private fun ConstraintSystemError.toDiagnostic(
source: FirSourceElement, source: FirSourceElement,
qualifiedAccessSource: FirSourceElement?, qualifiedAccessSource: FirSourceElement?,
typeContext: ConeTypeContext, typeContext: ConeTypeContext,
errorsToIgnore: MutableSet<ConstraintSystemError>,
): FirDiagnostic<FirSourceElement>? { ): FirDiagnostic<FirSourceElement>? {
return when (this) { return when (this) {
is NewConstraintError -> { is NewConstraintError -> {
@@ -204,7 +223,11 @@ private fun ConstraintSystemError.toDiagnostic(
} }
when (position) { when (position) {
is ExpectedTypeConstraintPosition<*> -> { is ConeExpectedTypeConstraintPosition -> {
if (position.expectedTypeMismatchIsReportedInChecker) {
errorsToIgnore.add(this)
return null
}
val inferredType = val inferredType =
if (!lowerConeType.isNullableNothing) if (!lowerConeType.isNullableNothing)
lowerConeType lowerConeType
@@ -15,8 +15,13 @@ sealed class ResolutionMode {
object ContextDependent : ResolutionMode() object ContextDependent : ResolutionMode()
object ContextDependentDelegate : ResolutionMode() object ContextDependentDelegate : ResolutionMode()
object ContextIndependent : ResolutionMode() object ContextIndependent : ResolutionMode()
// TODO: it's better not to use WithExpectedType(FirImplicitTypeRef) // 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() class WithStatus(val status: FirDeclarationStatus) : ResolutionMode()
@@ -29,8 +34,13 @@ fun ResolutionMode.expectedType(components: BodyResolveComponents): FirTypeRef?
else -> null else -> null
} }
fun withExpectedType(expectedTypeRef: FirTypeRef?): ResolutionMode = fun withExpectedType(expectedTypeRef: FirTypeRef?, expectedTypeMismatchIsReportedInChecker: Boolean = false): ResolutionMode =
expectedTypeRef?.let { ResolutionMode.WithExpectedType(it) } ?: ResolutionMode.ContextDependent expectedTypeRef?.let {
ResolutionMode.WithExpectedType(
it,
expectedTypeMismatchIsReportedInChecker = expectedTypeMismatchIsReportedInChecker
)
} ?: ResolutionMode.ContextDependent
@JvmName("withExpectedTypeNullable") @JvmName("withExpectedTypeNullable")
fun withExpectedType(coneType: ConeKotlinType?, mayBeCoercionToUnitApplied: Boolean = false): ResolutionMode { fun withExpectedType(coneType: ConeKotlinType?, mayBeCoercionToUnitApplied: Boolean = false): ResolutionMode {
@@ -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.resolve.typeFromCallee
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.types.* 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.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.visitors.transformSingle import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -56,13 +54,26 @@ class FirCallCompleter(
data class CompletionResult<T>(val result: T, val callCompleted: Boolean) data class CompletionResult<T>(val result: T, val callCompleted: Boolean)
fun <T> completeCall(call: T, expectedTypeRef: FirTypeRef?): CompletionResult<T> where T : FirResolvable, T : FirStatement = fun <T> completeCall(
completeCall(call, expectedTypeRef, mayBeCoercionToUnitApplied = false) call: T,
expectedTypeRef: FirTypeRef?,
expectedTypeMismatchIsReportedInChecker: Boolean = false,
): CompletionResult<T> where T : FirResolvable, T : FirStatement =
completeCall(call, expectedTypeRef, mayBeCoercionToUnitApplied = false, expectedTypeMismatchIsReportedInChecker)
fun <T> completeCall(call: T, data: ResolutionMode): CompletionResult<T> where T : FirResolvable, T : FirStatement = fun <T> completeCall(call: T, data: ResolutionMode): CompletionResult<T> 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 <T> completeCall(call: T, expectedTypeRef: FirTypeRef?, mayBeCoercionToUnitApplied: Boolean): CompletionResult<T> private fun <T> completeCall(
call: T, expectedTypeRef: FirTypeRef?,
mayBeCoercionToUnitApplied: Boolean,
expectedTypeMismatchIsReportedInChecker: Boolean,
): CompletionResult<T>
where T : FirResolvable, T : FirStatement { where T : FirResolvable, T : FirStatement {
val typeRef = components.typeFromCallee(call) val typeRef = components.typeFromCallee(call)
@@ -82,14 +93,15 @@ class FirCallCompleter(
} }
if (expectedTypeRef is FirResolvedTypeRef) { if (expectedTypeRef is FirResolvedTypeRef) {
val expectedTypeConstraintPosition = ConeExpectedTypeConstraintPosition(expectedTypeMismatchIsReportedInChecker)
if (expectedTypeRef.coneType.isUnitOrFlexibleUnit && mayBeCoercionToUnitApplied) { if (expectedTypeRef.coneType.isUnitOrFlexibleUnit && mayBeCoercionToUnitApplied) {
if (candidate.system.notFixedTypeVariables.isNotEmpty()) { if (candidate.system.notFixedTypeVariables.isNotEmpty()) {
candidate.system.addSubtypeConstraintIfCompatible( candidate.system.addSubtypeConstraintIfCompatible(
initialType, expectedTypeRef.type, ConeExpectedTypeConstraintPosition() initialType, expectedTypeRef.type, expectedTypeConstraintPosition
) )
} }
} else { } else {
candidate.system.addSubtypeConstraint(initialType, expectedTypeRef.type, ConeExpectedTypeConstraintPosition()) candidate.system.addSubtypeConstraint(initialType, expectedTypeRef.type, expectedTypeConstraintPosition)
} }
} }
@@ -17,7 +17,9 @@ class ConeFixVariableConstraintPosition(variable: TypeVariableMarker) : FixVaria
class ConeArgumentConstraintPosition(argument: FirElement) : ArgumentConstraintPosition<FirElement>(argument) class ConeArgumentConstraintPosition(argument: FirElement) : ArgumentConstraintPosition<FirElement>(argument)
class ConeExpectedTypeConstraintPosition : ExpectedTypeConstraintPosition<Nothing?>(null) class ConeExpectedTypeConstraintPosition(
val expectedTypeMismatchIsReportedInChecker: Boolean
) : ExpectedTypeConstraintPosition<Nothing?>(null)
class ConeExplicitTypeParameterConstraintPosition( class ConeExplicitTypeParameterConstraintPosition(
typeArgument: FirTypeProjection, typeArgument: FirTypeProjection,
@@ -423,7 +423,11 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
} }
fun chooseOperator(): FirStatement { fun chooseOperator(): FirStatement {
callCompleter.completeCall(resolvedOperatorCall, lhsVariable?.returnTypeRef ?: noExpectedType) callCompleter.completeCall(
resolvedOperatorCall,
lhsVariable?.returnTypeRef ?: noExpectedType,
expectedTypeMismatchIsReportedInChecker = true
)
dataFlowAnalyzer.exitFunctionCall(resolvedOperatorCall, callCompleted = true) dataFlowAnalyzer.exitFunctionCall(resolvedOperatorCall, callCompleted = true)
val assignment = val assignment =
buildVariableAssignment { buildVariableAssignment {
@@ -684,7 +688,10 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
val resolvedAssignment = callResolver.resolveVariableAccessAndSelectCandidate(variableAssignment) val resolvedAssignment = callResolver.resolveVariableAccessAndSelectCandidate(variableAssignment)
val result = if (resolvedAssignment is FirVariableAssignment) { val result = if (resolvedAssignment is FirVariableAssignment) {
val completeAssignment = callCompleter.completeCall(resolvedAssignment, noExpectedType).result // TODO: check val completeAssignment = callCompleter.completeCall(resolvedAssignment, noExpectedType).result // TODO: check
completeAssignment.transformRValue(transformer, withExpectedType(variableAssignment.lValueTypeRef)) completeAssignment.transformRValue(
transformer,
withExpectedType(variableAssignment.lValueTypeRef, expectedTypeMismatchIsReportedInChecker = true),
)
} else { } else {
// This can happen in erroneous code only // This can happen in erroneous code only
resolvedAssignment resolvedAssignment