From 9dcf8f836aef6450285301936f1ae515f16ca6f5 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Thu, 18 Apr 2019 14:27:18 +0300 Subject: [PATCH] FIR: support varargs in resolve --- .../kotlin/fir/symbols/StandardClassIds.kt | 24 +++++++++-- .../kotlin/fir/resolve/calls/Arguments.kt | 26 +++++++++--- .../kotlin/fir/resolve/calls/CallResolver.kt | 13 +++--- .../kotlin/fir/resolve/calls/InferenceUtil.kt | 3 +- .../calls/PostponedArgumentsAnalyzer.kt | 6 +-- .../transformers/FirBodyResolveTransformer.kt | 10 ++--- .../transformers/FirTypeResolveTransformer.kt | 21 ++++++++++ .../jetbrains/kotlin/fir/types/ArrayUtils.kt | 40 +++++++++++++++++++ .../testData/resolve/arguments/default.txt | 2 +- .../testData/resolve/arguments/vararg.txt | 6 +-- .../testData/resolve/expresssions/vararg.kt | 8 ++++ .../testData/resolve/expresssions/vararg.txt | 9 +++++ .../fir/FirResolveTestCaseGenerated.java | 5 +++ 13 files changed, 145 insertions(+), 28 deletions(-) create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ArrayUtils.kt create mode 100644 compiler/fir/resolve/testData/resolve/expresssions/vararg.kt create mode 100644 compiler/fir/resolve/testData/resolve/expresssions/vararg.txt diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/StandardClassIds.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/StandardClassIds.kt index d9ab20da195..51043112f4c 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/StandardClassIds.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/StandardClassIds.kt @@ -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 = mutableMapOf().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() } \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt index 248f13793ed..7f9a2250cf8 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt @@ -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() -// } \ No newline at end of file + 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()}!") +} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt index de26e440f82..463ab1a0105 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt @@ -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( } -class CallResolver(val typeCalculator: ReturnTypeCalculator, val session: FirSession) { +class CallResolver(val typeCalculator: ReturnTypeCalculator, val components: InferenceComponents) { var callInfo: CallInfo? = null var scopes: List? = 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): 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() val candidates = mutableListOf() @@ -530,7 +533,7 @@ class CandidateCollector(val callInfo: CallInfo) { candidate: Candidate ): CandidateApplicability { - val sink = CheckerSinkImpl() + val sink = CheckerSinkImpl(components) callInfo.callKind.sequence().forEach { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceUtil.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceUtil.kt index da515007a17..cda03ac283b 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceUtil.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceUtil.kt @@ -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) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArgumentsAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArgumentsAnalyzer.kt index d9d02a04913..9d0f24f30fe 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArgumentsAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArgumentsAnalyzer.kt @@ -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 diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt index d3e70831c4f..ca19c897b87 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt @@ -192,7 +192,7 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn override fun KotlinTypeMarker.removeExactAnnotation(): KotlinTypeMarker { return this } - }) + }, session) private fun 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() { } } -private object StoreType : FirTransformer() { +internal object StoreType : FirTransformer() { override fun transformElement(element: E, data: FirResolvedTypeRef): CompositeTransformResult { return element.compose() } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTypeResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTypeResolveTransformer.kt index 75ffebdb60f..69c6ddd37a7 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTypeResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTypeResolveTransformer.kt @@ -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 { return FirSpecificTypeResolverTransformer(towerScope, FirPosition.OTHER, session).transformTypeRef(typeRef, data) } + + override fun transformValueParameter(valueParameter: FirValueParameter, data: Nothing?): CompositeTransformResult { + + 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() + } + } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ArrayUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ArrayUtils.kt new file mode 100644 index 00000000000..3e3e48e7000 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ArrayUtils.kt @@ -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 +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/arguments/default.txt b/compiler/fir/resolve/testData/resolve/arguments/default.txt index a9f81abb907..b75749b330a 100644 --- a/compiler/fir/resolve/testData/resolve/arguments/default.txt +++ b/compiler/fir/resolve/testData/resolve/arguments/default.txt @@ -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|, z: R|kotlin/Boolean| = Boolean(false)): R|kotlin/Unit| { } public final fun test(): R|kotlin/Unit| { R|/foo|(Int(1)) diff --git a/compiler/fir/resolve/testData/resolve/arguments/vararg.txt b/compiler/fir/resolve/testData/resolve/arguments/vararg.txt index d86173b312a..26a6bb89e5e 100644 --- a/compiler/fir/resolve/testData/resolve/arguments/vararg.txt +++ b/compiler/fir/resolve/testData/resolve/arguments/vararg.txt @@ -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|): 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|, 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)) #(String()) #(Int(1), Int(2)) - R|/bar|(Int(1), z = Boolean(true), y = #(String(my), String(yours))) + R|/bar|(Int(1), z = Boolean(true), y = R|kotlin/arrayOf|(String(my), String(yours))) #(Int(0), z = Boolean(false), y = String(), y = String(other)) #(Int(0), String(), Boolean(true)) } diff --git a/compiler/fir/resolve/testData/resolve/expresssions/vararg.kt b/compiler/fir/resolve/testData/resolve/expresssions/vararg.kt new file mode 100644 index 00000000000..0f3aae11065 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/vararg.kt @@ -0,0 +1,8 @@ +fun foo(vararg x: String) {} + +fun foo() {} + +fun main() { + foo() + foo("!") +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/expresssions/vararg.txt b/compiler/fir/resolve/testData/resolve/expresssions/vararg.txt new file mode 100644 index 00000000000..d6b99fd8ac4 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/vararg.txt @@ -0,0 +1,9 @@ +FILE: vararg.kt + public final fun foo(vararg x: R|kotlin/Array|): R|kotlin/Unit| { + } + public final fun foo(): R|kotlin/Unit| { + } + public final fun main(): R|kotlin/Unit| { + R|/foo|() + R|/foo|(String(!)) + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java index 4329d1add9a..787740ac2aa 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java @@ -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");