Add expected type constraints in a builder inference call

This commit is contained in:
Victor Petukhov
2021-04-29 11:26:59 +03:00
parent 7da47dcde8
commit 90066d7e50
9 changed files with 165 additions and 4 deletions
@@ -12686,6 +12686,22 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/tests/inference/builderInference/specialCallsWithLambdas.kt");
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/inference/builderInference/addingConstraints")
@TestDataPath("$PROJECT_ROOT")
public class AddingConstraints {
@Test
public void testAllFilesPresentInAddingConstraints() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/builderInference/addingConstraints"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@Test
@TestMetadata("bySpecifiedReturnType.kt")
public void testBySpecifiedReturnType() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/builderInference/addingConstraints/bySpecifiedReturnType.kt");
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/inference/builderInference/stubTypes")
@TestDataPath("$PROJECT_ROOT")
@@ -12686,6 +12686,22 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/tests/inference/builderInference/specialCallsWithLambdas.kt");
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/inference/builderInference/addingConstraints")
@TestDataPath("$PROJECT_ROOT")
public class AddingConstraints {
@Test
public void testAllFilesPresentInAddingConstraints() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/builderInference/addingConstraints"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@Test
@TestMetadata("bySpecifiedReturnType.kt")
public void testBySpecifiedReturnType() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/builderInference/addingConstraints/bySpecifiedReturnType.kt");
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/inference/builderInference/stubTypes")
@TestDataPath("$PROJECT_ROOT")
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.callUtil.reportTrailingLambdaErrorOr
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.inference.BuilderInferenceExpectedTypeConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
@@ -396,6 +397,13 @@ class DiagnosticReporterByTrackingStrategy(
}
}
(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
@@ -324,6 +324,23 @@ class BuilderInferenceSession(
return introducedConstraint
}
fun addExpectedTypeConstraint(
callExpression: KtExpression,
a: KotlinType,
b: KotlinType
) {
val nonFixedToVariablesSubstitutor: NewTypeSubstitutor = createNonFixedTypeToVariableSubstitutor()
val (lower, upper) = substituteNotFixedVariables(a, b, nonFixedToVariablesSubstitutor)
val position = BuilderInferenceExpectedTypeConstraintPosition(callExpression)
val currentSubstitutor = commonSystem.buildCurrentSubstitutor()
commonSystem.addSubtypeConstraint(
currentSubstitutor.safeSubstitute(commonSystem.typeSystemContext, lower),
currentSubstitutor.safeSubstitute(commonSystem.typeSystemContext, upper),
position
)
}
private fun substituteNotFixedVariables(
lowerType: KotlinType,
upperType: KotlinType,
@@ -530,3 +547,5 @@ class ComposedSubstitutor(val left: NewTypeSubstitutor, val right: NewTypeSubsti
override val isEmpty: Boolean get() = left.isEmpty && right.isEmpty
}
class BuilderInferenceExpectedTypeConstraintPosition(callElement: KtExpression) : ExpectedTypeConstraintPosition<KtExpression>(callElement)
@@ -35,15 +35,14 @@ import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker;
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
import org.jetbrains.kotlin.resolve.calls.inference.BuilderInferenceSession;
import org.jetbrains.kotlin.resolve.calls.smartcasts.*;
import org.jetbrains.kotlin.resolve.constants.*;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.KotlinTypeKt;
import org.jetbrains.kotlin.types.TypeConstructor;
import org.jetbrains.kotlin.types.TypeUtils;
import org.jetbrains.kotlin.types.*;
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt;
import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt;
import org.jetbrains.kotlin.util.OperatorNameConventions;
import java.util.Collection;
@@ -287,6 +286,11 @@ public class DataFlowAnalyzer {
@NotNull Ref<Boolean> hasError,
boolean reportErrorForTypeMismatch
) {
if (!noExpectedType(c.expectedType) && TypeUtilsKt.contains(expressionType, (type) -> type instanceof StubType)) {
if (c.inferenceSession instanceof BuilderInferenceSession) {
((BuilderInferenceSession) c.inferenceSession).addExpectedTypeConstraint(expression, expressionType, c.expectedType);
}
}
if (noExpectedType(c.expectedType) || !c.expectedType.getConstructor().isDenotable() ||
kotlinTypeChecker.isSubtypeOf(expressionType, c.expectedType)) {
return expressionType;
@@ -0,0 +1,36 @@
// WITH_RUNTIME
interface A {
fun foo(): MutableList<String>
}
@ExperimentalStdlibApi
fun main() {
buildList {
add(3)
object : A {
override fun foo(): MutableList<String> = this@buildList
}
}
buildList {
add(3)
val x: String = get(0)
}
buildList {
add("3")
val x: MutableList<Int> = this@buildList
}
buildList {
val y: CharSequence = ""
add(y)
val x: MutableList<String> = this@buildList
}
buildList {
add("")
val x: MutableList<CharSequence> = this@buildList
}
buildList {
add("")
val x: StringBuilder = get(0)
}
}
@@ -0,0 +1,36 @@
// WITH_RUNTIME
interface A {
fun foo(): MutableList<String>
}
@ExperimentalStdlibApi
fun main() {
buildList {
add(<!CONSTANT_EXPECTED_TYPE_MISMATCH, CONSTANT_EXPECTED_TYPE_MISMATCH!>3<!>)
object : A {
override fun foo(): MutableList<String> = this@buildList
}
}
buildList {
add(<!CONSTANT_EXPECTED_TYPE_MISMATCH, CONSTANT_EXPECTED_TYPE_MISMATCH!>3<!>)
val x: String = get(0)
}
buildList {
add(<!TYPE_MISMATCH, TYPE_MISMATCH!>"3"<!>)
val x: MutableList<Int> = this@buildList
}
buildList {
val y: CharSequence = ""
add(<!TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH!>y<!>)
val x: MutableList<String> = this@buildList
}
buildList {
add("")
val x: MutableList<CharSequence> = this@buildList
}
buildList {
add(<!TYPE_MISMATCH, TYPE_MISMATCH!>""<!>)
val x: StringBuilder = get(0)
}
}
@@ -0,0 +1,10 @@
package
@kotlin.ExperimentalStdlibApi public fun main(): kotlin.Unit
public interface A {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract fun foo(): kotlin.collections.MutableList<kotlin.String>
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -12692,6 +12692,22 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/inference/builderInference/specialCallsWithLambdas.kt");
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/inference/builderInference/addingConstraints")
@TestDataPath("$PROJECT_ROOT")
public class AddingConstraints {
@Test
public void testAllFilesPresentInAddingConstraints() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/builderInference/addingConstraints"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@Test
@TestMetadata("bySpecifiedReturnType.kt")
public void testBySpecifiedReturnType() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/builderInference/addingConstraints/bySpecifiedReturnType.kt");
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/inference/builderInference/stubTypes")
@TestDataPath("$PROJECT_ROOT")