Use separate constraint position during call substitution as part of inferring postponed type variables

^KT-47052 Fixed
^KT-47082 Fixed
This commit is contained in:
Victor Petukhov
2021-06-02 12:16:22 +03:00
parent 7a2ecc58d4
commit 124a14c8df
17 changed files with 314 additions and 81 deletions
@@ -344,84 +344,91 @@ class DiagnosticReporterByTrackingStrategy(
)
}
private fun reportConstraintErrorByPosition(error: NewConstraintError, position: ConstraintPosition) {
val argument = when (position) {
is ArgumentConstraintPositionImpl -> position.argument
is ReceiverConstraintPositionImpl -> position.argument
is LHSArgumentConstraintPositionImpl -> position.argument
is LambdaArgumentConstraintPositionImpl -> position.lambda.atom
else -> null
}
val typeMismatchDiagnostic = if (error.isWarning) TYPE_MISMATCH_WARNING else TYPE_MISMATCH
val report = if (error.isWarning) trace::reportDiagnosticOnce else trace::report
argument?.let {
it.safeAs<LambdaKotlinCallArgument>()?.let lambda@{ lambda ->
val parameterTypes = lambda.parametersTypes?.toList() ?: return@lambda
val index = parameterTypes.indexOf(error.upperKotlinType.unwrap())
val lambdaExpression = lambda.psiExpression as? KtLambdaExpression ?: return@lambda
val parameter = lambdaExpression.valueParameters.getOrNull(index) ?: return@lambda
val diagnosticFactory =
if (error.isWarning) EXPECTED_PARAMETER_TYPE_MISMATCH_WARNING else EXPECTED_PARAMETER_TYPE_MISMATCH
report(diagnosticFactory.on(parameter, error.upperKotlinType))
return
}
val expression = it.psiExpression ?: return
val deparenthesized = KtPsiUtil.safeDeparenthesize(expression)
if (reportConstantTypeMismatch(error, deparenthesized)) return
val compileTimeConstant = trace[BindingContext.COMPILE_TIME_VALUE, deparenthesized] as? TypedCompileTimeConstant
if (compileTimeConstant != null) {
val expressionType = trace[BindingContext.EXPRESSION_TYPE_INFO, expression]?.type
if (expressionType != null &&
!UnsignedTypes.isUnsignedType(compileTimeConstant.type) && UnsignedTypes.isUnsignedType(expressionType)
) {
return
}
}
report(typeMismatchDiagnostic.on(deparenthesized, error.upperKotlinType, error.lowerKotlinType))
}
(position as? ExpectedTypeConstraintPositionImpl)?.let {
val call = it.topLevelCall.psiKotlinCall.psiCall.callElement.safeAs<KtExpression>()
val inferredType =
if (!error.lowerKotlinType.isNullableNothing()) error.lowerKotlinType
else error.upperKotlinType.makeNullable()
if (call != null) {
report(typeMismatchDiagnostic.on(call, error.upperKotlinType, inferredType))
}
}
(position as? BuilderInferenceExpectedTypeConstraintPosition)?.let {
val inferredType =
if (!error.lowerKotlinType.isNullableNothing()) error.lowerKotlinType
else error.upperKotlinType.makeNullable()
trace.report(TYPE_MISMATCH.on(it.topLevelCall, error.upperKotlinType, inferredType))
}
(position as? BuilderInferenceSubstitutionConstraintPositionImpl)?.let {
reportConstraintErrorByPosition(error, it.initialConstraint.position)
}
(position as? ExplicitTypeParameterConstraintPositionImpl)?.let {
val typeArgumentReference = (it.typeArgument as SimpleTypeArgumentImpl).typeReference
val diagnosticFactory = if (error.isWarning) UPPER_BOUND_VIOLATED_WARNING else UPPER_BOUND_VIOLATED
report(diagnosticFactory.on(typeArgumentReference, error.upperKotlinType, error.lowerKotlinType))
}
(position as? FixVariableConstraintPositionImpl)?.let {
val morePreciseDiagnosticExists = allDiagnostics.any { other ->
val otherError = other.constraintSystemError ?: return@any false
otherError is NewConstraintError && otherError.position.from !is FixVariableConstraintPositionImpl
}
if (morePreciseDiagnosticExists) return
val call = it.resolvedAtom?.atom?.safeAs<PSIKotlinCall>()?.psiCall ?: call
val expression = call.calleeExpression ?: return
trace.reportDiagnosticOnce(typeMismatchDiagnostic.on(expression, error.upperKotlinType, error.lowerKotlinType))
}
}
override fun constraintError(error: ConstraintSystemError) {
when (error.javaClass) {
NewConstraintError::class.java -> {
error as NewConstraintError
val position = error.position.from
val argument =
when (position) {
is ArgumentConstraintPositionImpl -> position.argument
is ReceiverConstraintPositionImpl -> position.argument
is LHSArgumentConstraintPositionImpl -> position.argument
is LambdaArgumentConstraintPositionImpl -> position.lambda.atom
else -> null
}
val typeMismatchDiagnostic = if (error.isWarning) TYPE_MISMATCH_WARNING else TYPE_MISMATCH
val report = if (error.isWarning) trace::reportDiagnosticOnce else trace::report
argument?.let {
it.safeAs<LambdaKotlinCallArgument>()?.let lambda@{ lambda ->
val parameterTypes = lambda.parametersTypes?.toList() ?: return@lambda
val index = parameterTypes.indexOf(error.upperKotlinType.unwrap())
val lambdaExpression = lambda.psiExpression as? KtLambdaExpression ?: return@lambda
val parameter = lambdaExpression.valueParameters.getOrNull(index) ?: return@lambda
val diagnosticFactory =
if (error.isWarning) EXPECTED_PARAMETER_TYPE_MISMATCH_WARNING else EXPECTED_PARAMETER_TYPE_MISMATCH
report(diagnosticFactory.on(parameter, error.upperKotlinType))
return
}
val expression = it.psiExpression ?: return
val deparenthesized = KtPsiUtil.safeDeparenthesize(expression)
if (reportConstantTypeMismatch(error, deparenthesized)) return
val compileTimeConstant = trace[BindingContext.COMPILE_TIME_VALUE, deparenthesized] as? TypedCompileTimeConstant
if (compileTimeConstant != null) {
val expressionType = trace[BindingContext.EXPRESSION_TYPE_INFO, expression]?.type
if (expressionType != null &&
!UnsignedTypes.isUnsignedType(compileTimeConstant.type) && UnsignedTypes.isUnsignedType(expressionType)
) {
return
}
}
report(typeMismatchDiagnostic.on(deparenthesized, error.upperKotlinType, error.lowerKotlinType))
}
(position as? ExpectedTypeConstraintPositionImpl)?.let {
val call = it.topLevelCall.psiKotlinCall.psiCall.callElement.safeAs<KtExpression>()
val inferredType =
if (!error.lowerKotlinType.isNullableNothing()) error.lowerKotlinType
else error.upperKotlinType.makeNullable()
if (call != null) {
report(typeMismatchDiagnostic.on(call, error.upperKotlinType, inferredType))
}
}
(position as? BuilderInferenceExpectedTypeConstraintPosition)?.let {
val inferredType =
if (!error.lowerKotlinType.isNullableNothing()) error.lowerKotlinType
else error.upperKotlinType.makeNullable()
trace.report(TYPE_MISMATCH.on(it.topLevelCall, error.upperKotlinType, inferredType))
}
(position as? ExplicitTypeParameterConstraintPositionImpl)?.let {
val typeArgumentReference = (it.typeArgument as SimpleTypeArgumentImpl).typeReference
val diagnosticFactory = if (error.isWarning) UPPER_BOUND_VIOLATED_WARNING else UPPER_BOUND_VIOLATED
report(diagnosticFactory.on(typeArgumentReference, error.upperKotlinType, error.lowerKotlinType))
}
(position as? FixVariableConstraintPositionImpl)?.let {
val morePreciseDiagnosticExists = allDiagnostics.any { other ->
val otherError = other.constraintSystemError ?: return@any false
otherError is NewConstraintError && otherError.position.from !is FixVariableConstraintPositionImpl
}
if (morePreciseDiagnosticExists) return
val call = it.resolvedAtom?.atom?.safeAs<PSIKotlinCall>()?.psiCall ?: call
val expression = call.calleeExpression ?: return
trace.reportDiagnosticOnce(typeMismatchDiagnostic.on(expression, error.upperKotlinType, error.lowerKotlinType))
}
reportConstraintErrorByPosition(error, error.position.from)
}
CapturedTypeFromSubtyping::class.java -> {
@@ -329,22 +329,24 @@ class BuilderInferenceSession(
val callSubstitutor = storage.buildResultingSubstitutor(commonSystem, transformTypeVariablesToErrorTypes = false)
for (initialConstraint in storage.initialConstraints) {
val lowerCallSubstituted = callSubstitutor.safeSubstitute(initialConstraint.a as UnwrappedType)
val upperCallSubstituted = callSubstitutor.safeSubstitute(initialConstraint.b as UnwrappedType)
val (lower, upper) = substituteNotFixedVariables(lowerCallSubstituted, upperCallSubstituted, nonFixedToVariablesSubstitutor)
val substitutedConstraint = initialConstraint.substitute(callSubstitutor)
val (lower, upper) = substituteNotFixedVariables(
substitutedConstraint.a as KotlinType,
substitutedConstraint.b as KotlinType,
nonFixedToVariablesSubstitutor
)
if (commonSystem.isProperType(lower) && commonSystem.isProperType(upper)) continue
when (initialConstraint.constraintKind) {
ConstraintKind.LOWER -> error("LOWER constraint shouldn't be used, please use UPPER")
ConstraintKind.UPPER -> commonSystem.addSubtypeConstraint(lower, upper, initialConstraint.position)
ConstraintKind.UPPER -> commonSystem.addSubtypeConstraint(lower, upper, substitutedConstraint.position)
ConstraintKind.EQUALITY ->
with(commonSystem) {
addSubtypeConstraint(lower, upper, initialConstraint.position)
addSubtypeConstraint(upper, lower, initialConstraint.position)
addSubtypeConstraint(lower, upper, substitutedConstraint.position)
addSubtypeConstraint(upper, lower, substitutedConstraint.position)
}
}
}
@@ -542,6 +544,20 @@ class BuilderInferenceSession(
}
}
private fun InitialConstraint.substitute(substitutor: NewTypeSubstitutor): InitialConstraint {
val lowerSubstituted = substitutor.safeSubstitute(a as UnwrappedType)
val upperSubstituted = substitutor.safeSubstitute(b as UnwrappedType)
if (lowerSubstituted == a && upperSubstituted == b) return this
return InitialConstraint(
lowerSubstituted,
upperSubstituted,
constraintKind,
BuilderInferenceSubstitutionConstraintPositionImpl(lambdaArgument, this)
)
}
companion object {
private fun BuilderInferenceSession.updateCalls(
lambda: ResolvedLambdaAtom,