From 1dae1358407f0cf3a3f11b3b0f4fc20034576690 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Tue, 19 Mar 2019 16:49:37 +0300 Subject: [PATCH] Expand ConeTypeContext, implement FIR type inference & related checkers --- .../jetbrains/kotlin/fir/types/ConeTypes.kt | 19 +- .../kotlin/fir/backend/ConversionUtils.kt | 3 +- .../src/org/jetbrains/kotlin/fir/CopyUtils.kt | 38 +++ .../kotlin/fir/resolve/ResolveUtils.kt | 5 +- .../kotlin/fir/resolve/calls/Arguments.kt | 95 ++++++ .../kotlin/fir/resolve/calls/CallResolver.kt | 111 +++++-- .../fir/resolve/calls/CandidateFactory.kt | 51 +++ .../fir/resolve/calls/ConeInferenceContext.kt | 308 ++++++++++++++++++ ...CreateFreshTypeVariableSubstitutorStage.kt | 133 ++++++++ .../calls/FirNamedReferenceWithCandidate.kt | 15 + .../fir/resolve/calls/InferenceCompletion.kt | 134 ++++++++ .../kotlin/fir/resolve/calls/InferenceUtil.kt | 58 ++++ .../resolve/calls/OverloadConflictResolver.kt | 215 ++++++++++++ .../kotlin/fir/resolve/calls/ResolverParts.kt | 26 +- .../resolve/substitution/ConeSubstitutor.kt | 130 ++++++++ .../transformers/FirBodyResolveTransformer.kt | 209 +++++++++--- .../FirCallCompleterTransformer.kt | 86 +++++ .../impl/FirAbstractSimpleImportingScope.kt | 2 +- .../scopes/impl/FirClassSubstitutionScope.kt | 63 +--- .../kotlin/fir/types/ConeTypeContext.kt | 71 ++-- .../testData/resolve/expresssions/access.txt | 4 +- .../resolve/expresssions/checkArguments.kt | 16 + .../resolve/expresssions/checkArguments.txt | 27 ++ .../resolve/expresssions/inference/id.kt | 8 + .../resolve/expresssions/inference/id.txt | 9 + .../expresssions/inference/typeParameters.kt | 11 + .../expresssions/inference/typeParameters.txt | 22 ++ .../expresssions/inference/typeParameters2.kt | 11 + .../inference/typeParameters2.txt | 22 ++ .../testData/resolve/fromBuilder/enums.txt | 2 +- .../testData/resolve/stdlib/helloWorld.kt | 3 + .../testData/resolve/stdlib/helloWorld.txt | 4 + .../fir/FirResolveTestCaseGenerated.java | 35 ++ ...FirResolveTestCaseWithStdlibGenerated.java | 5 + .../org/jetbrains/kotlin/fir/FirRenderer.kt | 2 + .../FirResolvedCallableReferenceImpl.kt | 2 +- .../impl/FirTypePlaceholderProjection.kt | 17 + 37 files changed, 1784 insertions(+), 188 deletions(-) create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/CopyUtils.kt create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeInferenceContext.kt create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CreateFreshTypeVariableSubstitutorStage.kt create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirNamedReferenceWithCandidate.kt create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceCompletion.kt create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceUtil.kt create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/OverloadConflictResolver.kt create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/substitution/ConeSubstitutor.kt create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompleterTransformer.kt create mode 100644 compiler/fir/resolve/testData/resolve/expresssions/checkArguments.kt create mode 100644 compiler/fir/resolve/testData/resolve/expresssions/checkArguments.txt create mode 100644 compiler/fir/resolve/testData/resolve/expresssions/inference/id.kt create mode 100644 compiler/fir/resolve/testData/resolve/expresssions/inference/id.txt create mode 100644 compiler/fir/resolve/testData/resolve/expresssions/inference/typeParameters.kt create mode 100644 compiler/fir/resolve/testData/resolve/expresssions/inference/typeParameters.txt create mode 100644 compiler/fir/resolve/testData/resolve/expresssions/inference/typeParameters2.kt create mode 100644 compiler/fir/resolve/testData/resolve/expresssions/inference/typeParameters2.txt create mode 100644 compiler/fir/resolve/testData/resolve/stdlib/helloWorld.kt create mode 100644 compiler/fir/resolve/testData/resolve/stdlib/helloWorld.txt create mode 100644 compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/impl/FirTypePlaceholderProjection.kt diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt index 624bcb019db..351f75a2e41 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt @@ -123,8 +123,9 @@ class ConeFlexibleType(val lowerBound: ConeLookupTagBasedType, val upperBound: C get() = lowerBound.nullability.takeIf { it == upperBound.nullability } ?: ConeNullability.UNKNOWN } -class ConeCapturedTypeConstructor(val projection: ConeKotlinTypeProjection) : TypeConstructorMarker { - var supertypes: List? = null +class ConeCapturedTypeConstructor(val projection: ConeKotlinTypeProjection, var supertypes: List? = null) : + TypeConstructorMarker { + } class ConeCapturedType( @@ -143,4 +144,18 @@ class ConeCapturedType( override val typeArguments: Array get() = emptyArray() +} + +class ConeTypeVariableType( + override val nullability: ConeNullability, + override val lookupTag: ConeClassifierLookupTag +) : ConeLookupTagBasedType() { + override val typeArguments: Array get() = emptyArray() +} + +class ConeDefinitelyNotNullType(val original: ConeKotlinType): ConeKotlinType(), DefinitelyNotNullTypeMarker { + override val typeArguments: Array + get() = original.typeArguments + override val nullability: ConeNullability + get() = ConeNullability.NOT_NULL } \ No newline at end of file diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt index 922a79cde74..7374537a9a1 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt @@ -55,6 +55,7 @@ fun ConeKotlinType.toIrType(session: FirSession, declarationStorage: Fir2IrDecla upperBound.toIrType(session, declarationStorage) } is ConeCapturedType -> TODO() + is ConeDefinitelyNotNullType -> TODO() } } @@ -102,7 +103,7 @@ fun FirReference.toSymbol(declarationStorage: Fir2IrDeclarationStorage): IrSymbo fun FirNamedReference.toSymbol(declarationStorage: Fir2IrDeclarationStorage): IrSymbol? { if (this is FirResolvedCallableReference) { - when (val callableSymbol = this.callableSymbol) { + when (val callableSymbol = this.coneSymbol) { is FirFunctionSymbol -> return callableSymbol.toFunctionSymbol(declarationStorage) is FirPropertySymbol -> return callableSymbol.toPropertySymbol(declarationStorage) is FirVariableSymbol -> return callableSymbol.toValueSymbol(declarationStorage) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/CopyUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/CopyUtils.kt new file mode 100644 index 00000000000..709f2f7c541 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/CopyUtils.kt @@ -0,0 +1,38 @@ +/* + * 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 + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall +import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.expressions.impl.FirFunctionCallImpl +import org.jetbrains.kotlin.fir.types.FirTypeProjection +import org.jetbrains.kotlin.fir.types.FirTypeRef + +fun FirFunctionCall.copy( + annotations: List = this.annotations, + arguments: List = this.arguments, + calleeReference: FirNamedReference = this.calleeReference, + explicitReceiver: FirExpression? = this.explicitReceiver, + psi: PsiElement? = this.psi, + safe: Boolean = this.safe, + session: FirSession = this.session, + typeArguments: List = this.typeArguments, + resultType: FirTypeRef = this.typeRef +): FirFunctionCall { + return FirFunctionCallImpl( + session, psi, safe + ).apply { + this.annotations.addAll(annotations) + this.arguments.addAll(arguments) + this.calleeReference = calleeReference + this.explicitReceiver = explicitReceiver + this.typeArguments.addAll(typeArguments) + this.typeRef = resultType + } +} + diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt index badad299f29..f2ec2cf213f 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt @@ -42,7 +42,7 @@ fun ConeClassifierLookupTag.toSymbol(useSiteSession: FirSession): ConeClassifier when (this) { is ConeClassLikeLookupTag -> toSymbol(useSiteSession) is ConeTypeParameterSymbol -> this - else -> error("sealed") + else -> error("sealed ${this::class}") } fun ConeClassLikeLookupTag.constructClassType(typeArguments: Array, isNullable: Boolean): ConeLookupTagBasedType { @@ -116,7 +116,8 @@ fun T.withNullability(nullability: ConeNullability): T { is ConeFunctionTypeImpl -> ConeFunctionTypeImpl(receiverType, parameterTypes, returnType, lookupTag, nullability.isNullable) as T is ConeTypeParameterTypeImpl -> ConeTypeParameterTypeImpl(lookupTag, nullability.isNullable) as T is ConeFlexibleType -> ConeFlexibleType(lowerBound.withNullability(nullability), upperBound.withNullability(nullability)) as T - else -> TODO("FIX KOTLIN COMPILER") + is ConeTypeVariableType -> ConeTypeVariableType(nullability, lookupTag) as T + else -> error("sealed: ${this::class}") } } 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 new file mode 100644 index 00000000000..1821e287324 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt @@ -0,0 +1,95 @@ +/* + * 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.resolve.calls + +import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction +import org.jetbrains.kotlin.fir.declarations.FirValueParameter +import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess +import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression +import org.jetbrains.kotlin.fir.resolve.withNullability +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder +import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible +import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition + + +fun resolveArgumentExpression( + /* + csBuilder: ConstraintSystemBuilder, + argument: KotlinCallArgument, + expectedType: UnwrappedType?, + diagnosticsHolder: KotlinDiagnosticsHolder, + isReceiver: Boolean + */ + csBuilder: ConstraintSystemBuilder, + argument: FirExpression, + expectedType: ConeKotlinType, + sink: CheckerSink, + isReceiver: Boolean, + typeProvider: (FirExpression) -> FirTypeRef? +) { + return when (argument) { + is FirQualifiedAccessExpression, is FirFunctionCall -> checkPlainExpressionArgument(csBuilder, argument, expectedType, sink, isReceiver, typeProvider) + // TODO:! + is FirAnonymousFunction -> Unit + // TODO:! + is FirCallableReferenceAccess -> Unit + // TODO:! + //TODO: Collection literal + else -> checkPlainExpressionArgument(csBuilder, argument, expectedType, sink, isReceiver, typeProvider) + } +} + +fun checkPlainExpressionArgument( + csBuilder: ConstraintSystemBuilder, + argument: FirExpression, + expectedType: ConeKotlinType?, + sink: CheckerSink, + isReceiver: Boolean, + typeProvider: (FirExpression) -> FirTypeRef? +) { + if (expectedType == null) return + val argumentType = typeProvider(argument)?.coneTypeSafe() ?: return + + val position = SimpleConstraintSystemConstraintPosition //TODO + + if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedType, position)) { + if (!isReceiver) { + csBuilder.addSubtypeConstraint(argumentType, expectedType, position) + } + val nullableExpectedType = expectedType.withNullability(ConeNullability.NULLABLE) + if (csBuilder.addSubtypeConstraintIfCompatible(argumentType, nullableExpectedType, position)) { + sink.reportApplicability(CandidateApplicability.WRONG_RECEIVER) // TODO + } + + } +} + +internal fun Candidate.resolveArgument( + argument: FirExpression, + parameter: FirValueParameter, + isReceiver: Boolean, + typeProvider: (FirExpression) -> FirTypeRef?, + sink: CheckerSink +) { + + val expectedType = prepareExpectedType(argument, parameter) + resolveArgumentExpression(this.system.getBuilder(), argument, expectedType, sink, isReceiver, typeProvider) +} + +private fun Candidate.prepareExpectedType(argument: FirExpression, parameter: FirValueParameter): ConeKotlinType { + val expectedType = argument.getExpectedType(parameter/*, LanguageVersionSettings*/) + return this.substitutor.substituteOrSelf(expectedType) +} + +internal fun FirExpression.getExpectedType(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 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 1a99c47ccf7..196f8afacd4 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 @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.renderWithType import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider import org.jetbrains.kotlin.fir.resolve.defaultType import org.jetbrains.kotlin.fir.resolve.scope +import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator import org.jetbrains.kotlin.fir.scopes.FirPosition import org.jetbrains.kotlin.fir.scopes.FirScope @@ -22,14 +23,22 @@ import org.jetbrains.kotlin.fir.symbols.* import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind import org.jetbrains.kotlin.utils.addToStdlib.cast + + class CallInfo( - val variableAccess: Boolean, + val callKind: CallKind, + val explicitReceiver: FirExpression?, - val argumentCount: Int + + val arguments: List, + val typeArguments: List, + + val typeProvider: (FirExpression) -> FirTypeRef? ) { } @@ -50,11 +59,20 @@ class CheckerSinkImpl : CheckerSink { class Candidate( val symbol: ConeSymbol, val receiverKind: ExplicitReceiverKind, - val callKind: CallKind -) + private val inferenceComponents: InferenceComponents, + private val baseSystem: ConstraintStorage +) { + val system by lazy { + val system = inferenceComponents.createConstraintSystem() + system.addOtherSystem(baseSystem) + system + } + lateinit var substitutor: ConeSubstitutor +} sealed class CallKind { abstract fun sequence(): List + object Function : CallKind() { override fun sequence(): List { return functionCallResolutionSequence() @@ -177,19 +195,31 @@ abstract class TowerDataConsumer { fun createVariableConsumer( session: FirSession, name: Name, - explicitReceiver: FirExpression?, - explicitReceiverType: FirTypeRef? + callInfo: CallInfo, + inferenceComponents: InferenceComponents ): TowerDataConsumer { - return createSimpleConsumer(session, name, TowerScopeLevel.Token.Properties, explicitReceiver, explicitReceiverType, CallKind.VariableAccess) + return createSimpleConsumer( + session, + name, + TowerScopeLevel.Token.Properties, + callInfo, + inferenceComponents + ) } fun createFunctionConsumer( session: FirSession, name: Name, - explicitReceiver: FirExpression?, - explicitReceiverType: FirTypeRef? + callInfo: CallInfo, + inferenceComponents: InferenceComponents ): TowerDataConsumer { - return createSimpleConsumer(session, name, TowerScopeLevel.Token.Functions, explicitReceiver, explicitReceiverType, CallKind.Function) + return createSimpleConsumer( + session, + name, + TowerScopeLevel.Token.Functions, + callInfo, + inferenceComponents + ) } @@ -197,18 +227,24 @@ fun createSimpleConsumer( session: FirSession, name: Name, token: TowerScopeLevel.Token<*>, - explicitReceiver: FirExpression?, - explicitReceiverType: FirTypeRef?, - callKind: CallKind + callInfo: CallInfo, + inferenceComponents: InferenceComponents ): TowerDataConsumer { - return if (explicitReceiver != null) { - ExplicitReceiverTowerDataConsumer(session, name, token, object : ReceiverValueWithPossibleTypes { - override val type: ConeKotlinType - get() = explicitReceiverType?.coneTypeSafe() - ?: ConeKotlinErrorType("No type calculated for: ${explicitReceiver.renderWithType()}") // TODO: assert here - }, callKind) + val factory = CandidateFactory(inferenceComponents, callInfo) + return if (callInfo.explicitReceiver != null) { + ExplicitReceiverTowerDataConsumer( + session, + name, + token, + object : ReceiverValueWithPossibleTypes { + override val type: ConeKotlinType + get() = callInfo.typeProvider(callInfo.explicitReceiver)?.coneTypeSafe() + ?: ConeKotlinErrorType("No type calculated for: ${callInfo.explicitReceiver.renderWithType()}") // TODO: assert here + }, + factory + ) } else { - NoExplicitReceiverTowerDataConsumer(session, name, token, callKind) + NoExplicitReceiverTowerDataConsumer(session, name, token, factory) } } @@ -217,7 +253,7 @@ class ExplicitReceiverTowerDataConsumer( val name: Name, val token: TowerScopeLevel.Token, val explicitReceiver: ReceiverValueWithPossibleTypes, - val callKind: CallKind + val candidateFactory: CandidateFactory ) : TowerDataConsumer() { var groupId = 0 @@ -237,7 +273,14 @@ class ExplicitReceiverTowerDataConsumer( null, object : TowerScopeLevel.TowerScopeLevelProcessor { override fun consumeCandidate(symbol: T, boundDispatchReceiver: ReceiverValueWithPossibleTypes?): ProcessorAction { - resultCollector.consumeCandidate(groupId, Candidate(symbol, ExplicitReceiverKind.DISPATCH_RECEIVER, callKind)) + resultCollector.consumeCandidate( + groupId, + candidateFactory.createCandidate( + symbol, + boundDispatchReceiver, + ExplicitReceiverKind.DISPATCH_RECEIVER + ) + ) return ProcessorAction.NEXT } } @@ -249,7 +292,14 @@ class ExplicitReceiverTowerDataConsumer( explicitReceiver, object : TowerScopeLevel.TowerScopeLevelProcessor { override fun consumeCandidate(symbol: T, boundDispatchReceiver: ReceiverValueWithPossibleTypes?): ProcessorAction { - resultCollector.consumeCandidate(groupId, Candidate(symbol, ExplicitReceiverKind.EXTENSION_RECEIVER, callKind)) + resultCollector.consumeCandidate( + groupId, + candidateFactory.createCandidate( + symbol, + boundDispatchReceiver, + ExplicitReceiverKind.EXTENSION_RECEIVER + ) + ) return ProcessorAction.NEXT } } @@ -263,7 +313,7 @@ class NoExplicitReceiverTowerDataConsumer( val session: FirSession, val name: Name, val token: TowerScopeLevel.Token, - val callKind: CallKind + val candidateFactory: CandidateFactory ) : TowerDataConsumer() { var groupId = 0 @@ -284,7 +334,10 @@ class NoExplicitReceiverTowerDataConsumer( null, object : TowerScopeLevel.TowerScopeLevelProcessor { override fun consumeCandidate(symbol: T, boundDispatchReceiver: ReceiverValueWithPossibleTypes?): ProcessorAction { - resultCollector.consumeCandidate(groupId, Candidate(symbol, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, callKind)) + resultCollector.consumeCandidate( + groupId, + candidateFactory.createCandidate(symbol, boundDispatchReceiver, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER) + ) return ProcessorAction.NEXT } } @@ -350,7 +403,7 @@ class CandidateCollector(val callInfo: CallInfo) { val sink = CheckerSinkImpl() - candidate.callKind.sequence().forEach { + callInfo.callKind.sequence().forEach { it.check(candidate, sink, callInfo) } @@ -374,9 +427,9 @@ class CandidateCollector(val callInfo: CallInfo) { } - fun successCandidates(): List { + fun bestCandidates(): List { if (groupNumbers.isEmpty()) return emptyList() - val result = mutableListOf() + val result = mutableListOf() var bestGroup = groupNumbers.first() for ((index, candidate) in candidates.withIndex()) { val group = groupNumbers[index] @@ -385,7 +438,7 @@ class CandidateCollector(val callInfo: CallInfo) { result.clear() } if (bestGroup == group) { - result.add(candidate.symbol) + result.add(candidate) } } return result diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt new file mode 100644 index 00000000000..616288918a7 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt @@ -0,0 +1,51 @@ +/* + * 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.resolve.calls + +import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.symbols.ConeSymbol +import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzer +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage +import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind + +class CandidateFactory( + val inferenceComponents: InferenceComponents, + callInfo: CallInfo +) { + + val baseSystem: ConstraintStorage + + init { + val system = inferenceComponents.createConstraintSystem() + callInfo.arguments.forEach { + system.addSubsystemFromExpression(it) + } + baseSystem = system.asReadOnlyStorage() + } + + fun createCandidate( + symbol: ConeSymbol, + boundDispatchReceiver: ReceiverValueWithPossibleTypes?, + explicitReceiverKind: ExplicitReceiverKind + ): Candidate { + return Candidate(symbol, explicitReceiverKind, inferenceComponents, baseSystem) + } +} + +private fun PostponedArgumentsAnalyzer.Context.addSubsystemFromExpression(expression: FirExpression) { + when (expression) { + is FirFunctionCall -> expression.candidate()?.let { addOtherSystem(it.system.asReadOnlyStorage()) } + } +} + +internal fun FirFunctionCall.candidate(): Candidate? { + val callee = this.calleeReference + return when (callee) { + is FirNamedReferenceWithCandidate -> return callee.candidate + else -> null + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeInferenceContext.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeInferenceContext.kt new file mode 100644 index 00000000000..d98bf470821 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeInferenceContext.kt @@ -0,0 +1,308 @@ +/* + * 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.resolve.calls + +import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider +import org.jetbrains.kotlin.fir.resolve.constructType +import org.jetbrains.kotlin.fir.resolve.substitution.AbstractConeSubstitutor +import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor +import org.jetbrains.kotlin.fir.resolve.withArguments +import org.jetbrains.kotlin.fir.resolve.withNullability +import org.jetbrains.kotlin.fir.service +import org.jetbrains.kotlin.fir.symbols.ConeClassLikeSymbol +import org.jetbrains.kotlin.fir.symbols.ConeClassifierSymbol +import org.jetbrains.kotlin.fir.symbols.StandardClassIds +import org.jetbrains.kotlin.fir.symbols.invoke +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl +import org.jetbrains.kotlin.types.AbstractTypeChecker +import org.jetbrains.kotlin.types.AbstractTypeCheckerContext +import org.jetbrains.kotlin.types.model.* +import org.jetbrains.kotlin.utils.addToStdlib.cast + +interface ConeInferenceContext : TypeSystemInferenceExtensionContext, + ConeTypeContext { + + val symbolProvider: FirSymbolProvider get() = session.service() + + override fun nullableNothingType(): SimpleTypeMarker { + return StandardClassIds.Nothing(symbolProvider).constructType(emptyArray(), true) + } + + override fun nullableAnyType(): SimpleTypeMarker { + return StandardClassIds.Any(symbolProvider).constructType(emptyArray(), true) + } + + override fun nothingType(): SimpleTypeMarker { + return StandardClassIds.Nothing(symbolProvider).constructType(emptyArray(), false) + } + + override fun createFlexibleType(lowerBound: SimpleTypeMarker, upperBound: SimpleTypeMarker): KotlinTypeMarker { + require(lowerBound is ConeLookupTagBasedType) + require(upperBound is ConeLookupTagBasedType) + + return ConeFlexibleType(lowerBound, upperBound) + } + + override fun createSimpleType( + constructor: TypeConstructorMarker, + arguments: List, + nullable: Boolean + ): SimpleTypeMarker { + require(constructor is ConeClassifierSymbol) + when (constructor) { + is ConeClassLikeSymbol -> return ConeClassTypeImpl( + constructor.toLookupTag(), + arguments.cast(), + nullable + ) + else -> error("!") + } + + } + + override fun createTypeArgument(type: KotlinTypeMarker, variance: TypeVariance): TypeArgumentMarker { + require(type is ConeKotlinType) + return when (variance) { + TypeVariance.INV -> type + TypeVariance.IN -> ConeKotlinTypeProjectionIn(type) + TypeVariance.OUT -> ConeKotlinTypeProjectionOut(type) + } + } + + override fun createStarProjection(typeParameter: TypeParameterMarker): TypeArgumentMarker { + return ConeStarProjection + } + + override fun newBaseTypeCheckerContext(errorTypesEqualToAnything: Boolean): AbstractTypeCheckerContext { + return ConeTypeCheckerContext(errorTypesEqualToAnything, session) + } + + override fun KotlinTypeMarker.canHaveUndefinedNullability(): Boolean { + require(this is ConeKotlinType) + return this is ConeCapturedType /*|| this is ConeTypeVariable // TODO */ + || this is ConeTypeParameterType + } + + fun ConeKotlinType.typeDepthSimple(): Int { + // if (this is TypeUtils.SpecialType) return 0 // TODO: WTF? + + val maxInArguments = this.typeArguments.asSequence().map { + if (it.isStarProjection()) 1 else it.getType().typeDepth() + }.max() ?: 0 + + return maxInArguments + 1 + } + + override fun SimpleTypeMarker.typeDepth(): Int { + require(this is ConeKotlinType) + return this.typeDepthSimple() + } + + override fun KotlinTypeMarker.typeDepth(): Int { + require(this is ConeKotlinType) + return when (this) { + is ConeFlexibleType -> Math.max(lowerBound.typeDepthSimple(), upperBound.typeDepthSimple()) + else -> typeDepthSimple() + } + } + + override fun KotlinTypeMarker.contains(predicate: (KotlinTypeMarker) -> Boolean): Boolean { + return this.containsInternal(predicate) + } + + private fun KotlinTypeMarker?.containsInternal( + predicate: (KotlinTypeMarker) -> Boolean, + visited: HashSet = hashSetOf() + ): Boolean { + if (this == null) return false + if (this in visited) return false + visited += this + + /* + TODO:? + UnwrappedType unwrappedType = type.unwrap(); + */ + + if (predicate(this)) return true + + val flexibleType = this.asFlexibleType() + if (flexibleType != null + && (flexibleType.lowerBound().containsInternal(predicate, visited) + || flexibleType.upperBound().containsInternal(predicate, visited)) + ) { + return true + } + + + if (this is DefinitelyNotNullTypeMarker + && this.original().containsInternal(predicate, visited) + ) { + return true + } + /* + TODO: + + TypeConstructor typeConstructor = type.getConstructor(); + if (typeConstructor instanceof IntersectionTypeConstructor) { + IntersectionTypeConstructor intersectionTypeConstructor = (IntersectionTypeConstructor) typeConstructor; + for (KotlinType supertype : intersectionTypeConstructor.getSupertypes()) { + if (contains(supertype, isSpecialType, visited)) return true; + } + return false; + } + */ + + val simpleType = this.asSimpleType() ?: return false + repeat(simpleType.argumentsCount()) { index -> + val argument = simpleType.getArgument(index) + if (!argument.isStarProjection() && argument.getType().containsInternal(predicate, visited)) return true + } + + return false + } + + override fun TypeConstructorMarker.isUnitTypeConstructor(): Boolean { + return this is ConeClassLikeSymbol && this.classId == StandardClassIds.Unit + } + + override fun Collection.singleBestRepresentative(): KotlinTypeMarker? { + if (this.size == 1) return this.first() + + val context = newBaseTypeCheckerContext(true) + return this.firstOrNull { candidate -> + this.all { other -> + // We consider error types equal to anything here, so that intersections like + // {Array, Array<[ERROR]>} work correctly + candidate == other || AbstractTypeChecker.equalTypes(context, candidate, other) + } + } + } + + override fun KotlinTypeMarker.isUnit(): Boolean { + require(this is ConeKotlinType) + return this.typeConstructor().isUnitTypeConstructor() && !this.isNullable + } + + override fun KotlinTypeMarker.withNullability(nullable: Boolean): KotlinTypeMarker { + require(this is ConeKotlinType) + return this.withNullability(ConeNullability.create(nullable)) + } + + override fun KotlinTypeMarker.makeDefinitelyNotNullOrNotNull(): KotlinTypeMarker { + return this.withNullability(false) //TODO("not implemented") + } + + override fun SimpleTypeMarker.makeSimpleTypeDefinitelyNotNullOrNotNull(): SimpleTypeMarker { + return this.withNullability(false) //TODO("not implemented") + } + + override fun createCapturedType( + constructorProjection: TypeArgumentMarker, + constructorSupertypes: List, + lowerType: KotlinTypeMarker?, + captureStatus: CaptureStatus + ): CapturedTypeMarker { + require(lowerType is ConeKotlinType?) + require(constructorProjection is ConeKotlinTypeProjection) + return ConeCapturedType( + captureStatus, + lowerType, + constructor = ConeCapturedTypeConstructor(constructorProjection, constructorSupertypes.cast()) + ) + } + + override fun createStubType(typeVariable: TypeVariableMarker): StubTypeMarker { + TODO("not implemented") + } + + override fun KotlinTypeMarker.removeAnnotations(): KotlinTypeMarker { + return this // TODO + } + + override fun SimpleTypeMarker.replaceArguments(newArguments: List): SimpleTypeMarker { + require(this is ConeKotlinType) + return this.withArguments(newArguments.cast>().toTypedArray()) + } + + override fun KotlinTypeMarker.hasExactAnnotation(): Boolean { + return false // TODO + } + + override fun KotlinTypeMarker.hasNoInferAnnotation(): Boolean { + return false // TODO + } + + override fun TypeVariableMarker.freshTypeConstructor(): TypeConstructorMarker { + require(this is ConeTypeVariable) + return this.typeConstructor + } + + override fun CapturedTypeMarker.typeConstructorProjection(): TypeArgumentMarker { + require(this is ConeCapturedType) + return this.constructor.projection + } + + override fun KotlinTypeMarker.isNullableType(): Boolean { + require(this is ConeKotlinType) + if (this.isMarkedNullable) + return true + + if (this is ConeFlexibleType && this.upperBound.isNullableType()) + return true + + if (this is ConeTypeParameterType /* || is TypeVariable */) + return hasNullableSuperType(type) + + // TODO: Intersection types + return false + } + + override fun DefinitelyNotNullTypeMarker.original(): SimpleTypeMarker { + require(this is ConeDefinitelyNotNullType) + return this.original() + } + + override fun typeSubstitutorByTypeConstructor(map: Map): TypeSubstitutorMarker { + return object : AbstractConeSubstitutor(), + TypeSubstitutorMarker { + override fun substituteType(type: ConeKotlinType): ConeKotlinType? { + val new = map[type.typeConstructor()] ?: return null + return new as ConeKotlinType + } + } + } + + override fun TypeSubstitutorMarker.safeSubstitute(type: KotlinTypeMarker): KotlinTypeMarker { + if (this === NoSubstitutor) return type + require(this is ConeSubstitutor) + require(type is ConeKotlinType) + return this.substituteOrSelf(type) + } + + override fun TypeVariableMarker.defaultType(): SimpleTypeMarker { + require(this is ConeTypeVariable) + return this.defaultType + } + + override fun captureFromExpression(type: KotlinTypeMarker): KotlinTypeMarker? { + return type + } + + + override fun createErrorTypeWithCustomConstructor(debugName: String, constructor: TypeConstructorMarker): KotlinTypeMarker { + return ConeKotlinErrorType("$debugName c: $constructor") + } + + override fun CapturedTypeMarker.captureStatus(): CaptureStatus { + require(this is ConeCapturedType) + return this.captureStatus + } + + override fun TypeConstructorMarker.isCapturedTypeConstructor(): Boolean { + return this is ConeCapturedTypeConstructor + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CreateFreshTypeVariableSubstitutorStage.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CreateFreshTypeVariableSubstitutorStage.kt new file mode 100644 index 00000000000..23e47eb4da3 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CreateFreshTypeVariableSubstitutorStage.kt @@ -0,0 +1,133 @@ +/* + * 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.resolve.calls + +import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration +import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration +import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor +import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap +import org.jetbrains.kotlin.fir.resolve.transformers.firUnsafe +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.types.impl.FirTypePlaceholderProjection +import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation +import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition + + +internal object CreateFreshTypeVariableSubstitutorStage : ResolutionStage() { + override fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) { + val csBuilder = candidate.system.getBuilder() + val declaration = candidate.symbol.firUnsafe() + if (declaration !is FirCallableMemberDeclaration || declaration.typeParameters.isEmpty()) { + candidate.substitutor = ConeSubstitutor.Empty + return + } + val (substitutor, freshVariables) = createToFreshVariableSubstitutorAndAddInitialConstraints(declaration, candidate, csBuilder) + candidate.substitutor = substitutor + + + // bad function -- error on declaration side + if (csBuilder.hasContradiction) { + sink.reportApplicability(CandidateApplicability.INAPPLICABLE) //TODO: auto report it + return + } + + // optimization +// if (resolvedCall.typeArgumentMappingByOriginal == NoExplicitArguments && knownTypeParametersResultingSubstitutor == null) { +// return +// } + + val typeParameters = declaration.typeParameters + for (index in typeParameters.indices) { + val typeParameter = typeParameters[index] + val freshVariable = freshVariables[index] + +// val knownTypeArgument = knownTypeParametersResultingSubstitutor?.substitute(typeParameter.defaultType) +// if (knownTypeArgument != null) { +// csBuilder.addEqualityConstraint( +// freshVariable.defaultType, +// knownTypeArgument.unwrap(), +// KnownTypeParameterConstraintPosition(knownTypeArgument) +// ) +// continue +// } + + + val typeArgument = + callInfo.typeArguments.getOrElse(index) { FirTypePlaceholderProjection }//resolvedCall.typeArgumentMappingByOriginal.getTypeArgument(typeParameter) +// + if (typeArgument is FirTypeProjectionWithVariance) { + csBuilder.addEqualityConstraint( + freshVariable.defaultType, + typeArgument.typeRef.coneTypeUnsafe(), + SimpleConstraintSystemConstraintPosition // TODO + ) + } else { + assert(typeArgument == FirTypePlaceholderProjection) // TODO +// assert(typeArgument == TypeArgumentPlaceholder) { +// "Unexpected typeArgument: $typeArgument, ${typeArgument.javaClass.canonicalName}" +// } + } + } + } + +} + +fun createToFreshVariableSubstitutorAndAddInitialConstraints( + declaration: FirCallableMemberDeclaration, + candidate: Candidate, + csBuilder: ConstraintSystemOperation +): Pair> { + + val typeParameters = declaration.typeParameters + + val freshTypeVariables = typeParameters.map { TypeParameterBasedTypeVariable(it.symbol) } + + val toFreshVariables = ConeSubstitutorByMap(freshTypeVariables.associate { it.typeParameterSymbol to it.defaultType }) + + for (freshVariable in freshTypeVariables) { + csBuilder.registerVariable(freshVariable) + } + + fun TypeParameterBasedTypeVariable.addSubtypeConstraint( + upperBound: ConeKotlinType//, + //position: DeclaredUpperBoundConstraintPosition + ) { + csBuilder.addSubtypeConstraint(defaultType, toFreshVariables.substituteOrSelf(upperBound), SimpleConstraintSystemConstraintPosition) + } + + for (index in typeParameters.indices) { + val typeParameter = typeParameters[index] + val freshVariable = freshTypeVariables[index] + //val position = DeclaredUpperBoundConstraintPosition(typeParameter) + + for (upperBound in typeParameter.bounds) { + freshVariable.addSubtypeConstraint(upperBound.coneTypeUnsafe()/*, position*/) + } + } + +// if (candidateDescriptor is TypeAliasConstructorDescriptor) { +// val typeAliasDescriptor = candidateDescriptor.typeAliasDescriptor +// val originalTypes = typeAliasDescriptor.underlyingType.arguments.map { it.type } +// val originalTypeParameters = candidateDescriptor.underlyingConstructorDescriptor.typeParameters +// for (index in typeParameters.indices) { +// val typeParameter = typeParameters[index] +// val freshVariable = freshTypeVariables[index] +// val typeMapping = originalTypes.mapIndexedNotNull { i: Int, kotlinType: KotlinType -> +// if (kotlinType == typeParameter.defaultType) i else null +// } +// for (originalIndex in typeMapping) { +// // there can be null in case we already captured type parameter in outer class (in case of inner classes) +// // see test innerClassTypeAliasConstructor.kt +// val originalTypeParameter = originalTypeParameters.getOrNull(originalIndex) ?: continue +// val position = DeclaredUpperBoundConstraintPosition(originalTypeParameter) +// for (upperBound in originalTypeParameter.upperBounds) { +// freshVariable.addSubtypeConstraint(upperBound, position) +// } +// } +// } +// } + return toFreshVariables to freshTypeVariables +} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirNamedReferenceWithCandidate.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirNamedReferenceWithCandidate.kt new file mode 100644 index 00000000000..6a43c539781 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirNamedReferenceWithCandidate.kt @@ -0,0 +1,15 @@ +/* + * 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.resolve.calls + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.fir.* +import org.jetbrains.kotlin.fir.references.FirResolvedCallableReferenceImpl +import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol +import org.jetbrains.kotlin.name.Name + +class FirNamedReferenceWithCandidate(session: FirSession, psi: PsiElement?, name: Name, val candidate: Candidate) : + FirResolvedCallableReferenceImpl(session, psi, name, candidate.symbol as ConeCallableSymbol) \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceCompletion.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceCompletion.kt new file mode 100644 index 00000000000..e4c8618641c --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceCompletion.kt @@ -0,0 +1,134 @@ +/* + * 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.resolve.calls + +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.FirTypeRef +import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter +import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDirectionCalculator +import org.jetbrains.kotlin.resolve.calls.inference.components.VariableFixationFinder +import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints +import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtom +import org.jetbrains.kotlin.types.model.KotlinTypeMarker +import org.jetbrains.kotlin.types.model.isIntegerLiteralTypeConstructor +import org.jetbrains.kotlin.types.model.typeConstructor + + +fun Candidate.computeCompletionMode( + components: InferenceComponents, + expectedType: FirTypeRef?, + currentReturnType: ConeKotlinType? +): KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode { + // Presence of expected type means that we trying to complete outermost call => completion mode should be full + if (expectedType != null) return KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.FULL + + // This is questionable as null return type can be only for error call + if (currentReturnType == null) return KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.PARTIAL + + return when { + // Consider call foo(bar(x)), if return type of bar is a proper one, then we can complete resolve for bar => full completion mode + // Otherwise, we shouldn't complete bar until we process call foo + system.getBuilder().isProperType(currentReturnType) -> KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.FULL + + // Nested call is connected with the outer one through the UPPER constraint (returnType <: expectedOuterType) + // This means that there will be no new LOWER constraints => + // it's possible to complete call now if there are proper LOWER constraints + system.getBuilder().isTypeVariable(currentReturnType) -> + if (hasProperNonTrivialLowerConstraints(components, currentReturnType)) + KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.FULL + else + KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.PARTIAL + + else -> KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.PARTIAL + } +} + +val Candidate.csBuilder get() = system.getBuilder() + +private fun Candidate.hasProperNonTrivialLowerConstraints(components: InferenceComponents, typeVariable: ConeKotlinType): Boolean { + assert(csBuilder.isTypeVariable(typeVariable)) { "$typeVariable is not a type variable" } + + val context = components.ctx + val constructor = typeVariable.typeConstructor(context) + val variableWithConstraints = csBuilder.currentStorage().notFixedTypeVariables[constructor] ?: return false + val constraints = variableWithConstraints.constraints + return constraints.isNotEmpty() && constraints.all { + !it.type.typeConstructor(context).isIntegerLiteralTypeConstructor(context) && + it.kind.isLower() && csBuilder.isProperType(it.type) + } + +} + + +class ConstraintSystemCompleter(val components: InferenceComponents) { + val variableFixationFinder = VariableFixationFinder(components.trivialConstraintTypeInferenceOracle) + fun complete( + c: KotlinConstraintSystemCompleter.Context, + completionMode: KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode, + candidateReturnType: ConeKotlinType + ) { + + while (true) { +// if (analyzePostponeArgumentIfPossible(c, topLevelAtoms, analyze)) continue + + +// val allTypeVariables = getOrderedAllTypeVariables(c, collectVariablesFromContext, topLevelAtoms) + val allTypeVariables = c.notFixedTypeVariables.keys.toList() +// val postponedKtPrimitives = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms) + val variableForFixation = + variableFixationFinder.findFirstVariableForFixation( + c, allTypeVariables, emptyList(), completionMode, candidateReturnType + ) ?: break + +// if (shouldForceCallableReferenceOrLambdaResolution(completionMode, variableForFixation)) { +// if (forcePostponedAtomResolution(topLevelAtoms, analyze)) continue +// if (forcePostponedAtomResolution(topLevelAtoms, analyze)) continue +// } + + if (variableForFixation.hasProperConstraint || completionMode == KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.FULL) { + val variableWithConstraints = c.notFixedTypeVariables.getValue(variableForFixation.variable) + + fixVariable(c, candidateReturnType, variableWithConstraints, emptyList()) + +// if (!variableForFixation.hasProperConstraint) { +// c.addError(NotEnoughInformationForTypeParameter(variableWithConstraints.typeVariable)) +// } + continue + } + + break + } + + if (completionMode == KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.FULL) { + // force resolution for all not-analyzed argument's +// getOrderedNotAnalyzedPostponedArguments(topLevelAtoms).forEach(analyze) +// +// if (c.notFixedTypeVariables.isNotEmpty() && c.postponedTypeVariables.isEmpty()) { +// runCompletion(c, completionMode, topLevelAtoms, topLevelType, analyze) +// } + } + } + + private fun fixVariable( + c: KotlinConstraintSystemCompleter.Context, + topLevelType: KotlinTypeMarker, + variableWithConstraints: VariableWithConstraints, + postponedResolveKtPrimitives: List + ) { + val direction = TypeVariableDirectionCalculator(c, postponedResolveKtPrimitives, topLevelType).getDirection(variableWithConstraints) + fixVariable(c, variableWithConstraints, direction) + } + + fun fixVariable( + c: KotlinConstraintSystemCompleter.Context, + variableWithConstraints: VariableWithConstraints, + direction: TypeVariableDirectionCalculator.ResolveDirection + ) { + val resultType = components.resultTypeResolver.findResultType(c, variableWithConstraints, direction) + c.fixVariable(variableWithConstraints.typeVariable, resultType) + } + +} 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 new file mode 100644 index 00000000000..4121f9bce79 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceUtil.kt @@ -0,0 +1,58 @@ +/* + * 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.resolve.calls + +import org.jetbrains.kotlin.fir.resolve.* +import org.jetbrains.kotlin.fir.symbols.* +import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.calls.inference.components.* +import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl +import org.jetbrains.kotlin.types.AbstractTypeApproximator +import org.jetbrains.kotlin.types.model.* + + +fun ConeInferenceContext.hasNullableSuperType(type: ConeKotlinType): Boolean { + if (type is ConeClassLikeType) return false + + if (type !is ConeLookupTagBasedType) return false // TODO? + val symbol = type.lookupTag.toSymbol(session) ?: return false // TODO?! + for (superType in symbol.supertypes()) { + if (superType.isNullableType()) return true + } +// +// for (KotlinType supertype : getImmediateSupertypes(type)) { +// if (isNullableType(supertype)) return true; +// } + + return false +} + +class ConeTypeVariableTypeConstructor(val debugName: String) : ConeSymbol, ConeClassifierLookupTag, TypeVariableTypeConstructorMarker { + override val name: Name get() = Name.identifier(debugName) +} + +class TypeParameterBasedTypeVariable(val typeParameterSymbol: FirTypeParameterSymbol) : + ConeTypeVariable(typeParameterSymbol.name.identifier) + +open class ConeTypeVariable(name: String) : TypeVariableMarker { + val typeConstructor = ConeTypeVariableTypeConstructor(name) + val defaultType = ConeTypeVariableType(ConeNullability.NOT_NULL, typeConstructor) +} + +class InferenceComponents(val ctx: TypeSystemInferenceExtensionContextDelegate) { + private val approximator = object : AbstractTypeApproximator(ctx) {} + val trivialConstraintTypeInferenceOracle = TrivialConstraintTypeInferenceOracle(ctx) + private val incorporator = ConstraintIncorporator(approximator, trivialConstraintTypeInferenceOracle) + private val injector = ConstraintInjector(incorporator, approximator) + val resultTypeResolver = ResultTypeResolver(approximator, trivialConstraintTypeInferenceOracle) + + fun createConstraintSystem(): NewConstraintSystemImpl { + return NewConstraintSystemImpl(injector, ctx) + } +} + diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/OverloadConflictResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/OverloadConflictResolver.kt new file mode 100644 index 00000000000..83fb898cbd3 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/OverloadConflictResolver.kt @@ -0,0 +1,215 @@ +/* + * 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.resolve.calls + +import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration +import org.jetbrains.kotlin.fir.declarations.FirConstructor +import org.jetbrains.kotlin.fir.declarations.FirNamedFunction +import org.jetbrains.kotlin.fir.resolve.transformers.firUnsafe +import org.jetbrains.kotlin.fir.types.coneTypeUnsafe +import org.jetbrains.kotlin.resolve.OverloadabilitySpecificityCallbacks +import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl +import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition +import org.jetbrains.kotlin.resolve.calls.results.FlatSignature +import org.jetbrains.kotlin.resolve.calls.results.SimpleConstraintSystem +import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator +import org.jetbrains.kotlin.resolve.calls.results.isSignatureNotLessSpecific +import org.jetbrains.kotlin.types.model.KotlinTypeMarker +import org.jetbrains.kotlin.types.model.TypeParameterMarker +import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker +import org.jetbrains.kotlin.types.model.TypeSystemInferenceExtensionContext + +class ConeOverloadConflictResolver( + val specificityComparator: TypeSpecificityComparator, + val inferenceComponents: InferenceComponents + +) { + + fun chooseMaximallySpecificCandidates( + candidates: Collection, + //checkArgumentsMode: CheckArgumentTypesMode, + discriminateGenerics: Boolean//, + //isDebuggerContext: Boolean + ): Set { + + val candidatesSet = candidates.toSet() + + val maximallySpecific = findMaximallySpecificCall(candidatesSet, false/*, isDebuggerContext*/) + if (maximallySpecific != null) { + return setOf(maximallySpecific) + } + + return candidatesSet + + } + + private fun createFlatSignature(call: Candidate): FlatSignature { + val declaration = call.symbol.firUnsafe() + return when (declaration) { + is FirNamedFunction -> createFlatSignature(call, declaration) + is FirConstructor -> createFlatSignature(call, declaration) + else -> error("Not supported: $declaration") + } + } + + private fun createFlatSignature(call: Candidate, constructor: FirConstructor): FlatSignature { + return FlatSignature( + call, + constructor.typeParameters.map { it.symbol }, + constructor.valueParameters.map { it.returnTypeRef.coneTypeUnsafe() }, + //constructor.receiverTypeRef != null, + false, + constructor.valueParameters.any { it.isVararg }, + constructor.valueParameters.count { it.defaultValue != null }, + constructor.isExpect, + false // TODO + ) + } + + private fun createFlatSignature(call: Candidate, function: FirNamedFunction): FlatSignature { + return FlatSignature( + call, + function.typeParameters.map { it.symbol }, + function.valueParameters.map { it.returnTypeRef.coneTypeUnsafe() }, + function.receiverTypeRef != null, + function.valueParameters.any { it.isVararg }, + function.valueParameters.count { it.defaultValue != null }, + function.isExpect, + false // TODO + ) + } + + private fun createEmptyConstraintSystem(): SimpleConstraintSystem { + return ConeSimpleConstraintSystemImpl(inferenceComponents.createConstraintSystem()) + } + + private fun findMaximallySpecificCall( + candidates: Set, + discriminateGenerics: Boolean//, + //isDebuggerContext: Boolean + ): Candidate? { + val filteredCandidates = candidates//uniquifyCandidatesSet(candidates) + + if (filteredCandidates.size <= 1) return filteredCandidates.singleOrNull() + + val conflictingCandidates = filteredCandidates.map { candidateCall -> + createFlatSignature(candidateCall) + } + + val bestCandidatesByParameterTypes = conflictingCandidates.filter { candidate -> + isMostSpecific(candidate, conflictingCandidates) { call1, call2 -> + isNotLessSpecificCallWithArgumentMapping(call1, call2, discriminateGenerics) + } + } + + return bestCandidatesByParameterTypes.exactMaxWith { call1, call2 -> + isOfNotLessSpecificShape(call1, call2)// && isOfNotLessSpecificVisibilityForDebugger(call1, call2, isDebuggerContext) + }?.origin + } + + + private inline fun Collection.exactMaxWith(isNotWorse: (C, C) -> Boolean): C? { + var result: C? = null + for (candidate in this) { + if (result == null || isNotWorse(candidate, result)) { + result = candidate + } + } + if (result == null) return null + if (any { it != result && isNotWorse(it, result!!) }) { + return null + } + return result + } + + private inline fun isMostSpecific(candidate: C, candidates: Collection, isNotLessSpecific: (C, C) -> Boolean): Boolean = + candidates.all { other -> + candidate === other || + isNotLessSpecific(candidate, other) + } + + /** + * `call1` is not less specific than `call2` + */ + private fun isNotLessSpecificCallWithArgumentMapping( + call1: FlatSignature, + call2: FlatSignature, + discriminateGenerics: Boolean + ): Boolean { + return compareCallsByUsedArguments( + call1, + call2, + discriminateGenerics + ) + } + + + + /** + * Returns `true` if [call1] is definitely more or equally specific [call2], + * `false` otherwise. + */ + private fun compareCallsByUsedArguments( + call1: FlatSignature, + call2: FlatSignature, + discriminateGenerics: Boolean + ): Boolean { + if (discriminateGenerics) { + val isGeneric1 = call1.isGeneric + val isGeneric2 = call2.isGeneric + // generic loses to non-generic + if (isGeneric1 && !isGeneric2) return false + if (!isGeneric1 && isGeneric2) return true + // two generics are non-comparable + if (isGeneric1 && isGeneric2) return false + } + + if (!call1.isExpect && call2.isExpect) return true + if (call1.isExpect && !call2.isExpect) return false + + return createEmptyConstraintSystem().isSignatureNotLessSpecific( + call1, + call2, + OverloadabilitySpecificityCallbacks, + specificityComparator + ) + } + + private fun isOfNotLessSpecificShape( + call1: FlatSignature, + call2: FlatSignature + ): Boolean { + val hasVarargs1 = call1.hasVarargs + val hasVarargs2 = call2.hasVarargs + if (hasVarargs1 && !hasVarargs2) return false + if (!hasVarargs1 && hasVarargs2) return true + + if (call1.numDefaults > call2.numDefaults) { + return false + } + + return true + } + +} + +object NoSubstitutor : TypeSubstitutorMarker + +class ConeSimpleConstraintSystemImpl(val system: NewConstraintSystemImpl) : SimpleConstraintSystem { + override fun registerTypeVariables(typeParameters: Collection): TypeSubstitutorMarker { + return NoSubstitutor + } + + override fun addSubtypeConstraint(subType: KotlinTypeMarker, superType: KotlinTypeMarker) { + system.addSubtypeConstraint(subType, superType, SimpleConstraintSystemConstraintPosition) + } + + override fun hasContradiction(): Boolean = system.hasContradiction + + override val context: TypeSystemInferenceExtensionContext + get() = system + +} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt index b23adc87852..070538487a8 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt @@ -10,26 +10,42 @@ import org.jetbrains.kotlin.fir.resolve.transformers.firUnsafe import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol -sealed class ResolutionStage { +abstract class ResolutionStage { abstract fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) } -sealed class CheckerStage : ResolutionStage() +abstract class CheckerStage : ResolutionStage() internal object MapArguments : ResolutionStage() { override fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) { val symbol = candidate.symbol as? FirFunctionSymbol ?: return sink.reportApplicability(CandidateApplicability.HIDDEN) - if (symbol.firUnsafe().valueParameters.size != callInfo.argumentCount) { + if (symbol.firUnsafe().valueParameters.size != callInfo.arguments.size) { return sink.reportApplicability(CandidateApplicability.PARAMETER_MAPPING_ERROR) } } } +internal object CheckArguments : CheckerStage() { + override fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) { + val symbol = candidate.symbol as? FirFunctionSymbol ?: error("Can't check arguments for non function") + val declaration = symbol.fir as FirFunction + for ((parameter, argument) in declaration.valueParameters.zip(callInfo.arguments)) { + + candidate.resolveArgument(argument, parameter, isReceiver = false, typeProvider = callInfo.typeProvider, sink = sink) + } + + if (candidate.system.hasContradiction) { + sink.reportApplicability(CandidateApplicability.INAPPLICABLE) + } + } + +} + internal fun functionCallResolutionSequence() = - listOf(MapArguments) + listOf(MapArguments, CreateFreshTypeVariableSubstitutorStage, CheckArguments) internal fun qualifiedAccessResolutionSequence() = - listOf() \ No newline at end of file + listOf(CreateFreshTypeVariableSubstitutorStage) \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/substitution/ConeSubstitutor.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/substitution/ConeSubstitutor.kt new file mode 100644 index 00000000000..5294aa8dcf7 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/substitution/ConeSubstitutor.kt @@ -0,0 +1,130 @@ +/* + * 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.resolve.substitution + +import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterSymbol +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.types.impl.ConeAbbreviatedTypeImpl +import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl + + +interface ConeSubstitutor { + fun substituteOrSelf(type: ConeKotlinType): ConeKotlinType + fun substituteOrNull(type: ConeKotlinType): ConeKotlinType? + + object Empty : ConeSubstitutor { + override fun substituteOrSelf(type: ConeKotlinType): ConeKotlinType { + return type + } + + override fun substituteOrNull(type: ConeKotlinType): ConeKotlinType? { + return null + } + + } +} + +fun ConeSubstitutor.substituteOrNull(type: ConeKotlinType?): ConeKotlinType? { + return type?.let { substituteOrNull(it) } +} + +abstract class AbstractConeSubstitutor : ConeSubstitutor { + private fun wrapProjection(old: ConeKotlinTypeProjection, newType: ConeKotlinType): ConeKotlinTypeProjection { + return when (old) { + is ConeStarProjection -> old + is ConeKotlinTypeProjectionIn -> ConeKotlinTypeProjectionIn(newType) + is ConeKotlinTypeProjectionOut -> ConeKotlinTypeProjectionOut(newType) + is ConeKotlinType -> newType + else -> old + } + } + + abstract fun substituteType(type: ConeKotlinType): ConeKotlinType? + + override fun substituteOrSelf(type: ConeKotlinType): ConeKotlinType { + return substituteOrNull(type) ?: type + } + + override fun substituteOrNull(type: ConeKotlinType): ConeKotlinType? { + val newType = substituteType(type) + return (newType ?: type).substituteRecursive() ?: newType + } + + private fun ConeKotlinType.substituteRecursive(): ConeKotlinType? { + return when (this) { + is ConeClassErrorType -> return null + is ConeClassType -> this.substituteArguments() + is ConeAbbreviatedType -> this.substituteArguments() + is ConeFunctionType -> TODO() + is ConeTypeParameterType -> return null + is ConeTypeVariableType -> return null + is ConeFlexibleType -> this.substituteBounds() + is ConeCapturedType -> TODO() + is ConeDefinitelyNotNullType -> this.substituteOriginal() + } + } + + private fun ConeDefinitelyNotNullType.substituteOriginal(): ConeDefinitelyNotNullType? { + TODO() + } + + private fun ConeFlexibleType.substituteBounds(): ConeFlexibleType? { + val newLowerBound = substituteOrNull(lowerBound) as ConeLookupTagBasedType? + val newUpperBound = substituteOrNull(upperBound) as ConeLookupTagBasedType? + if (newLowerBound != null || newUpperBound != null) { + return ConeFlexibleType(newLowerBound ?: lowerBound, newUpperBound ?: upperBound) + } + return null + } + + private fun ConeKotlinType.substituteArguments(): ConeKotlinType? { + val newArguments by lazy { arrayOfNulls(typeArguments.size) } + var initialized = false + for ((index, typeArgument) in this.typeArguments.withIndex()) { + val type = (typeArgument as? ConeTypedProjection)?.type ?: continue + val newType = substituteOrNull(type) + if (newType != null) { + initialized = true + newArguments[index] = wrapProjection(typeArgument, newType) + } + } + + if (initialized) { + for ((index, typeArgument) in this.typeArguments.withIndex()) { + if (newArguments[index] == null) { + newArguments[index] = typeArgument + } + } + @Suppress("UNCHECKED_CAST") + return when (this) { + is ConeClassTypeImpl -> ConeClassTypeImpl( + lookupTag, + newArguments as Array, + nullability.isNullable + ) + is ConeAbbreviatedTypeImpl -> ConeAbbreviatedTypeImpl( + abbreviationLookupTag, + newArguments as Array, + nullability.isNullable + ) + is ConeClassLikeType -> error("Unknown class-like type to substitute: $this, ${this::class}") + else -> error("Unknown type to substitute: $this, ${this::class}") + } + } + return null + } + + +} + + +class ConeSubstitutorByMap(val substitution: Map) : AbstractConeSubstitutor() { + + override fun substituteType(type: ConeKotlinType): ConeKotlinType? { + if (type !is ConeTypeParameterType) return null + return substitution[type.lookupTag] + } +} 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 22f9c549f3e..f670ba0a223 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 @@ -10,13 +10,10 @@ import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.references.FirErrorNamedReference -import org.jetbrains.kotlin.fir.references.FirResolvedCallableReferenceImpl import org.jetbrains.kotlin.fir.references.FirSimpleNamedReference import org.jetbrains.kotlin.fir.resolve.* -import org.jetbrains.kotlin.fir.resolve.calls.CallInfo -import org.jetbrains.kotlin.fir.resolve.calls.CallResolver -import org.jetbrains.kotlin.fir.resolve.calls.createFunctionConsumer -import org.jetbrains.kotlin.fir.resolve.calls.createVariableConsumer +import org.jetbrains.kotlin.fir.resolve.calls.* +import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.addImportingScopes import org.jetbrains.kotlin.fir.scopes.impl.FirLocalScope @@ -24,16 +21,17 @@ import org.jetbrains.kotlin.fir.scopes.impl.FirTopLevelDeclaredMemberScope import org.jetbrains.kotlin.fir.scopes.impl.withReplacedConeType import org.jetbrains.kotlin.fir.symbols.* import org.jetbrains.kotlin.fir.types.* -import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl -import org.jetbrains.kotlin.fir.types.impl.FirComputingImplicitTypeRef -import org.jetbrains.kotlin.fir.types.impl.FirErrorTypeRefImpl -import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl +import org.jetbrains.kotlin.fir.types.impl.* import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult import org.jetbrains.kotlin.fir.visitors.FirTransformer import org.jetbrains.kotlin.fir.visitors.compose import org.jetbrains.kotlin.ir.expressions.IrConstKind import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor +import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter +import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator +import org.jetbrains.kotlin.types.model.* import org.jetbrains.kotlin.utils.addIfNotNull open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOnly: Boolean) : FirTransformer() { @@ -115,7 +113,7 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn resolved.resultType = resolved.conversionTypeRef.withReplacedConeType( session, - resolved.conversionTypeRef.coneTypeUnsafe().withNullability(ConeNullability.NULLABLE) + resolved.conversionTypeRef.coneTypeUnsafe().withNullability(ConeNullability.NULLABLE) ) } else -> error("Unknown type operator") @@ -143,16 +141,36 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn val jump = ReturnTypeCalculatorWithJump(session) private fun storeTypeFromCallee(access: T) where T : FirQualifiedAccess, T : FirExpression { - access.resultType = - when (val newCallee = access.calleeReference) { - is FirErrorNamedReference -> - FirErrorTypeRefImpl(session, access.psi, newCallee.errorReason) - is FirResolvedCallableReference -> - jump.tryCalculateReturnType(newCallee.callableSymbol.firUnsafe()) - else -> return - } + access.resultType = typeFromCallee(access) } + private fun typeFromCallee(access: T): FirResolvedTypeRef where T : FirQualifiedAccess, T : FirExpression { + return when (val newCallee = access.calleeReference) { + is FirErrorNamedReference -> + FirErrorTypeRefImpl(session, access.psi, newCallee.errorReason) + is FirResolvedCallableReference -> + jump.tryCalculateReturnType(newCallee.callableSymbol.firUnsafe()) + else -> error("Failed to extract type from: $newCallee") + } + } + + val inferenceComponents = InferenceComponents(object : ConeInferenceContext, TypeSystemInferenceExtensionContextDelegate { + override fun findCommonIntegerLiteralTypesSuperType(explicitSupertypes: List): SimpleTypeMarker? { + TODO("not implemented") + } + + override fun TypeConstructorMarker.getApproximatedIntegerLiteralType(): KotlinTypeMarker { + TODO("not implemented") + } + + override val session: FirSession + get() = this@FirBodyResolveTransformer.session + + override fun KotlinTypeMarker.removeExactAnnotation(): KotlinTypeMarker { + return this + } + }) + override fun transformQualifiedAccessExpression( qualifiedAccessExpression: FirQualifiedAccessExpression, data: Any? @@ -180,13 +198,9 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn } val callee = qualifiedAccessExpression.calleeReference as? FirSimpleNamedReference ?: return qualifiedAccessExpression.compose() - qualifiedAccessExpression.explicitReceiver?.visitNoTransform(this, null) + val receiver = qualifiedAccessExpression.explicitReceiver?.transformSingle(this, null) - val receiver = qualifiedAccessExpression.explicitReceiver - - //val checkers = listOf(VariableApplicabilityChecker(callee.name)) - - val info = CallInfo(true, receiver, 0) + val info = CallInfo(CallKind.VariableAccess, receiver, emptyList(), emptyList()) { it.resultType } val resolver = CallResolver(jump, session) resolver.callInfo = info resolver.scopes = (scopes + localScopes).asReversed() @@ -194,40 +208,44 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn val consumer = createVariableConsumer( session, callee.name, - qualifiedAccessExpression.explicitReceiver, - qualifiedAccessExpression.explicitReceiver?.resultType + info, inferenceComponents ) val result = resolver.runTowerResolver(consumer) - val successCandidates = result.successCandidates() - val resultExpression = qualifiedAccessExpression.transformCalleeReference(this, successCandidates) + + val nameReference = createResolvedNamedReference( + callee, + result.bestCandidates(), + result.currentApplicability + ) + + val resultExpression = + qualifiedAccessExpression.transformCalleeReference(StoreNameReference, nameReference) storeTypeFromCallee(resultExpression as FirQualifiedAccessExpression) return resultExpression.compose() } - override fun transformFunctionCall(functionCall: FirFunctionCall, data: Any?): CompositeTransformResult { + private fun resolveCallAndSelectCandidate(functionCall: FirFunctionCall, expectedTypeRef: FirTypeRef?): FirFunctionCall { val functionCall = functionCall.transformChildren(this, null) as FirFunctionCall - if (functionCall.calleeReference !is FirSimpleNamedReference) return functionCall.compose() - val name = functionCall.calleeReference.name - val expectedTypeRef = data as FirTypeRef? - val receiver = functionCall.explicitReceiver val arguments = functionCall.arguments + val typeArguments = functionCall.typeArguments - val info = CallInfo(false, receiver, arguments.size) + val info = CallInfo(CallKind.Function, receiver, arguments, typeArguments) { it.resultType } val resolver = CallResolver(jump, session) resolver.callInfo = info resolver.scopes = (scopes + localScopes).asReversed() - - - val consumer = createFunctionConsumer(session, name, receiver, receiver?.resultType) + val consumer = createFunctionConsumer(session, name, info, inferenceComponents) val result = resolver.runTowerResolver(consumer) - val successCandidates = result.successCandidates() + val bestCandidates = result.bestCandidates() + val reducedCandidates = ConeOverloadConflictResolver(TypeSpecificityComparator.NONE, inferenceComponents) + .chooseMaximallySpecificCandidates(bestCandidates, discriminateGenerics = false) + // fun isInvoke() // @@ -257,33 +275,103 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn // } // else -> functionCall // } - val resultExpression = functionCall.transformCalleeReference(this, successCandidates) + val nameReference = createResolvedNamedReference( + functionCall.calleeReference, + reducedCandidates, + result.currentApplicability + ) - storeTypeFromCallee(resultExpression as FirFunctionCall) - return resultExpression.compose() + val resultExpression = functionCall.transformCalleeReference(StoreNameReference, nameReference) as FirFunctionCall + val typeRef = typeFromCallee(functionCall) + if (typeRef.type is ConeKotlinErrorType) { + functionCall.resultType = typeRef + } + return resultExpression } - private fun createResolvedNamedReference(namedReference: FirNamedReference, candidates: List): FirNamedReference { + private fun completeTypeInference(functionCall: FirFunctionCall, expectedTypeRef: FirTypeRef?): FirFunctionCall { + val typeRef = typeFromCallee(functionCall) + if (typeRef.type is ConeKotlinErrorType) { + functionCall.resultType = typeRef + return functionCall + } + val candidate = functionCall.candidate() ?: return functionCall + val initialSubstitutor = candidate.substitutor + + val initialType = initialSubstitutor.substituteOrSelf(typeRef.type) + + val completionMode = candidate.computeCompletionMode(inferenceComponents, expectedTypeRef, initialType) + val completer = ConstraintSystemCompleter(inferenceComponents) + completer.complete(candidate.system.asConstraintSystemCompleterContext(), completionMode, initialType) + + + if (completionMode == KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.FULL) { + val finalSubstitutor = + candidate.system.asReadOnlyStorage().buildAbstractResultingSubstitutor(inferenceComponents.ctx) as ConeSubstitutor + return functionCall.transformSingle( + FirCallCompleterTransformer(session, finalSubstitutor, jump), + null + ) + } + return functionCall + } + + override fun transformFunctionCall(functionCall: FirFunctionCall, data: Any?): CompositeTransformResult { + if (functionCall.calleeReference !is FirSimpleNamedReference) return functionCall.compose() + val expectedTypeRef = data as FirTypeRef? + val completeInference = + try { + val resultExpression = resolveCallAndSelectCandidate(functionCall, expectedTypeRef) + completeTypeInference(resultExpression, expectedTypeRef) + } catch (e: Throwable) { + throw RuntimeException("While resolving call ${functionCall.render()}", e) + } + + + return completeInference.compose() + + } + + private fun describeSymbol(symbol: ConeSymbol): String { + return when (symbol) { + is ConeClassLikeSymbol -> symbol.classId.asString() + is ConeCallableSymbol -> symbol.callableId.toString() + else -> "$symbol" + } + } + + private fun createResolvedNamedReference( + namedReference: FirNamedReference, + candidates: Collection, + applicability: CandidateApplicability + ): FirNamedReference { val name = namedReference.name - return when (candidates.size) { - 0 -> FirErrorNamedReference( + return when { + candidates.isEmpty() -> FirErrorNamedReference( namedReference.session, namedReference.psi, "Unresolved name: $name" ) - 1 -> FirResolvedCallableReferenceImpl( + applicability < CandidateApplicability.RESOLVED -> { + FirErrorNamedReference( + namedReference.session, + namedReference.psi, + "Inapplicable($applicability): ${candidates.map { describeSymbol(it.symbol) }}" + ) + } + candidates.size == 1 -> FirNamedReferenceWithCandidate( namedReference.session, namedReference.psi, name, candidates.single() ) else -> FirErrorNamedReference( - namedReference.session, namedReference.psi, "Ambiguity: $name, ${candidates.map { it.callableId }}" + namedReference.session, namedReference.psi, "Ambiguity: $name, ${candidates.map { describeSymbol(it.symbol) }}" ) } } - override fun transformNamedReference(namedReference: FirNamedReference, data: Any?): CompositeTransformResult { - if (namedReference is FirErrorNamedReference || namedReference is FirResolvedCallableReference) return namedReference.compose() - val referents = data as? List ?: return namedReference.compose() - return createResolvedNamedReference(namedReference, referents).compose() - } +// override fun transformNamedReference(namedReference: FirNamedReference, data: Any?): CompositeTransformResult { +// if (namedReference is FirErrorNamedReference || namedReference is FirResolvedCallableReference) return namedReference.compose() +// val referents = data as? List ?: return namedReference.compose() +// return createResolvedNamedReference(namedReference, referents).compose() +// } override fun transformBlock(block: FirBlock, data: Any?): CompositeTransformResult { @@ -315,10 +403,10 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn } override fun transformConstExpression(constExpression: FirConstExpression, data: Any?): CompositeTransformResult { - val expectedType = data as? FirTypeRef + val expectedType = data as FirTypeRef? val kind = constExpression.kind - if (expectedType is FirImplicitTypeRef || expectedType == null || + if (expectedType == null || expectedType is FirImplicitTypeRef || expectedType == null || kind == IrConstKind.Null || kind == IrConstKind.Boolean || kind == IrConstKind.Char ) { val symbol = when (kind) { @@ -424,7 +512,7 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn override fun transformExpression(expression: FirExpression, data: Any?): CompositeTransformResult { if (expression.resultType is FirImplicitTypeRef) { - val type = FirErrorTypeRefImpl(session, expression.psi, "Type calculating for ${expression.render()} is not supported") + val type = FirErrorTypeRefImpl(session, expression.psi, "Type calculating for ${expression::class} is not supported") expression.resultType = type } return super.transformExpression(expression, data) @@ -568,3 +656,16 @@ inline fun ConeSymbol.firSafeNullable(): T? { interface ReturnTypeCalculator { fun tryCalculateReturnType(declaration: FirTypedDeclaration): FirResolvedTypeRef } + +private object StoreNameReference : FirTransformer() { + override fun transformElement(element: E, data: FirNamedReference): CompositeTransformResult { + return element.compose() + } + + override fun transformNamedReference( + namedReference: FirNamedReference, + data: FirNamedReference + ): CompositeTransformResult { + return data.compose() + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompleterTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompleterTransformer.kt new file mode 100644 index 00000000000..6a9d866b3b9 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompleterTransformer.kt @@ -0,0 +1,86 @@ +/* + * 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.resolve.transformers + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.copy +import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration +import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.expressions.FirStatement +import org.jetbrains.kotlin.fir.references.FirResolvedCallableReferenceImpl +import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate +import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor +import org.jetbrains.kotlin.fir.resolve.substitution.substituteOrNull +import org.jetbrains.kotlin.fir.scopes.impl.withReplacedConeType +import org.jetbrains.kotlin.fir.symbols.ConeSymbol +import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef +import org.jetbrains.kotlin.fir.types.FirTypeProjectionWithVariance +import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl +import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl +import org.jetbrains.kotlin.fir.types.impl.FirTypeProjectionWithVarianceImpl +import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult +import org.jetbrains.kotlin.fir.visitors.compose +import org.jetbrains.kotlin.types.Variance + +class FirCallCompleterTransformer( + val session: FirSession, + private val finalSubstitutor: ConeSubstitutor, + private val typeCalculator: ReturnTypeCalculator +) : FirAbstractTreeTransformer() { + + + override fun transformFunctionCall(functionCall: FirFunctionCall, data: Nothing?): CompositeTransformResult { + val calleeReference = functionCall.calleeReference as? FirNamedReferenceWithCandidate ?: return functionCall.compose() + val functionCall = functionCall.transformChildren(this, data) as FirFunctionCall + + val subCandidate = calleeReference.candidate + val declaration = subCandidate.symbol.firUnsafe() + val newTypeParameters = declaration.typeParameters.map { ConeTypeParameterTypeImpl(it.symbol, false) } + .map { subCandidate.substitutor.substituteOrSelf(it) } + .map { finalSubstitutor.substituteOrSelf(it) } + .mapIndexed { index, type -> + when (val argument = functionCall.typeArguments.getOrNull(index)) { + is FirTypeProjectionWithVariance -> { + val typeRef = argument.typeRef as FirResolvedTypeRef + FirTypeProjectionWithVarianceImpl( + session, + argument.psi, + argument.variance, + typeRef.withReplacedConeType(session, type) + ) + } + else -> { + FirTypeProjectionWithVarianceImpl( + session, + argument?.psi, + Variance.INVARIANT, + FirResolvedTypeRefImpl(session, null, type, false, emptyList()) + ) + } + } + } + + val typeRef = typeCalculator.tryCalculateReturnType(declaration) + + val initialType = subCandidate.substitutor.substituteOrNull(typeRef.type) + val finalType = finalSubstitutor.substituteOrNull(initialType) + + val resultType = typeRef.withReplacedConeType(session, finalType) + + return functionCall.copy( + resultType = resultType, + typeArguments = newTypeParameters, + calleeReference = FirResolvedCallableReferenceImpl( + calleeReference.session, + calleeReference.psi, + calleeReference.name, + calleeReference.callableSymbol + ) + ).compose() + + } + +} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractSimpleImportingScope.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractSimpleImportingScope.kt index cd7e2195af3..cf1bf67e56c 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractSimpleImportingScope.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractSimpleImportingScope.kt @@ -52,7 +52,7 @@ abstract class FirAbstractSimpleImportingScope(session: FirSession) : FirAbstrac if (imports.isEmpty()) return ProcessorAction.NEXT for (import in imports) { - if (processCallables(import, name, token, processor).stop()) { + if (processCallables(import, import.importedName!!, token, processor).stop()) { return ProcessorAction.STOP } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassSubstitutionScope.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassSubstitutionScope.kt index a99def46851..b99bda13bd7 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassSubstitutionScope.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassSubstitutionScope.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.declarations.FirNamedFunction import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl import org.jetbrains.kotlin.fir.declarations.impl.FirMemberFunctionImpl import org.jetbrains.kotlin.fir.declarations.impl.FirValueParameterImpl +import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculatorWithJump import org.jetbrains.kotlin.fir.resolve.transformers.firUnsafe import org.jetbrains.kotlin.fir.scopes.FirScope @@ -20,72 +21,18 @@ import org.jetbrains.kotlin.fir.symbols.* import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol import org.jetbrains.kotlin.fir.types.* -import org.jetbrains.kotlin.fir.types.impl.ConeAbbreviatedTypeImpl -import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl import org.jetbrains.kotlin.name.Name class FirClassSubstitutionScope( private val session: FirSession, private val useSiteScope: FirScope, - private val substitution: Map + substitution: Map ) : FirScope { private val fakeOverrides = mutableMapOf() - private fun wrapProjection(old: ConeKotlinTypeProjection, newType: ConeKotlinType): ConeKotlinTypeProjection { - return when (old) { - is ConeStarProjection -> old - is ConeKotlinTypeProjectionIn -> ConeKotlinTypeProjectionIn(newType) - is ConeKotlinTypeProjectionOut -> ConeKotlinTypeProjectionOut(newType) - is ConeKotlinType -> newType - else -> old - } - } - - private fun ConeKotlinType.substitute(): ConeKotlinType? { - if (this is ConeTypeParameterType) return substitution[lookupTag] - - val newArguments by lazy { arrayOfNulls(typeArguments.size) } - var initialized = false - for ((index, typeArgument) in this.typeArguments.withIndex()) { - val type = (typeArgument as? ConeTypedProjection)?.type ?: continue - val newType = type.substitute() - if (newType != null) { - initialized = true - newArguments[index] = wrapProjection(typeArgument, newType) - } - } - - if (initialized) { - for ((index, typeArgument) in this.typeArguments.withIndex()) { - if (newArguments[index] == null) { - newArguments[index] = typeArgument - } - } - @Suppress("UNCHECKED_CAST") - return when (this) { - is ConeKotlinErrorType -> error("Trying to substitute arguments for error type") - is ConeTypeParameterType -> error("Trying to substitute arguments for type parameter") - is ConeClassTypeImpl -> ConeClassTypeImpl( - lookupTag, - newArguments as Array, - nullability.isNullable - ) - is ConeAbbreviatedTypeImpl -> ConeAbbreviatedTypeImpl( - abbreviationLookupTag, - newArguments as Array, - nullability.isNullable - ) - is ConeFunctionType -> TODO("Substitute function type properly") - is ConeClassLikeType -> error("Unknown class-like type to substitute: $this, ${this::class}") - is ConeFlexibleType -> error("Trying to substitute arguments for flexible type") - is ConeCapturedType -> error("Not supported") - } - } - return null - } - + private val substitutor = ConeSubstitutorByMap(substitution) override fun processFunctionsByName(name: Name, processor: (ConeFunctionSymbol) -> ProcessorAction): ProcessorAction { useSiteScope.processFunctionsByName(name) process@{ original -> @@ -104,6 +51,10 @@ class FirClassSubstitutionScope( private val typeCalculator by lazy { ReturnTypeCalculatorWithJump(session) } + private fun ConeKotlinType.substitute(): ConeKotlinType? { + return substitutor.substituteOrNull(this) + } + private fun createFakeOverride(original: ConeFunctionSymbol): FirFunctionSymbol { val member = original.firUnsafe() diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt index 4ca3291d084..76bdfcbc279 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt @@ -10,13 +10,13 @@ import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.expandedConeType import org.jetbrains.kotlin.fir.declarations.superConeTypes +import org.jetbrains.kotlin.fir.resolve.calls.ConeTypeVariableTypeConstructor +import org.jetbrains.kotlin.fir.resolve.constructType import org.jetbrains.kotlin.fir.resolve.directExpansionType import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.resolve.withArguments -import org.jetbrains.kotlin.fir.symbols.ConeClassLikeSymbol -import org.jetbrains.kotlin.fir.symbols.ConeClassSymbol -import org.jetbrains.kotlin.fir.symbols.ConeSymbol -import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterSymbol +import org.jetbrains.kotlin.fir.service +import org.jetbrains.kotlin.fir.symbols.* import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol @@ -47,9 +47,12 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext { assert(this is ConeKotlinType) return when (this) { is ConeAbbreviatedType -> directExpansionType(session) + ?: ConeClassErrorType("no expansion for type-alias: ${this.abbreviationLookupTag.classId}") is ConeCapturedType -> this is ConeLookupTagBasedType -> this - else -> null + is ConeDefinitelyNotNullType -> this + is ConeFlexibleType -> null + else -> error("Unknown simpleType: $this") } } @@ -130,6 +133,7 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext { override fun SimpleTypeMarker.typeConstructor(): TypeConstructorMarker { return when (this) { is ConeCapturedType -> constructor + is ConeTypeVariableType -> this.lookupTag as ConeTypeVariableTypeConstructor // TODO: WTF is ConeLookupTagBasedType -> this.lookupTag.toSymbol(session) ?: ErrorTypeConstructor("Unresolved: ${this.lookupTag}") else -> error("?: ${this}") } @@ -145,7 +149,8 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext { override fun SimpleTypeMarker.getArgument(index: Int): TypeArgumentMarker { require(this is ConeKotlinType) - return this.typeArguments[index] + return this.typeArguments.getOrNull(index) + ?: StandardClassIds.Any(session.service()).constructType(emptyArray(), false) // TODO wtf } override fun KotlinTypeMarker.asTypeArgument(): TypeArgumentMarker { @@ -184,7 +189,7 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext { override fun TypeConstructorMarker.parametersCount(): Int { //require(this is ConeSymbol) return when (this) { - is ConeTypeParameterSymbol, is ConeCapturedTypeConstructor, is ErrorTypeConstructor -> 0 + is ConeTypeParameterSymbol, is ConeCapturedTypeConstructor, is ErrorTypeConstructor, is ConeTypeVariableTypeConstructor -> 0 is FirClassSymbol -> fir.typeParameters.size is FirTypeAliasSymbol -> fir.typeParameters.size else -> error("?!:10") @@ -205,7 +210,8 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext { if (this is ErrorTypeConstructor) return emptyList() //require(this is ConeSymbol) return when (this) { - is ConeTypeParameterSymbol -> emptyList() + is ConeTypeVariableTypeConstructor -> emptyList() + is FirTypeParameterSymbol -> fir.bounds.map { it.coneTypeUnsafe() } is FirClassSymbol -> fir.superConeTypes is FirTypeAliasSymbol -> listOfNotNull(fir.expandedConeType) is ConeCapturedTypeConstructor -> supertypes!! @@ -263,7 +269,7 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext { } override fun captureFromArguments(type: SimpleTypeMarker, status: CaptureStatus): SimpleTypeMarker? { - require(type is ConeLookupTagBasedType) { ":( $type" } + require(type is ConeKotlinType) val typeConstructor = type.typeConstructor() if (type.argumentsCount() != typeConstructor.parametersCount()) return null if (type.asArgumentList().all(this) { !it.isStarProjection() && it.getVariance() == TypeVariance.INV }) return null @@ -314,7 +320,6 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext { } override fun TypeConstructorMarker.isAnyConstructor(): Boolean { - assert(this is ConeSymbol) return this is ConeClassLikeSymbol && classId.asString() == "kotlin/Any" } @@ -332,36 +337,39 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext { override fun SimpleTypeMarker.isSingleClassifierType(): Boolean { if (isError()) return false if (this is ConeCapturedType) return true + if (this is ConeTypeVariableType) return false require(this is ConeLookupTagBasedType) val symbol = this.lookupTag.toSymbol(session) return symbol is FirClassSymbol || symbol is FirTypeParameterSymbol } - override fun intersectTypes(types: List): KotlinTypeMarker { - TODO("not implemented") - } - - override fun intersectTypes(types: List): SimpleTypeMarker { - TODO("not implemented") - } - - override fun prepareType(type: KotlinTypeMarker): KotlinTypeMarker { - TODO("not implemented") - } - - override fun SimpleTypeMarker.isStubType(): Boolean { - TODO("not implemented") - } - override fun captureFromExpression(type: KotlinTypeMarker): KotlinTypeMarker? { TODO("not implemented") } override fun SimpleTypeMarker.isPrimitiveType(): Boolean { - TODO("not implemented") + return false //TODO } + override fun SimpleTypeMarker.isStubType(): Boolean { + return false // TODO + } + + override fun intersectTypes(types: List): SimpleTypeMarker { + return types.first() // TODO: proper implementation + } + + override fun intersectTypes(types: List): KotlinTypeMarker { + return types.first() // TODO: proper implementation + } + + override fun prepareType(type: KotlinTypeMarker): KotlinTypeMarker { + return when (type) { + is ConeAbbreviatedType -> prepareType(type.directExpansionType(session) ?: ConeClassErrorType("unresolved")) + else -> type + } + } } class ConeTypeCheckerContext(override val isErrorTypeEqualsToAnything: Boolean, override val session: FirSession) : @@ -379,16 +387,11 @@ class ConeTypeCheckerContext(override val isErrorTypeEqualsToAnything: Boolean, return a == b } - override fun intersectTypes(types: List): KotlinTypeMarker { - return types.first() // TODO: proper implementation + override fun prepareType(type: KotlinTypeMarker): KotlinTypeMarker { + return super.prepareType(type) } override val KotlinTypeMarker.isAllowedTypeVariable: Boolean get() = false - - override fun prepareType(type: KotlinTypeMarker): KotlinTypeMarker { - return super.prepareType(type) - } - } diff --git a/compiler/fir/resolve/testData/resolve/expresssions/access.txt b/compiler/fir/resolve/testData/resolve/expresssions/access.txt index 5f07f209f7f..599d8868a52 100644 --- a/compiler/fir/resolve/testData/resolve/expresssions/access.txt +++ b/compiler/fir/resolve/testData/resolve/expresssions/access.txt @@ -36,8 +36,8 @@ FILE: access.kt ^plus String() } - public final fun R|Foo|.check(): { - ^check R|/Foo.abc|().#(R|/Bar.bar|()) + public final fun R|Foo|.check(): R|kotlin/String| { + ^check R|/Foo.abc|().R|/Bar.plus|(R|/Bar.bar|()) } } diff --git a/compiler/fir/resolve/testData/resolve/expresssions/checkArguments.kt b/compiler/fir/resolve/testData/resolve/expresssions/checkArguments.kt new file mode 100644 index 00000000000..862f3f6f243 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/checkArguments.kt @@ -0,0 +1,16 @@ +class A +open class B +class C : B + + +fun bar(a: A) = a +fun bar(b: B) = b + +fun foo() { + val a = A() + val b = B() + val c = C() + val ra = bar(a) + val rb = bar(b) + val rc = bar(c) +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/expresssions/checkArguments.txt b/compiler/fir/resolve/testData/resolve/expresssions/checkArguments.txt new file mode 100644 index 00000000000..1f8aa19024b --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/checkArguments.txt @@ -0,0 +1,27 @@ +FILE: checkArguments.kt + public final class A : R|kotlin/Any| { + public constructor(): super() + + } + public open class B : R|kotlin/Any| { + public constructor(): super() + + } + public final class C : R|B|, R|kotlin/Any| { + public constructor(): super() + + } + public final fun bar(a: R|A|): R|A| { + ^bar R|/a| + } + public final fun bar(b: R|B|): R|B| { + ^bar R|/b| + } + public final fun foo(): R|kotlin/Unit| { + lval a: R|A| = R|/A.A|() + lval b: R|B| = R|/B.B|() + lval c: R|C| = R|/C.C|() + lval ra: R|A| = R|/bar|(R|/a|) + lval rb: R|B| = R|/bar|(R|/b|) + lval rc: R|B| = R|/bar|(R|/c|) + } diff --git a/compiler/fir/resolve/testData/resolve/expresssions/inference/id.kt b/compiler/fir/resolve/testData/resolve/expresssions/inference/id.kt new file mode 100644 index 00000000000..5a54aba8bd7 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/inference/id.kt @@ -0,0 +1,8 @@ +fun id(t: T) = t + + +fun main() { + val a = id("string") + val b = id(null) + val c = id(id(a)) +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/expresssions/inference/id.txt b/compiler/fir/resolve/testData/resolve/expresssions/inference/id.txt new file mode 100644 index 00000000000..b1f30fb1096 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/inference/id.txt @@ -0,0 +1,9 @@ +FILE: id.kt + public final fun id(t: R|T|): R|T| { + ^id R|/t| + } + public final fun main(): R|kotlin/Unit| { + lval a: R|kotlin/String| = R|/id|(String(string)) + lval b: R|kotlin/Nothing|? = R|/id|(Null(null)) + lval c: R|kotlin/String| = R|/id|(R|/id|(R|/a|)) + } diff --git a/compiler/fir/resolve/testData/resolve/expresssions/inference/typeParameters.kt b/compiler/fir/resolve/testData/resolve/expresssions/inference/typeParameters.kt new file mode 100644 index 00000000000..d41cc7c59f6 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/inference/typeParameters.kt @@ -0,0 +1,11 @@ +interface Foo +class FooImpl : Foo +class Bar + +fun foo(t: T) = t + + +fun main(fooImpl: FooImpl, bar: Bar) { + val a = foo(fooImpl) + val b = foo(bar) +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/expresssions/inference/typeParameters.txt b/compiler/fir/resolve/testData/resolve/expresssions/inference/typeParameters.txt new file mode 100644 index 00000000000..aeec9b3958a --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/inference/typeParameters.txt @@ -0,0 +1,22 @@ +FILE: typeParameters.kt + public abstract interface Foo : R|kotlin/Any| { + } + public final class FooImpl : R|Foo| { + public constructor(): R|FooImpl| { + super() + } + + } + public final class Bar : R|kotlin/Any| { + public constructor(): R|Bar| { + super() + } + + } + public final fun foo(t: R|T|): R|T| { + ^foo R|/t| + } + public final fun main(fooImpl: R|FooImpl|, bar: R|Bar|): R|kotlin/Unit| { + lval a: R|FooImpl| = R|/foo|(R|/fooImpl|) + lval b: = #(R|/bar|) + } diff --git a/compiler/fir/resolve/testData/resolve/expresssions/inference/typeParameters2.kt b/compiler/fir/resolve/testData/resolve/expresssions/inference/typeParameters2.kt new file mode 100644 index 00000000000..fe9dbcb1be5 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/inference/typeParameters2.kt @@ -0,0 +1,11 @@ +interface Foo +class FooImpl : Foo +class FooBarImpl : Foo + +fun foo(t: T) = t + + +fun main(fooImpl: FooImpl, fooBarImpl: FooBarImpl) { + val a = foo(fooBarImpl) + val b = foo(fooImpl) +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/expresssions/inference/typeParameters2.txt b/compiler/fir/resolve/testData/resolve/expresssions/inference/typeParameters2.txt new file mode 100644 index 00000000000..db7289b97f7 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/inference/typeParameters2.txt @@ -0,0 +1,22 @@ +FILE: typeParameters2.kt + public abstract interface Foo : R|kotlin/Any| { + } + public final class FooImpl : R|Foo| { + public constructor(): R|FooImpl| { + super() + } + + } + public final class FooBarImpl : R|Foo| { + public constructor(): R|FooBarImpl| { + super() + } + + } + public final fun foo(t: R|T|): R|T| { + ^foo R|/t| + } + public final fun main(fooImpl: R|FooImpl|, fooBarImpl: R|FooBarImpl|): R|kotlin/Unit| { + lval a: = #(R|/fooBarImpl|) + lval b: R|Foo| = R|/foo|(R|/fooImpl|) + } diff --git a/compiler/fir/resolve/testData/resolve/fromBuilder/enums.txt b/compiler/fir/resolve/testData/resolve/fromBuilder/enums.txt index 365ea4db8b1..0c2b61c5176 100644 --- a/compiler/fir/resolve/testData/resolve/fromBuilder/enums.txt +++ b/compiler/fir/resolve/testData/resolve/fromBuilder/enums.txt @@ -70,7 +70,7 @@ FILE: enums.kt } - public final val g: R|kotlin/Double| = #.#(R|/Planet.m|).#(R|/Planet.r|.#(R|/Planet.r|)) + public final val g: R|kotlin/Double| = #.#(R|/Planet.m|).#(R|/Planet.r|.R|kotlin/Double.times|(R|/Planet.r|)) public get(): R|kotlin/Double| public abstract fun sayHello(): R|kotlin/Unit| diff --git a/compiler/fir/resolve/testData/resolve/stdlib/helloWorld.kt b/compiler/fir/resolve/testData/resolve/stdlib/helloWorld.kt new file mode 100644 index 00000000000..e95707aa981 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/stdlib/helloWorld.kt @@ -0,0 +1,3 @@ +fun main() { + println("Hello, world!") +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/stdlib/helloWorld.txt b/compiler/fir/resolve/testData/resolve/stdlib/helloWorld.txt new file mode 100644 index 00000000000..325260a7341 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/stdlib/helloWorld.txt @@ -0,0 +1,4 @@ +FILE: helloWorld.kt + public final fun main(): R|kotlin/Unit| { + R|kotlin/io/println|(String(Hello, world!)) + } 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 d2c22239f2e..9a84994fb23 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java @@ -159,6 +159,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/fir/resolve/testData/resolve/expresssions"), Pattern.compile("^([^.]+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("checkArguments.kt") + public void testCheckArguments() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/expresssions/checkArguments.kt"); + } + @TestMetadata("constructor.kt") public void testConstructor() throws Exception { runTest("compiler/fir/resolve/testData/resolve/expresssions/constructor.kt"); @@ -189,6 +194,36 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase { runTest("compiler/fir/resolve/testData/resolve/expresssions/when.kt"); } + @TestMetadata("compiler/fir/resolve/testData/resolve/expresssions/inference") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Inference extends AbstractFirResolveTestCase { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInInference() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), + new File("compiler/fir/resolve/testData/resolve/expresssions/inference"), + Pattern.compile("^([^.]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("id.kt") + public void testId() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/expresssions/inference/id.kt"); + } + + @TestMetadata("typeParameters.kt") + public void testTypeParameters() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/expresssions/inference/typeParameters.kt"); + } + + @TestMetadata("typeParameters2.kt") + public void testTypeParameters2() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/expresssions/inference/typeParameters2.kt"); + } + } + @TestMetadata("compiler/fir/resolve/testData/resolve/expresssions/invoke") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseWithStdlibGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseWithStdlibGenerated.java index 0f4b83aefd8..8893b6983b4 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseWithStdlibGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseWithStdlibGenerated.java @@ -39,6 +39,11 @@ public class FirResolveTestCaseWithStdlibGenerated extends AbstractFirResolveTes runTest("compiler/fir/resolve/testData/resolve/stdlib/functionX.kt"); } + @TestMetadata("helloWorld.kt") + public void testHelloWorld() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/stdlib/helloWorld.kt"); + } + @TestMetadata("reflectionClass.kt") public void testReflectionClass() throws Exception { runTest("compiler/fir/resolve/testData/resolve/stdlib/reflectionClass.kt"); diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt index 9833710de8b..5c341663853 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt @@ -34,6 +34,8 @@ fun FirElement.render(): String = buildString { this@render.accept(FirRenderer(t fun ConeKotlinType.render(): String { return when (this) { + is ConeTypeVariableType -> "TypeVariable(${this.lookupTag.name})" + is ConeDefinitelyNotNullType -> "${original.render()}!" is ConeKotlinErrorType -> "error: $reason" is ConeClassErrorType -> "class error: $reason" is ConeCapturedType -> "captured type: lowerType = ${lowerType?.render()}" diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/references/FirResolvedCallableReferenceImpl.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/references/FirResolvedCallableReferenceImpl.kt index 7ec08331094..8005732e160 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/references/FirResolvedCallableReferenceImpl.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/references/FirResolvedCallableReferenceImpl.kt @@ -12,7 +12,7 @@ import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol import org.jetbrains.kotlin.name.Name -class FirResolvedCallableReferenceImpl( +open class FirResolvedCallableReferenceImpl( session: FirSession, psi: PsiElement?, override val name: Name, diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/impl/FirTypePlaceholderProjection.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/impl/FirTypePlaceholderProjection.kt new file mode 100644 index 00000000000..c11d90c63f6 --- /dev/null +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/impl/FirTypePlaceholderProjection.kt @@ -0,0 +1,17 @@ +/* + * 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.impl + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.types.FirTypeProjection + +object FirTypePlaceholderProjection : FirTypeProjection { + override val psi: PsiElement? + get() = null // TODO + override val session: FirSession + get() = error("ahahahhahah") +} \ No newline at end of file