FIR: support varargs in resolve

This commit is contained in:
Simon Ogorodnik
2019-04-18 14:27:18 +03:00
committed by Mikhail Glukhikh
parent fa135bbab0
commit 9dcf8f836a
13 changed files with 145 additions and 28 deletions
@@ -13,6 +13,7 @@ object StandardClassIds {
private val BASE_KOTLIN_PACKAGE = FqName("kotlin")
private fun String.baseId() = ClassId(BASE_KOTLIN_PACKAGE, Name.identifier(this))
private fun Name.arrayId() = ClassId(Array.packageFqName, Name.identifier(identifier + Array.shortClassName.identifier))
val Nothing = "Nothing".baseId()
val Unit = "Unit".baseId()
@@ -25,14 +26,29 @@ object StandardClassIds {
val Char = "Char".baseId()
val Byte = "Byte".baseId()
val Short = "Short".baseId()
val Int = "Int".baseId()
val Long = "Long".baseId()
val String = "String".baseId()
val Float = "Float".baseId()
val Double = "Double".baseId()
val String = "String".baseId()
fun byName(name: String) = name.baseId()
val primitiveArrayTypeByElementType: Map<ClassId, ClassId> = mutableMapOf<ClassId, ClassId>().apply {
fun addPrimitive(id: ClassId) {
put(id, id.shortClassName.arrayId())
}
addPrimitive(Boolean)
addPrimitive(Char)
addPrimitive(Byte)
addPrimitive(Short)
addPrimitive(Int)
addPrimitive(Long)
addPrimitive(Float)
addPrimitive(Double)
}
val elementTypeByPrimitiveArrayType = primitiveArrayTypeByElementType.map { (k, v) -> v to k }.toMap()
}
@@ -5,9 +5,11 @@
package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.withNullability
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
@@ -118,7 +120,7 @@ internal fun Candidate.resolveArgument(
sink: CheckerSink
) {
val expectedType = prepareExpectedType(argument, parameter)
val expectedType = prepareExpectedType(sink.components.session, argument, parameter)
resolveArgumentExpression(
this.system.getBuilder(),
argument,
@@ -131,14 +133,26 @@ internal fun Candidate.resolveArgument(
)
}
private fun Candidate.prepareExpectedType(argument: FirExpression, parameter: FirValueParameter): ConeKotlinType {
val expectedType = argument.getExpectedType(parameter/*, LanguageVersionSettings*/)
private fun Candidate.prepareExpectedType(session: FirSession, argument: FirExpression, parameter: FirValueParameter): ConeKotlinType {
val expectedType = argument.getExpectedType(session, parameter/*, LanguageVersionSettings*/)
return this.substitutor.substituteOrSelf(expectedType)
}
internal fun FirExpression.getExpectedType(parameter: FirValueParameter/*, languageVersionSettings: LanguageVersionSettings*/) =
internal fun FirExpression.getExpectedType(
session: FirSession,
parameter: FirValueParameter/*, languageVersionSettings: LanguageVersionSettings*/
) =
// if (this.isSpread || this.isArrayAssignedAsNamedArgumentInAnnotation(parameter, languageVersionSettings)) {
// parameter.type.unwrap()
// } else {
parameter.returnTypeRef.coneTypeUnsafe()//?.varargElementType?.unwrap() ?: parameter.type.unwrap()
// }
if (parameter.isVararg) {
parameter.returnTypeRef.coneTypeUnsafe().varargElementType(session)
} else {
parameter.returnTypeRef.coneTypeUnsafe()
}//?.varargElementType?.unwrap() ?: parameter.type.unwrap()
// }
private fun ConeKotlinType.varargElementType(session: FirSession): ConeKotlinType {
return this.arrayElementType(session) ?: error("Failed to extract! ${this.render()}!")
}
@@ -47,10 +47,11 @@ class CallInfo(
interface CheckerSink {
fun reportApplicability(new: CandidateApplicability)
val components: InferenceComponents
}
class CheckerSinkImpl : CheckerSink {
class CheckerSinkImpl(override val components: InferenceComponents) : CheckerSink {
var current = CandidateApplicability.RESOLVED
override fun reportApplicability(new: CandidateApplicability) {
if (new < current) current = new
@@ -452,12 +453,14 @@ class NoExplicitReceiverTowerDataConsumer<T : ConeSymbol>(
}
class CallResolver(val typeCalculator: ReturnTypeCalculator, val session: FirSession) {
class CallResolver(val typeCalculator: ReturnTypeCalculator, val components: InferenceComponents) {
var callInfo: CallInfo? = null
var scopes: List<FirScope>? = null
val session: FirSession get() = components.session
private fun processImplicitReceiver(
towerDataConsumer: TowerDataConsumer,
implicitReceiverValue: ImplicitReceiverValue,
@@ -471,7 +474,7 @@ class CallResolver(val typeCalculator: ReturnTypeCalculator, val session: FirSes
}
fun runTowerResolver(towerDataConsumer: TowerDataConsumer, implicitReceiverValues: List<ImplicitReceiverValue>): CandidateCollector {
val collector = CandidateCollector(callInfo!!)
val collector = CandidateCollector(callInfo!!, components)
var group = 0
@@ -510,7 +513,7 @@ enum class CandidateApplicability {
RESOLVED
}
class CandidateCollector(val callInfo: CallInfo) {
class CandidateCollector(val callInfo: CallInfo, val components: InferenceComponents) {
val groupNumbers = mutableListOf<Int>()
val candidates = mutableListOf<Candidate>()
@@ -530,7 +533,7 @@ class CandidateCollector(val callInfo: CallInfo) {
candidate: Candidate
): CandidateApplicability {
val sink = CheckerSinkImpl()
val sink = CheckerSinkImpl(components)
callInfo.callKind.sequence().forEach {
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.symbols.*
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
@@ -44,7 +45,7 @@ open class ConeTypeVariable(name: String) : TypeVariableMarker {
val defaultType = ConeTypeVariableType(ConeNullability.NOT_NULL, typeConstructor)
}
class InferenceComponents(val ctx: TypeSystemInferenceExtensionContextDelegate) {
class InferenceComponents(val ctx: TypeSystemInferenceExtensionContextDelegate, val session: FirSession) {
private val approximator = object : AbstractTypeApproximator(ctx) {
override fun createErrorType(message: String): SimpleTypeMarker {
return ConeClassErrorType(message)
@@ -35,7 +35,7 @@ interface LambdaAnalyzer {
class PostponedArgumentsAnalyzer(
val lambdaAnalyzer: LambdaAnalyzer,
val typeProvider: (FirExpression) -> FirTypeRef?,
val session: FirSession
val components: InferenceComponents
) {
fun analyze(
@@ -68,7 +68,7 @@ class PostponedArgumentsAnalyzer(
lambda: ResolvedLambdaAtom//,
//diagnosticHolder: KotlinDiagnosticsHolder
) {
val unitType = Unit(session.service()).constructType(emptyArray(), false)
val unitType = Unit(components.session.service()).constructType(emptyArray(), false)
val stubsForPostponedVariables = c.bindingStubsForPostponedVariables()
val currentSubstitutor = c.buildCurrentSubstitutor(stubsForPostponedVariables.mapKeys { it.key.freshTypeConstructor(c) })
@@ -98,7 +98,7 @@ class PostponedArgumentsAnalyzer(
returnArguments.forEach { c.addSubsystemFromExpression(it) }
val checkerSink: CheckerSink = CheckerSinkImpl()
val checkerSink: CheckerSink = CheckerSinkImpl(components)
val subResolvedKtPrimitives = returnArguments.map {
var atom: PostponedResolvedAtomMarker? = null
@@ -192,7 +192,7 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn
override fun KotlinTypeMarker.removeExactAnnotation(): KotlinTypeMarker {
return this
}
})
}, session)
private fun <T : FirQualifiedAccess> transformCallee(qualifiedAccess: T): T {
val callee = qualifiedAccess.calleeReference as? FirSimpleNamedReference ?: return qualifiedAccess
@@ -200,7 +200,7 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn
val receiver = qualifiedAccess.explicitReceiver?.transformSingle(this, noExpectedType)
val info = CallInfo(CallKind.VariableAccess, receiver, emptyList(), emptyList()) { it.resultType }
val resolver = CallResolver(jump, session)
val resolver = CallResolver(jump, inferenceComponents)
resolver.callInfo = info
resolver.scopes = (scopes + localScopes).asReversed()
@@ -304,7 +304,7 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn
val typeArguments = functionCall.typeArguments
val info = CallInfo(CallKind.Function, explicitReceiver, arguments, typeArguments) { it.resultType }
val resolver = CallResolver(jump, session)
val resolver = CallResolver(jump, inferenceComponents)
resolver.callInfo = info
resolver.scopes = (scopes + localScopes).asReversed()
@@ -416,7 +416,7 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn
return listOfNotNull(newLambdaExpression.body?.statements?.lastOrNull() as? FirExpression) to InferenceSession.default
}
}, { it.resultType }, session)
}, { it.resultType }, inferenceComponents)
completer.complete(candidate.system.asConstraintSystemCompleterContext(), completionMode, listOf(functionCall), initialType) {
analyzer.analyze(
@@ -813,7 +813,7 @@ private object StoreNameReference : FirTransformer<FirNamedReference>() {
}
}
private object StoreType : FirTransformer<FirResolvedTypeRef>() {
internal object StoreType : FirTransformer<FirResolvedTypeRef>() {
override fun <E : FirElement> transformElement(element: E, data: FirResolvedTypeRef): CompositeTransformResult<E> {
return element.compose()
}
@@ -15,6 +15,8 @@ import org.jetbrains.kotlin.fir.scopes.addImportingScopes
import org.jetbrains.kotlin.fir.scopes.impl.*
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.coneTypeUnsafe
import org.jetbrains.kotlin.fir.types.createArrayOf
import org.jetbrains.kotlin.fir.types.impl.FirImplicitBuiltinTypeRef
import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult
import org.jetbrains.kotlin.fir.visitors.compose
@@ -87,4 +89,23 @@ open class FirTypeResolveTransformer : FirAbstractTreeTransformerWithSuperTypes(
override fun transformTypeRef(typeRef: FirTypeRef, data: Nothing?): CompositeTransformResult<FirTypeRef> {
return FirSpecificTypeResolverTransformer(towerScope, FirPosition.OTHER, session).transformTypeRef(typeRef, data)
}
override fun transformValueParameter(valueParameter: FirValueParameter, data: Nothing?): CompositeTransformResult<FirDeclaration> {
val valueParameter = super.transformValueParameter(valueParameter, data).single as FirValueParameter
if (valueParameter.isVararg) {
val returnTypeRef = valueParameter.returnTypeRef
val returnType = returnTypeRef.coneTypeUnsafe()
valueParameter.transformReturnTypeRef(
StoreType,
valueParameter.returnTypeRef.withReplacedConeType(
session,
returnType.createArrayOf(session)
)
)
}
return valueParameter.compose()
}
}
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.types
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.constructType
import org.jetbrains.kotlin.fir.service
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
import org.jetbrains.kotlin.fir.symbols.invoke
fun ConeKotlinType.createArrayOf(session: FirSession, nullable: Boolean = false): ConeKotlinType {
val symbolProvider: FirSymbolProvider = session.service()
if (this is ConeClassType) {
val primitiveArrayId = StandardClassIds.primitiveArrayTypeByElementType[lookupTag.classId]
if (primitiveArrayId != null) {
return primitiveArrayId.invoke(symbolProvider).constructType(emptyArray(), nullable)
}
}
return StandardClassIds.Array.invoke(symbolProvider).constructType(arrayOf(this), nullable)
}
fun ConeKotlinType.arrayElementType(session: FirSession): ConeKotlinType? {
if (this !is ConeClassType) return null
val classId = this.lookupTag.classId
if (classId == StandardClassIds.Array)
return (typeArguments.first() as ConeTypedProjection).type
val elementType = StandardClassIds.elementTypeByPrimitiveArrayType[classId]
if (elementType != null) {
return elementType.invoke(session.service()).constructType(emptyArray(), isNullable = false)
}
return null
}
@@ -3,7 +3,7 @@ FILE: default.kt
}
public final fun bar(first: R|kotlin/Int|, second: R|kotlin/Double| = Double(2.71), third: R|kotlin/Boolean|, fourth: R|kotlin/String| = String()): R|kotlin/Unit| {
}
public final fun baz(x: R|kotlin/Int|, vararg y: R|kotlin/String|, z: R|kotlin/Boolean| = Boolean(false)): R|kotlin/Unit| {
public final fun baz(x: R|kotlin/Int|, vararg y: R|kotlin/Array<kotlin/String>|, z: R|kotlin/Boolean| = Boolean(false)): R|kotlin/Unit| {
}
public final fun test(): R|kotlin/Unit| {
R|/foo|(Int(1))
+3 -3
View File
@@ -1,7 +1,7 @@
FILE: vararg.kt
public final fun foo(x: R|kotlin/Int|, vararg y: R|kotlin/String|): R|kotlin/Unit| {
public final fun foo(x: R|kotlin/Int|, vararg y: R|kotlin/Array<kotlin/String>|): R|kotlin/Unit| {
}
public final fun bar(x: R|kotlin/Int|, vararg y: R|kotlin/String|, z: R|kotlin/Boolean|): R|kotlin/Unit| {
public final fun bar(x: R|kotlin/Int|, vararg y: R|kotlin/Array<kotlin/String>|, z: R|kotlin/Boolean|): R|kotlin/Unit| {
}
public final fun test(): R|kotlin/Unit| {
R|/foo|(Int(1))
@@ -9,7 +9,7 @@ FILE: vararg.kt
R|/foo|(Int(1), String(my), String(yours))
<Inapplicable(INAPPLICABLE): [/foo]>#(String())
<Inapplicable(INAPPLICABLE): [/foo]>#(Int(1), Int(2))
R|/bar|(Int(1), z = Boolean(true), y = <Inapplicable(INAPPLICABLE): [kotlin/arrayOf]>#(String(my), String(yours)))
R|/bar|(Int(1), z = Boolean(true), y = R|kotlin/arrayOf|<R|TypeVariable(T)|>(String(my), String(yours)))
<Inapplicable(PARAMETER_MAPPING_ERROR): [/bar]>#(Int(0), z = Boolean(false), y = String(), y = String(other))
<Inapplicable(PARAMETER_MAPPING_ERROR): [/bar]>#(Int(0), String(), Boolean(true))
}
@@ -0,0 +1,8 @@
fun foo(vararg x: String) {}
fun foo() {}
fun main() {
foo()
foo("!")
}
@@ -0,0 +1,9 @@
FILE: vararg.kt
public final fun foo(vararg x: R|kotlin/Array<kotlin/String>|): R|kotlin/Unit| {
}
public final fun foo(): R|kotlin/Unit| {
}
public final fun main(): R|kotlin/Unit| {
R|/foo|()
R|/foo|(String(!))
}
@@ -257,6 +257,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase {
runTest("compiler/fir/resolve/testData/resolve/expresssions/this.kt");
}
@TestMetadata("vararg.kt")
public void testVararg() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/expresssions/vararg.kt");
}
@TestMetadata("when.kt")
public void testWhen() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/expresssions/when.kt");