PSI2IR: Post-process return expressions based on expected return type

Consider the following example:

Java:
  public class J {
    public static String foo() { return null; }
  }

Kotlin:
  fun check(fn: () -> Any) = fn()
  fun test() = check { J.foo() }

When a lambda expression returns a value of platform type ('String!'),
corresponding lambda has platform type in its return type, which is
approximated to corresponding nullable type ('String?') in IR.
However, the lambda itself could occur in position with a functional
expected type ('() -> Any'). This implies an extra implicit cast on a
return value of lambda expression ('J.foo()'), although it conforms to
the return type of lambda.
This commit is contained in:
Dmitry Petrov
2019-12-30 19:38:52 +03:00
parent 0e4e5ac287
commit 8054e2960e
7 changed files with 241 additions and 59 deletions
@@ -0,0 +1,60 @@
FILE fqName:<root> fileName:/nullCheckOnLambdaReturn.kt
FUN name:check visibility:public modality:FINAL <> (fn:kotlin.Function0<kotlin.Any>) returnType:kotlin.Any
VALUE_PARAMETER name:fn index:0 type:kotlin.Function0<kotlin.Any>
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun check (fn: kotlin.Function0<kotlin.Any>): kotlin.Any declared in <root>'
CALL 'public abstract fun invoke (): kotlin.Any [operator] declared in kotlin.Function0' type=kotlin.Any origin=null
$this: GET_VAR 'fn: kotlin.Function0<kotlin.Any> declared in <root>.check' type=kotlin.Function0<kotlin.Any> origin=null
FUN name:id visibility:public modality:FINAL <T> (x:T of <root>.id) returnType:T of <root>.id
TYPE_PARAMETER name:T index:0 variance: superTypes:[kotlin.Any?]
VALUE_PARAMETER name:x index:0 type:T of <root>.id
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun id <T> (x: T of <root>.id): T of <root>.id declared in <root>'
GET_VAR 'x: T of <root>.id declared in <root>.id' type=T of <root>.id origin=null
FUN name:test1 visibility:public modality:FINAL <> () returnType:kotlin.Any
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun test1 (): kotlin.Any declared in <root>'
CALL 'public final fun check (fn: kotlin.Function0<kotlin.Any>): kotlin.Any declared in <root>' type=kotlin.Any origin=null
fn: FUN_EXPR type=kotlin.Function0<kotlin.Any> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.Any
BLOCK_BODY
CALL 'public open fun foo (): kotlin.String? [operator] declared in <root>.J' type=kotlin.String? origin=null
PROPERTY name:test2 visibility:public modality:FINAL [val]
FIELD PROPERTY_BACKING_FIELD name:test2 type:kotlin.Function0<kotlin.Any> visibility:private [final,static]
EXPRESSION_BODY
FUN_EXPR type=kotlin.Function0<kotlin.String?> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.String?
BLOCK_BODY
CALL 'public open fun foo (): kotlin.String? [operator] declared in <root>.J' type=kotlin.String? origin=null
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-test2> visibility:public modality:FINAL <> () returnType:kotlin.Function0<kotlin.Any>
correspondingProperty: PROPERTY name:test2 visibility:public modality:FINAL [val]
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun <get-test2> (): kotlin.Function0<kotlin.Any> declared in <root>'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:test2 type:kotlin.Function0<kotlin.Any> visibility:private [final,static]' type=kotlin.Function0<kotlin.Any> origin=null
PROPERTY name:test3 visibility:public modality:FINAL [val]
FIELD PROPERTY_BACKING_FIELD name:test3 type:kotlin.Function0<kotlin.Any> visibility:private [final,static]
EXPRESSION_BODY
TYPE_OP type=kotlin.Function0<kotlin.Any> origin=CAST typeOperand=kotlin.Function0<kotlin.Any>
FUN_EXPR type=kotlin.Function0<kotlin.String?> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.String?
BLOCK_BODY
CALL 'public open fun foo (): kotlin.String? [operator] declared in <root>.J' type=kotlin.String? origin=null
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-test3> visibility:public modality:FINAL <> () returnType:kotlin.Function0<kotlin.Any>
correspondingProperty: PROPERTY name:test3 visibility:public modality:FINAL [val]
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun <get-test3> (): kotlin.Function0<kotlin.Any> declared in <root>'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:test3 type:kotlin.Function0<kotlin.Any> visibility:private [final,static]' type=kotlin.Function0<kotlin.Any> origin=null
PROPERTY name:test4 visibility:public modality:FINAL [val]
FIELD PROPERTY_BACKING_FIELD name:test4 type:kotlin.Function0<kotlin.Any> visibility:private [final,static]
EXPRESSION_BODY
CALL 'public final fun id <T> (x: T of <root>.id): T of <root>.id declared in <root>' type=kotlin.Function0<kotlin.String?> origin=null
<T>: kotlin.Function0<kotlin.String?>
x: FUN_EXPR type=kotlin.Function0<kotlin.String?> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.String?
BLOCK_BODY
CALL 'public open fun foo (): kotlin.String? [operator] declared in <root>.J' type=kotlin.String? origin=null
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-test4> visibility:public modality:FINAL <> () returnType:kotlin.Function0<kotlin.Any>
correspondingProperty: PROPERTY name:test4 visibility:public modality:FINAL [val]
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun <get-test4> (): kotlin.Function0<kotlin.Any> declared in <root>'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:test4 type:kotlin.Function0<kotlin.Any> visibility:private [final,static]' type=kotlin.Function0<kotlin.Any> origin=null
@@ -0,0 +1,17 @@
// FILE: nullCheckOnLambdaReturn.kt
fun check(fn: () -> Any) = fn()
fun <T> id(x: T) = x
fun test1() = check { J.foo() }
val test2: () -> Any = { J.foo() }
val test3: () -> Any = { J.foo() } as () -> Any
val test4: () -> Any = id { J.foo() }
// FILE: J.java
public class J {
public static String foo() { return null; }
}
@@ -0,0 +1,66 @@
FILE fqName:<root> fileName:/nullCheckOnLambdaReturn.kt
FUN name:check visibility:public modality:FINAL <> (fn:kotlin.Function0<kotlin.Any>) returnType:kotlin.Any
VALUE_PARAMETER name:fn index:0 type:kotlin.Function0<kotlin.Any>
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun check (fn: kotlin.Function0<kotlin.Any>): kotlin.Any declared in <root>'
CALL 'public abstract fun invoke (): R of kotlin.Function0 [operator] declared in kotlin.Function0' type=kotlin.Any origin=INVOKE
$this: GET_VAR 'fn: kotlin.Function0<kotlin.Any> declared in <root>.check' type=kotlin.Function0<kotlin.Any> origin=VARIABLE_AS_FUNCTION
FUN name:id visibility:public modality:FINAL <T> (x:T of <root>.id) returnType:T of <root>.id
TYPE_PARAMETER name:T index:0 variance: superTypes:[kotlin.Any?]
VALUE_PARAMETER name:x index:0 type:T of <root>.id
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun id <T> (x: T of <root>.id): T of <root>.id declared in <root>'
GET_VAR 'x: T of <root>.id declared in <root>.id' type=T of <root>.id origin=null
FUN name:test1 visibility:public modality:FINAL <> () returnType:kotlin.Any
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun test1 (): kotlin.Any declared in <root>'
CALL 'public final fun check (fn: kotlin.Function0<kotlin.Any>): kotlin.Any declared in <root>' type=kotlin.Any origin=null
fn: FUN_EXPR type=kotlin.Function0<kotlin.String?> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.String?
BLOCK_BODY
RETURN type=kotlin.Nothing from='local final fun <anonymous> (): kotlin.String? declared in <root>.test1'
TYPE_OP type=kotlin.String origin=IMPLICIT_NOTNULL typeOperand=kotlin.String
CALL 'public open fun foo (): kotlin.String? declared in <root>.J' type=kotlin.String? origin=null
PROPERTY name:test2 visibility:public modality:FINAL [val]
FIELD PROPERTY_BACKING_FIELD name:test2 type:kotlin.Function0<kotlin.Any> visibility:private [final,static]
EXPRESSION_BODY
FUN_EXPR type=kotlin.Function0<kotlin.String?> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.String?
BLOCK_BODY
RETURN type=kotlin.Nothing from='local final fun <anonymous> (): kotlin.String? declared in <root>.test2'
TYPE_OP type=kotlin.String origin=IMPLICIT_NOTNULL typeOperand=kotlin.String
CALL 'public open fun foo (): kotlin.String? declared in <root>.J' type=kotlin.String? origin=null
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-test2> visibility:public modality:FINAL <> () returnType:kotlin.Function0<kotlin.Any>
correspondingProperty: PROPERTY name:test2 visibility:public modality:FINAL [val]
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun <get-test2> (): kotlin.Function0<kotlin.Any> declared in <root>'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:test2 type:kotlin.Function0<kotlin.Any> visibility:private [final,static]' type=kotlin.Function0<kotlin.Any> origin=null
PROPERTY name:test3 visibility:public modality:FINAL [val]
FIELD PROPERTY_BACKING_FIELD name:test3 type:kotlin.Function0<kotlin.Any> visibility:private [final,static]
EXPRESSION_BODY
TYPE_OP type=kotlin.Function0<kotlin.Any> origin=CAST typeOperand=kotlin.Function0<kotlin.Any>
FUN_EXPR type=kotlin.Function0<kotlin.String?> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.String?
BLOCK_BODY
RETURN type=kotlin.Nothing from='local final fun <anonymous> (): kotlin.String? declared in <root>.test3'
CALL 'public open fun foo (): kotlin.String? declared in <root>.J' type=kotlin.String? origin=null
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-test3> visibility:public modality:FINAL <> () returnType:kotlin.Function0<kotlin.Any>
correspondingProperty: PROPERTY name:test3 visibility:public modality:FINAL [val]
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun <get-test3> (): kotlin.Function0<kotlin.Any> declared in <root>'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:test3 type:kotlin.Function0<kotlin.Any> visibility:private [final,static]' type=kotlin.Function0<kotlin.Any> origin=null
PROPERTY name:test4 visibility:public modality:FINAL [val]
FIELD PROPERTY_BACKING_FIELD name:test4 type:kotlin.Function0<kotlin.Any> visibility:private [final,static]
EXPRESSION_BODY
CALL 'public final fun id <T> (x: T of <root>.id): T of <root>.id declared in <root>' type=kotlin.Function0<kotlin.String?> origin=null
<T>: kotlin.Function0<kotlin.String?>
x: FUN_EXPR type=kotlin.Function0<kotlin.String?> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> () returnType:kotlin.String?
BLOCK_BODY
RETURN type=kotlin.Nothing from='local final fun <anonymous> (): kotlin.String? declared in <root>.test4'
CALL 'public open fun foo (): kotlin.String? declared in <root>.J' type=kotlin.String? origin=null
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-test4> visibility:public modality:FINAL <> () returnType:kotlin.Function0<kotlin.Any>
correspondingProperty: PROPERTY name:test4 visibility:public modality:FINAL [val]
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun <get-test4> (): kotlin.Function0<kotlin.Any> declared in <root>'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:test4 type:kotlin.Function0<kotlin.Any> visibility:private [final,static]' type=kotlin.Function0<kotlin.Any> origin=null