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
This commit is contained in:
Denis.Zharkov
2023-03-21 13:37:57 +01:00
committed by Space Team
parent a643dfbe83
commit 9c2c11f7e9
6 changed files with 58 additions and 2 deletions
@@ -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<FirConstructorSymbol>() ?: 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
@@ -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;
}
@@ -0,0 +1 @@
class MyClass(val name: @Nls(capitalization = Nls.Capitalization.Title) String)
@@ -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
}
@@ -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)