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:
+81
-74
@@ -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 -> {
|
||||
|
||||
+23
-7
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user