[FIR] Add equality constraint from expected type for some synthetic function calls

This fixes some cases where we infer some type variable inside one
of the branches to Nothing instead of the expected type because Nothing
appeared in some other branch.

Specifically, we add an equality instead of a subtype constraint during
completion of calls to synthetic functions for if/when, try and !!.
We don't do it when the call contains a (possibly nested) elvis or is
inside the RHS of an assignment.
Otherwise, we would prevent some smart-casts.

#KT-65882 Fixed
This commit is contained in:
Kirill Rakhman
2024-02-21 16:06:47 +01:00
committed by Space Team
parent eaef7122f6
commit 69a7bf7f68
106 changed files with 1441 additions and 578 deletions
@@ -0,0 +1,67 @@
FILE fqName:<root> fileName:/1.kt
CLASS CLASS name:A modality:FINAL visibility:public superTypes:[<root>.Java1]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.A
CONSTRUCTOR visibility:public <> () returnType:<root>.A [primary]
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () declared in <root>.Java1'
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:A modality:FINAL visibility:public superTypes:[<root>.Java1]'
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in <root>.Java1
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:foo visibility:public modality:OPEN <> ($this:<root>.Java1) returnType:@[FlexibleNullability] java.lang.Void? [fake_override]
overridden:
public open fun foo (): @[FlexibleNullability] java.lang.Void? declared in <root>.Java1
$this: VALUE_PARAMETER name:<this> type:<root>.Java1
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int declared in <root>.Java1
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String declared in <root>.Java1
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
CLASS CLASS name:B modality:FINAL visibility:public superTypes:[<root>.Java1]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.B
CONSTRUCTOR visibility:public <> () returnType:<root>.B [primary]
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () declared in <root>.Java1'
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:B modality:FINAL visibility:public superTypes:[<root>.Java1]'
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in <root>.Java1
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int declared in <root>.Java1
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String declared in <root>.Java1
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN name:foo visibility:public modality:OPEN <> ($this:<root>.B) returnType:java.lang.Void
overridden:
public open fun foo (): @[FlexibleNullability] java.lang.Void? declared in <root>.Java1
$this: VALUE_PARAMETER name:<this> type:<root>.B
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun foo (): java.lang.Void declared in <root>.B'
CALL 'public final fun CHECK_NOT_NULL <T0> (arg0: T0 of kotlin.internal.ir.CHECK_NOT_NULL?): {T0 of kotlin.internal.ir.CHECK_NOT_NULL & Any} declared in kotlin.internal.ir' type=java.lang.Void origin=EXCLEXCL
<T0>: kotlin.Nothing
arg0: CONST Null type=kotlin.Nothing? value=null
FUN name:test visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY
VAR name:k type:java.lang.Void? [val]
CALL 'public open fun foo (): @[FlexibleNullability] java.lang.Void? declared in <root>.A' type=@[FlexibleNullability] java.lang.Void? origin=null
$this: CONSTRUCTOR_CALL 'public constructor <init> () declared in <root>.A' type=<root>.A origin=null
VAR name:k2 type:kotlin.Any [val]
TYPE_OP type=java.lang.Void origin=IMPLICIT_NOTNULL typeOperand=java.lang.Void
CALL 'public open fun foo (): @[FlexibleNullability] java.lang.Void? declared in <root>.A' type=@[FlexibleNullability] java.lang.Void? origin=null
$this: CONSTRUCTOR_CALL 'public constructor <init> () declared in <root>.A' type=<root>.A origin=null
VAR name:k3 type:java.lang.Void [val]
CALL 'public open fun foo (): java.lang.Void declared in <root>.B' type=java.lang.Void origin=null
$this: CONSTRUCTOR_CALL 'public constructor <init> () declared in <root>.B' type=<root>.B origin=null
VAR name:k4 type:kotlin.Any [val]
CALL 'public open fun foo (): java.lang.Void declared in <root>.B' type=java.lang.Void origin=null
$this: CONSTRUCTOR_CALL 'public constructor <init> () declared in <root>.B' type=<root>.B origin=null
@@ -0,0 +1,28 @@
class A : Java1 {
constructor() /* primary */ {
super/*Java1*/()
/* <init>() */
}
}
class B : Java1 {
constructor() /* primary */ {
super/*Java1*/()
/* <init>() */
}
override fun foo(): Void {
return CHECK_NOT_NULL<Nothing>(arg0 = null)
}
}
fun test() {
val k: Void? = A().foo()
val k2: Any = A().foo() /*!! Void */
val k3: Void = B().foo()
val k4: Any = B().foo()
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// TARGET_BACKEND: JVM
// FILE: Java1.java
@@ -0,0 +1,73 @@
FILE fqName:<root> fileName:/1.kt
CLASS CLASS name:C modality:FINAL visibility:public superTypes:[<root>.Java1; <root>.Java2]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.C
CONSTRUCTOR visibility:public <> () returnType:<root>.C [primary]
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () declared in <root>.Java1'
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:C modality:FINAL visibility:public superTypes:[<root>.Java1; <root>.Java2]'
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in <root>.Java1
public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in <root>.Java2
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:foo visibility:public modality:OPEN <> ($this:<root>.Java1) returnType:@[FlexibleNullability] java.lang.Void? [fake_override]
overridden:
public open fun foo (): @[FlexibleNullability] java.lang.Void? declared in <root>.Java1
public abstract fun foo (): @[FlexibleNullability] kotlin.Any? declared in <root>.Java2
$this: VALUE_PARAMETER name:<this> type:<root>.Java1
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int declared in <root>.Java1
public open fun hashCode (): kotlin.Int declared in <root>.Java2
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String declared in <root>.Java1
public open fun toString (): kotlin.String declared in <root>.Java2
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
CLASS CLASS name:D modality:FINAL visibility:public superTypes:[<root>.Java1; <root>.Java2]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.D
CONSTRUCTOR visibility:public <> () returnType:<root>.D [primary]
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () declared in <root>.Java1'
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:D modality:FINAL visibility:public superTypes:[<root>.Java1; <root>.Java2]'
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in <root>.Java1
public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in <root>.Java2
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int declared in <root>.Java1
public open fun hashCode (): kotlin.Int declared in <root>.Java2
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String declared in <root>.Java1
public open fun toString (): kotlin.String declared in <root>.Java2
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN name:foo visibility:public modality:OPEN <> ($this:<root>.D) returnType:java.lang.Void
overridden:
public open fun foo (): @[FlexibleNullability] java.lang.Void? declared in <root>.Java1
public abstract fun foo (): @[FlexibleNullability] kotlin.Any? declared in <root>.Java2
$this: VALUE_PARAMETER name:<this> type:<root>.D
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun foo (): java.lang.Void declared in <root>.D'
CALL 'public final fun CHECK_NOT_NULL <T0> (arg0: T0 of kotlin.internal.ir.CHECK_NOT_NULL?): {T0 of kotlin.internal.ir.CHECK_NOT_NULL & Any} declared in kotlin.internal.ir' type=java.lang.Void origin=EXCLEXCL
<T0>: kotlin.Nothing
arg0: CONST Null type=kotlin.Nothing? value=null
FUN name:test visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY
VAR name:k1 type:kotlin.Any [val]
TYPE_OP type=java.lang.Void origin=IMPLICIT_NOTNULL typeOperand=java.lang.Void
CALL 'public open fun foo (): @[FlexibleNullability] java.lang.Void? declared in <root>.C' type=@[FlexibleNullability] java.lang.Void? origin=null
$this: CONSTRUCTOR_CALL 'public constructor <init> () declared in <root>.C' type=<root>.C origin=null
VAR name:k2 type:java.lang.Void [val]
TYPE_OP type=java.lang.Void origin=IMPLICIT_NOTNULL typeOperand=java.lang.Void
CALL 'public open fun foo (): @[FlexibleNullability] java.lang.Void? declared in <root>.C' type=@[FlexibleNullability] java.lang.Void? origin=null
$this: CONSTRUCTOR_CALL 'public constructor <init> () declared in <root>.C' type=<root>.C origin=null
VAR name:k3 type:java.lang.Void [val]
CALL 'public open fun foo (): java.lang.Void declared in <root>.D' type=java.lang.Void origin=null
$this: CONSTRUCTOR_CALL 'public constructor <init> () declared in <root>.D' type=<root>.D origin=null
@@ -0,0 +1,27 @@
class C : Java1, Java2 {
constructor() /* primary */ {
super/*Java1*/()
/* <init>() */
}
}
class D : Java1, Java2 {
constructor() /* primary */ {
super/*Java1*/()
/* <init>() */
}
override fun foo(): Void {
return CHECK_NOT_NULL<Nothing>(arg0 = null)
}
}
fun test() {
val k1: Any = C().foo() /*!! Void */
val k2: Void = C().foo() /*!! Void */
val k3: Void = D().foo()
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// TARGET_BACKEND: JVM
// FILE: Java1.java