Skip to content

Commit

Permalink
Rethink constraints incorporation
Browse files Browse the repository at this point in the history
Namely, remove incorporation “otherInsideMyConstraint” to eliminate
constraint system redundancy and produce a potentially very large number
 of constructs.
Instead, introduce not so “spreadable” incorporation during variable
fixation (equality constraint with result type into other constraints).
^KT-41644 Fixed
^KT-42195 Fixed
^KT-42920 Fixed
^KT-42791 Fixed
^KT-41741 Fixed
  • Loading branch information
petukhovv committed Nov 25, 2020
1 parent 616e40f commit 0857b9c
Show file tree
Hide file tree
Showing 24 changed files with 299 additions and 136 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,12 @@ class ConstraintIncorporator(
fun addNewIncorporatedConstraint(typeVariable: TypeVariableMarker, type: KotlinTypeMarker, constraintContext: ConstraintContext)
}

fun incorporateEqualityConstraint(c: Context, typeVariable: TypeVariableMarker, constraint: Constraint) = with(c) {
// we shouldn't incorporate recursive constraint -- It is too dangerous
if (c.areThereRecursiveConstraints(typeVariable, constraint)) return

c.directWithVariable(typeVariable, constraint)
if (constraint.type.contains { it is TypeVariableTypeConstructorMarker }) {
c.otherInsideMyConstraint(typeVariable, constraint)
}
c.insideOtherConstraint(typeVariable, constraint)
}

// \alpha is typeVariable, \beta -- other type variable registered in ConstraintStorage
fun incorporateSubtypeConstraint(c: Context, typeVariable: TypeVariableMarker, constraint: Constraint) {
fun incorporate(c: Context, typeVariable: TypeVariableMarker, constraint: Constraint) {
// we shouldn't incorporate recursive constraint -- It is too dangerous
if (c.areThereRecursiveConstraints(typeVariable, constraint)) return

c.directWithVariable(typeVariable, constraint)
c.otherInsideMyConstraint(typeVariable, constraint)
c.insideOtherConstraint(typeVariable, constraint)
}

Expand Down Expand Up @@ -98,26 +86,6 @@ class ConstraintIncorporator(
}
}

// \alpha <: Inv<\beta>, \beta <: Number => \alpha <: Inv<out Number>
private fun Context.otherInsideMyConstraint(
typeVariable: TypeVariableMarker,
constraint: Constraint
) {
val otherInMyConstraint = SmartSet.create<TypeVariableMarker>()
constraint.type.contains {
otherInMyConstraint.addIfNotNull(this.getTypeVariable(it.typeConstructor()))
false
}

for (otherTypeVariable in otherInMyConstraint) {
// to avoid ConcurrentModificationException
val otherConstraints = SmartList(this.getConstraintsForVariable(otherTypeVariable))
for (otherConstraint in otherConstraints) {
generateNewConstraint(typeVariable, constraint, otherTypeVariable, otherConstraint)
}
}
}

// \alpha <: Number, \beta <: Inv<\alpha> => \beta <: Inv<out Number>
private fun Context.insideOtherConstraint(
typeVariable: TypeVariableMarker,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class ConstraintInjector(
typeCheckerContext.setConstrainingTypesToPrintDebugInfo(lowerType, upperType)
typeCheckerContext.runIsSubtypeOf(lowerType, upperType)

processConstraints(c, typeCheckerContext, constraintIncorporator::incorporateSubtypeConstraint)
processConstraints(c, typeCheckerContext)
}

private fun addEqualityConstraintAndIncorporateIt(
Expand All @@ -97,14 +97,10 @@ class ConstraintInjector(
typeCheckerContext.setConstrainingTypesToPrintDebugInfo(typeVariable, equalType)
typeCheckerContext.addEqualityConstraint(typeVariable.typeConstructor(c), equalType)

processConstraints(c, typeCheckerContext, constraintIncorporator::incorporateEqualityConstraint)
processConstraints(c, typeCheckerContext)
}

private fun processConstraints(
c: Context,
typeCheckerContext: TypeCheckerContext,
incorporate: (c: TypeCheckerContext, typeVariable: TypeVariableMarker, constraint: Constraint) -> Unit
) {
private fun processConstraints(c: Context, typeCheckerContext: TypeCheckerContext) {
while (typeCheckerContext.hasConstraintsToProcess()) {
for ((typeVariable, constraint) in typeCheckerContext.extractAllConstraints()!!) {
if (c.shouldWeSkipConstraint(typeVariable, constraint)) continue
Expand All @@ -113,10 +109,17 @@ class ConstraintInjector(
c.notFixedTypeVariables[typeVariable.freshTypeConstructor(c)] ?: typeCheckerContext.fixedTypeVariable(typeVariable)

// it is important, that we add constraint here(not inside TypeCheckerContext), because inside incorporation we read constraints
constraints.addConstraint(constraint)?.let {
if (!constraint.isNullabilityConstraint) {
incorporate(typeCheckerContext, typeVariable, it)
}
val (addedOrNonRedundantExistedConstraint, wasAdded) = constraints.addConstraint(constraint)
val positionFrom = constraint.position.from
val constraintToIncorporate = when {
wasAdded && !constraint.isNullabilityConstraint -> addedOrNonRedundantExistedConstraint
positionFrom is FixVariableConstraintPosition<*> && positionFrom.variable == typeVariable && constraint.kind == EQUALITY ->
addedOrNonRedundantExistedConstraint
else -> null
}

if (constraintToIncorporate != null) {
constraintIncorporator.incorporate(typeCheckerContext, typeVariable, constraintToIncorporate)
}
}

Expand Down Expand Up @@ -255,9 +258,8 @@ class ConstraintInjector(
isFromNullabilityConstraint: Boolean
) = addConstraint(typeVariable, subType, LOWER, isFromNullabilityConstraint)

override fun addEqualityConstraint(typeVariable: TypeConstructorMarker, type: KotlinTypeMarker) {
override fun addEqualityConstraint(typeVariable: TypeConstructorMarker, type: KotlinTypeMarker) =
addConstraint(typeVariable, type, EQUALITY, false)
}

private fun isCapturedTypeFromSubtyping(type: KotlinTypeMarker) =
when ((type as? CapturedTypeMarker)?.captureStatus()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,20 @@ class MutableVariableWithConstraints private constructor(

private var simplifiedConstraints: SmartList<Constraint>? = mutableConstraints

// return new actual constraint, if this constraint is new
fun addConstraint(constraint: Constraint): Constraint? {
// return new actual constraint, if this constraint is new, otherwise return already existed not redundant constraint
// the second element of pair is a flag whether a constraint was added in fact
fun addConstraint(constraint: Constraint): Pair<Constraint, Boolean> {
val isLowerAndFlexibleTypeWithDefNotNullLowerBound = constraint.isLowerAndFlexibleTypeWithDefNotNullLowerBound()

for (previousConstraint in constraints) {
if (previousConstraint.typeHashCode == constraint.typeHashCode
&& previousConstraint.type == constraint.type
&& previousConstraint.isNullabilityConstraint == constraint.isNullabilityConstraint
) {
if (newConstraintIsUseless(previousConstraint, constraint)) return null
if (newConstraintIsUseless(previousConstraint, constraint)) {
return previousConstraint to false
}

val isMatchingForSimplification = when (previousConstraint.kind) {
ConstraintKind.LOWER -> constraint.kind.isUpper()
ConstraintKind.UPPER -> constraint.kind.isLower()
Expand All @@ -75,14 +79,14 @@ class MutableVariableWithConstraints private constructor(
} else constraint
mutableConstraints.add(actualConstraint)
simplifiedConstraints = null
return actualConstraint
return actualConstraint to true
}
}

if (isLowerAndFlexibleTypeWithDefNotNullLowerBound &&
previousConstraint.isStrongerThanLowerAndFlexibleTypeWithDefNotNullLowerBound(constraint)
) {
return null
return previousConstraint to false
}
}

Expand All @@ -95,7 +99,7 @@ class MutableVariableWithConstraints private constructor(
simplifiedConstraints = null
}

return constraint
return constraint to true
}

// This method should be used only for transaction in constraint system
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ fun testVariableWithBound() {

<!DEBUG_INFO_EXPRESSION_TYPE("Inv<kotlin.Int>")!>c1<!>

val c2 = <!TYPE_MISMATCH!>select<!>(SubInv<String>(), createWithNumberBound())
val c2 = <!TYPE_MISMATCH!>select<!>(SubInv<String>(), <!TYPE_MISMATCH!>createWithNumberBound<!>())

<!DEBUG_INFO_EXPRESSION_TYPE("Inv<kotlin.String>")!>c2<!>

val c3 = <!TYPE_MISMATCH!>select<!>(SubInv<Double>(), createWithIntBound())
val c3 = <!TYPE_MISMATCH!>select<!>(SubInv<Double>(), <!TYPE_MISMATCH!>createWithIntBound<!>())

<!DEBUG_INFO_EXPRESSION_TYPE("Inv<kotlin.Double>")!>c3<!>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ fun main() {
* K <: (A) -> Unit -> TypeVariable(_RP1) >: A
* K >: (C) -> TypeVariable(_R) -> TypeVariable(_RP1) <: C
*/
val x12 = selectC(id <!TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH!>{ <!DEBUG_INFO_EXPRESSION_TYPE("C")!>it<!> }<!>, id <!TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH!>{ x: B -> }<!><!NO_VALUE_FOR_PARAMETER!>)<!>
val x13 = selectA(id <!TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH!>{ <!DEBUG_INFO_EXPRESSION_TYPE("A")!>it<!> }<!>, id <!TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH!>{ x: C -> }<!><!NO_VALUE_FOR_PARAMETER!>)<!>
val x12 = selectC(id <!TYPE_MISMATCH!>{ <!DEBUG_INFO_EXPRESSION_TYPE("C")!>it<!> }<!>, id <!TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH!>{ x: B -> }<!><!NO_VALUE_FOR_PARAMETER!>)<!>
val x13 = selectA(id <!TYPE_MISMATCH!>{ <!DEBUG_INFO_EXPRESSION_TYPE("A")!>it<!> }<!>, id <!TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH!>{ x: C -> }<!><!NO_VALUE_FOR_PARAMETER!>)<!>
val x14 = selectC(id { <!DEBUG_INFO_EXPRESSION_TYPE("C")!>it<!> }, id { x: A -> }, { x -> x })
val x15 = selectC(id { <!DEBUG_INFO_EXPRESSION_TYPE("C")!>it<!> }, { x: A -> }, id { x -> x })
/*
Expand All @@ -138,8 +138,8 @@ fun main() {
* K <: (C) -> Unit -> TypeVariable(_RP1) >: C
* K == (B) -> Unit -> TypeVariable(_RP1) == B
*/
val x17: (C) -> Unit = <!TYPE_MISMATCH, TYPE_MISMATCH!>selectB(id <!TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH!>{ <!DEBUG_INFO_EXPRESSION_TYPE("B")!>it<!> }<!>, id <!TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH!>{ it }<!>, id<(B) -> Unit> { x -> x })<!>
val x18: (C) -> Unit = <!TYPE_MISMATCH!>select(id <!TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH!>{ <!DEBUG_INFO_EXPRESSION_TYPE("C")!>it<!> }<!>, <!TYPE_MISMATCH, TYPE_MISMATCH!>{ <!DEBUG_INFO_EXPRESSION_TYPE("C")!>it<!> }<!>, id<(B) -> Unit> { x -> x })<!>
val x17: (C) -> Unit = <!TYPE_MISMATCH, TYPE_MISMATCH!>selectB(id <!TYPE_MISMATCH, TYPE_MISMATCH!>{ <!DEBUG_INFO_EXPRESSION_TYPE("B")!>it<!> }<!>, id <!TYPE_MISMATCH, TYPE_MISMATCH!>{ it }<!>, id<(B) -> Unit> { x -> x })<!>
val x18: (C) -> Unit = <!TYPE_MISMATCH!>select(id <!TYPE_MISMATCH, TYPE_MISMATCH!>{ <!DEBUG_INFO_EXPRESSION_TYPE("C")!>it<!> }<!>, <!TYPE_MISMATCH!>{ <!DEBUG_INFO_EXPRESSION_TYPE("C")!>it<!> }<!>, id<(B) -> Unit> { x -> x })<!>

// Resolution of extension/non-extension functions combination
val x19: String.() -> Unit = select(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String.() -> kotlin.Unit")!>id { <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String"), DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>this<!> }<!>, <!DEBUG_INFO_EXPRESSION_TYPE("(kotlin.String) -> kotlin.Unit")!>id(fun(x: String) {})<!>)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,11 +240,11 @@ fun case_13() {
<!DEBUG_INFO_EXPRESSION_TYPE("dynamic")!>result_4<!>
<!DEBUG_INFO_EXPRESSION_TYPE("dynamic")!>result_5<!>
<!DEBUG_INFO_EXPRESSION_TYPE("dynamic")!>result_6<!>
<!DEBUG_INFO_EXPRESSION_TYPE("A<dynamic>")!>result_7<!>
<!DEBUG_INFO_EXPRESSION_TYPE("A<dynamic>")!>result_8<!>
<!DEBUG_INFO_EXPRESSION_TYPE("A<dynamic>")!>result_9<!>
<!DEBUG_INFO_EXPRESSION_TYPE("A<dynamic>")!>result_10<!>
<!DEBUG_INFO_EXPRESSION_TYPE("A<dynamic>")!>result_11<!>
<!DEBUG_INFO_EXPRESSION_TYPE("A<(kotlin.collections.MutableList<(kotlin.Int..kotlin.Int?)>..kotlin.collections.List<(kotlin.Int..kotlin.Int?)>?)>")!>result_7<!>
<!DEBUG_INFO_EXPRESSION_TYPE("A<(kotlin.collections.MutableList<(kotlin.Int..kotlin.Int?)>..kotlin.collections.List<(kotlin.Int..kotlin.Int?)>?)>")!>result_8<!>
<!DEBUG_INFO_EXPRESSION_TYPE("A<(kotlin.collections.MutableList<(kotlin.Int..kotlin.Int?)>..kotlin.collections.List<(kotlin.Int..kotlin.Int?)>?)>")!>result_9<!>
<!DEBUG_INFO_EXPRESSION_TYPE("A<kotlin.collections.MutableList<kotlin.Int>>")!>result_10<!>
<!DEBUG_INFO_EXPRESSION_TYPE("A<kotlin.collections.List<kotlin.Int>?>")!>result_11<!>
<!DEBUG_INFO_EXPRESSION_TYPE("A<dynamic>")!>result_12<!>
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// !DIAGNOSTICS: -CAST_NEVER_SUCCEEDS

interface I

interface Inv<P>
interface Out<out T>

class Bar<U : I>(val x: Inv<Out<U>>)

fun <T> materializeFoo(): Inv<T> = null as Inv<T>

fun main() {
Bar(materializeFoo())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// !DIAGNOSTICS: -CAST_NEVER_SUCCEEDS

interface I

interface Inv<P>
interface Out<out T>

class Bar<U : I>(val x: Inv<Out<U>>)

fun <T> materializeFoo(): Inv<T> = null as Inv<T>

fun main() {
<!NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>Bar<!>(<!NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>materializeFoo<!>())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package

public fun main(): kotlin.Unit
public fun </*0*/ T> materializeFoo(): Inv<T>

public final class Bar</*0*/ U : I> {
public constructor Bar</*0*/ U : I>(/*0*/ x: Inv<Out<U>>)
public final val x: Inv<Out<U>>
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}

public interface I {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}

public interface Inv</*0*/ P> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}

public interface Out</*0*/ out T> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
Loading

0 comments on commit 0857b9c

Please sign in to comment.