From 9c2c11f7e9cafd5ba850574425a77daf874b66e5 Mon Sep 17 00:00:00 2001 From: "Denis.Zharkov" Date: Tue, 21 Mar 2023 13:37:57 +0100 Subject: [PATCH] FIR2IR: Fix "expected FirResolvedTypeRef but was FirJavaTypeRef" crash The problem was that we were using unenhanced constructors that still might contain java type refs. Basically, most of the `firClass.declarations` usages are unsafe in that meaning. The test is very complicated because to reproduce the exception there should be some conditions met: - MyClass should be compiled with K1, so it has `@EnhancedNullability` annotation serialized (that is unnecessary, but don't do any harm) - `@Nls` scope should be properly resolved, so enum entries are converted to resolved type refs - That leads to attempt to check if null-check might be needed to be inserted and to failure at FirTypeUtilsKt.getCanBeNull While this quite controversial to have this check being performed for freshly created annotations calls, the problem with improper constructor lookup anyway existed and fixed correctly. ^KT-57213 Fixed --- .../generators/CallAndReferenceGenerator.kt | 8 ++++-- .../libraryAnnotation/Nls.java | 25 +++++++++++++++++++ .../libraryUsingAnnotation/MyClass.kt | 1 + .../output.txt | 1 + .../usage.kt | 6 +++++ .../CompileKotlinAgainstCustomBinariesTest.kt | 19 ++++++++++++++ 6 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/deserializedAnnotationReferencesJava/libraryAnnotation/Nls.java create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/deserializedAnnotationReferencesJava/libraryUsingAnnotation/MyClass.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/deserializedAnnotationReferencesJava/output.txt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/deserializedAnnotationReferencesJava/usage.kt diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt index 47fc37beef0..f018144c21b 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.approximateDeclarationType +import org.jetbrains.kotlin.fir.scopes.getDeclaredConstructors import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.* @@ -43,7 +44,6 @@ import org.jetbrains.kotlin.ir.util.isMethodOfAny import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator.commonSuperType import org.jetbrains.kotlin.util.OperatorNameConventions -import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull class CallAndReferenceGenerator( private val components: Fir2IrComponents, @@ -691,7 +691,11 @@ class CallAndReferenceGenerator( useSiteTarget = this@toAnnotationCall.useSiteTarget annotationTypeRef = this@toAnnotationCall.annotationTypeRef val symbol = annotationTypeRef.coneType.fullyExpandedType(session).toSymbol(session) as? FirRegularClassSymbol ?: return null - val constructorSymbol = symbol.declarationSymbols.firstIsInstanceOrNull() ?: return null + + val constructorSymbol = + symbol.unsubstitutedScope(session, scopeSession, withForcedTypeCalculator = false) + .getDeclaredConstructors().firstOrNull() ?: return null + val argumentToParameterToMapping = constructorSymbol.valueParameterSymbols.mapNotNull { val parameter = it.fir val argument = this@toAnnotationCall.argumentMapping.mapping[parameter.name] ?: return@mapNotNull null diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/deserializedAnnotationReferencesJava/libraryAnnotation/Nls.java b/compiler/testData/compileKotlinAgainstCustomBinaries/deserializedAnnotationReferencesJava/libraryAnnotation/Nls.java new file mode 100644 index 00000000000..76e13371e53 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/deserializedAnnotationReferencesJava/libraryAnnotation/Nls.java @@ -0,0 +1,25 @@ +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE, ElementType.TYPE_USE, ElementType.TYPE, ElementType.PACKAGE}) +public @interface Nls { + enum Capitalization { + + NotSpecified, + /** + * e.g. This Is a Title + */ + Title, + /** + * e.g. This is a sentence + */ + Sentence + } + + Capitalization capitalization() default Capitalization.NotSpecified; +} diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/deserializedAnnotationReferencesJava/libraryUsingAnnotation/MyClass.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/deserializedAnnotationReferencesJava/libraryUsingAnnotation/MyClass.kt new file mode 100644 index 00000000000..0a1677ae483 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/deserializedAnnotationReferencesJava/libraryUsingAnnotation/MyClass.kt @@ -0,0 +1 @@ +class MyClass(val name: @Nls(capitalization = Nls.Capitalization.Title) String) diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/deserializedAnnotationReferencesJava/output.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/deserializedAnnotationReferencesJava/output.txt new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/deserializedAnnotationReferencesJava/output.txt @@ -0,0 +1 @@ +OK diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/deserializedAnnotationReferencesJava/usage.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/deserializedAnnotationReferencesJava/usage.kt new file mode 100644 index 00000000000..04f26e98637 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/deserializedAnnotationReferencesJava/usage.kt @@ -0,0 +1,6 @@ +fun box(): String { + // This class is only needed to force resolution of `@Nls` scope, so its types would be converted to resolved + class LocalClassToUseAnnotation(val name: @Nls(capitalization = Nls.Capitalization.Title) String) + + return MyClass("OK").name +} diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt index 4b4543fda27..155b023679a 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt @@ -795,6 +795,25 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration ) } + fun testDeserializedAnnotationReferencesJava() { + // Only Java + val libraryAnnotation = compileLibrary("libraryAnnotation") + // Specifically, use K1 + // Remove "-Xuse-k2=false" argument once it becomes forbidden + val libraryUsingAnnotation = compileLibrary( + "libraryUsingAnnotation", + additionalOptions = listOf("-language-version", "1.8", "-Xuse-k2=false"), + extraClassPath = listOf(libraryAnnotation) + ) + + compileKotlin( + "usage.kt", + output = tmpdir, + classpath = listOf(libraryAnnotation, libraryUsingAnnotation), + additionalOptions = listOf("-language-version", "2.0") + ) + } + private fun loadClassFile(className: String, dir: File, library: File) { val classLoader = URLClassLoader(arrayOf(dir.toURI().toURL(), library.toURI().toURL())) val mainClass = classLoader.loadClass(className)