Make IrExpression.type nullable.

This commit is contained in:
Dmitry Petrov
2016-08-12 19:58:26 +03:00
committed by Dmitry Petrov
parent 030111b130
commit 55eb79febf
26 changed files with 96 additions and 89 deletions
@@ -17,15 +17,10 @@
package org.jetbrains.kotlin.psi2ir
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginKind
import org.jetbrains.kotlin.ir.declarations.IrLocalVariableImpl
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptor
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.scopes.receivers.*
@@ -117,7 +112,7 @@ class IrCallGenerator(
for (valueArgument in valueArgumentsInEvaluationOrder) {
val irArgument = generateValueArgument(valueArgument) ?: continue
val irTemporary = irExpressionGenerator.declarationFactory.createTemporaryVariable(irArgument)
val irTemporaryDeclaration = IrLocalVariableDeclarationExpressionImpl(irArgument.startOffset, irArgument.endOffset, irArgument.type)
val irTemporaryDeclaration = IrLocalVariableDeclarationExpressionImpl(irArgument.startOffset, irArgument.endOffset)
irTemporaryDeclaration.childDeclaration = irTemporary
irBlock.addChildExpression(irTemporaryDeclaration)
@@ -137,6 +132,7 @@ class IrCallGenerator(
return irBlock
}
// TODO smart casts on implicit receivers
private fun generateReceiver(ktExpression: KtExpression, receiver: ReceiverValue?): IrExpression? =
when (receiver) {
is ImplicitClassReceiver ->
@@ -78,10 +78,11 @@ class IrLocalDeclarationsFactory(val scopeOwner: DeclarationDescriptor) : IrDecl
fun createTemporaryVariable(irExpression: IrExpression): IrLocalVariable =
IrLocalVariableImpl(irExpression.startOffset, irExpression.endOffset,
IrDeclarationOriginKind.IR_TEMPORARY_VARIABLE,
createDescriptorForTemporaryVariable(irExpression.type)
createDescriptorForTemporaryVariable(irExpression.type
?: throw AssertionError("No type for $irExpression"))
).apply { initializerExpression = irExpression }
fun createLocalVariable(ktElement: KtElement, type: KotlinType, descriptor: VariableDescriptor): IrLocalVariable =
fun createLocalVariable(ktElement: KtElement, descriptor: VariableDescriptor): IrLocalVariable =
IrLocalVariableImpl(ktElement.startOffset, ktElement.endOffset,
IrDeclarationOriginKind.DEFINED,
descriptor)
@@ -24,7 +24,6 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrExpressionBodyImpl
import org.jetbrains.kotlin.ir.expressions.IrReturnExpressionImpl
import org.jetbrains.kotlin.ir.expressions.returnedExpression
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
@@ -60,6 +60,17 @@ class IrExpressionGenerator(
override fun visitExpression(expression: KtExpression, data: Nothing?): IrExpression =
IrDummyExpression(expression.startOffset, expression.endOffset, getTypeOrFail(expression), expression.javaClass.simpleName)
override fun visitProperty(property: KtProperty, data: Nothing?): IrExpression {
if (property.delegateExpression != null) TODO("Local delegated property")
val variableDescriptor = getOrFail(BindingContext.VARIABLE, property) { "No descriptor for local variable ${property.name}" }
val irLocalVariable = declarationFactory.createLocalVariable(property, variableDescriptor)
irLocalVariable.initializerExpression = property.initializer?.generate()
return IrLocalVariableDeclarationExpressionImpl(property.startOffset, property.endOffset, irLocalVariable)
}
override fun visitBlockExpression(expression: KtBlockExpression, data: Nothing?): IrExpression {
val irBlock = IrBlockExpressionImpl(expression.startOffset, expression.endOffset, getTypeOrFail(expression), false)
expression.statements.forEach { irBlock.addChildExpression(it.generate()) }
@@ -159,6 +170,4 @@ class IrExpressionGenerator(
error("Expected this or receiver: $referenceTarget")
}
}
}
@@ -30,7 +30,7 @@ fun IrBlockExpression.getResultExpression() =
class IrBlockExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
type: KotlinType?,
override val hasResult: Boolean
) : IrCompoundExpressionNBase(startOffset, endOffset, type), IrBlockExpression {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
@@ -38,7 +38,7 @@ interface IrCallExpression : IrMemberAccessExpression, IrCompoundExpression {
class IrCallExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
type: KotlinType?,
override val descriptor: CallableDescriptor,
isSafe: Boolean,
override val operator: IrOperator?,
@@ -33,7 +33,7 @@ interface IrCompoundExpressionN : IrCompoundExpression, IrExpressionOwnerN
abstract class IrCompoundExpressionNBase(
startOffset: Int,
endOffset: Int,
override val type: KotlinType
override val type: KotlinType?
) : IrExpressionBase(startOffset, endOffset, type), IrCompoundExpressionN {
override val childExpressions: MutableList<IrExpression> = ArrayList()
@@ -74,7 +74,7 @@ abstract class IrCompoundExpressionNBase(
abstract class IrCompoundExpression1Base(
startOffset: Int,
endOffset: Int,
override val type: KotlinType
override val type: KotlinType?
) : IrExpressionBase(startOffset, endOffset, type), IrCompoundExpression1 {
override var argument: IrExpression? = null
set(newExpression) {
@@ -103,7 +103,7 @@ abstract class IrCompoundExpression1Base(
abstract class IrCompoundExpression2Base(
startOffset: Int,
endOffset: Int,
override val type: KotlinType
override val type: KotlinType?
) : IrExpressionBase(startOffset, endOffset, type), IrCompoundExpression2 {
override var argument0: IrExpression? = null
set(newExpression) {
@@ -37,14 +37,14 @@ interface IrGetEnumValueExpression : IrGetSingletonValueExpression
abstract class IrDeclarationReferenceBase<out D : DeclarationDescriptor>(
startOffset: Int,
endOffset: Int,
type: KotlinType,
type: KotlinType?,
override val descriptor: D
) : IrTerminalExpressionBase(startOffset, endOffset, type), IrDeclarationReference
class IrGetObjectValueExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
type: KotlinType?,
descriptor: ClassDescriptor
) : IrDeclarationReferenceBase<ClassDescriptor>(startOffset, endOffset, type, descriptor), IrGetObjectValueExpression {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
@@ -54,7 +54,7 @@ class IrGetObjectValueExpressionImpl(
class IrGetEnumValueExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
type: KotlinType?,
descriptor: ClassDescriptor
) : IrDeclarationReferenceBase<ClassDescriptor>(startOffset, endOffset, type, descriptor), IrGetEnumValueExpression {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.types.KotlinType
class IrDummyExpression(
startOffset: Int,
endOffset: Int,
type: KotlinType,
type: KotlinType?,
val description: String
) : IrExpressionBase(startOffset, endOffset, type) {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
@@ -26,7 +26,7 @@ import org.jetbrains.kotlin.types.KotlinType
interface IrExpression : IrElement {
override val parent: IrExpressionOwner?
val index: Int
val type: KotlinType
val type: KotlinType?
fun setTreeLocation(parent: IrExpressionOwner?, index: Int)
}
@@ -42,7 +42,7 @@ fun IrExpressionOwner.validateChild(child: IrExpression) {
abstract class IrExpressionBase(
startOffset: Int,
endOffset: Int,
override val type: KotlinType
override val type: KotlinType?
) : IrElementBase(startOffset, endOffset), IrExpression {
override var parent: IrExpressionOwner? = null
override var index: Int = DETACHED_INDEX
@@ -56,7 +56,7 @@ abstract class IrExpressionBase(
abstract class IrTerminalExpressionBase(
startOffset: Int,
endOffset: Int,
type: KotlinType
type: KotlinType?
) : IrExpressionBase(startOffset, endOffset, type) {
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
// No children
@@ -32,7 +32,7 @@ interface IrGetExtensionReceiverExpression : IrDeclarationReference {
class IrGetVariableExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
type: KotlinType?,
descriptor: VariableDescriptor
) : IrDeclarationReferenceBase<VariableDescriptor>(startOffset, endOffset, type, descriptor), IrGetVariableExpression {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
@@ -42,7 +42,7 @@ class IrGetVariableExpressionImpl(
class IrGetExtensionReceiverExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
type: KotlinType?,
override val descriptor: CallableDescriptor
) : IrTerminalExpressionBase(startOffset, endOffset, type), IrGetExtensionReceiverExpression {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
@@ -45,7 +45,7 @@ sealed class IrLiteralKind<out T>(val asString: kotlin.String) {
class IrLiteralExpressionImpl<out T> (
startOffset: Int,
endOffset: Int,
type: KotlinType,
type: KotlinType?,
override val kind: IrLiteralKind<T>,
override val value: T
) : IrTerminalExpressionBase(startOffset, endOffset, type), IrLiteralExpression<T> {
@@ -32,7 +32,7 @@ interface IrLocalVariableDeclarationExpression : IrLocalDeclarationExpression<Ir
abstract class IrLocalDeclarationExpressionBase(
startOffset: Int,
endOffset: Int,
type: KotlinType
type: KotlinType?
) : IrExpressionBase(startOffset, endOffset, type), IrLocalVariableDeclarationExpression {
private var childDeclarationImpl: IrLocalVariable? = null
override var childDeclaration: IrLocalVariable
@@ -59,9 +59,12 @@ abstract class IrLocalDeclarationExpressionBase(
class IrLocalVariableDeclarationExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType
) : IrLocalDeclarationExpressionBase(startOffset, endOffset, type), IrLocalVariableDeclarationExpression {
endOffset: Int
) : IrLocalDeclarationExpressionBase(startOffset, endOffset, null), IrLocalVariableDeclarationExpression {
constructor(startOffset: Int, endOffset: Int, childDeclaration: IrLocalVariable) : this(startOffset, endOffset) {
this.childDeclaration = childDeclaration
}
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitLocalVariableDeclarationExpression(this, data)
}
@@ -29,7 +29,7 @@ interface IrMemberAccessExpression : IrDeclarationReference, IrExpressionOwner {
abstract class IrMemberAccessExpressionBase(
startOffset: Int,
endOffset: Int,
type: KotlinType,
type: KotlinType?,
override val isSafe: Boolean
) : IrExpressionBase(startOffset, endOffset, type), IrMemberAccessExpression {
override var dispatchReceiver: IrExpression? = null
@@ -32,7 +32,7 @@ interface IrBinaryOperatorExpression : IrOperatorExpression, IrCompoundExpressio
class IrUnaryOperatorExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
type: KotlinType?,
override val operator: IrOperator,
override val relatedDescriptor: FunctionDescriptor?
) : IrCompoundExpression1Base(startOffset, endOffset, type), IrUnaryOperatorExpression {
@@ -44,7 +44,7 @@ class IrUnaryOperatorExpressionImpl(
class IrBinaryOperatorExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
type: KotlinType?,
override val operator: IrOperator,
override val relatedDescriptor: FunctionDescriptor?
) : IrCompoundExpression2Base(startOffset, endOffset, type), IrBinaryOperatorExpression {
@@ -34,7 +34,7 @@ interface IrSetPropertyExpression : IrPropertyAccessExpression, IrCompoundExpres
class IrGetPropertyExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
type: KotlinType?,
isSafe: Boolean,
override val descriptor: PropertyDescriptor
) : IrMemberAccessExpressionBase(startOffset, endOffset, type, isSafe), IrGetPropertyExpression {
@@ -70,7 +70,7 @@ class IrGetPropertyExpressionImpl(
class IrSetPropertyExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
type: KotlinType?,
isSafe: Boolean,
override val descriptor: PropertyDescriptor
) : IrMemberAccessExpressionBase(startOffset, endOffset, type, isSafe), IrSetPropertyExpression {
@@ -22,16 +22,10 @@ import org.jetbrains.kotlin.types.KotlinType
interface IrReturnExpression : IrCompoundExpression1
var IrReturnExpression.returnedExpression: IrExpression?
get() = argument
set(newReturnedExpression) {
argument = newReturnedExpression
}
class IrReturnExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType
type: KotlinType?
) : IrCompoundExpression1Base(startOffset, endOffset, type), IrReturnExpression {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitReturnExpression(this, data)
@@ -24,7 +24,7 @@ interface IrStringConcatenationExpression : IrCompoundExpressionN
class IrStringConcatenationExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType
type: KotlinType?
) : IrCompoundExpressionNBase(startOffset, endOffset, type), IrStringConcatenationExpression {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitStringTemplate(this, data)
@@ -27,7 +27,7 @@ interface IrThisExpression : IrExpression {
class IrThisExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
type: KotlinType?,
override val classDescriptor: ClassDescriptor
) : IrTerminalExpressionBase(startOffset, endOffset, type), IrThisExpression {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
@@ -35,7 +35,7 @@ interface IrTypeOperatorExpression : IrExpression, IrCompoundExpression1 {
class IrTypeOperatorExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
type: KotlinType?,
override val operator: IrTypeOperator,
override val typeOperand: KotlinType
) : IrCompoundExpression1Base(startOffset, endOffset, type), IrTypeOperatorExpression {
@@ -70,31 +70,34 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
"RETURN type=${expression.renderType()}"
override fun visitGetExtensionReceiver(expression: IrGetExtensionReceiverExpression, data: Nothing?): String =
"\$RECEIVER of: ${expression.descriptor.containingDeclaration.name}"
"\$RECEIVER of: ${expression.descriptor.containingDeclaration.name} type=${expression.renderType()}"
override fun visitThisExpression(expression: IrThisExpression, data: Nothing?): String =
"THIS ${expression.classDescriptor.render()}"
"THIS ${expression.classDescriptor.render()} type=${expression.renderType()}"
override fun visitCallExpression(expression: IrCallExpression, data: Nothing?): String =
"CALL ${if (expression.isSafe) "?." else "."}${expression.descriptor.name} ${expression.operator ?: ""}"
"CALL ${if (expression.isSafe) "?." else "."}${expression.descriptor.name} " +
"type=${expression.renderType()} operator=${expression.operator ?: ""}"
override fun visitGetProperty(expression: IrGetPropertyExpression, data: Nothing?): String =
"GET_PROPERTY ${if (expression.isSafe) "?." else "."}${expression.descriptor.name}"
"GET_PROPERTY ${if (expression.isSafe) "?." else "."}${expression.descriptor.name} " +
"type=${expression.renderType()}"
override fun visitGetVariable(expression: IrGetVariableExpression, data: Nothing?): String =
"GET_VAR ${expression.descriptor.name}"
"GET_VAR ${expression.descriptor.name} type=${expression.renderType()}"
override fun visitGetObjectValue(expression: IrGetObjectValueExpression, data: Nothing?): String =
"GET_OBJECT ${expression.descriptor.name}"
"GET_OBJECT ${expression.descriptor.name} type=${expression.renderType()}"
override fun visitGetEnumValue(expression: IrGetEnumValueExpression, data: Nothing?): String =
"GET_ENUM_VALUE ${expression.descriptor.name}"
"GET_ENUM_VALUE ${expression.descriptor.name} type=${expression.renderType()}"
override fun visitSetProperty(expression: IrSetPropertyExpression, data: Nothing?): String =
"SET_PROPERTY ${if (expression.isSafe) "?." else "."}${expression.descriptor.name}"
"SET_PROPERTY ${if (expression.isSafe) "?." else "."}${expression.descriptor.name}" +
"type=${expression.renderType()}"
override fun visitDummyExpression(expression: IrDummyExpression, data: Nothing?): String =
"DUMMY ${expression.description}"
"DUMMY ${expression.description} type=${expression.renderType()}"
companion object {
private val DESCRIPTOR_RENDERER = DescriptorRenderer.withOptions {
@@ -113,6 +116,6 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
DESCRIPTOR_RENDERER.render(this)
internal fun IrExpression.renderType(): String =
DESCRIPTOR_RENDERER.renderType(type)
type?.let { DESCRIPTOR_RENDERER.renderType(it) } ?: "<no-type>"
}
}
+8 -8
View File
@@ -17,16 +17,16 @@ IrFile /callWithReorderedArguments.kt
IrFunction public fun test(): kotlin.Unit
IrExpressionBody
BLOCK type=kotlin.Unit
CALL .foo
a: CALL .noReorder1
b: CALL .noReorder2
CALL .foo type=kotlin.Unit operator=
a: CALL .noReorder1 type=kotlin.Int operator=
b: CALL .noReorder2 type=kotlin.Int operator=
BLOCK type=kotlin.Unit
LOCAL tmp0
IrLocalVariable val tmp0: kotlin.Int
CALL .reordered1
CALL .reordered1 type=kotlin.Int operator=
LOCAL tmp1
IrLocalVariable val tmp1: kotlin.Int
CALL .reordered2
CALL .foo
a: GET_VAR tmp1
b: GET_VAR tmp0
CALL .reordered2 type=kotlin.Int operator=
CALL .foo type=kotlin.Unit operator=
a: GET_VAR tmp1 type=kotlin.Int
b: GET_VAR tmp0 type=kotlin.Int
+12 -12
View File
@@ -1,24 +1,24 @@
IrFile /calls.kt
IrFunction public fun foo(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int): kotlin.Int
IrExpressionBody
GET_VAR x
GET_VAR x type=kotlin.Int
IrFunction public fun bar(/*0*/ x: kotlin.Int): kotlin.Int
IrExpressionBody
CALL .foo
x: GET_VAR x
CALL .foo type=kotlin.Int operator=
x: GET_VAR x type=kotlin.Int
y: LITERAL Int type=kotlin.Int value='1'
IrFunction public fun qux(/*0*/ x: kotlin.Int): kotlin.Int
IrExpressionBody
CALL .foo
x: CALL .foo
x: GET_VAR x
y: GET_VAR x
y: GET_VAR x
CALL .foo type=kotlin.Int operator=
x: CALL .foo type=kotlin.Int operator=
x: GET_VAR x type=kotlin.Int
y: GET_VAR x type=kotlin.Int
y: GET_VAR x type=kotlin.Int
IrFunction public fun kotlin.Int.ext1(): kotlin.Int
IrExpressionBody
$RECEIVER of: ext1
$RECEIVER of: ext1 type=kotlin.Int
IrFunction public fun kotlin.Int.ext2(/*0*/ x: kotlin.Int): kotlin.Int
IrExpressionBody
CALL .foo
x: $RECEIVER of: ext2
y: GET_VAR x
CALL .foo type=kotlin.Int operator=
x: $RECEIVER of: ext2 type=kotlin.Int
y: GET_VAR x type=kotlin.Int
+4 -4
View File
@@ -1,9 +1,9 @@
IrFile /dotQualified.kt
IrFunction public fun length(/*0*/ s: kotlin.String): kotlin.Int
IrExpressionBody
GET_PROPERTY .length
$this: GET_VAR s
GET_PROPERTY .length type=kotlin.Int
$this: GET_VAR s type=kotlin.String
IrFunction public fun lengthN(/*0*/ s: kotlin.String?): kotlin.Int?
IrExpressionBody
GET_PROPERTY ?.length
$this: GET_VAR s
GET_PROPERTY ?.length type=kotlin.Int?
$this: GET_VAR s type=kotlin.String?
@@ -5,5 +5,5 @@ IrFile /extensionPropertyGetterCall.kt
LITERAL String type=kotlin.String value='OK'
IrFunction public fun kotlin.String.test5(): kotlin.String
IrExpressionBody
GET_PROPERTY .okext
$receiver: $RECEIVER of: test5
GET_PROPERTY .okext type=kotlin.String
$receiver: $RECEIVER of: test5 type=kotlin.String
+10 -8
View File
@@ -4,31 +4,33 @@ IrFile /references.kt
LITERAL String type=kotlin.String value='OK'
IrProperty public val ok2: kotlin.String = "OK" getter=null setter=null
IrExpressionBody
GET_PROPERTY .ok
GET_PROPERTY .ok type=kotlin.String
IrProperty public val ok3: kotlin.String getter=<get-ok3> setter=null
IrPropertyGetter public fun <get-ok3>(): kotlin.String property=ok3
IrExpressionBody
LITERAL String type=kotlin.String value='OK'
IrFunction public fun test1(): kotlin.String
IrExpressionBody
GET_PROPERTY .ok
GET_PROPERTY .ok type=kotlin.String
IrFunction public fun test2(/*0*/ x: kotlin.String): kotlin.String
IrExpressionBody
GET_VAR x
GET_VAR x type=kotlin.String
IrFunction public fun test3(): kotlin.String
IrExpressionBody
BLOCK type=kotlin.Nothing
DUMMY KtProperty
LOCAL x
IrLocalVariable val x: kotlin.String = "OK"
LITERAL String type=kotlin.String value='OK'
RETURN type=kotlin.Nothing
GET_VAR x
GET_VAR x type=kotlin.String
IrFunction public fun test4(): kotlin.String
IrExpressionBody
GET_PROPERTY .ok3
GET_PROPERTY .ok3 type=kotlin.String
IrProperty public val kotlin.String.okext: kotlin.String getter=<get-okext> setter=null
IrPropertyGetter public fun kotlin.String.<get-okext>(): kotlin.String property=okext
IrExpressionBody
LITERAL String type=kotlin.String value='OK'
IrFunction public fun kotlin.String.test5(): kotlin.String
IrExpressionBody
GET_PROPERTY .okext
$receiver: $RECEIVER of: test5
GET_PROPERTY .okext type=kotlin.String
$receiver: $RECEIVER of: test5 type=kotlin.String