KT-56687: Unify handling of EnumEntries.
`supportsEnumEntries` in `EnumCLassLowering` is not really needed, but it was left just in case. Both K1 and K2 ensure the feature is enabled before they generate IR. `supportsEnumEntries` is not enough, because the frontend will not generate `entries` if `kotlin.enums.EnumEntries` is not available (linking against an outdated stdlib is considered a valid use-case). From the resolution point of view, it's OK to resolve into `entries` while the feature is off, because then K1 reports an error on the call site. ^KT-56687 Fixed ^KT-55614 Fixed
This commit is contained in:
committed by
Space Team
parent
bcfafc601e
commit
bb368bd191
+2
-4
@@ -40,10 +40,7 @@ import org.jetbrains.kotlin.fir.resolve.transformers.plugin.GeneratedClassIndex
|
||||
import org.jetbrains.kotlin.fir.scopes.FirOverrideService
|
||||
import org.jetbrains.kotlin.fir.scopes.FirPlatformClassMapper
|
||||
import org.jetbrains.kotlin.fir.scopes.PlatformSpecificOverridabilityRules
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirDeclaredMemberScopeProvider
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirDynamicMembersStorage
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirIntersectionOverrideStorage
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirSubstitutionOverrideStorage
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.*
|
||||
import org.jetbrains.kotlin.fir.symbols.FirLazyDeclarationResolver
|
||||
import org.jetbrains.kotlin.fir.types.FirCorrespondingSupertypesCache
|
||||
import org.jetbrains.kotlin.fir.types.FirFunctionTypeKindService
|
||||
@@ -104,6 +101,7 @@ fun FirSession.registerCommonJavaComponents(javaModuleResolver: JavaModuleResolv
|
||||
register(PlatformSupertypeUpdater::class, JvmSupertypeUpdater(this))
|
||||
register(PlatformSpecificOverridabilityRules::class, JavaOverridabilityRules(this))
|
||||
register(DeserializedClassConfigurator::class, JvmDeserializedClassConfigurator(this))
|
||||
register(FirEnumEntriesSupport::class, FirJvmEnumEntriesSupport(this))
|
||||
}
|
||||
|
||||
// -------------------------- Resolve components --------------------------
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.resolve.calls.ConeCallConflictResolverFactory
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.impl.*
|
||||
import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider
|
||||
import org.jetbrains.kotlin.fir.scopes.FirPlatformClassMapper
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirEnumEntriesSupport
|
||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
import org.jetbrains.kotlin.library.metadata.resolver.KotlinResolvedLibrary
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -90,5 +91,6 @@ object FirJsSessionFactory : FirAbstractSessionFactory() {
|
||||
register(ConeCallConflictResolverFactory::class, JsCallConflictResolverFactory)
|
||||
register(FirPlatformClassMapper::class, FirPlatformClassMapper.Default)
|
||||
register(FirOverridesBackwardCompatibilityHelper::class, FirOverridesBackwardCompatibilityHelper.Default())
|
||||
register(FirEnumEntriesSupport::class, FirEnumEntriesSupport(this))
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.resolve.providers.impl.FirCloneableSymbolProvide
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirExtensionSyntheticFunctionInterfaceProvider
|
||||
import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider
|
||||
import org.jetbrains.kotlin.fir.scopes.FirPlatformClassMapper
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirEnumEntriesSupport
|
||||
import org.jetbrains.kotlin.library.metadata.resolver.KotlinResolvedLibrary
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
@@ -94,5 +95,6 @@ object FirNativeSessionFactory : FirAbstractSessionFactory() {
|
||||
register(ConeCallConflictResolverFactory::class, NativeCallConflictResolverFactory)
|
||||
register(FirPlatformClassMapper::class, FirPlatformClassMapper.Default)
|
||||
register(FirOverridesBackwardCompatibilityHelper::class, FirOverridesBackwardCompatibilityHelper.Default())
|
||||
register(FirEnumEntriesSupport::class, FirEnumEntriesSupport(this))
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -245,6 +245,10 @@ fun deserializeClassToSymbol(
|
||||
session.deserializedClassConfigurator?.run {
|
||||
configure(classId)
|
||||
}
|
||||
|
||||
if (!Flags.HAS_ENUM_ENTRIES.get(flags)) {
|
||||
hasNoEnumEntriesAttr = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -373,8 +373,8 @@ class Fir2IrConverter(
|
||||
)
|
||||
}
|
||||
is FirProperty -> {
|
||||
if (declaration.source?.kind == KtFakeSourceElementKind.EnumGeneratedDeclaration &&
|
||||
declaration.name == StandardNames.ENUM_ENTRIES &&
|
||||
if (containingClass != null &&
|
||||
declaration.isEnumEntries(containingClass) &&
|
||||
!session.languageVersionSettings.supportsFeature(LanguageFeature.EnumEntries)
|
||||
) {
|
||||
// Note: we have to do it, because backend without the feature
|
||||
|
||||
+7
-1
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.classId
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isSynthetic
|
||||
import org.jetbrains.kotlin.fir.isEnumEntries
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
||||
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
|
||||
@@ -38,7 +39,10 @@ class FirClassDeclaredMemberScopeImpl(
|
||||
if (declaration is FirCallableDeclaration) {
|
||||
val name = when (declaration) {
|
||||
is FirConstructor -> SpecialNames.INIT
|
||||
is FirVariable -> if (declaration.isSynthetic) continue@loop else declaration.name
|
||||
is FirVariable -> when {
|
||||
declaration.isSynthetic || declaration.isEnumEntries(klass) && !klass.supportsEnumEntries -> continue@loop
|
||||
else -> declaration.name
|
||||
}
|
||||
is FirSimpleFunction -> declaration.name
|
||||
else -> continue@loop
|
||||
}
|
||||
@@ -48,6 +52,8 @@ class FirClassDeclaredMemberScopeImpl(
|
||||
result
|
||||
}
|
||||
|
||||
private val FirClass.supportsEnumEntries get() = useSiteSession.enumEntriesSupport.canSynthesizeEnumEntriesFor(this)
|
||||
|
||||
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
|
||||
if (name == SpecialNames.INIT) return
|
||||
processCallables(name, processor)
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.scopes.impl
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.FirSessionComponent
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.hasEnumEntries
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.getRegularClassSymbolByClassId
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
|
||||
open class FirEnumEntriesSupport(val session: FirSession) : FirSessionComponent {
|
||||
protected val isEnumEntriesAvailable by lazy {
|
||||
session.symbolProvider.getRegularClassSymbolByClassId(StandardClassIds.EnumEntries) != null
|
||||
}
|
||||
|
||||
open fun canSynthesizeEnumEntriesFor(klass: FirClass): Boolean {
|
||||
return klass.hasEnumEntries && isEnumEntriesAvailable
|
||||
}
|
||||
}
|
||||
|
||||
class FirJvmEnumEntriesSupport(session: FirSession) : FirEnumEntriesSupport(session) {
|
||||
// In JVM modules "entries" can be called even on enum compiled without this property.
|
||||
override fun canSynthesizeEnumEntriesFor(klass: FirClass): Boolean = isEnumEntriesAvailable
|
||||
}
|
||||
|
||||
val FirSession.enumEntriesSupport: FirEnumEntriesSupport by FirSession.sessionComponentAccessor()
|
||||
@@ -45,6 +45,12 @@ var FirCallableDeclaration.containingClassForStaticMemberAttr: ConeClassLikeLook
|
||||
var FirRegularClass.containingClassForLocalAttr: ConeClassLikeLookupTag? by FirDeclarationDataRegistry.data(ContainingClassKey)
|
||||
var FirDanglingModifierList.containingClassAttr: ConeClassLikeLookupTag? by FirDeclarationDataRegistry.data(ContainingClassKey)
|
||||
|
||||
private object HasNoEnumEntriesKey : FirDeclarationDataKey()
|
||||
var FirClass.hasNoEnumEntriesAttr: Boolean? by FirDeclarationDataRegistry.data(HasNoEnumEntriesKey)
|
||||
|
||||
// Must be true iff the class metadata contains the hasEnumEntries flag
|
||||
val FirClass.hasEnumEntries get() = hasNoEnumEntriesAttr != true
|
||||
|
||||
private object IsNewPlaceForBodyGeneration : FirDeclarationDataKey()
|
||||
var FirRegularClass.isNewPlaceForBodyGeneration: Boolean? by FirDeclarationDataRegistry.data(IsNewPlaceForBodyGeneration)
|
||||
|
||||
|
||||
@@ -7,14 +7,13 @@ package org.jetbrains.kotlin.fir
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.*
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.fir.declarations.FirContextReceiver
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclarationStatus
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvedDeclarationStatus
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
|
||||
import org.jetbrains.kotlin.fir.expressions.FirBlock
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.renderer.FirRenderer
|
||||
@@ -254,3 +253,6 @@ fun <T> List<T>.smartPlus(other: List<T>): List<T> = when {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
// Source element may be missing if the class came from a library
|
||||
fun FirVariable.isEnumEntries(containingClass: FirClass) = name == StandardNames.ENUM_ENTRIES && containingClass.isEnumClass
|
||||
|
||||
+10
-7
@@ -124,7 +124,13 @@ private class EnumClassLowering(private val context: JvmBackendContext) : ClassL
|
||||
val valuesField = buildValuesField(valuesHelperFunction)
|
||||
|
||||
val entriesField = when {
|
||||
irClass.declarations.any { it.isGetEntriesFunction } -> {
|
||||
!irClass.hasGetEntriesFunction -> {
|
||||
null
|
||||
}
|
||||
!supportsEnumEntries -> {
|
||||
error("The frontend must have checked if the feature is supported while emitting the IR")
|
||||
}
|
||||
else -> {
|
||||
// Constructs the synthetic $entries() function that returns plain $VALUES without copy
|
||||
val entriesHelperFunction = buildEntriesHelperFunction(valuesField)
|
||||
|
||||
@@ -137,12 +143,6 @@ private class EnumClassLowering(private val context: JvmBackendContext) : ClassL
|
||||
*/
|
||||
buildEntriesField(entriesHelperFunction)
|
||||
}
|
||||
supportsEnumEntries -> {
|
||||
error("kotlin.enums.EnumEntries is not available to the frontend. Is non-full kotlin stdlib being used?")
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
// Add synthetic parameters to enum constructors and implement the values and valueOf functions
|
||||
@@ -152,6 +152,9 @@ private class EnumClassLowering(private val context: JvmBackendContext) : ClassL
|
||||
irClass.transformChildrenVoid(EnumClassCallTransformer())
|
||||
}
|
||||
|
||||
private val IrClass.hasGetEntriesFunction: Boolean
|
||||
get() = declarations.any { it.isGetEntriesFunction }
|
||||
|
||||
private val IrDeclaration.isGetEntriesFunction: Boolean
|
||||
get() = this is IrFunction && name == SpecialNames.ENUM_GET_ENTRIES && origin == IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER
|
||||
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.gradle
|
||||
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.jetbrains.kotlin.gradle.testbase.*
|
||||
import org.jetbrains.kotlin.gradle.util.replaceText
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
|
||||
@DisplayName("EnumEntries feature test when no kotlin.enums.EnumEntries found on the classpath")
|
||||
@JvmGradlePluginTests
|
||||
class EnumEntriesWithOutdatedStdlibTest : KGPBaseTest() {
|
||||
@DisplayName("EnumEntries should not be accessible without kotlin.enums.EnumEntries")
|
||||
@GradleTest
|
||||
fun enumEntriesNotAccessible(gradleVersion: GradleVersion) {
|
||||
project("enumEntriesNotAccessible", gradleVersion) {
|
||||
buildGradleKts.replaceText("\"<language-version>\"", "\"1.9\" // <language-version>")
|
||||
|
||||
buildAndFail(":compileKotlin") {
|
||||
assertOutputContains("Main.kt:13:20 Unresolved reference: entries")
|
||||
assertOutputContains("Main.kt:14:30 Unresolved reference: entries")
|
||||
}
|
||||
|
||||
buildGradleKts.replaceText("\"1.9\" // <language-version>", "\"2.0\" // <language-version>")
|
||||
|
||||
buildAndFail(":compileKotlin") {
|
||||
assertOutputContains("Main.kt:13:20 Unresolved reference: entries")
|
||||
assertOutputContains("Main.kt:14:30 Unresolved reference: entries")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Code should compile normally if `entries` is never referenced")
|
||||
@GradleTest
|
||||
fun codeCompilesWithoutReferencingEntries(gradleVersion: GradleVersion) {
|
||||
project("enumEntriesNotAccessible", gradleVersion) {
|
||||
projectPath.resolve("src/main/kotlin/Main.kt")
|
||||
.replaceText("entries", "values()")
|
||||
|
||||
buildGradleKts.replaceText("\"<language-version>\"", "\"1.9\" // <language-version>")
|
||||
build(":compileKotlin")
|
||||
|
||||
buildGradleKts.replaceText("\"1.9\" // <language-version>", "\"2.0\" // <language-version>")
|
||||
build(":compileKotlin")
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
group = "com.example"
|
||||
version = "1.0"
|
||||
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.7.20")
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
kotlinOptions.languageVersion = "<language-version>"
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
include(":k1")
|
||||
include(":k2")
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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 com.example
|
||||
|
||||
enum class MyEnum {
|
||||
A, B
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val a = MyEnum.entries
|
||||
val b = AnnotationTarget.entries
|
||||
println("$a :: $b")
|
||||
}
|
||||
Reference in New Issue
Block a user