[FIR] Properly support @JvmRecord

^KT-53867 Fixed
This commit is contained in:
Dmitriy Novozhilov
2022-09-19 14:52:39 +03:00
parent c0ad67c4f9
commit 6623456d2a
23 changed files with 490 additions and 29 deletions
@@ -61,7 +61,7 @@ internal class LLFirDesignatedSupertypeResolverTransformer(
}
private inner class DesignatedFirApplySupertypesTransformer(classDesignation: FirDeclarationDesignation) :
FirApplySupertypesTransformer(supertypeComputationSession) {
FirApplySupertypesTransformer(supertypeComputationSession, session, scopeSession) {
val declarationTransformer = LLFirDeclarationTransformer(classDesignation)
@@ -25,13 +25,13 @@ import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.JvmNames.JVM_RECORD_ANNOTATION_CLASS_ID
import org.jetbrains.kotlin.name.StandardClassIds
object FirJvmRecordChecker : FirRegularClassChecker() {
private val JAVA_RECORD_CLASS_ID = ClassId.fromString("java/lang/Record")
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
declaration.superTypeRefs.firstOrNull()?.let { typeRef ->
if (typeRef.coneTypeSafe<ConeClassLikeType>()?.classId == JAVA_RECORD_CLASS_ID) {
// compiler automatically adds java.lang.Record supertype, so we should check only for explicit type declarations
if (typeRef.source != null && typeRef.coneTypeSafe<ConeClassLikeType>()?.classId == StandardClassIds.Java.Record) {
reporter.reportOn(typeRef.source, FirJvmErrors.ILLEGAL_JAVA_LANG_RECORD_SUPERTYPE, context)
return
}
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProviderImpl
import org.jetbrains.kotlin.fir.extensions.*
import org.jetbrains.kotlin.fir.java.FirJavaVisibilityChecker
import org.jetbrains.kotlin.fir.java.FirJvmDefaultModeComponent
import org.jetbrains.kotlin.fir.java.JvmSupertypeUpdater
import org.jetbrains.kotlin.fir.java.enhancement.FirAnnotationTypeQualifierResolver
import org.jetbrains.kotlin.fir.java.enhancement.FirEnhancedSymbolsStorage
import org.jetbrains.kotlin.fir.resolve.*
@@ -30,6 +31,7 @@ import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirQualifierResolverImpl
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirTypeResolverImpl
import org.jetbrains.kotlin.fir.resolve.transformers.FirCompilerLazyDeclarationResolver
import org.jetbrains.kotlin.fir.resolve.transformers.PlatformSupertypeUpdater
import org.jetbrains.kotlin.fir.resolve.transformers.plugin.GeneratedClassIndex
import org.jetbrains.kotlin.fir.scopes.FirOverrideService
import org.jetbrains.kotlin.fir.scopes.FirPlatformClassMapper
@@ -85,6 +87,7 @@ fun FirSession.registerCommonJavaComponents(javaModuleResolver: JavaModuleResolv
FirJvmDefaultModeComponent::class,
FirJvmDefaultModeComponent(languageVersionSettings.getFlag(JvmAnalysisFlags.jvmDefaultMode))
)
register(PlatformSupertypeUpdater::class, JvmSupertypeUpdater(this))
}
// -------------------------- Resolve components --------------------------
@@ -951,6 +951,12 @@ public class Fir2IrTextTestGenerated extends AbstractFir2IrTextTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/ir/irText/declarations/jvmRecord"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("dataClassWithJvmRecord.kt")
public void testDataClassWithJvmRecord() throws Exception {
runTest("compiler/testData/ir/irText/declarations/jvmRecord/dataClassWithJvmRecord.kt");
}
@Test
@TestMetadata("javaRecordComponentAccess.kt")
public void testJavaRecordComponentAccess() throws Exception {
@@ -951,6 +951,12 @@ public class LightTreeFir2IrTextTestGenerated extends AbstractLightTreeFir2IrTex
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/ir/irText/declarations/jvmRecord"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("dataClassWithJvmRecord.kt")
public void testDataClassWithJvmRecord() throws Exception {
runTest("compiler/testData/ir/irText/declarations/jvmRecord/dataClassWithJvmRecord.kt");
}
@Test
@TestMetadata("javaRecordComponentAccess.kt")
public void testJavaRecordComponentAccess() throws Exception {
@@ -0,0 +1,104 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.java
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirConstructor
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.hasAnnotationSafe
import org.jetbrains.kotlin.fir.declarations.utils.isData
import org.jetbrains.kotlin.fir.expressions.FirDelegatedConstructorCall
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.java.JvmSupertypeUpdater.DelegatedConstructorCallTransformer.Companion.recordType
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
import org.jetbrains.kotlin.fir.resolve.transformers.PlatformSupertypeUpdater
import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype
import org.jetbrains.kotlin.fir.scopes.getDeclaredConstructors
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.FirImplicitBuiltinTypeRef
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.name.StandardClassIds
class JvmSupertypeUpdater(private val session: FirSession) : PlatformSupertypeUpdater() {
private val jvmRecordUpdater = DelegatedConstructorCallTransformer(session)
override fun updateSupertypesIfNeeded(firClass: FirClass, scopeSession: ScopeSession) {
if (!(firClass is FirRegularClass && firClass.isData && firClass.hasAnnotationSafe(StandardClassIds.Annotations.JvmRecord))) return
var anyFound = false
var hasExplicitSuperClass = false
val newSuperTypeRefs = firClass.superTypeRefs.mapTo(mutableListOf()) {
when {
it is FirImplicitBuiltinTypeRef && it.id == StandardClassIds.Any -> {
anyFound = true
it.withReplacedConeType(recordType)
}
it.coneType.toRegularClassSymbol(session)?.classKind == ClassKind.CLASS -> {
hasExplicitSuperClass = true
it
}
else -> it
}
}
if (!anyFound && !hasExplicitSuperClass) {
newSuperTypeRefs += recordType.toFirResolvedTypeRef()
}
if (anyFound || !hasExplicitSuperClass) {
firClass.replaceSuperTypeRefs(newSuperTypeRefs)
firClass.transformSingle(jvmRecordUpdater, scopeSession)
}
}
private class DelegatedConstructorCallTransformer(private val session: FirSession) : FirTransformer<ScopeSession>() {
companion object {
val recordType = StandardClassIds.Java.Record.constructClassLikeType(emptyArray(), isNullable = false)
}
override fun <E : FirElement> transformElement(element: E, data: ScopeSession): E {
return element
}
override fun transformRegularClass(regularClass: FirRegularClass, data: ScopeSession): FirStatement {
return regularClass.transformDeclarations(this, data)
}
override fun transformConstructor(constructor: FirConstructor, data: ScopeSession): FirStatement {
return constructor.transformDelegatedConstructor(this, data)
}
override fun transformDelegatedConstructorCall(
delegatedConstructorCall: FirDelegatedConstructorCall,
data: ScopeSession
): FirStatement {
val constructedTypeRef = delegatedConstructorCall.constructedTypeRef
if (constructedTypeRef is FirImplicitTypeRef || constructedTypeRef.coneTypeSafe<ConeKotlinType>()?.isAny == true) {
delegatedConstructorCall.replaceConstructedTypeRef(constructedTypeRef.resolvedTypeFromPrototype(recordType))
}
val recordConstructorSymbol = recordType.lookupTag.toFirRegularClassSymbol(session)
?.unsubstitutedScope(session, data, withForcedTypeCalculator = false)
?.getDeclaredConstructors()
?.firstOrNull { it.fir.valueParameters.isEmpty() }
if (recordConstructorSymbol != null) {
val newReference = buildResolvedNamedReference {
name = StandardClassIds.Java.Record.shortClassName
resolvedSymbol = recordConstructorSymbol
}
delegatedConstructorCall.replaceCalleeReference(newReference)
}
return delegatedConstructorCall
}
}
}
@@ -26,9 +26,15 @@ private fun FirAnnotation.toAnnotationLookupTag(): ConeClassLikeLookupTag? =
// this cast fails when we have generic-typed annotations @T
(annotationTypeRef.coneType as? ConeClassLikeType)?.lookupTag
private fun FirAnnotation.toAnnotationLookupTagSafe(): ConeClassLikeLookupTag? =
annotationTypeRef.coneTypeSafe<ConeClassLikeType>()?.lookupTag
fun FirAnnotation.toAnnotationClassId(): ClassId? =
toAnnotationLookupTag()?.classId
fun FirAnnotation.toAnnotationClassIdSafe(): ClassId? =
toAnnotationLookupTagSafe()?.classId
private fun FirAnnotation.toAnnotationClass(session: FirSession): FirRegularClass? =
toAnnotationLookupTag()?.toSymbol(session)?.fir as? FirRegularClass
@@ -99,6 +105,10 @@ fun FirDeclaration.hasAnnotation(classId: ClassId): Boolean {
return annotations.hasAnnotation(classId)
}
fun FirDeclaration.hasAnnotationSafe(classId: ClassId): Boolean {
return annotations.hasAnnotationSafe(classId)
}
fun FirBasedSymbol<*>.hasAnnotation(classId: ClassId): Boolean {
return resolvedAnnotationsWithClassIds.hasAnnotation(classId)
}
@@ -107,6 +117,10 @@ fun List<FirAnnotation>.hasAnnotation(classId: ClassId): Boolean {
return this.any { it.toAnnotationClassId() == classId }
}
fun List<FirAnnotation>.hasAnnotationSafe(classId: ClassId): Boolean {
return this.any { it.toAnnotationClassIdSafe() == classId }
}
fun <D> FirBasedSymbol<out D>.getAnnotationByClassId(classId: ClassId): FirAnnotation? where D : FirAnnotationContainer, D : FirDeclaration {
return fir.getAnnotationByClassId(classId)
}
@@ -8,26 +8,28 @@ package org.jetbrains.kotlin.fir.resolve.transformers
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.expandedConeType
import org.jetbrains.kotlin.fir.declarations.utils.isCompanion
import org.jetbrains.kotlin.fir.declarations.utils.isData
import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.expressions.FirDelegatedConstructorCall
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension
import org.jetbrains.kotlin.fir.extensions.extensionService
import org.jetbrains.kotlin.fir.extensions.supertypeGenerators
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.isLocalClassOrAnonymousObject
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeTypeParameterSupertype
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.LocalClassesNavigationInfo
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.createImportingScopes
import org.jetbrains.kotlin.fir.scopes.getNestedClassifierScope
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.scopes.impl.FirMemberTypeParameterScope
import org.jetbrains.kotlin.fir.scopes.impl.nestedClassifierScope
import org.jetbrains.kotlin.fir.scopes.impl.wrapNestedClassifierScopeWithSubstitutionForSuperType
@@ -40,7 +42,9 @@ import org.jetbrains.kotlin.fir.types.impl.FirImplicitBuiltinTypeRef
import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.types.model.TypeArgumentMarker
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -65,7 +69,7 @@ open class FirSupertypeResolverTransformer(
protected val supertypeComputationSession = SupertypeComputationSession()
private val supertypeResolverVisitor = FirSupertypeResolverVisitor(session, supertypeComputationSession, scopeSession)
private val applySupertypesTransformer = FirApplySupertypesTransformer(supertypeComputationSession)
private val applySupertypesTransformer = FirApplySupertypesTransformer(supertypeComputationSession, session, scopeSession)
override fun <E : FirElement> transformElement(element: E, data: Any?): E {
return element
@@ -101,13 +105,16 @@ fun <F : FirClassLikeDeclaration> F.runSupertypeResolvePhaseForLocalClass(
this.accept(supertypeResolverVisitor, null)
supertypeComputationSession.breakLoops(session)
val applySupertypesTransformer = FirApplySupertypesTransformer(supertypeComputationSession)
val applySupertypesTransformer = FirApplySupertypesTransformer(supertypeComputationSession, session, scopeSession)
return this.transform<F, Nothing?>(applySupertypesTransformer, null)
}
open class FirApplySupertypesTransformer(
private val supertypeComputationSession: SupertypeComputationSession
private val supertypeComputationSession: SupertypeComputationSession,
private val session: FirSession,
private val scopeSession: ScopeSession
) : FirDefaultTransformer<Any?>() {
override fun <E : FirElement> transformElement(element: E, data: Any?): E {
return element
}
@@ -133,8 +140,11 @@ open class FirApplySupertypesTransformer(
// TODO: Replace with an immutable version or transformer
firClass.replaceSuperTypeRefs(supertypeRefs)
}
session.platformSupertypeUpdater?.updateSupertypesIfNeeded(firClass, scopeSession)
}
override fun transformAnonymousObject(anonymousObject: FirAnonymousObject, data: Any?): FirStatement {
applyResolvedSupertypesToClass(anonymousObject)
@@ -0,0 +1,17 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.resolve.transformers
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.resolve.ScopeSession
abstract class PlatformSupertypeUpdater : FirSessionComponent {
abstract fun updateSupertypesIfNeeded(firClass: FirClass, scopeSession: ScopeSession)
}
val FirSession.platformSupertypeUpdater: PlatformSupertypeUpdater? by FirSession.nullableSessionComponentAccessor()
@@ -32,8 +32,10 @@ import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.Deprecated
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.DeprecatedSinceKotlin
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.JvmRecord
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.WasExperimental
class FirCompilerRequiredAnnotationsResolveProcessor(
@@ -154,7 +156,7 @@ private class FirAnnotationResolveTransformer(
) {
companion object {
private val REQUIRED_ANNOTATIONS: Set<ClassId> = setOf(
Deprecated, DeprecatedSinceKotlin, WasExperimental
Deprecated, DeprecatedSinceKotlin, WasExperimental, JvmRecord
)
private val REQUIRED_ANNOTATION_NAMES: Set<Name> = REQUIRED_ANNOTATIONS.mapTo(mutableSetOf()) { it.shortClassName }
+3 -1
View File
@@ -2,9 +2,11 @@
// API_VERSION: 1.5
// JVM_TARGET: 17
// COMPILER_ARGUMENTS: -XXLanguage:+JvmRecordSupport -Xjvm-enable-preview
// IGNORE_FIR
// reason: test framework does not support using JDK 17 as dependency
package pkg
@JvmRecord
data class MyRec(val name: String)
// JDK 15 should be used to compile this file
// COMPILATION_ERRORS
// COMPILATION_ERRORS
@@ -1,6 +1,5 @@
// !API_VERSION: 1.5
// !LANGUAGE: +JvmRecordSupport
// IGNORE_BACKEND_FIR: JVM_IR
// ENABLE_JVM_PREVIEW
// FILE: JavaClass.java
public class JavaClass {
@@ -1,6 +1,5 @@
// !API_VERSION: 1.5
// !LANGUAGE: +JvmRecordSupport
// IGNORE_BACKEND_FIR: JVM_IR
// ENABLE_JVM_PREVIEW
// FILE: JavaClass.java
@@ -1,8 +1,5 @@
// !API_VERSION: 1.5
// !LANGUAGE: +JvmRecordSupport
// ENABLE_JVM_PREVIEW
// IGNORE_BACKEND_FIR: JVM_IR
// !JVM_DEFAULT_MODE: all-compatibility
// JVM_DEFAULT_MODE: all-compatibility
// FILE: JavaClass.java
public class JavaClass {
@@ -1,7 +1,6 @@
// !API_VERSION: 1.5
// !LANGUAGE: +JvmRecordSupport
// ENABLE_JVM_PREVIEW
// IGNORE_BACKEND_FIR: JVM_IR
// !JVM_DEFAULT_MODE: enable
// FILE: JavaClass.java
@@ -0,0 +1,98 @@
FILE fqName:<root> fileName:/dataClassWithJvmRecord.kt
CLASS CLASS name:MyRec modality:FINAL visibility:public [data] superTypes:[java.lang.Record]
annotations:
JvmRecord
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.MyRec
CONSTRUCTOR visibility:public <> (name:kotlin.String) returnType:<root>.MyRec [primary]
VALUE_PARAMETER name:name index:0 type:kotlin.String
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'protected/*protected and package*/ constructor <init> () declared in java.lang.Record'
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:MyRec modality:FINAL visibility:public [data] superTypes:[java.lang.Record]'
PROPERTY name:name visibility:public modality:FINAL [val]
FIELD PROPERTY_BACKING_FIELD name:name type:kotlin.String visibility:private [final]
EXPRESSION_BODY
GET_VAR 'name: kotlin.String declared in <root>.MyRec.<init>' type=kotlin.String origin=INITIALIZE_PROPERTY_FROM_PARAMETER
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-name> visibility:public modality:FINAL <> ($this:<root>.MyRec) returnType:kotlin.String
correspondingProperty: PROPERTY name:name visibility:public modality:FINAL [val]
$this: VALUE_PARAMETER name:<this> type:<root>.MyRec
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun <get-name> (): kotlin.String declared in <root>.MyRec'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:name type:kotlin.String visibility:private [final]' type=kotlin.String origin=null
receiver: GET_VAR '<this>: <root>.MyRec declared in <root>.MyRec.<get-name>' type=<root>.MyRec origin=null
FUN name:component1 visibility:public modality:FINAL <> ($this:<root>.MyRec) returnType:kotlin.String [operator]
$this: VALUE_PARAMETER name:<this> type:<root>.MyRec
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun component1 (): kotlin.String [operator] declared in <root>.MyRec'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:name type:kotlin.String visibility:private [final]' type=kotlin.String origin=null
receiver: GET_VAR '<this>: <root>.MyRec declared in <root>.MyRec.component1' type=<root>.MyRec origin=null
FUN name:copy visibility:public modality:FINAL <> ($this:<root>.MyRec, name:kotlin.String) returnType:<root>.MyRec
$this: VALUE_PARAMETER name:<this> type:<root>.MyRec
VALUE_PARAMETER name:name index:0 type:kotlin.String
EXPRESSION_BODY
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:name type:kotlin.String visibility:private [final]' type=kotlin.String origin=null
receiver: GET_VAR '<this>: <root>.MyRec declared in <root>.MyRec.copy' type=<root>.MyRec origin=null
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun copy (name: kotlin.String): <root>.MyRec declared in <root>.MyRec'
CONSTRUCTOR_CALL 'public constructor <init> (name: kotlin.String) [primary] declared in <root>.MyRec' type=<root>.MyRec origin=null
name: GET_VAR 'name: kotlin.String declared in <root>.MyRec.copy' type=kotlin.String origin=null
FUN GENERATED_DATA_CLASS_MEMBER name:equals visibility:public modality:OPEN <> ($this:<root>.MyRec, other:kotlin.Any?) returnType:kotlin.Boolean
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any
$this: VALUE_PARAMETER GENERATED_DATA_CLASS_MEMBER name:<this> type:<root>.MyRec
VALUE_PARAMETER GENERATED_DATA_CLASS_MEMBER name:other index:0 type:kotlin.Any?
BLOCK_BODY
WHEN type=kotlin.Unit origin=null
BRANCH
if: CALL 'public final fun EQEQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQEQ
arg0: GET_VAR '<this>: <root>.MyRec declared in <root>.MyRec.equals' type=<root>.MyRec origin=null
arg1: GET_VAR 'other: kotlin.Any? declared in <root>.MyRec.equals' type=kotlin.Any? origin=null
then: RETURN type=kotlin.Nothing from='public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in <root>.MyRec'
CONST Boolean type=kotlin.Boolean value=true
WHEN type=kotlin.Unit origin=null
BRANCH
if: TYPE_OP type=kotlin.Boolean origin=NOT_INSTANCEOF typeOperand=<root>.MyRec
GET_VAR 'other: kotlin.Any? declared in <root>.MyRec.equals' type=kotlin.Any? origin=null
then: RETURN type=kotlin.Nothing from='public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in <root>.MyRec'
CONST Boolean type=kotlin.Boolean value=false
VAR IR_TEMPORARY_VARIABLE name:tmp_0 type:<root>.MyRec [val]
TYPE_OP type=<root>.MyRec origin=CAST typeOperand=<root>.MyRec
GET_VAR 'other: kotlin.Any? declared in <root>.MyRec.equals' type=kotlin.Any? origin=null
WHEN type=kotlin.Unit origin=null
BRANCH
if: CALL 'public final fun not (): kotlin.Boolean [operator] declared in kotlin.Boolean' type=kotlin.Boolean origin=EXCLEQ
$this: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EXCLEQ
arg0: GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:name type:kotlin.String visibility:private [final]' type=kotlin.String origin=null
receiver: GET_VAR '<this>: <root>.MyRec declared in <root>.MyRec.equals' type=<root>.MyRec origin=null
arg1: GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:name type:kotlin.String visibility:private [final]' type=kotlin.String origin=null
receiver: GET_VAR 'val tmp_0: <root>.MyRec [val] declared in <root>.MyRec.equals' type=<root>.MyRec origin=null
then: RETURN type=kotlin.Nothing from='public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in <root>.MyRec'
CONST Boolean type=kotlin.Boolean value=false
RETURN type=kotlin.Nothing from='public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in <root>.MyRec'
CONST Boolean type=kotlin.Boolean value=true
FUN GENERATED_DATA_CLASS_MEMBER name:hashCode visibility:public modality:OPEN <> ($this:<root>.MyRec) returnType:kotlin.Int
overridden:
public open fun hashCode (): kotlin.Int declared in kotlin.Any
$this: VALUE_PARAMETER GENERATED_DATA_CLASS_MEMBER name:<this> type:<root>.MyRec
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun hashCode (): kotlin.Int declared in <root>.MyRec'
CALL 'public open fun hashCode (): kotlin.Int [fake_override] declared in kotlin.String' type=kotlin.Int origin=null
$this: GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:name type:kotlin.String visibility:private [final]' type=kotlin.String origin=null
receiver: GET_VAR '<this>: <root>.MyRec declared in <root>.MyRec.hashCode' type=<root>.MyRec origin=null
FUN GENERATED_DATA_CLASS_MEMBER name:toString visibility:public modality:OPEN <> ($this:<root>.MyRec) returnType:kotlin.String
overridden:
public open fun toString (): kotlin.String declared in kotlin.Any
$this: VALUE_PARAMETER GENERATED_DATA_CLASS_MEMBER name:<this> type:<root>.MyRec
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun toString (): kotlin.String declared in <root>.MyRec'
STRING_CONCATENATION type=kotlin.String
CONST String type=kotlin.String value="MyRec("
CONST String type=kotlin.String value="name="
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:name type:kotlin.String visibility:private [final]' type=kotlin.String origin=null
receiver: GET_VAR '<this>: <root>.MyRec declared in <root>.MyRec.toString' type=<root>.MyRec origin=null
CONST String type=kotlin.String value=")"
FUN name:test visibility:public modality:FINAL <> (rec:<root>.MyRec) returnType:kotlin.Unit
VALUE_PARAMETER name:rec index:0 type:<root>.MyRec
BLOCK_BODY
TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
CALL 'public final fun <get-name> (): kotlin.String declared in <root>.MyRec' type=kotlin.String origin=GET_PROPERTY
$this: GET_VAR 'rec: <root>.MyRec declared in <root>.test' type=<root>.MyRec origin=null
@@ -0,0 +1,47 @@
@JvmRecord
data class MyRec : Record {
constructor(name: String) /* primary */ {
super/*Record*/()
/* <init>() */
}
val name: String
field = name
get
operator fun component1(): String {
return <this>.#name
}
fun copy(name: String = <this>.#name): MyRec {
return MyRec(name = name)
}
override fun equals(other: Any?): Boolean {
when {
EQEQEQ(arg0 = <this>, arg1 = other) -> return true
}
when {
other !is MyRec -> return false
}
val tmp0_other_with_cast: MyRec = other as MyRec
when {
EQEQ(arg0 = <this>.#name, arg1 = tmp0_other_with_cast.#name).not() -> return false
}
return true
}
override fun hashCode(): Int {
return <this>.#name.hashCode()
}
override fun toString(): String {
return "MyRec(" + "name=" + <this>.#name + ")"
}
}
fun test(rec: MyRec) {
rec.<get-name>() /*~> Unit */
}
@@ -0,0 +1,98 @@
FILE fqName:<root> fileName:/dataClassWithJvmRecord.kt
CLASS CLASS name:MyRec modality:FINAL visibility:public [data] superTypes:[java.lang.Record]
annotations:
JvmRecord
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.MyRec
CONSTRUCTOR visibility:public <> (name:kotlin.String) returnType:<root>.MyRec [primary]
VALUE_PARAMETER name:name index:0 type:kotlin.String
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'protected/*protected and package*/ constructor <init> () declared in java.lang.Record'
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:MyRec modality:FINAL visibility:public [data] superTypes:[java.lang.Record]'
PROPERTY name:name visibility:public modality:FINAL [val]
FIELD PROPERTY_BACKING_FIELD name:name type:kotlin.String visibility:private [final]
EXPRESSION_BODY
GET_VAR 'name: kotlin.String declared in <root>.MyRec.<init>' type=kotlin.String origin=INITIALIZE_PROPERTY_FROM_PARAMETER
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-name> visibility:public modality:FINAL <> ($this:<root>.MyRec) returnType:kotlin.String
correspondingProperty: PROPERTY name:name visibility:public modality:FINAL [val]
$this: VALUE_PARAMETER name:<this> type:<root>.MyRec
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun <get-name> (): kotlin.String declared in <root>.MyRec'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:name type:kotlin.String visibility:private [final]' type=kotlin.String origin=null
receiver: GET_VAR '<this>: <root>.MyRec declared in <root>.MyRec.<get-name>' type=<root>.MyRec origin=null
FUN GENERATED_DATA_CLASS_MEMBER name:component1 visibility:public modality:FINAL <> ($this:<root>.MyRec) returnType:kotlin.String [operator]
$this: VALUE_PARAMETER name:<this> type:<root>.MyRec
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun component1 (): kotlin.String [operator] declared in <root>.MyRec'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:name type:kotlin.String visibility:private [final]' type=kotlin.String origin=null
receiver: GET_VAR '<this>: <root>.MyRec declared in <root>.MyRec.component1' type=<root>.MyRec origin=null
FUN GENERATED_DATA_CLASS_MEMBER name:copy visibility:public modality:FINAL <> ($this:<root>.MyRec, name:kotlin.String) returnType:<root>.MyRec
$this: VALUE_PARAMETER name:<this> type:<root>.MyRec
VALUE_PARAMETER name:name index:0 type:kotlin.String
EXPRESSION_BODY
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:name type:kotlin.String visibility:private [final]' type=kotlin.String origin=null
receiver: GET_VAR '<this>: <root>.MyRec declared in <root>.MyRec.copy' type=<root>.MyRec origin=null
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun copy (name: kotlin.String): <root>.MyRec declared in <root>.MyRec'
CONSTRUCTOR_CALL 'public constructor <init> (name: kotlin.String) [primary] declared in <root>.MyRec' type=<root>.MyRec origin=null
name: GET_VAR 'name: kotlin.String declared in <root>.MyRec.copy' type=kotlin.String origin=null
FUN GENERATED_DATA_CLASS_MEMBER name:toString visibility:public modality:OPEN <> ($this:<root>.MyRec) returnType:kotlin.String
overridden:
public abstract fun toString (): @[EnhancedNullability] kotlin.String declared in java.lang.Record
$this: VALUE_PARAMETER name:<this> type:<root>.MyRec
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun toString (): kotlin.String declared in <root>.MyRec'
STRING_CONCATENATION type=kotlin.String
CONST String type=kotlin.String value="MyRec("
CONST String type=kotlin.String value="name="
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:name type:kotlin.String visibility:private [final]' type=kotlin.String origin=null
receiver: GET_VAR '<this>: <root>.MyRec declared in <root>.MyRec.toString' type=<root>.MyRec origin=null
CONST String type=kotlin.String value=")"
FUN GENERATED_DATA_CLASS_MEMBER name:hashCode visibility:public modality:OPEN <> ($this:<root>.MyRec) returnType:kotlin.Int
overridden:
public abstract fun hashCode (): kotlin.Int declared in java.lang.Record
$this: VALUE_PARAMETER name:<this> type:<root>.MyRec
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun hashCode (): kotlin.Int declared in <root>.MyRec'
CALL 'public open fun hashCode (): kotlin.Int [fake_override] declared in kotlin.String' type=kotlin.Int origin=null
$this: GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:name type:kotlin.String visibility:private [final]' type=kotlin.String origin=null
receiver: GET_VAR '<this>: <root>.MyRec declared in <root>.MyRec.hashCode' type=<root>.MyRec origin=null
FUN GENERATED_DATA_CLASS_MEMBER name:equals visibility:public modality:OPEN <> ($this:<root>.MyRec, other:kotlin.Any?) returnType:kotlin.Boolean [operator]
overridden:
public abstract fun equals (other: @[EnhancedNullability] kotlin.Any?): kotlin.Boolean [operator] declared in java.lang.Record
$this: VALUE_PARAMETER name:<this> type:<root>.MyRec
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
BLOCK_BODY
WHEN type=kotlin.Unit origin=null
BRANCH
if: CALL 'public final fun EQEQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQEQ
arg0: GET_VAR '<this>: <root>.MyRec declared in <root>.MyRec.equals' type=<root>.MyRec origin=null
arg1: GET_VAR 'other: kotlin.Any? declared in <root>.MyRec.equals' type=kotlin.Any? origin=null
then: RETURN type=kotlin.Nothing from='public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in <root>.MyRec'
CONST Boolean type=kotlin.Boolean value=true
WHEN type=kotlin.Unit origin=null
BRANCH
if: TYPE_OP type=kotlin.Boolean origin=NOT_INSTANCEOF typeOperand=<root>.MyRec
GET_VAR 'other: kotlin.Any? declared in <root>.MyRec.equals' type=kotlin.Any? origin=null
then: RETURN type=kotlin.Nothing from='public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in <root>.MyRec'
CONST Boolean type=kotlin.Boolean value=false
VAR IR_TEMPORARY_VARIABLE name:tmp_0 type:<root>.MyRec [val]
TYPE_OP type=<root>.MyRec origin=CAST typeOperand=<root>.MyRec
GET_VAR 'other: kotlin.Any? declared in <root>.MyRec.equals' type=kotlin.Any? origin=null
WHEN type=kotlin.Unit origin=null
BRANCH
if: CALL 'public final fun not (): kotlin.Boolean [operator] declared in kotlin.Boolean' type=kotlin.Boolean origin=EXCLEQ
$this: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EXCLEQ
arg0: GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:name type:kotlin.String visibility:private [final]' type=kotlin.String origin=null
receiver: GET_VAR '<this>: <root>.MyRec declared in <root>.MyRec.equals' type=<root>.MyRec origin=null
arg1: GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:name type:kotlin.String visibility:private [final]' type=kotlin.String origin=null
receiver: GET_VAR 'val tmp_0: <root>.MyRec [val] declared in <root>.MyRec.equals' type=<root>.MyRec origin=null
then: RETURN type=kotlin.Nothing from='public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in <root>.MyRec'
CONST Boolean type=kotlin.Boolean value=false
RETURN type=kotlin.Nothing from='public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in <root>.MyRec'
CONST Boolean type=kotlin.Boolean value=true
FUN name:test visibility:public modality:FINAL <> (rec:<root>.MyRec) returnType:kotlin.Unit
VALUE_PARAMETER name:rec index:0 type:<root>.MyRec
BLOCK_BODY
TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
CALL 'public final fun <get-name> (): kotlin.String declared in <root>.MyRec' type=kotlin.String origin=GET_PROPERTY
$this: GET_VAR 'rec: <root>.MyRec declared in <root>.test' type=<root>.MyRec origin=null
@@ -0,0 +1,11 @@
// TARGET_BACKEND: JVM_IR
// JDK_KIND: FULL_JDK_17
// WITH_STDLIB
// JVM_TARGET: 17
@JvmRecord
data class MyRec(val name: String)
fun test(rec: MyRec) {
rec.name
}
@@ -0,0 +1,47 @@
@JvmRecord
data class MyRec : Record {
constructor(name: String) /* primary */ {
super/*Record*/()
/* <init>() */
}
val name: String
field = name
get
operator fun component1(): String {
return <this>.#name
}
fun copy(name: String = <this>.#name): MyRec {
return MyRec(name = name)
}
override fun toString(): String {
return "MyRec(" + "name=" + <this>.#name + ")"
}
override fun hashCode(): Int {
return <this>.#name.hashCode()
}
override operator fun equals(other: Any?): Boolean {
when {
EQEQEQ(arg0 = <this>, arg1 = other) -> return true
}
when {
other !is MyRec -> return false
}
val tmp0_other_with_cast: MyRec = other as MyRec
when {
EQEQ(arg0 = <this>.#name, arg1 = tmp0_other_with_cast.#name).not() -> return false
}
return true
}
}
fun test(rec: MyRec) {
rec.<get-name>() /*~> Unit */
}
+1 -10
View File
@@ -39,20 +39,11 @@ dependencies {
optInToExperimentalCompilerApi()
val generationRoot = projectDir.resolve("tests-gen")
sourceSets {
"main" { none() }
"test" {
projectDefault()
this.java.srcDir(generationRoot.name)
}
}
if (kotlinBuildProperties.isInJpsBuildIdeaSync) {
apply(plugin = "idea")
idea {
this.module.generatedSourceDirs.add(generationRoot)
generatedTestDir()
}
}
@@ -951,6 +951,12 @@ public class IrTextTestGenerated extends AbstractIrTextTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/ir/irText/declarations/jvmRecord"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("dataClassWithJvmRecord.kt")
public void testDataClassWithJvmRecord() throws Exception {
runTest("compiler/testData/ir/irText/declarations/jvmRecord/dataClassWithJvmRecord.kt");
}
@Test
@TestMetadata("javaRecordComponentAccess.kt")
public void testJavaRecordComponentAccess() throws Exception {
@@ -157,6 +157,7 @@ object StandardClassIds {
val JvmField = "JvmField".jvmId()
val JvmDefault = "JvmDefault".jvmId()
val JvmRepeatable = "JvmRepeatable".jvmId()
val JvmRecord = "JvmRecord".jvmId()
val RawTypeAnnotation = "RawType".internalIrId()
val FlexibleNullability = "FlexibleNullability".internalIrId()
@@ -202,6 +203,10 @@ object StandardClassIds {
val not = "not".callableId(Boolean)
}
object Java {
val Record = "Record".javaLangId()
}
}
private fun String.baseId() = ClassId(StandardClassIds.BASE_KOTLIN_PACKAGE, Name.identifier(this))