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.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<FirDiagnostic<FirSourceElement>> {
val errorsToIgnore = mutableSetOf<ConstraintSystemError>()
return buildList<FirDiagnostic<FirSourceElement>> {
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} <!: ${it.upperType}"
// Error should be reported on the error type itself
is ConstrainingTypeIsError -> 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<ConstraintSystemError>,
): FirDiagnostic<FirSourceElement>? {
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
@@ -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 {
@@ -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<T>(val result: T, val callCompleted: Boolean)
fun <T> completeCall(call: T, expectedTypeRef: FirTypeRef?): CompletionResult<T> where T : FirResolvable, T : FirStatement =
completeCall(call, expectedTypeRef, mayBeCoercionToUnitApplied = false)
fun <T> completeCall(
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 =
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 {
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)
}
}
@@ -17,7 +17,9 @@ class ConeFixVariableConstraintPosition(variable: TypeVariableMarker) : FixVaria
class ConeArgumentConstraintPosition(argument: FirElement) : ArgumentConstraintPosition<FirElement>(argument)
class ConeExpectedTypeConstraintPosition : ExpectedTypeConstraintPosition<Nothing?>(null)
class ConeExpectedTypeConstraintPosition(
val expectedTypeMismatchIsReportedInChecker: Boolean
) : ExpectedTypeConstraintPosition<Nothing?>(null)
class ConeExplicitTypeParameterConstraintPosition(
typeArgument: FirTypeProjection,
@@ -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