diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index c563c092b8d..7a6d03697aa 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -63,6 +63,10 @@ public interface Errors { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// DiagnosticFactory1 UNSUPPORTED = DiagnosticFactory1.create(ERROR); + + DiagnosticFactory1 NEW_INFERENCE_ERROR = DiagnosticFactory1.create(ERROR); + DiagnosticFactory1 NEW_INFERENCE_DIAGNOSTIC = DiagnosticFactory1.create(WARNING); + DiagnosticFactory1> UNSUPPORTED_FEATURE = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 EXCEPTION_FROM_ANALYZER = DiagnosticFactory1.create(ERROR); @@ -586,6 +590,7 @@ public interface Errors { DiagnosticFactory0 VARARG_OUTSIDE_PARENTHESES = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 NON_VARARG_SPREAD = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 SPREAD_OF_NULLABLE = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 SPREAD_OF_LAMBDA_OR_CALLABLE_REFERENCE = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 MANY_LAMBDA_EXPRESSION_ARGUMENTS = DiagnosticFactory0.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 20a10fbd340..25770fd05da 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -202,6 +202,7 @@ public class DefaultErrorMessages { MAP.put(VARARG_OUTSIDE_PARENTHESES, "Passing value as a vararg is only allowed inside a parenthesized argument list"); MAP.put(NON_VARARG_SPREAD, "The spread operator (*foo) may only be applied in a vararg position"); MAP.put(SPREAD_OF_NULLABLE, "The spread operator (*foo) may not be applied to an argument of nullable type"); + MAP.put(SPREAD_OF_LAMBDA_OR_CALLABLE_REFERENCE, "The spread operator (*foo) cannot be applied to lambda argument or callable reference"); MAP.put(MANY_LAMBDA_EXPRESSION_ARGUMENTS, "Only one lambda expression is allowed outside a parenthesized argument list"); MAP.put(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER, "This property must either have a type annotation, be initialized or be delegated"); @@ -573,6 +574,8 @@ public class DefaultErrorMessages { MAP.put(UNSAFE_IMPLICIT_INVOKE_CALL, "Reference has a nullable type ''{0}'', use explicit ''?.invoke()'' to make a function-like call instead", RENDER_TYPE); MAP.put(AMBIGUOUS_LABEL, "Ambiguous label"); MAP.put(UNSUPPORTED, "Unsupported [{0}]", STRING); + MAP.put(NEW_INFERENCE_ERROR, "New inference error [{0}]", STRING); + MAP.put(NEW_INFERENCE_DIAGNOSTIC, "New inference [{0}]", STRING); MAP.put(UNSUPPORTED_FEATURE, "{0}", new LanguageFeatureMessageRenderer(LanguageFeatureMessageRenderer.Type.UNSUPPORTED)); MAP.put(EXPERIMENTAL_FEATURE_WARNING, "{0}", new LanguageFeatureMessageRenderer(LanguageFeatureMessageRenderer.Type.WARNING)); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/frontend/di/injection.kt b/compiler/frontend/src/org/jetbrains/kotlin/frontend/di/injection.kt index ec3286c23f3..8358a2a4986 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/frontend/di/injection.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/frontend/di/injection.kt @@ -28,6 +28,8 @@ import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.* +import org.jetbrains.kotlin.resolve.calls.tower.CommonSupertypeCalculatorImpl +import org.jetbrains.kotlin.resolve.calls.tower.IsDescriptorFromSourcePredicateImpl import org.jetbrains.kotlin.resolve.lazy.* import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory @@ -60,6 +62,8 @@ fun StorageComponentContainer.configureModule( private fun StorageComponentContainer.configurePlatformIndependentComponents() { useImpl() + useInstance(CommonSupertypeCalculatorImpl) + useInstance(IsDescriptorFromSourcePredicateImpl) } fun StorageComponentContainer.configureModule( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java index 014525de733..c9e004806da 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqNameUnsafe; import org.jetbrains.kotlin.psi.*; +import org.jetbrains.kotlin.resolve.calls.model.ResolvedKotlinCall; import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemCompleter; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo; @@ -120,6 +121,7 @@ public interface BindingContext { new BasicWritableSlice<>(DO_NOTHING); WritableSlice> RESOLVED_CALL = new BasicWritableSlice<>(DO_NOTHING); + WritableSlice ONLY_RESOLVED_CALL = new BasicWritableSlice<>(DO_NOTHING); WritableSlice TAIL_RECURSION_CALL = Slices.createSimpleSlice(); WritableSlice CONSTRAINT_SYSTEM_COMPLETER = new BasicWritableSlice<>(DO_NOTHING); WritableSlice CALL = new BasicWritableSlice<>(DO_NOTHING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.kt index 79214422a53..bbff4a0d293 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.kt @@ -281,7 +281,7 @@ class CallExpressionResolver( } else when (resolutionResult.resultCode) { NAME_NOT_FOUND, CANDIDATES_WITH_WRONG_RECEIVER -> false - else -> true + else -> !USE_NEW_INFERENCE || resolutionResult.isSuccess } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java index 535ac53eea6..3a3ab36ef03 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java @@ -35,12 +35,14 @@ import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode; import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; import org.jetbrains.kotlin.resolve.calls.context.*; import org.jetbrains.kotlin.resolve.calls.inference.CoroutineInferenceUtilKt; +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallKind; import org.jetbrains.kotlin.resolve.calls.model.MutableDataFlowInfoForArguments; import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults; import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsImpl; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo; import org.jetbrains.kotlin.resolve.calls.tasks.*; import org.jetbrains.kotlin.resolve.calls.tower.NewResolutionOldInference; +import org.jetbrains.kotlin.resolve.calls.tower.PSICallResolver; import org.jetbrains.kotlin.resolve.calls.util.CallMaker; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil; @@ -70,8 +72,9 @@ public class CallResolver { private ArgumentTypeResolver argumentTypeResolver; private GenericCandidateResolver genericCandidateResolver; private CallCompleter callCompleter; - private NewResolutionOldInference newCallResolver; private SyntheticScopes syntheticScopes; + private NewResolutionOldInference newResolutionOldInference; + private PSICallResolver PSICallResolver; private final KotlinBuiltIns builtIns; private final LanguageVersionSettings languageVersionSettings; @@ -117,8 +120,14 @@ public class CallResolver { // component dependency cycle @Inject - public void setCallCompleter(@NotNull NewResolutionOldInference newCallResolver) { - this.newCallResolver = newCallResolver; + public void setResolutionOldInference(@NotNull NewResolutionOldInference newResolutionOldInference) { + this.newResolutionOldInference = newResolutionOldInference; + } + + // component dependency cycle + @Inject + public void setPSICallResolver(@NotNull PSICallResolver PSICallResolver) { + this.PSICallResolver = PSICallResolver; } @Inject @@ -520,7 +529,7 @@ public class CallResolver { }); } - private OverloadResolutionResultsImpl doResolveCallOrGetCachedResults( + private OverloadResolutionResults doResolveCallOrGetCachedResults( @NotNull BasicCallResolutionContext context, @NotNull ResolutionTask resolutionTask, @NotNull TracingStrategy tracing @@ -528,6 +537,16 @@ public class CallResolver { Call call = context.call; tracing.bindCall(context.trace, call); + if (KotlinResolutionConfigurationKt.getUSE_NEW_INFERENCE() && (resolutionTask.resolutionKind.getKotlinCallKind() != KotlinCallKind.UNSUPPORTED)) { + assert resolutionTask.name != null; + return PSICallResolver.runResolutionAndInference(context, resolutionTask.name, resolutionTask.resolutionKind, tracing); + } + + if (KotlinResolutionConfigurationKt.getUSE_NEW_INFERENCE() && resolutionTask.resolutionKind instanceof NewResolutionOldInference.ResolutionKind.GivenCandidates) { + assert resolutionTask.givenCandidates != null; + return PSICallResolver.runResolutionAndInferenceForGivenCandidates(context, resolutionTask.givenCandidates, tracing); + } + TemporaryBindingTrace traceToResolveCall = TemporaryBindingTrace.create(context.trace, "trace to resolve call", call); BasicCallResolutionContext newContext = context.replaceBindingTrace(traceToResolveCall); @@ -624,11 +643,11 @@ public class CallResolver { if (!(resolutionTask.resolutionKind instanceof NewResolutionOldInference.ResolutionKind.GivenCandidates)) { assert resolutionTask.name != null; - return newCallResolver.runResolution(context, resolutionTask.name, resolutionTask.resolutionKind, tracing); + return newResolutionOldInference.runResolution(context, resolutionTask.name, resolutionTask.resolutionKind, tracing); } else { assert resolutionTask.givenCandidates != null; - return newCallResolver.runResolutionForGivenCandidates(context, tracing, resolutionTask.givenCandidates); + return newResolutionOldInference.runResolutionForGivenCandidates(context, tracing, resolutionTask.givenCandidates); } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt new file mode 100644 index 00000000000..3b9b33fabea --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt @@ -0,0 +1,155 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls + +import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.diagnostics.Errors.* +import org.jetbrains.kotlin.diagnostics.Errors.BadNamedArgumentsTarget.* +import org.jetbrains.kotlin.psi.Call +import org.jetbrains.kotlin.psi.KtPsiUtil +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.calls.components.* +import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext +import org.jetbrains.kotlin.resolve.calls.inference.model.* +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory +import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager +import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy +import org.jetbrains.kotlin.resolve.calls.tower.* +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver + +class DiagnosticReporterByTrackingStrategy( + val context: BasicCallResolutionContext, + val trace: BindingTrace, + val psiKotlinCall: PSIKotlinCall +): DiagnosticReporter { + private val tracingStrategy: TracingStrategy get() = psiKotlinCall.tracingStrategy + private val call: Call get() = psiKotlinCall.psiCall + + override fun onExplicitReceiver(diagnostic: KotlinCallDiagnostic) { + + } + + override fun onCall(diagnostic: KotlinCallDiagnostic) { + when (diagnostic.javaClass) { + VisibilityError::class.java -> tracingStrategy.invisibleMember(trace, (diagnostic as VisibilityError).invisibleMember) + NoValueForParameter::class.java -> tracingStrategy.noValueForParameter(trace, (diagnostic as NoValueForParameter).parameterDescriptor) + } + } + + override fun onTypeArguments(diagnostic: KotlinCallDiagnostic) { + + } + + override fun onCallName(diagnostic: KotlinCallDiagnostic) { + + } + + override fun onTypeArgument(typeArgument: TypeArgument, diagnostic: KotlinCallDiagnostic) { + + } + + override fun onCallReceiver(callReceiver: SimpleKotlinCallArgument, diagnostic: KotlinCallDiagnostic) { + when (diagnostic.javaClass) { + UnsafeCallError::class.java -> { + val implicitInvokeCheck = (callReceiver as? ReceiverExpressionKotlinCallArgument)?.isVariableReceiverForInvoke ?: false + tracingStrategy.unsafeCall(trace, callReceiver.receiver.receiverValue.type, implicitInvokeCheck) + } + } + } + + override fun onCallArgument(callArgument: KotlinCallArgument, diagnostic: KotlinCallDiagnostic) { + when (diagnostic.javaClass) { + SmartCastDiagnostic::class.java -> reportSmartCast(diagnostic as SmartCastDiagnostic) + UnstableSmartCast::class.java -> reportUnstableSmartCast(diagnostic as UnstableSmartCast) + TooManyArguments::class.java -> + trace.report(TOO_MANY_ARGUMENTS.on(callArgument.psiExpression!!, (diagnostic as TooManyArguments).descriptor)) + VarargArgumentOutsideParentheses::class.java -> + trace.report(VARARG_OUTSIDE_PARENTHESES.on(callArgument.psiExpression!!)) + } + } + + override fun onCallArgumentName(callArgument: KotlinCallArgument, diagnostic: KotlinCallDiagnostic) { + val nameReference = callArgument.psiCallArgument.valueArgument.getArgumentName()?.referenceExpression ?: + error("Argument name should be not null for argument: $callArgument") + when (diagnostic.javaClass) { + NamedArgumentReference::class.java -> + trace.record(BindingContext.REFERENCE_TARGET, nameReference, (diagnostic as NamedArgumentReference).parameterDescriptor) + NameForAmbiguousParameter::class.java -> trace.report(NAME_FOR_AMBIGUOUS_PARAMETER.on(nameReference)) + NameNotFound::class.java -> trace.report(NAMED_PARAMETER_NOT_FOUND.on(nameReference, nameReference)) + + NamedArgumentNotAllowed::class.java -> trace.report(NAMED_ARGUMENTS_NOT_ALLOWED.on( + nameReference, + if ((diagnostic as NamedArgumentNotAllowed).descriptor is FunctionInvokeDescriptor) INVOKE_ON_FUNCTION_TYPE else NON_KOTLIN_FUNCTION + )) + ArgumentPassedTwice::class.java -> trace.report(ARGUMENT_PASSED_TWICE.on(nameReference)) + } + } + + override fun onCallArgumentSpread(callArgument: KotlinCallArgument, diagnostic: KotlinCallDiagnostic) { + + } + + private fun reportSmartCast(smartCastDiagnostic: SmartCastDiagnostic) { + val expressionArgument = smartCastDiagnostic.expressionArgument + if (expressionArgument is ExpressionKotlinCallArgumentImpl) { + val context = context.replaceDataFlowInfo(expressionArgument.dataFlowInfoBeforeThisArgument) + val argumentExpression = KtPsiUtil.getLastElementDeparenthesized(expressionArgument.valueArgument.getArgumentExpression (), context.statementFilter) + val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expressionArgument.receiver.receiverValue, context) + SmartCastManager.checkAndRecordPossibleCast( + dataFlowValue, smartCastDiagnostic.smartCastType, argumentExpression, context, call, + recordExpressionType = true) + } + else if(expressionArgument is ReceiverExpressionKotlinCallArgument) { + val receiverValue = expressionArgument.receiver.receiverValue + val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverValue, context) + SmartCastManager.checkAndRecordPossibleCast( + dataFlowValue, smartCastDiagnostic.smartCastType, (receiverValue as? ExpressionReceiver)?.expression, context, call, + recordExpressionType = true) + } + } + + private fun reportUnstableSmartCast(unstableSmartCast: UnstableSmartCast) { + // todo hack -- remove it after removing SmartCastManager + reportSmartCast(SmartCastDiagnostic(unstableSmartCast.expressionArgument, unstableSmartCast.targetType)) + } + + override fun constraintError(diagnostic: KotlinCallDiagnostic) { + when (diagnostic.javaClass) { + NewConstraintError::class.java -> { + val constraintError = diagnostic as NewConstraintError + (constraintError.position as? ArgumentConstraintPosition)?.let { + val expression = it.argument.psiExpression ?: return + trace.report(Errors.TYPE_MISMATCH.on(expression, constraintError.upperType, constraintError.lowerType)) + } + (constraintError.position as? ExplicitTypeParameterConstraintPosition)?.let { + val typeArgumentReference = (it.typeArgument as SimpleTypeArgumentImpl).typeReference + trace.report(UPPER_BOUND_VIOLATED.on(typeArgumentReference, constraintError.upperType, constraintError.lowerType)) + } + } + CapturedTypeFromSubtyping::class.java -> { + val capturedError = diagnostic as CapturedTypeFromSubtyping + (capturedError.position as? ArgumentConstraintPosition)?.let { + val expression = it.argument.psiExpression ?: return + trace.report(NEW_INFERENCE_ERROR.on(expression, "Capture type from subtyping ${capturedError.constraintType} for variable ${capturedError.typeVariable}")) + } + } + } + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterImpl.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterImpl.kt new file mode 100644 index 00000000000..f03129fac64 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterImpl.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.diagnostics.* +import org.jetbrains.kotlin.psi.Call +import org.jetbrains.kotlin.psi.ValueArgument +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability +import org.jetbrains.kotlin.types.KotlinType +import java.util.* + +// this file is example for future use + + +object CallDiagnosticToDiagnostic { + private val diagnosticMap: MutableMap, KotlinCallDiagnostic.(PsiElement) -> ParametrizedDiagnostic<*>> = HashMap() + + private fun checkPut(klass: Class, factory: C.(PsiElement) -> ParametrizedDiagnostic?) { + @Suppress("UNCHECKED_CAST") + diagnosticMap.put(klass, factory as KotlinCallDiagnostic.(PsiElement) -> ParametrizedDiagnostic<*>) + } + + private inline fun put(factory0: DiagnosticFactory0, klass: Class) { + checkPut(klass) { + (it as? E)?.let { factory0.on(it) } + } + } + + private inline fun put(factory1: DiagnosticFactory1, klass: Class, crossinline getA: C.() -> A) { + checkPut(klass) { + (it as? E)?.let { factory1.on(it, getA()) } + } + } + + private inline fun put( + factory2: DiagnosticFactory2, klass: Class, crossinline getA: C.() -> A, crossinline getB: C.() -> B) { + checkPut(klass) { + (it as? E)?.let { factory2.on(it, getA(), getB()) } + } + } + + init { +// put(Errors.UNSAFE_CALL, UnsafeCallDiagnostic::class.java, UnsafeCallDiagnostic::receiverType) + put(Errors.TYPE_MISMATCH, TypeMismatchDiagnostic::class.java, TypeMismatchDiagnostic::expectedType, TypeMismatchDiagnostic::actualType) + } + + + // null means, that E is not subtype of required type for diagnostic factory + fun toDiagnostic(element: E, diagnostic: KotlinCallDiagnostic): ParametrizedDiagnostic? { + val diagnosticClass = diagnostic.javaClass + val factory = diagnosticMap[diagnosticClass] ?: error("Illegal call diagnostic class: ${diagnosticClass.canonicalName}") + + @Suppress("UNCHECKED_CAST") + return factory(diagnostic, element) as ParametrizedDiagnostic? + } + +} + +abstract class DiagnosticReporterImpl(private val bindingTrace: BindingTrace, private val call: Call) : DiagnosticReporter { + + override fun onCallArgument(callArgument: KotlinCallArgument, diagnostic: KotlinCallDiagnostic) { + val d = CallDiagnosticToDiagnostic.toDiagnostic((callArgument as ValueArgument).asElement(), diagnostic) + if (d != null) { + bindingTrace.report(d) + } + } + +} + +class TypeMismatchDiagnostic( + val callArgument: KotlinCallArgument, + val expectedType: KotlinType, + val actualType: KotlinType +) : KotlinCallDiagnostic(ResolutionCandidateApplicability.INAPPLICABLE) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(callArgument, this) +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/NewOverloadResolutionResults.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/NewOverloadResolutionResults.kt new file mode 100644 index 00000000000..2ebe3c484f9 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/NewOverloadResolutionResults.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.results + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults.Code + +abstract class AbstractOverloadResolutionResults : OverloadResolutionResults { + override fun isSuccess() = resultCode.isSuccess + override fun isSingleResult() = resultingCalls.size == 1 && resultCode != OverloadResolutionResults.Code.CANDIDATES_WITH_WRONG_RECEIVER + override fun isNothing() = resultCode == OverloadResolutionResults.Code.NAME_NOT_FOUND + override fun isAmbiguity() = resultCode == OverloadResolutionResults.Code.AMBIGUITY + override fun isIncomplete() = resultCode == OverloadResolutionResults.Code.INCOMPLETE_TYPE_INFERENCE +} + +class SingleOverloadResolutionResult(val result: ResolvedCall) : AbstractOverloadResolutionResults() { + override fun getAllCandidates(): Collection>? = null + override fun getResultingCalls(): Collection> = listOf(result) + override fun getResultingCall() = result + + override fun getResultingDescriptor(): D = result.resultingDescriptor + + override fun getResultCode(): Code = when (result.status) { + ResolutionStatus.SUCCESS -> Code.SUCCESS + ResolutionStatus.RECEIVER_TYPE_ERROR -> Code.CANDIDATES_WITH_WRONG_RECEIVER + ResolutionStatus.INCOMPLETE_TYPE_INFERENCE -> Code.INCOMPLETE_TYPE_INFERENCE + else -> Code.SINGLE_CANDIDATE_ARGUMENT_MISMATCH + } +} + +open class NameNotFoundResolutionResult : AbstractOverloadResolutionResults() { + override fun getAllCandidates(): Collection>? = null + override fun getResultingCalls(): Collection> = emptyList() + override fun getResultingCall() = error("No candidates") + override fun getResultingDescriptor() = error("No candidates") + override fun getResultCode() = Code.NAME_NOT_FOUND +} + +class ManyCandidates( + val candidates: Collection> +) : AbstractOverloadResolutionResults() { + override fun getAllCandidates(): Collection>? = null + override fun getResultingCalls(): Collection> = candidates + override fun getResultingCall() = error("Many candidates") + override fun getResultingDescriptor() = error("Many candidates") + override fun getResultCode() = + when(candidates.first().status) { + ResolutionStatus.RECEIVER_TYPE_ERROR -> Code.CANDIDATES_WITH_WRONG_RECEIVER + ResolutionStatus.SUCCESS -> Code.AMBIGUITY + ResolutionStatus.INCOMPLETE_TYPE_INFERENCE -> Code.INCOMPLETE_TYPE_INFERENCE + else -> Code.MANY_FAILED_CANDIDATES + } +} + + + +class AllCandidates(private val allCandidates: Collection>): NameNotFoundResolutionResult() { + override fun getAllCandidates() = allCandidates +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/OverloadResolutionResultsUtil.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/OverloadResolutionResultsUtil.java index 79d8f74f1e3..e64bf0d49a8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/OverloadResolutionResultsUtil.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/OverloadResolutionResultsUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,9 +20,11 @@ import com.google.common.collect.Lists; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.CallableDescriptor; +import org.jetbrains.kotlin.resolve.calls.KotlinResolutionConfigurationKt; import org.jetbrains.kotlin.resolve.calls.context.ContextDependency; import org.jetbrains.kotlin.resolve.calls.model.MutableResolvedCall; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; +import org.jetbrains.kotlin.resolve.calls.tower.StubOnlyResolvedCall; import org.jetbrains.kotlin.types.KotlinType; import java.util.Collection; @@ -52,8 +54,15 @@ public class OverloadResolutionResultsUtil { ) { if (results.isSingleResult() && contextDependency == ContextDependency.INDEPENDENT) { ResolvedCall resultingCall = results.getResultingCall(); - if (!((MutableResolvedCall)resultingCall).hasInferredReturnType()) { - return null; + if (!KotlinResolutionConfigurationKt.getUSE_NEW_INFERENCE()) { + if (!((MutableResolvedCall) resultingCall).hasInferredReturnType()) { + return null; + } + } + else { + if (resultingCall instanceof StubOnlyResolvedCall) { + return null; + } } } return results.isSingleResult() ? results.getResultingCall() : null; diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt new file mode 100644 index 00000000000..bc3b912c9bc --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt @@ -0,0 +1,347 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.tower + +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.psi.Call +import org.jetbrains.kotlin.psi.KtPsiUtil +import org.jetbrains.kotlin.psi.ValueArgument +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.calls.* +import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getEffectiveExpectedType +import org.jetbrains.kotlin.resolve.calls.callUtil.isFakeElement +import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker +import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext +import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext +import org.jetbrains.kotlin.resolve.calls.context.CallPosition +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo +import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.expressions.DataFlowAnalyzer +import java.util.* +import kotlin.collections.HashMap + + +class KotlinToResolvedCallTransformer( + private val callCheckers: Iterable, + private val languageFeatureSettings: LanguageVersionSettings, + private val dataFlowAnalyzer: DataFlowAnalyzer, + private val argumentTypeResolver: ArgumentTypeResolver +) { + + fun transformAndReport( + baseResolvedCall: ResolvedKotlinCall, + context: BasicCallResolutionContext, + trace: BindingTrace? // if trace is not null then all information will be reported to this trace + ): ResolvedCall { + if (baseResolvedCall is ResolvedKotlinCall.CompletedResolvedKotlinCall) { + baseResolvedCall.allInnerCalls.forEach { transformAndReportCompletedCall(it, context, trace) } + return transformAndReportCompletedCall(baseResolvedCall.completedCall, context, trace) + } + + val onlyResolvedCall = (baseResolvedCall as ResolvedKotlinCall.OnlyResolvedKotlinCall) + trace?.record(BindingContext.ONLY_RESOLVED_CALL, onlyResolvedCall.candidate.kotlinCall.psiKotlinCall.psiCall, onlyResolvedCall) + + return StubOnlyResolvedCall(onlyResolvedCall.candidate.lastCall) + } + + private fun transformAndReportCompletedCall( + completedCall: CompletedKotlinCall, + context: BasicCallResolutionContext, + trace: BindingTrace? + ): ResolvedCall { + fun C.runIfTraceNotNull(action: (BasicCallResolutionContext, BindingTrace, C) -> Unit): C { + if (trace != null) action(context, trace, this) + return this + } + + val resolvedCall = when (completedCall) { + is CompletedKotlinCall.Simple -> { + NewResolvedCallImpl(completedCall).runIfTraceNotNull(this::bindResolvedCall).runIfTraceNotNull(this::runArgumentsChecks) + } + is CompletedKotlinCall.VariableAsFunction -> { + val resolvedCall = NewVariableAsFunctionResolvedCallImpl( + completedCall, + NewResolvedCallImpl(completedCall.variableCall), + NewResolvedCallImpl(completedCall.invokeCall).runIfTraceNotNull(this::runArgumentsChecks) + ).runIfTraceNotNull(this::bindResolvedCall) + + @Suppress("UNCHECKED_CAST") + (resolvedCall as ResolvedCall) + } + } + runCallCheckers(resolvedCall, context) + + return resolvedCall + } + + private fun runCallCheckers(resolvedCall: ResolvedCall<*>, context: BasicCallResolutionContext) { + val calleeExpression = if (resolvedCall is VariableAsFunctionResolvedCall) + resolvedCall.variableCall.call.calleeExpression + else + resolvedCall.call.calleeExpression + val reportOn = + if (calleeExpression != null && !calleeExpression.isFakeElement) calleeExpression + else resolvedCall.call.callElement + + val callCheckerContext = CallCheckerContext(context, languageFeatureSettings) + for (callChecker in callCheckers) { + callChecker.check(resolvedCall, reportOn, callCheckerContext) + } + } + + + // todo very beginning code + private fun runArgumentsChecks( + context: BasicCallResolutionContext, + trace: BindingTrace, + resolvedCall: NewResolvedCallImpl<*> + ) { + + for (valueArgument in resolvedCall.call.valueArguments) { + val argumentMapping = resolvedCall.getArgumentMapping(valueArgument!!) + val (expectedType, callPosition) = when (argumentMapping) { + is ArgumentMatch -> Pair( + getEffectiveExpectedType(argumentMapping.valueParameter, valueArgument), + CallPosition.ValueArgumentPosition(resolvedCall, argumentMapping.valueParameter, valueArgument)) + else -> Pair(TypeUtils.NO_EXPECTED_TYPE, CallPosition.Unknown) + } + val newContext = + context.replaceDataFlowInfo(resolvedCall.dataFlowInfoForArguments.getInfo(valueArgument)) + .replaceExpectedType(expectedType) + .replaceCallPosition(callPosition) + .replaceBindingTrace(trace) + + // todo +// if (valueArgument.isExternal()) continue + + val deparenthesized = valueArgument.getArgumentExpression()?.let { + KtPsiUtil.getLastElementDeparenthesized(it, context.statementFilter) + } ?: continue + + var recordedType = context.trace.getType(deparenthesized) + + // For the cases like 'foo(1)' the type of '1' depends on expected type (it can be Int, Byte, etc.), + // so while the expected type is not known, it's IntegerValueType(1), and should be updated when the expected type is known. + if (recordedType != null && !recordedType.constructor.isDenotable) { + recordedType = argumentTypeResolver.updateResultArgumentTypeIfNotDenotable(newContext, deparenthesized) ?: recordedType + } + +// dataFlowAnalyzer.checkType(recordedType, deparenthesized, newContext) + } + + } + + private fun bindResolvedCall(context: BasicCallResolutionContext, trace: BindingTrace, simpleResolvedCall: NewResolvedCallImpl<*>) { + reportCallDiagnostic(context, trace, simpleResolvedCall.completedCall) + val tracing = simpleResolvedCall.completedCall.kotlinCall.psiKotlinCall.tracingStrategy + + tracing.bindReference(trace, simpleResolvedCall) + tracing.bindResolvedCall(trace, simpleResolvedCall) + } + + private fun bindResolvedCall(context: BasicCallResolutionContext, trace: BindingTrace, variableAsFunction: NewVariableAsFunctionResolvedCallImpl) { + reportCallDiagnostic(context, trace, variableAsFunction.variableCall.completedCall) + reportCallDiagnostic(context, trace, variableAsFunction.functionCall.completedCall) + + val outerTracingStrategy = variableAsFunction.completedCall.kotlinCall.psiKotlinCall.tracingStrategy + outerTracingStrategy.bindReference(trace, variableAsFunction.variableCall) + outerTracingStrategy.bindResolvedCall(trace, variableAsFunction) + variableAsFunction.functionCall.kotlinCall.psiKotlinCall.tracingStrategy.bindReference(trace, variableAsFunction.functionCall) + } + + private fun reportCallDiagnostic( + context: BasicCallResolutionContext, + trace: BindingTrace, + completedCall: CompletedKotlinCall.Simple + ) { + var reported: Boolean + val reportTrackedTrace = object : BindingTrace by trace { + override fun report(diagnostic: Diagnostic) { + trace.report(diagnostic) + reported = true + } + } + val diagnosticReporter = DiagnosticReporterByTrackingStrategy(context, reportTrackedTrace, completedCall.kotlinCall.psiKotlinCall) + + for (diagnostic in completedCall.resolutionStatus.diagnostics) { + reported = false + diagnostic.report(diagnosticReporter) + if (!reported && REPORT_MISSING_NEW_INFERENCE_DIAGNOSTIC) { + if (diagnostic.candidateApplicability.isSuccess) { + trace.report(Errors.NEW_INFERENCE_DIAGNOSTIC.on(diagnosticReporter.psiKotlinCall.psiCall.callElement, "Missing diagnostic: $diagnostic")) + } + else { + trace.report(Errors.NEW_INFERENCE_ERROR.on(diagnosticReporter.psiKotlinCall.psiCall.callElement, "Missing diagnostic: $diagnostic")) + } + } + } + } +} + +sealed class NewAbstractResolvedCall(): ResolvedCall { + abstract val argumentMappingByOriginal: Map + abstract val kotlinCall: KotlinCall + + private var argumentToParameterMap: Map? = null + private val _valueArguments: Map by lazy(this::createValueArguments) + + override fun getCall(): Call = kotlinCall.psiKotlinCall.psiCall + + override fun getValueArguments(): Map = _valueArguments + + override fun getValueArgumentsByIndex(): List? { + val arguments = ArrayList(candidateDescriptor.valueParameters.size) + for (i in 0..candidateDescriptor.valueParameters.size - 1) { + arguments.add(null) + } + + for ((parameterDescriptor, value) in valueArguments) { + val oldValue = arguments.set(parameterDescriptor.index, value) + if (oldValue != null) { + return null + } + } + + if (arguments.any { it == null }) return null + + @Suppress("UNCHECKED_CAST") + return arguments as List + } + + override fun getArgumentMapping(valueArgument: ValueArgument): ArgumentMapping { + if (argumentToParameterMap == null) { + argumentToParameterMap = argumentToParameterMap(resultingDescriptor, valueArguments) + } + val argumentMatch = argumentToParameterMap!![valueArgument] ?: return ArgumentUnmapped + return argumentMatch + } + + override fun getDataFlowInfoForArguments() = object : DataFlowInfoForArguments { + override fun getResultInfo() = kotlinCall.psiKotlinCall.resultDataFlowInfo + override fun getInfo(valueArgument: ValueArgument): DataFlowInfo { + val externalPsiCallArgument = kotlinCall.externalArgument?.psiCallArgument + if (externalPsiCallArgument?.valueArgument == valueArgument) { + return externalPsiCallArgument.dataFlowInfoAfterThisArgument + } + kotlinCall.argumentsInParenthesis.find { it.psiCallArgument.valueArgument == valueArgument }?.let { + return it.psiCallArgument.dataFlowInfoAfterThisArgument + } + + // valueArgument is not found + // may be we should return initial DataFlowInfo but I think that it isn't important + return kotlinCall.psiKotlinCall.resultDataFlowInfo + } + } + + private fun argumentToParameterMap( + resultingDescriptor: CallableDescriptor, + valueArguments: Map + ): Map = + HashMap().also { result -> + for (parameter in resultingDescriptor.valueParameters) { + val resolvedArgument = valueArguments[parameter] ?: continue + for (arguments in resolvedArgument.arguments) { + result[arguments] = ArgumentMatchImpl(parameter).apply { recordMatchStatus(ArgumentMatchStatus.SUCCESS) } + } + } + } + + private fun createValueArguments(): Map { + val result = HashMap() + for (parameter in candidateDescriptor.valueParameters) { + val resolvedCallArgument = argumentMappingByOriginal[parameter.original] ?: continue + val valueArgument = when (resolvedCallArgument) { + ResolvedCallArgument.DefaultArgument -> DefaultValueArgument.DEFAULT + is ResolvedCallArgument.SimpleArgument -> ExpressionValueArgument(resolvedCallArgument.callArgument.psiCallArgument.valueArgument) + is ResolvedCallArgument.VarargArgument -> VarargValueArgument().apply { + resolvedCallArgument.arguments.map { it.psiCallArgument.valueArgument }.forEach(this::addArgument) + } + } + result[parameter] = valueArgument + } + + return result + } +} + +class NewResolvedCallImpl( + val completedCall: CompletedKotlinCall.Simple +): NewAbstractResolvedCall() { + override val kotlinCall: KotlinCall get() = completedCall.kotlinCall + + override fun getStatus(): ResolutionStatus = completedCall.resolutionStatus.resultingApplicability.toResolutionStatus() + + override val argumentMappingByOriginal: Map + get() = completedCall.argumentMappingByOriginal + + override fun getCandidateDescriptor(): D = completedCall.candidateDescriptor as D + override fun getResultingDescriptor(): D = completedCall.resultingDescriptor as D + override fun getExtensionReceiver(): ReceiverValue? = completedCall.extensionReceiver?.receiverValue + override fun getDispatchReceiver(): ReceiverValue? = completedCall.dispatchReceiver?.receiverValue + override fun getExplicitReceiverKind(): ExplicitReceiverKind = completedCall.explicitReceiverKind + + override fun getTypeArguments(): Map { + val typeParameters = candidateDescriptor.typeParameters.takeIf { it.isNotEmpty() } ?: return emptyMap() + + val result = HashMap() + for ((parameter, argument) in typeParameters.zip(completedCall.typeArguments)) { + result[parameter] = argument + } + return result + } + + override fun getSmartCastDispatchReceiverType(): KotlinType? = null // todo + + fun ResolutionCandidateApplicability.toResolutionStatus(): ResolutionStatus = when (this) { + ResolutionCandidateApplicability.RESOLVED, ResolutionCandidateApplicability.RESOLVED_LOW_PRIORITY -> ResolutionStatus.SUCCESS + else -> ResolutionStatus.OTHER_ERROR + } +} + +class NewVariableAsFunctionResolvedCallImpl( + val completedCall: CompletedKotlinCall.VariableAsFunction, + override val variableCall: NewResolvedCallImpl, + override val functionCall: NewResolvedCallImpl +): VariableAsFunctionResolvedCall, ResolvedCall by functionCall + +class StubOnlyResolvedCall(val candidate: SimpleKotlinResolutionCandidate): NewAbstractResolvedCall() { + override fun getStatus() = ResolutionStatus.UNKNOWN_STATUS + + override fun getCandidateDescriptor(): D = candidate.candidateDescriptor as D + override fun getResultingDescriptor(): D = candidateDescriptor + override fun getExtensionReceiver() = candidate.extensionReceiver?.receiver?.receiverValue + override fun getDispatchReceiver() = candidate.dispatchReceiverArgument?.receiver?.receiverValue + override fun getExplicitReceiverKind() = candidate.explicitReceiverKind + + override fun getTypeArguments(): Map = emptyMap() + + override fun getSmartCastDispatchReceiverType(): KotlinType? = null + + override val argumentMappingByOriginal: Map + get() = candidate.argumentMappingByOriginal + override val kotlinCall: KotlinCall get() = candidate.kotlinCall +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/LambdaAnalyzerImpl.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/LambdaAnalyzerImpl.kt new file mode 100644 index 00000000000..707ceee3316 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/LambdaAnalyzerImpl.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.tower + +import org.jetbrains.kotlin.builtins.createFunctionType +import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType +import org.jetbrains.kotlin.builtins.isFunctionType +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtPsiUtil +import org.jetbrains.kotlin.psi.psiUtil.lastBlockStatementOrThis +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.calls.components.LambdaAnalyzer +import org.jetbrains.kotlin.types.TypeApproximator +import org.jetbrains.kotlin.types.TypeApproximatorConfiguration +import org.jetbrains.kotlin.resolve.calls.context.ContextDependency +import org.jetbrains.kotlin.resolve.calls.model.KotlinCall +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallArgument +import org.jetbrains.kotlin.resolve.calls.model.LambdaKotlinCallArgument +import org.jetbrains.kotlin.resolve.calls.util.CallMaker +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices +import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo + +class LambdaAnalyzerImpl( + val expressionTypingServices: ExpressionTypingServices, + val trace: BindingTrace, + val typeApproximator: TypeApproximator +): LambdaAnalyzer { + + override fun analyzeAndGetRelatedCalls( + topLevelCall: KotlinCall, + lambdaArgument: LambdaKotlinCallArgument, + receiverType: UnwrappedType?, + parameters: List, + expectedReturnType: UnwrappedType? + ): List { + val psiCallArgument = lambdaArgument.psiCallArgument + val outerCallContext = (psiCallArgument as? LambdaKotlinCallArgumentImpl)?.outerCallContext ?: + (psiCallArgument as FunctionExpressionImpl).outerCallContext + val expression: KtExpression = (psiCallArgument as? LambdaKotlinCallArgumentImpl)?.ktLambdaExpression ?: + (psiCallArgument as FunctionExpressionImpl).ktFunction + + val builtIns = outerCallContext.scope.ownerDescriptor.builtIns + val expectedType = createFunctionType(builtIns, Annotations.EMPTY, receiverType, parameters, null, + expectedReturnType ?: TypeUtils.NO_EXPECTED_TYPE) + + val approximatesExpectedType = typeApproximator.approximateToSubType(expectedType, TypeApproximatorConfiguration.LocalDeclaration) ?: expectedType + + val actualContext = outerCallContext.replaceBindingTrace(trace). + replaceContextDependency(ContextDependency.DEPENDENT).replaceExpectedType(approximatesExpectedType) + + + val functionTypeInfo = expressionTypingServices.getTypeInfo(expression, actualContext) + val lastExpressionType = functionTypeInfo.type?.let { + if (it.isFunctionType) it.getReturnTypeFromFunctionType() else it + } + val lastExpressionTypeInfo = KotlinTypeInfo(lastExpressionType, functionTypeInfo.dataFlowInfo) + + val lastExpression: KtExpression? + if (psiCallArgument is LambdaKotlinCallArgumentImpl) { + lastExpression = psiCallArgument.ktLambdaExpression.bodyExpression?.statements?.lastOrNull() + } + else { + lastExpression = (psiCallArgument as FunctionExpressionImpl).ktFunction.bodyExpression?.lastBlockStatementOrThis() + } + + val deparentesized = KtPsiUtil.deparenthesize(lastExpression) ?: return emptyList() + + val simpleArgument = createSimplePSICallArgument(actualContext, CallMaker.makeExternalValueArgument(deparentesized), lastExpressionTypeInfo) + + return listOfNotNull(simpleArgument) + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt new file mode 100644 index 00000000000..64378ed6137 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt @@ -0,0 +1,163 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.tower + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getCall +import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.model.LambdaKotlinCallArgument +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo +import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.checker.prepareArgumentTypeRegardingCaptureTypes +import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo + +class SimpleTypeArgumentImpl( + val typeReference: KtTypeReference, + override val type: UnwrappedType +): SimpleTypeArgument + +// all arguments should be inherited from this class. +// But receivers is not, because for them there is no corresponding valueArgument +abstract class PSIKotlinCallArgument : KotlinCallArgument { + abstract val valueArgument: ValueArgument + abstract val dataFlowInfoBeforeThisArgument: DataFlowInfo + abstract val dataFlowInfoAfterThisArgument: DataFlowInfo + + override fun toString() = valueArgument.getArgumentExpression()?.text?.replace('\n', ' ') ?: valueArgument.toString() +} + +val KotlinCallArgument.psiCallArgument: PSIKotlinCallArgument get() { + assert(this is PSIKotlinCallArgument) { + "Incorrect KotlinCallArgument: $this. Java class: ${javaClass.canonicalName}" + } + return this as PSIKotlinCallArgument +} + +val KotlinCallArgument.psiExpression: KtExpression? get() { + if (this is ReceiverExpressionKotlinCallArgument) { + return (receiver.receiverValue as? ExpressionReceiver)?.expression + } + return psiCallArgument.valueArgument.getArgumentExpression() +} + +class ParseErrorKotlinCallArgument( + override val valueArgument: ValueArgument, + override val dataFlowInfoAfterThisArgument: DataFlowInfo, + builtIns: KotlinBuiltIns +): ExpressionKotlinCallArgument, PSIKotlinCallArgument() { + override val receiver = ReceiverValueWithSmartCastInfo(TransientReceiver(builtIns.nothingType), emptySet(), isStable = true) + + override val isSafeCall: Boolean get() = false + + override val isSpread: Boolean get() = valueArgument.getSpreadElement() != null + override val argumentName: Name? get() = valueArgument.getArgumentName()?.asName + + override val dataFlowInfoBeforeThisArgument: DataFlowInfo + get() = dataFlowInfoAfterThisArgument +} + +class LambdaKotlinCallArgumentImpl( + val outerCallContext: BasicCallResolutionContext, + override val valueArgument: ValueArgument, + override val dataFlowInfoBeforeThisArgument: DataFlowInfo, + val ktLambdaExpression: KtLambdaExpression, + override val argumentName: Name?, + override val parametersTypes: Array? +) : LambdaKotlinCallArgument, PSIKotlinCallArgument() { + override val dataFlowInfoAfterThisArgument: DataFlowInfo + get() = dataFlowInfoBeforeThisArgument +} + +class FunctionExpressionImpl( + val outerCallContext: BasicCallResolutionContext, + override val valueArgument: ValueArgument, + override val dataFlowInfoBeforeThisArgument: DataFlowInfo, + val ktFunction: KtNamedFunction, + override val argumentName: Name?, + override val receiverType: UnwrappedType?, + override val parametersTypes: Array, + override val returnType: UnwrappedType? +) : FunctionExpression, PSIKotlinCallArgument() { + override val dataFlowInfoAfterThisArgument: DataFlowInfo + get() = dataFlowInfoBeforeThisArgument +} + +class CallableReferenceKotlinCallArgumentImpl( + override val valueArgument: ValueArgument, + override val dataFlowInfoBeforeThisArgument: DataFlowInfo, + override val dataFlowInfoAfterThisArgument: DataFlowInfo, + val ktCallableReferenceExpression: KtCallableReferenceExpression, + override val argumentName: Name?, + override val lhsType: UnwrappedType?, + override val constraintStorage: ConstraintStorage +) : CallableReferenceKotlinCallArgument, PSIKotlinCallArgument() + +class SubKotlinCallArgumentImpl( + override val valueArgument: ValueArgument, + override val dataFlowInfoBeforeThisArgument: DataFlowInfo, + override val dataFlowInfoAfterThisArgument: DataFlowInfo, + override val receiver: ReceiverValueWithSmartCastInfo, + override val resolvedCall: ResolvedKotlinCall.OnlyResolvedKotlinCall +): PSIKotlinCallArgument(), SubKotlinCallArgument { + override val isSpread: Boolean get() = valueArgument.getSpreadElement() != null + override val argumentName: Name? get() = valueArgument.getArgumentName()?.asName + override val isSafeCall: Boolean get() = false +} + +class ExpressionKotlinCallArgumentImpl( + override val valueArgument: ValueArgument, + override val dataFlowInfoBeforeThisArgument: DataFlowInfo, + override val dataFlowInfoAfterThisArgument: DataFlowInfo, + override val receiver: ReceiverValueWithSmartCastInfo +): PSIKotlinCallArgument(), ExpressionKotlinCallArgument { + override val isSpread: Boolean get() = valueArgument.getSpreadElement() != null + override val argumentName: Name? get() = valueArgument.getArgumentName()?.asName + override val isSafeCall: Boolean get() = false +} + +internal fun createSimplePSICallArgument( + context: BasicCallResolutionContext, + valueArgument: ValueArgument, + typeInfo: KotlinTypeInfo +): PSIKotlinCallArgument? { + val ktExpression = KtPsiUtil.getLastElementDeparenthesized(valueArgument.getArgumentExpression(), context.statementFilter) ?: return null + val onlyResolvedCall = ktExpression.getCall(context.trace.bindingContext)?.let { + context.trace.bindingContext.get(BindingContext.ONLY_RESOLVED_CALL, it) + } + val baseType = onlyResolvedCall?.currentReturnType ?: typeInfo.type?.unwrap() ?: return null + val preparedType = prepareArgumentTypeRegardingCaptureTypes(baseType) ?: baseType + + val receiverToCast = context.transformToReceiverWithSmartCastInfo( + ExpressionReceiver.create(ktExpression, preparedType, context.trace.bindingContext) + ) + + return if (onlyResolvedCall == null) { + ExpressionKotlinCallArgumentImpl(valueArgument, context.dataFlowInfo, typeInfo.dataFlowInfo, receiverToCast) + } + else { + SubKotlinCallArgumentImpl(valueArgument, context.dataFlowInfo, typeInfo.dataFlowInfo, receiverToCast, onlyResolvedCall) + } + +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewResolutionOldInference.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewResolutionOldInference.kt index 08edeeecb52..843bb1bf453 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewResolutionOldInference.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewResolutionOldInference.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.psi.Call import org.jetbrains.kotlin.psi.KtReferenceExpression import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.TemporaryBindingTrace +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallKind import org.jetbrains.kotlin.resolve.calls.CallTransformer import org.jetbrains.kotlin.resolve.calls.CandidateResolver import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isBinaryRemOperator @@ -68,7 +69,7 @@ class NewResolutionOldInference( private val languageVersionSettings: LanguageVersionSettings, private val coroutineInferenceSupport: CoroutineInferenceSupport ) { - sealed class ResolutionKind { + sealed class ResolutionKind(val kotlinCallKind: KotlinCallKind = KotlinCallKind.UNSUPPORTED) { abstract internal fun createTowerProcessor( outer: NewResolutionOldInference, name: Name, @@ -78,7 +79,7 @@ class NewResolutionOldInference( context: BasicCallResolutionContext ): ScopeTowerProcessor - object Function : ResolutionKind() { + object Function : ResolutionKind(KotlinCallKind.FUNCTION) { override fun createTowerProcessor( outer: NewResolutionOldInference, name: Name, tracing: TracingStrategy, scopeTower: ImplicitScopeTower, explicitReceiver: DetailedReceiver?, context: BasicCallResolutionContext @@ -88,7 +89,7 @@ class NewResolutionOldInference( } } - object Variable : ResolutionKind() { + object Variable : ResolutionKind(KotlinCallKind.VARIABLE) { override fun createTowerProcessor( outer: NewResolutionOldInference, name: Name, tracing: TracingStrategy, scopeTower: ImplicitScopeTower, explicitReceiver: DetailedReceiver?, context: BasicCallResolutionContext @@ -456,7 +457,7 @@ class NewResolutionOldInference( } -private fun ResolutionContext<*>.transformToReceiverWithSmartCastInfo(receiver: ReceiverValue): ReceiverValueWithSmartCastInfo { +fun ResolutionContext<*>.transformToReceiverWithSmartCastInfo(receiver: ReceiverValue): ReceiverValueWithSmartCastInfo { val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, this) return ReceiverValueWithSmartCastInfo(receiver, dataFlowInfo.getCollectedTypes(dataFlowValue), dataFlowValue.isStable) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSICallResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSICallResolver.kt new file mode 100644 index 00000000000..00ad78b27e0 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSICallResolver.kt @@ -0,0 +1,487 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.tower + +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.incremental.components.LookupLocation +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.ModifierCheckerCore +import org.jetbrains.kotlin.resolve.TemporaryBindingTrace +import org.jetbrains.kotlin.resolve.TypeResolver +import org.jetbrains.kotlin.resolve.calls.* +import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode +import org.jetbrains.kotlin.resolve.calls.callUtil.createLookupLocation +import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall +import org.jetbrains.kotlin.resolve.calls.components.ArgumentsToParametersMapper +import org.jetbrains.kotlin.resolve.calls.components.CallableReferenceResolver +import org.jetbrains.kotlin.resolve.calls.components.LambdaAnalyzer +import org.jetbrains.kotlin.resolve.calls.components.TypeArgumentsToParametersMapper +import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext +import org.jetbrains.kotlin.resolve.calls.context.ContextDependency +import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintInjector +import org.jetbrains.kotlin.resolve.calls.inference.components.ResultTypeResolver +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.results.* +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory +import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallableDescriptors +import org.jetbrains.kotlin.resolve.calls.tasks.ResolutionCandidate +import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns +import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil +import org.jetbrains.kotlin.resolve.scopes.LexicalScope +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.resolve.scopes.SyntheticScopes +import org.jetbrains.kotlin.resolve.scopes.receivers.* +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.ControlStructureDataFlowInfo +import org.jetbrains.kotlin.types.expressions.DoubleColonExpressionResolver +import org.jetbrains.kotlin.types.expressions.DoubleColonLHS +import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext +import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices +import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult +import java.util.* + +class PSICallResolver( + private val typeResolver: TypeResolver, + private val expressionTypingServices: ExpressionTypingServices, + private val doubleColonExpressionResolver: DoubleColonExpressionResolver, + private val languageVersionSettings: LanguageVersionSettings, + private val dynamicCallableDescriptors: DynamicCallableDescriptors, + private val syntheticScopes: SyntheticScopes, + private val argumentsToParametersMapper: ArgumentsToParametersMapper, + val typeArgumentsToParametersMapper: TypeArgumentsToParametersMapper, + val resultTypeResolver: ResultTypeResolver, + val callableReferenceResolver: CallableReferenceResolver, + val constraintInjector: ConstraintInjector, + private val kotlinToResolvedCallTransformer: KotlinToResolvedCallTransformer, + private val kotlinCallResolver: KotlinCallResolver, + private val typeApproximator: TypeApproximator +) { + private val GIVEN_CANDIDATES_NAME = Name.special("") + + fun runResolutionAndInference( + context: BasicCallResolutionContext, + name: Name, + resolutionKind: NewResolutionOldInference.ResolutionKind, + tracingStrategy: TracingStrategy + ) : OverloadResolutionResults { + val kotlinCall = toKotlinCall(context, resolutionKind.kotlinCallKind, context.call, name, tracingStrategy) + val scopeTower = ASTScopeTower(context) + val lambdaAnalyzer = LambdaAnalyzerImpl(expressionTypingServices, context.trace, typeApproximator) + + val callContext = createCallContext(scopeTower, lambdaAnalyzer) + val factoryProviderForInvoke = FactoryProviderForInvoke(context, callContext, kotlinCall) + + val result = kotlinCallResolver.resolveCall(callContext, kotlinCall, calculateExpectedType(context), factoryProviderForInvoke) + if (result.isEmpty() && reportAdditionalDiagnosticIfNoCandidates(context, scopeTower, resolutionKind.kotlinCallKind, kotlinCall)) { + return OverloadResolutionResultsImpl.nameNotFound() + } + + return convertToOverloadResolutionResults(context, result, tracingStrategy) + } + + // actually, `D` is at least FunctionDescriptor, but right now because of CallResolver it isn't possible change upper bound for `D` + fun runResolutionAndInferenceForGivenCandidates( + context: BasicCallResolutionContext, + resolutionCandidates: Collection>, + tracingStrategy: TracingStrategy + ): OverloadResolutionResults { + val dispatchReceiver = resolutionCandidates.firstNotNullResult { it.dispatchReceiver } + + val kotlinCall = toKotlinCall(context, KotlinCallKind.FUNCTION, context.call, GIVEN_CANDIDATES_NAME, tracingStrategy, dispatchReceiver) + val scopeTower = ASTScopeTower(context) + val lambdaAnalyzer = LambdaAnalyzerImpl(expressionTypingServices, context.trace, typeApproximator) + val callContext = createCallContext(scopeTower, lambdaAnalyzer) + + val givenCandidates = resolutionCandidates.map { + GivenCandidate(it.descriptor as FunctionDescriptor, + it.dispatchReceiver?.let { context.transformToReceiverWithSmartCastInfo(it) }, + it.knownTypeParametersResultingSubstitutor) + } + + val result = kotlinCallResolver.resolveGivenCandidates(callContext, kotlinCall, calculateExpectedType(context), givenCandidates) + return convertToOverloadResolutionResults(context, result, tracingStrategy) + + } + + private fun calculateExpectedType(context: BasicCallResolutionContext): UnwrappedType? { + val expectedType = context.expectedType.unwrap() + + return if (context.contextDependency == ContextDependency.DEPENDENT) { + assert(expectedType == TypeUtils.NO_EXPECTED_TYPE) + null + } + else { + if (expectedType.isError) TypeUtils.NO_EXPECTED_TYPE else expectedType + } + } + + private fun createCallContext(scopeTower: ASTScopeTower, lambdaAnalyzer: LambdaAnalyzer) = + KotlinCallContext(scopeTower, lambdaAnalyzer, argumentsToParametersMapper, typeArgumentsToParametersMapper, resultTypeResolver, + callableReferenceResolver, constraintInjector) + + private fun convertToOverloadResolutionResults( + context: BasicCallResolutionContext, + result: Collection, + tracingStrategy: TracingStrategy + ): OverloadResolutionResults { + val trace = context.trace + when (result.size) { + 0 -> { + tracingStrategy.unresolvedReference(trace) + return OverloadResolutionResultsImpl.nameNotFound() + } + 1 -> { + val singleCandidate = result.single() + val resolvedCall = kotlinToResolvedCallTransformer.transformAndReport(singleCandidate, context, trace) + return SingleOverloadResolutionResult(resolvedCall) + } + else -> { + val resolvedCalls = result.map { kotlinToResolvedCallTransformer.transformAndReport(it, context, trace = null) } + if (result.areAllCompletedAndFailed()) { + tracingStrategy.noneApplicable(trace, resolvedCalls) + tracingStrategy.recordAmbiguity(trace, resolvedCalls) + } + else { + tracingStrategy.recordAmbiguity(trace, resolvedCalls) + if (resolvedCalls.first().status == ResolutionStatus.INCOMPLETE_TYPE_INFERENCE) { + tracingStrategy.cannotCompleteResolve(trace, resolvedCalls) + } + else { + tracingStrategy.ambiguity(trace, resolvedCalls) + } + } + return ManyCandidates(resolvedCalls) + } + } + } + + private fun Collection.areAllCompletedAndFailed() = + all { + it is ResolvedKotlinCall.CompletedResolvedKotlinCall && + !it.completedCall.resolutionStatus.resultingApplicability.isSuccess + } + + // true if we found something + private fun reportAdditionalDiagnosticIfNoCandidates( + context: BasicCallResolutionContext, + scopeTower: ImplicitScopeTower, + kind: KotlinCallKind, + kotlinCall: KotlinCall + ): Boolean { + val reference = context.call.calleeExpression as? KtReferenceExpression ?: return false + + val errorCandidates = when (kind) { + KotlinCallKind.FUNCTION -> + collectErrorCandidatesForFunction(scopeTower, kotlinCall.name, kotlinCall.explicitReceiver?.receiver) + KotlinCallKind.VARIABLE -> + collectErrorCandidatesForVariable(scopeTower, kotlinCall.name, kotlinCall.explicitReceiver?.receiver) + else -> emptyList() + } + + for (candidate in errorCandidates) { + if (candidate is ErrorCandidate.Classifier) { + context.trace.record(BindingContext.REFERENCE_TARGET, reference, candidate.descriptor) + context.trace.report(Errors.RESOLUTION_TO_CLASSIFIER.on(reference, candidate.descriptor, candidate.kind, candidate.errorMessage)) + return true + } + } + return false + } + + + private inner class ASTScopeTower( + val context: BasicCallResolutionContext + ): ImplicitScopeTower { + // todo may be for invoke for case variable + invoke we should create separate dynamicScope(by newCall for invoke) + override val dynamicScope: MemberScope = dynamicCallableDescriptors.createDynamicDescriptorScope(context.call, context.scope.ownerDescriptor) + // same for location + override val location: LookupLocation = context.call.createLookupLocation() + + override val syntheticScopes: SyntheticScopes get() = this@PSICallResolver.syntheticScopes + override val isDebuggerContext: Boolean get() = context.isDebuggerContext + override val lexicalScope: LexicalScope get() = context.scope + private val cache = HashMap() + + override fun getImplicitReceiver(scope: LexicalScope): ReceiverValueWithSmartCastInfo? { + val implicitReceiver = scope.implicitReceiver ?: return null + + return cache.getOrPut(implicitReceiver) { + context.transformToReceiverWithSmartCastInfo(implicitReceiver.value) + } + } + } + + private inner class FactoryProviderForInvoke( + val context: BasicCallResolutionContext, + val callContext: KotlinCallContext, + val kotlinCall: PSIKotlinCallImpl + ) : CandidateFactoryProviderForInvoke { + + init { + assert(kotlinCall.dispatchReceiverForInvokeExtension == null) { kotlinCall } + } + + override fun transformCandidate( + variable: KotlinResolutionCandidate, + invoke: KotlinResolutionCandidate + ): VariableAsFunctionKotlinResolutionCandidate { + assert(variable is SimpleKotlinResolutionCandidate) { + "VariableAsFunction variable is not allowed here: $variable" + } + assert(invoke is SimpleKotlinResolutionCandidate) { + "VariableAsFunction candidate is not allowed here: $invoke" + } + + return VariableAsFunctionKotlinResolutionCandidate(kotlinCall, variable as SimpleKotlinResolutionCandidate, invoke as SimpleKotlinResolutionCandidate) + } + + override fun factoryForVariable(stripExplicitReceiver: Boolean): CandidateFactory { + val explicitReceiver = if (stripExplicitReceiver) null else kotlinCall.explicitReceiver + val variableCall = PSIKotlinCallForVariable(kotlinCall, explicitReceiver, kotlinCall.name) + return SimpleCandidateFactory(callContext, variableCall) + } + + override fun factoryForInvoke(variable: KotlinResolutionCandidate, useExplicitReceiver: Boolean): + Pair>? { + assert(variable is SimpleKotlinResolutionCandidate) { + "VariableAsFunction variable is not allowed here: $variable" + } + if (isRecursiveVariableResolution(variable as SimpleKotlinResolutionCandidate)) return null + + assert(variable.isSuccessful) { + "Variable call should be successful: $variable " + + "Descriptor: ${variable.descriptorWithFreshTypes}" + } + val variableCallArgument = createReceiverCallArgument(variable) + + val explicitReceiver = kotlinCall.explicitReceiver + val callForInvoke = if (useExplicitReceiver && explicitReceiver is SimpleKotlinCallArgument) { + PSIKotlinCallForInvoke(kotlinCall, explicitReceiver, variableCallArgument) + } + else { + PSIKotlinCallForInvoke(kotlinCall, variableCallArgument, null) + } + + return variableCallArgument.receiver to SimpleCandidateFactory(callContext, callForInvoke) + } + + // todo: create special check that there is no invoke on variable + private fun isRecursiveVariableResolution(variable: SimpleKotlinResolutionCandidate): Boolean { + val variableType = variable.candidateDescriptor.returnType + return variableType is DeferredType && variableType.isComputing + } + + // todo: review + private fun createReceiverCallArgument(variable: SimpleKotlinResolutionCandidate): ExpressionKotlinCallArgument = + ReceiverExpressionKotlinCallArgument(createReceiverValueWithSmartCastInfo(variable), isVariableReceiverForInvoke = true) + + // todo: decrease hacks count + private fun createReceiverValueWithSmartCastInfo(variable: SimpleKotlinResolutionCandidate): ReceiverValueWithSmartCastInfo { + val callForVariable = variable.kotlinCall as PSIKotlinCallForVariable + val calleeExpression = callForVariable.baseCall.psiCall.calleeExpression as? KtReferenceExpression ?: + error("Unexpected call : ${callForVariable.baseCall.psiCall}") + + val temporaryTrace = TemporaryBindingTrace.create(context.trace, "Context for resolve candidate") + val type = variable.descriptorWithFreshTypes.returnType!!.unwrap() + val variableReceiver = ExpressionReceiver.create(calleeExpression, type, temporaryTrace.bindingContext) + + temporaryTrace.record(BindingContext.REFERENCE_TARGET, calleeExpression, variable.descriptorWithFreshTypes) + val dataFlowValue = DataFlowValueFactory.createDataFlowValue(variableReceiver, temporaryTrace.bindingContext, context.scope.ownerDescriptor) + return ReceiverValueWithSmartCastInfo(variableReceiver, context.dataFlowInfo.getCollectedTypes(dataFlowValue), dataFlowValue.isStable) + } + } + + + private fun toKotlinCall( + context: BasicCallResolutionContext, + kotlinCallKind: KotlinCallKind, + oldCall: Call, + name: Name, + tracingStrategy: TracingStrategy, + forcedExplicitReceiver: Receiver? = null + ): PSIKotlinCallImpl { + val resolvedExplicitReceiver = resolveExplicitReceiver(context, forcedExplicitReceiver?: oldCall.explicitReceiver, oldCall.isSafeCall()) + val resolvedTypeArguments = resolveTypeArguments(context, oldCall.typeArguments) + + // this is hack for special calls. Note that special call has only arguments in parenthesis. + val givenDataFlowInfo: ControlStructureDataFlowInfo? = context.dataFlowInfoForArguments as? ControlStructureDataFlowInfo + + val argumentsInParenthesis = if (oldCall.callType != Call.CallType.ARRAY_SET_METHOD && oldCall.functionLiteralArguments.isEmpty()) { + oldCall.valueArguments + } + else { + oldCall.valueArguments.dropLast(1) + } + + val (resolvedArgumentsInParenthesis, dataFlowInfoAfterArgumentsInParenthesis) = resolveArgumentsInParenthesis( + context, context.dataFlowInfoForArguments.resultInfo, argumentsInParenthesis, givenDataFlowInfo) + + val externalLambdaArguments = oldCall.functionLiteralArguments + val externalArgument = if (oldCall.callType == Call.CallType.ARRAY_SET_METHOD) { + assert(externalLambdaArguments.isEmpty()) { + "Unexpected lambda parameters for call $oldCall" + } + oldCall.valueArguments.last() + } + else { + if (externalLambdaArguments.size > 2) { + externalLambdaArguments.drop(1).forEach { + context.trace.report(Errors.MANY_LAMBDA_EXPRESSION_ARGUMENTS.on(it.getLambdaExpression())) + } + } + + externalLambdaArguments.firstOrNull() + } + + val astExternalArgument = externalArgument?.let { resolveValueArgument(context, dataFlowInfoAfterArgumentsInParenthesis, it) } + val resultDataFlowInfo = astExternalArgument?.dataFlowInfoAfterThisArgument ?: dataFlowInfoAfterArgumentsInParenthesis + + return PSIKotlinCallImpl(kotlinCallKind, oldCall, tracingStrategy, resolvedExplicitReceiver, name, resolvedTypeArguments, resolvedArgumentsInParenthesis, + astExternalArgument, context.dataFlowInfo, resultDataFlowInfo) + } + + private fun resolveExplicitReceiver(context: BasicCallResolutionContext, oldReceiver: Receiver?, isSafeCall: Boolean): ReceiverKotlinCallArgument? = + when(oldReceiver) { + null -> null + is QualifierReceiver -> QualifierReceiverKotlinCallArgument(oldReceiver) // todo report warning if isSafeCall + is ReceiverValue -> { + val detailedReceiver = context.transformToReceiverWithSmartCastInfo(oldReceiver) + ReceiverExpressionKotlinCallArgument(detailedReceiver, isSafeCall) + } + else -> error("Incorrect receiver: $oldReceiver") + } + + private fun resolveType(context: BasicCallResolutionContext, typeReference: KtTypeReference?): UnwrappedType? { + if (typeReference == null) return null + + val type = typeResolver.resolveType(context.scope, typeReference, context.trace, checkBounds = true) + ForceResolveUtil.forceResolveAllContents(type) + return type.unwrap() + } + + private fun resolveTypeArguments(context: BasicCallResolutionContext, typeArguments: List): List = + typeArguments.map { projection -> + if (projection.projectionKind != KtProjectionKind.NONE) { + context.trace.report(Errors.PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT.on(projection)) + } + ModifierCheckerCore.check(projection, context.trace, null, languageVersionSettings) + + resolveType(context, projection.typeReference)?.let { SimpleTypeArgumentImpl(projection.typeReference!!, it) } ?: TypeArgumentPlaceholder + } + + private fun resolveArgumentsInParenthesis( + context: BasicCallResolutionContext, + dataFlowInfoForArguments: DataFlowInfo, + arguments: List, + givenDataFlowInfo: ControlStructureDataFlowInfo? + ): Pair, DataFlowInfo> { + if (givenDataFlowInfo != null) { + val resolvedArguments = arguments.map { + resolveValueArgument(context, givenDataFlowInfo.getInfo(it), it) + } + return resolvedArguments to givenDataFlowInfo.resultInfo + } + + var dataFlowInfo = dataFlowInfoForArguments + + val resolvedArguments = arguments.map { + val argument = resolveValueArgument(context, dataFlowInfo, it) + dataFlowInfo = argument.dataFlowInfoAfterThisArgument + argument + } + + return resolvedArguments to dataFlowInfo + } + + private fun resolveValueArgument( + outerCallContext: BasicCallResolutionContext, + startDataFlowInfo: DataFlowInfo, + valueArgument: ValueArgument + ): PSIKotlinCallArgument { + val parseErrorArgument = ParseErrorKotlinCallArgument(valueArgument, startDataFlowInfo, outerCallContext.scope.ownerDescriptor.builtIns) + val ktExpression = KtPsiUtil.deparenthesize(valueArgument.getArgumentExpression()) ?: + return parseErrorArgument + + val argumentName = valueArgument.getArgumentName()?.asName + + val lambdaArgument: PSIKotlinCallArgument? = when (ktExpression) { + is KtLambdaExpression -> + LambdaKotlinCallArgumentImpl(outerCallContext, valueArgument, startDataFlowInfo, ktExpression, argumentName, + resolveParametersTypes(outerCallContext, ktExpression.functionLiteral)) + is KtNamedFunction -> { + val receiverType = resolveType(outerCallContext, ktExpression.receiverTypeReference) + val parametersTypes = resolveParametersTypes(outerCallContext, ktExpression) ?: emptyArray() + val returnType = resolveType(outerCallContext, ktExpression.typeReference) + FunctionExpressionImpl(outerCallContext, valueArgument, startDataFlowInfo, ktExpression, argumentName, receiverType, parametersTypes, returnType) + } + else -> null + } + if (lambdaArgument != null) { + checkNoSpread(outerCallContext, valueArgument) + return lambdaArgument + } + + val context = outerCallContext.replaceContextDependency(ContextDependency.DEPENDENT) + .replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceDataFlowInfo(startDataFlowInfo) + + if (ktExpression is KtCallableReferenceExpression) { + checkNoSpread(outerCallContext, valueArgument) + + // todo analyze left expression and get constraint system + val (lhsResult, rightResults) = doubleColonExpressionResolver.resolveCallableReference( + ktExpression, ExpressionTypingContext.newContext(context), ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS) + + val newDataFlowInfo = (lhsResult as? DoubleColonLHS.Expression)?.dataFlowInfo ?: startDataFlowInfo + + // todo ChosenCallableReferenceDescriptor + val argument = CallableReferenceKotlinCallArgumentImpl(valueArgument, startDataFlowInfo, newDataFlowInfo, + ktExpression, argumentName, (lhsResult as? DoubleColonLHS.Type)?.type?.unwrap(), + ConstraintStorage.Empty) // todo + + return argument + } + + // valueArgument.getArgumentExpression()!! instead of ktExpression is hack -- type info should be stored also for parenthesized expression + val typeInfo = expressionTypingServices.getTypeInfo(valueArgument.getArgumentExpression()!!, context) + return createSimplePSICallArgument(context, valueArgument, typeInfo) ?: parseErrorArgument + } + + private fun checkNoSpread(context: BasicCallResolutionContext, valueArgument: ValueArgument) { + valueArgument.getSpreadElement()?.let { + context.trace.report(Errors.SPREAD_OF_LAMBDA_OR_CALLABLE_REFERENCE.on(it)) + } + } + + private fun resolveParametersTypes(context: BasicCallResolutionContext, ktFunction: KtFunction): Array? { + val parameterList = ktFunction.valueParameterList ?: return null + + return Array(parameterList.parameters.size) { + parameterList.parameters[it]?.typeReference?.let { resolveType(context, it) } + } + } + + +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSIKotlinCalls.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSIKotlinCalls.kt new file mode 100644 index 00000000000..8a453d08e38 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSIKotlinCalls.kt @@ -0,0 +1,115 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.tower + +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.Call +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallKind +import org.jetbrains.kotlin.resolve.calls.CallTransformer +import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isConventionCall +import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInfixCall +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo +import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy +import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategyForInvoke +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver +import org.jetbrains.kotlin.util.OperatorNameConventions + +val KotlinCall.psiKotlinCall: PSIKotlinCall get() { + assert(this is PSIKotlinCall) { + "Incorrect ASTCAll: $this. Java class: ${javaClass.canonicalName}" + } + return this as PSIKotlinCall +} + +abstract class PSIKotlinCall : KotlinCall { + abstract val psiCall: Call + abstract val startingDataFlowInfo: DataFlowInfo + abstract val resultDataFlowInfo: DataFlowInfo + abstract val tracingStrategy: TracingStrategy + + override fun toString() = "$psiCall" +} + +class PSIKotlinCallImpl( + override val callKind: KotlinCallKind, + override val psiCall: Call, + override val tracingStrategy: TracingStrategy, + override val explicitReceiver: ReceiverKotlinCallArgument?, + override val name: Name, + override val typeArguments: List, + override val argumentsInParenthesis: List, + override val externalArgument: KotlinCallArgument?, + override val startingDataFlowInfo: DataFlowInfo, + override val resultDataFlowInfo: DataFlowInfo +) : PSIKotlinCall() { + override val isInfixCall: Boolean get() = isInfixCall(psiCall) + override val isOperatorCall: Boolean get() = isConventionCall(psiCall) +} + +class PSIKotlinCallForVariable( + val baseCall: PSIKotlinCallImpl, + override val explicitReceiver: ReceiverKotlinCallArgument?, + override val name: Name +) : PSIKotlinCall() { + override val callKind: KotlinCallKind get() = KotlinCallKind.VARIABLE + override val typeArguments: List get() = emptyList() + override val argumentsInParenthesis: List get() = emptyList() + override val externalArgument: KotlinCallArgument? get() = null + + override val startingDataFlowInfo: DataFlowInfo get() = baseCall.startingDataFlowInfo + override val resultDataFlowInfo: DataFlowInfo get() = baseCall.startingDataFlowInfo + + override val tracingStrategy: TracingStrategy get() = baseCall.tracingStrategy + override val psiCall: Call = CallTransformer.stripCallArguments(baseCall.psiCall).let { + if (explicitReceiver == null) CallTransformer.stripReceiver(it) else it + } + + override val isInfixCall: Boolean get() = false + override val isOperatorCall: Boolean get() = false +} + +class PSIKotlinCallForInvoke( + val baseCall: PSIKotlinCallImpl, + override val explicitReceiver: SimpleKotlinCallArgument, + override val dispatchReceiverForInvokeExtension: SimpleKotlinCallArgument? +) : PSIKotlinCall() { + override val callKind: KotlinCallKind get() = KotlinCallKind.FUNCTION + override val name: Name get() = OperatorNameConventions.INVOKE + override val typeArguments: List get() = baseCall.typeArguments + override val argumentsInParenthesis: List get() = baseCall.argumentsInParenthesis + override val externalArgument: KotlinCallArgument? get() = baseCall.externalArgument + + override val startingDataFlowInfo: DataFlowInfo get() = baseCall.startingDataFlowInfo + override val resultDataFlowInfo: DataFlowInfo get() = baseCall.resultDataFlowInfo + override val psiCall: Call + override val tracingStrategy: TracingStrategy + + override val isInfixCall: Boolean get() = false + override val isOperatorCall: Boolean get() = true + + init { + val variableReceiver = dispatchReceiverForInvokeExtension ?: explicitReceiver + val explicitExtensionReceiver = if (dispatchReceiverForInvokeExtension == null) null else explicitReceiver + val calleeExpression = baseCall.psiCall.calleeExpression!! + + psiCall = CallTransformer.CallForImplicitInvoke( + explicitExtensionReceiver?.receiver?.receiverValue, + variableReceiver.receiver.receiverValue as ExpressionReceiver, baseCall.psiCall, true) + tracingStrategy = TracingStrategyForInvoke(calleeExpression, psiCall, variableReceiver.receiver.receiverValue.type) + } +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/SpecialComponentImpls.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/SpecialComponentImpls.kt new file mode 100644 index 00000000000..f386d1a3ca1 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/SpecialComponentImpls.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.tower + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.calls.components.CommonSupertypeCalculator +import org.jetbrains.kotlin.resolve.calls.components.IsDescriptorFromSourcePredicate +import org.jetbrains.kotlin.types.CommonSupertypes +import org.jetbrains.kotlin.types.UnwrappedType + +object CommonSupertypeCalculatorImpl : CommonSupertypeCalculator { + override fun invoke(p1: Collection): UnwrappedType = CommonSupertypes.commonSupertype(p1).unwrap() +} + +object IsDescriptorFromSourcePredicateImpl: IsDescriptorFromSourcePredicate { + override fun invoke(p1: CallableDescriptor) = DescriptorToSourceUtils.descriptorToDeclaration(p1) != null +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt index 6681ee43d62..7e752565026 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt @@ -330,7 +330,7 @@ class DoubleColonExpressionResolver( return Pair(false, null) } - private fun resolveDoubleColonLHS(doubleColonExpression: KtDoubleColonExpression, c: ExpressionTypingContext): DoubleColonLHS? { + internal fun resolveDoubleColonLHS(doubleColonExpression: KtDoubleColonExpression, c: ExpressionTypingContext): DoubleColonLHS? { val resultForExpr = tryResolveLHS(doubleColonExpression, c, this::shouldTryResolveLHSAsExpression, this::resolveExpressionOnLHS) if (resultForExpr != null) { val lhs = resultForExpr.lhs diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/KotlinCallResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/KotlinCallResolver.kt new file mode 100644 index 00000000000..2c072dec787 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/KotlinCallResolver.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls + +import org.jetbrains.kotlin.resolve.calls.components.KotlinCallCompleter +import org.jetbrains.kotlin.resolve.calls.components.NewOverloadingConflictResolver +import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind +import org.jetbrains.kotlin.resolve.calls.tower.* +import org.jetbrains.kotlin.types.UnwrappedType +import java.lang.UnsupportedOperationException + + +class KotlinCallResolver( + private val towerResolver: TowerResolver, + private val kotlinCallCompleter: KotlinCallCompleter, + private val overloadingConflictResolver: NewOverloadingConflictResolver +) { + + fun resolveCall( + callContext: KotlinCallContext, + kotlinCall: KotlinCall, + expectedType: UnwrappedType?, + factoryProviderForInvoke: CandidateFactoryProviderForInvoke + ): Collection { + val scopeTower = callContext.scopeTower + + kotlinCall.checkCallInvariants() + + val candidateFactory = SimpleCandidateFactory(callContext, kotlinCall) + val processor = when(kotlinCall.callKind) { + KotlinCallKind.VARIABLE -> { + createVariableAndObjectProcessor(scopeTower, kotlinCall.name, candidateFactory, kotlinCall.explicitReceiver?.receiver) + } + KotlinCallKind.FUNCTION -> { + createFunctionProcessor(scopeTower, kotlinCall.name, candidateFactory, factoryProviderForInvoke, kotlinCall.explicitReceiver?.receiver) + } + KotlinCallKind.UNSUPPORTED -> throw UnsupportedOperationException() + } + + val candidates = towerResolver.runResolve(scopeTower, processor, useOrder = kotlinCall.callKind != KotlinCallKind.UNSUPPORTED) + + return choseMostSpecific(callContext, expectedType, candidates) + } + + fun resolveGivenCandidates( + callContext: KotlinCallContext, + kotlinCall: KotlinCall, + expectedType: UnwrappedType?, + givenCandidates: Collection + ): Collection { + kotlinCall.checkCallInvariants() + + val resolutionCandidates = givenCandidates.map { + SimpleKotlinResolutionCandidate(callContext, + kotlinCall, + if (it.dispatchReceiver == null) ExplicitReceiverKind.NO_EXPLICIT_RECEIVER else ExplicitReceiverKind.DISPATCH_RECEIVER, + it.dispatchReceiver?.let { ReceiverExpressionKotlinCallArgument(it) }, + null, + it.descriptor, + listOf() + ) + } + val candidates = towerResolver.runWithEmptyTowerData(KnownResultProcessor(resolutionCandidates), + TowerResolver.SuccessfulResultCollector { it.status }, + useOrder = true) + return choseMostSpecific(callContext, expectedType, candidates) + } + + private fun choseMostSpecific( + callContext: KotlinCallContext, + expectedType: UnwrappedType?, + candidates: Collection + ): Collection { + + val maximallySpecificCandidates = overloadingConflictResolver.chooseMaximallySpecificCandidates( + candidates, + CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS, + discriminateGenerics = true, // todo + isDebuggerContext = callContext.scopeTower.isDebuggerContext) + + val singleResult = maximallySpecificCandidates.singleOrNull()?.let { + kotlinCallCompleter.completeCallIfNecessary(it, expectedType, callContext.lambdaAnalyzer) + } + if (singleResult != null) { + return listOf(singleResult) + } + + return maximallySpecificCandidates.map { + kotlinCallCompleter.transformWhenAmbiguity(it) + } + } +} + diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/KotlinResolutionConfiguration.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/KotlinResolutionConfiguration.kt new file mode 100644 index 00000000000..c7a276c08a6 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/KotlinResolutionConfiguration.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls + +val USE_NEW_INFERENCE = false + +val REPORT_MISSING_NEW_INFERENCE_DIAGNOSTIC = false diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ArgumentsToParametersMapper.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ArgumentsToParametersMapper.kt new file mode 100644 index 00000000000..7e0b14a621e --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ArgumentsToParametersMapper.kt @@ -0,0 +1,328 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.components + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability.* +import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue +import java.util.* + +class ArgumentsToParametersMapper { + + data class ArgumentMapping( + // This map should be ordered by arguments as written, e.g.: + // fun foo(a: Int, b: Int) {} + // foo(b = bar(), a = qux()) + // parameterToCallArgumentMap.values() should be [ 'bar()', 'foo()' ] + val parameterToCallArgumentMap: Map, + val diagnostics: List + ) + + val EmptyArgumentMapping = ArgumentMapping(emptyMap(), emptyList()) + + fun mapArguments(call: KotlinCall, descriptor: CallableDescriptor): ArgumentMapping = + mapArguments(call.argumentsInParenthesis, call.externalArgument, descriptor) + + fun mapArguments( + argumentsInParenthesis: List, + externalArgument: KotlinCallArgument?, + descriptor: CallableDescriptor + ): ArgumentMapping { + // optimization for case of variable + if (argumentsInParenthesis.isEmpty() && externalArgument == null && descriptor.valueParameters.isEmpty()) { + return EmptyArgumentMapping + } + else { + val processor = CallArgumentProcessor(descriptor) + processor.processArgumentsInParenthesis(argumentsInParenthesis) + + if (externalArgument != null) { + processor.processExternalArgument(externalArgument) + } + processor.processDefaultsAndRunChecks() + + return ArgumentMapping(processor.result, processor.getDiagnostics()) + } + } + + private class CallArgumentProcessor(val descriptor: CallableDescriptor) { + val result: MutableMap = LinkedHashMap() + private var state = State.POSITION_ARGUMENTS + + private val parameters: List get() = descriptor.valueParameters + + private var diagnostics: MutableList? = null + private var nameToParameter: Map? = null + private var varargArguments: MutableList? = null + + private var currentParameterIndex = 0 + + private fun addDiagnostic(diagnostic: KotlinCallDiagnostic) { + if (diagnostics == null) { + diagnostics = ArrayList() + } + diagnostics!!.add(diagnostic) + } + + fun getDiagnostics() = diagnostics ?: emptyList() + + private fun getParameterByName(name: Name): ValueParameterDescriptor? { + if (nameToParameter == null) { + nameToParameter = parameters.associateBy { it.name } + } + return nameToParameter!![name] + } + + private fun addVarargArgument(argument: KotlinCallArgument) { + if (varargArguments == null) { + varargArguments = ArrayList() + } + varargArguments!!.add(argument) + } + + private enum class State { + POSITION_ARGUMENTS, + VARARG_POSITION, + NAMED_ARGUMENT + } + + private fun completeVarargPositionArguments() { + assert(state == State.VARARG_POSITION) { "Incorrect state: $state" } + val parameter = parameters[currentParameterIndex] + result.put(parameter.original, ResolvedCallArgument.VarargArgument(varargArguments!!)) + } + + // return true, if it was mapped to vararg parameter + private fun processPositionArgument(argument: KotlinCallArgument): Boolean { + if (state == State.NAMED_ARGUMENT) { + addDiagnostic(MixingNamedAndPositionArguments(argument)) + return false + } + + val parameter = parameters.getOrNull(currentParameterIndex) + if (parameter == null) { + addDiagnostic(TooManyArguments(argument, descriptor)) + return false + } + + if (!parameter.isVararg) { + currentParameterIndex++ + + result.put(parameter.original, ResolvedCallArgument.SimpleArgument(argument)) + return false + } + // all position arguments will be mapped to current vararg parameter + else { + addVarargArgument(argument) + return true + } + } + + private fun processNamedArgument(argument: KotlinCallArgument, name: Name) { + if (!descriptor.hasStableParameterNames()) { + addDiagnostic(NamedArgumentNotAllowed(argument, descriptor)) + } + + val parameter = findParameterByName(argument, name) ?: return + + addDiagnostic(NamedArgumentReference(argument, parameter)) + + result[parameter.original]?.let { + addDiagnostic(ArgumentPassedTwice(argument, parameter, it)) + return + } + + result[parameter.original] = ResolvedCallArgument.SimpleArgument(argument) + } + + private fun findParameterByName(argument: KotlinCallArgument, name: Name): ValueParameterDescriptor? { + val parameter = getParameterByName(name) + + if (descriptor is CallableMemberDescriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { + if (parameter == null) { + for (valueParameter in descriptor.valueParameters) { + val matchedParameter = valueParameter.overriddenDescriptors.firstOrNull { + it.containingDeclaration.hasStableParameterNames() && it.name == name + } + if (matchedParameter != null) { + addDiagnostic(NamedArgumentReference(argument, valueParameter)) + addDiagnostic(NameForAmbiguousParameter(argument, valueParameter, matchedParameter)) + return valueParameter + } + } + } + else { + parameter.getOverriddenParameterWithOtherName()?.let { + addDiagnostic(NameForAmbiguousParameter(argument, parameter, it)) + } + } + } + + if (parameter == null) addDiagnostic(NameNotFound(argument, descriptor)) + + return parameter + } + + + fun processArgumentsInParenthesis(arguments: List) { + for (argument in arguments) { + val argumentName = argument.argumentName + + // process position argument + if (argumentName == null) { + if (processPositionArgument(argument)) { + state = State.VARARG_POSITION + } + } + // process named argument + else { + if (state == State.VARARG_POSITION) { + completeVarargPositionArguments() + } + state = State.POSITION_ARGUMENTS + + processNamedArgument(argument, argumentName) + } + } + if (state == State.VARARG_POSITION) { + completeVarargPositionArguments() + } + } + + fun processExternalArgument(externalArgument: KotlinCallArgument) { + val lastParameter = parameters.lastOrNull() + if (lastParameter == null) { + addDiagnostic(TooManyArguments(externalArgument, descriptor)) + return + } + + if (lastParameter.isVararg) { + addDiagnostic(VarargArgumentOutsideParentheses(externalArgument, lastParameter)) + return + } + + val previousOccurrence = result[lastParameter.original] + if (previousOccurrence != null) { + addDiagnostic(TooManyArguments(externalArgument, descriptor)) + return + } + + + result[lastParameter.original] = ResolvedCallArgument.SimpleArgument(externalArgument) + } + + fun processDefaultsAndRunChecks() { + for ((parameter, resolvedArgument) in result) { + if (!parameter.isVararg) { + if (resolvedArgument !is ResolvedCallArgument.SimpleArgument) { + error("Incorrect resolved argument for parameter $parameter :$resolvedArgument") + } + else { + if (resolvedArgument.callArgument.isSpread) { + addDiagnostic(NonVarargSpread(resolvedArgument.callArgument, parameter)) + } + } + } + } + + for (parameter in parameters) { + if (!result.containsKey(parameter.original)) { + if (parameter.hasDefaultValue()) { + result[parameter.original] = ResolvedCallArgument.DefaultArgument + } + else if (parameter.isVararg) { + result[parameter.original] = ResolvedCallArgument.VarargArgument(emptyList()) + } + else { + addDiagnostic(NoValueForParameter(parameter, descriptor)) + } + } + } + } + } + +} + +class TooManyArguments(val argument: KotlinCallArgument, val descriptor: CallableDescriptor) : + KotlinCallDiagnostic(INAPPLICABLE) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this) +} + +class NonVarargSpread (val argument: KotlinCallArgument, val parameterDescriptor: ValueParameterDescriptor) : + KotlinCallDiagnostic(INAPPLICABLE) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallArgumentSpread(argument, this) +} + +class MixingNamedAndPositionArguments(val argument: KotlinCallArgument) : + KotlinCallDiagnostic(INAPPLICABLE) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this) +} + +class NamedArgumentNotAllowed(val argument: KotlinCallArgument, val descriptor: CallableDescriptor) : + KotlinCallDiagnostic(INAPPLICABLE) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallArgumentName(argument, this) +} + +class NameNotFound(val argument: KotlinCallArgument, val descriptor: CallableDescriptor) : + KotlinCallDiagnostic(INAPPLICABLE) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallArgumentName(argument, this) +} + +class NoValueForParameter(val parameterDescriptor: ValueParameterDescriptor, + val descriptor: CallableDescriptor) : + KotlinCallDiagnostic(INAPPLICABLE) { + override fun report(reporter: DiagnosticReporter) = reporter.onCall(this) +} + +class ArgumentPassedTwice(val argument: KotlinCallArgument, + val parameterDescriptor: ValueParameterDescriptor, + val firstOccurrence: ResolvedCallArgument) : + KotlinCallDiagnostic(INAPPLICABLE) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallArgumentName(argument, this) +} + +class VarargArgumentOutsideParentheses( + val argument: KotlinCallArgument, + val parameterDescriptor: ValueParameterDescriptor) : + KotlinCallDiagnostic(INAPPLICABLE) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this) +} + +class NameForAmbiguousParameter( + val argument: KotlinCallArgument, + val parameterDescriptor: ValueParameterDescriptor, + val overriddenParameterWithOtherName: ValueParameterDescriptor +) : KotlinCallDiagnostic(CONVENTION_ERROR) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallArgumentName(argument, this) +} + +class NamedArgumentReference( + val argument: KotlinCallArgument, + val parameterDescriptor: ValueParameterDescriptor +) : KotlinCallDiagnostic(RESOLVED) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallArgumentName(argument, this) +} + +val ValueParameterDescriptor.isVararg: Boolean get() = varargElementType != null + +fun ValueParameterDescriptor.getOverriddenParameterWithOtherName() = overriddenDescriptors.firstOrNull { + it.containingDeclaration.hasStableParameterNames() && it.name != name +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt new file mode 100644 index 00000000000..d1623fcb904 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt @@ -0,0 +1,119 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.components + +import org.jetbrains.kotlin.builtins.ReflectionTypes +import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType +import org.jetbrains.kotlin.builtins.isFunctionType +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.types.ErrorUtils +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.typeUtil.builtIns +import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes +import org.jetbrains.kotlin.types.typeUtil.isUnit +import org.jetbrains.kotlin.types.upperIfFlexible +import org.jetbrains.kotlin.utils.addIfNotNull +import java.util.* + + +class CallableReferenceResolver( + val reflectionTypes: ReflectionTypes, + val argumentsToParametersMapper: ArgumentsToParametersMapper +) { + + fun resolvePropertyReference( + descriptor: PropertyDescriptor, + propertyReference: ChosenCallableReferenceDescriptor, + outerCall: KotlinCall, + scopeOwnerDescriptor: DeclarationDescriptor + ): ResolvedPropertyReference { + val mutable = descriptor.isVar && run { + val setter = descriptor.setter + setter == null || Visibilities.isVisible(propertyReference.candidate.dispatchReceiver?.receiverValue, setter, scopeOwnerDescriptor) + } + + val reflectionType = reflectionTypes.getKPropertyType(Annotations.EMPTY, listOfNotNull(propertyReference.dispatchNotBoundReceiver, + propertyReference.extensionNotBoundReceiver), descriptor.type.unwrap(), mutable) + + return ResolvedPropertyReference(outerCall, propertyReference, reflectionType) + } + + private fun createFakeArgumentsAndMapArguments( + functionReference: ChosenCallableReferenceDescriptor, + argumentCount: Int? + ): Pair, ArgumentsToParametersMapper.ArgumentMapping?> { + if (argumentCount == null) { + return functionReference.candidate.descriptor.valueParameters.map { it.varargElementType?.unwrap() ?: it.type.unwrap() } to null + } + + val fakeArguments = (0..(argumentCount - 1)).map { FakeKotlinCallArgumentForCallableReference(functionReference, it) } + + val argumentsToParametersMapping = argumentsToParametersMapper.mapArguments(fakeArguments, null, functionReference.candidate.descriptor) + val parameters = Array(argumentCount) { null } + + for ((parameter, resolvedArgument) in argumentsToParametersMapping.parameterToCallArgumentMap) { + for (argument in resolvedArgument.arguments) { + val index = (argument as FakeKotlinCallArgumentForCallableReference).index + parameters[index] = parameter.type.unwrap() + } + } + + return parameters.map { it ?: ErrorUtils.createErrorType("Wrong parameters mapping") } to argumentsToParametersMapping + } + + fun resolveFunctionReference( + functionReference: ChosenCallableReferenceDescriptor, + outerCall: KotlinCall, + expectedType: UnwrappedType + ): ResolvedFunctionReference { + val functionType = + if (expectedType.isFunctionType) { + expectedType + } + else if (ReflectionTypes.isNumberedKFunction(expectedType)) { + expectedType.immediateSupertypes().first { it.isFunctionType } + } + else { + null + } + + val parameterTypes = ArrayList(functionType?.arguments?.size ?: 2) + parameterTypes.addIfNotNull(functionReference.dispatchNotBoundReceiver) + parameterTypes.addIfNotNull(functionReference.extensionNotBoundReceiver) + + // (A, B, C) -> Int if A -- receiver, then B & C -- parameters + // here parameterTypes contains only receivers, all parameters will be added later + val argumentCount = functionType?.arguments?.let { it.size - parameterTypes.size - 1 }?.takeIf { it >= 0 } + val (parameters, mapping) = createFakeArgumentsAndMapArguments(functionReference, argumentCount) + parameterTypes.addAll(parameters) + + val unitExpectedType = functionType?.let(KotlinType::getReturnTypeFromFunctionType)?.takeIf { it.upperIfFlexible().isUnit() } + // coercion to unit + val returnType = unitExpectedType ?: functionReference.candidate.descriptor.returnType + ?: ErrorUtils.createErrorType("Error return type") + + val kFunctionType = reflectionTypes.getKFunctionType(Annotations.EMPTY, null, parameterTypes, null, returnType, expectedType.builtIns) + + return ResolvedFunctionReference(outerCall, functionReference, kFunctionType, mapping) + } +} + diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt new file mode 100644 index 00000000000..ecea70aeaf2 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt @@ -0,0 +1,299 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.components + +import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType +import org.jetbrains.kotlin.builtins.isExtensionFunctionType +import org.jetbrains.kotlin.builtins.isFunctionType +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder +import org.jetbrains.kotlin.resolve.calls.inference.model.ArgumentConstraintPosition +import org.jetbrains.kotlin.resolve.calls.inference.model.LambdaTypeVariable +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.tower.isSuccess +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.checker.intersectWrappedTypes +import org.jetbrains.kotlin.types.typeUtil.builtIns +import org.jetbrains.kotlin.types.typeUtil.supertypes +import org.jetbrains.kotlin.utils.SmartList +import org.jetbrains.kotlin.utils.addIfNotNull +import java.lang.UnsupportedOperationException + +internal object CheckArguments : ResolutionPart { + override fun SimpleKotlinResolutionCandidate.process(): List { + val diagnostics = SmartList() + for (parameterDescriptor in descriptorWithFreshTypes.valueParameters) { + // error was reported in ArgumentsToParametersMapper + val resolvedCallArgument = argumentMappingByOriginal[parameterDescriptor.original] ?: continue + for (argument in resolvedCallArgument.arguments) { + + val diagnostic = checkArgument(callContext, kotlinCall, csBuilder, argument, argument.getExpectedType(parameterDescriptor)) + diagnostics.addIfNotNull(diagnostic) + + if (diagnostic != null && !diagnostic.candidateApplicability.isSuccess) break + } + } + return diagnostics + } + + fun checkArgument( + callContext: KotlinCallContext, + kotlinCall: KotlinCall, + csBuilder: ConstraintSystemBuilder, + argument: KotlinCallArgument, + expectedType: UnwrappedType + ): KotlinCallDiagnostic? { + return when (argument) { + is ExpressionKotlinCallArgument -> checkExpressionArgument(csBuilder, argument, expectedType, isReceiver = false) + is SubKotlinCallArgument -> checkSubCallArgument(csBuilder, argument, expectedType, isReceiver = false) + is LambdaKotlinCallArgument -> processLambdaArgument(kotlinCall, csBuilder, argument, expectedType) + is CallableReferenceKotlinCallArgument -> processCallableReferenceArgument(callContext, kotlinCall, csBuilder, argument, expectedType) + else -> error("Incorrect argument type: $argument, ${argument.javaClass.canonicalName}.") + } + } + + inline fun computeParameterTypes( + argument: LambdaKotlinCallArgument, + expectedType: UnwrappedType, + createFreshType: () -> UnwrappedType + ): List { + argument.parametersTypes?.map { it ?: createFreshType() } ?.let { return it } + + if (expectedType.isFunctionType) { + return expectedType.getValueParameterTypesFromFunctionType().map { createFreshType() } + } + + // if expected type is non-functional type and there is no declared parameters + return emptyList() + } + + inline fun computeReceiver( + argument: LambdaKotlinCallArgument, + expectedType: UnwrappedType, + createFreshType: () -> UnwrappedType + ) : UnwrappedType? { + if (argument is FunctionExpression) return argument.receiverType + + if (expectedType.isExtensionFunctionType) return createFreshType() + + return null + } + + inline fun computeReturnType( + argument: LambdaKotlinCallArgument, + createFreshType: () -> UnwrappedType + ) : UnwrappedType { + if (argument is FunctionExpression) return argument.receiverType ?: createFreshType() + + return createFreshType() + } + + fun processLambdaArgument( + kotlinCall: KotlinCall, + csBuilder: ConstraintSystemBuilder, + argument: LambdaKotlinCallArgument, + expectedType: UnwrappedType + ): KotlinCallDiagnostic? { + // initial checks + if (expectedType.isFunctionType) { + val expectedParameterCount = expectedType.getValueParameterTypesFromFunctionType().size + + argument.parametersTypes?.size?.let { + if (expectedParameterCount != it) return ExpectedLambdaParametersCountMismatch(argument, expectedParameterCount, it) + } + + if (argument is FunctionExpression) { + if (argument.receiverType != null && !expectedType.isExtensionFunctionType) return UnexpectedReceiver(argument) + if (argument.receiverType == null && expectedType.isExtensionFunctionType) return MissingReceiver(argument) + } + } + + val builtIns = expectedType.builtIns + val freshVariables = SmartList() + val receiver = computeReceiver(argument, expectedType) { + LambdaTypeVariable(argument, LambdaTypeVariable.Kind.RECEIVER, builtIns).apply { freshVariables.add(this) }.defaultType + } + + val parameters = computeParameterTypes(argument, expectedType) { + LambdaTypeVariable(argument, LambdaTypeVariable.Kind.PARAMETER, builtIns).apply { freshVariables.add(this) }.defaultType + } + + val returnType = computeReturnType(argument) { + LambdaTypeVariable(argument, LambdaTypeVariable.Kind.RETURN_TYPE, builtIns).apply { freshVariables.add(this) }.defaultType + } + + val resolvedArgument = ResolvedLambdaArgument(kotlinCall, argument, freshVariables, receiver, parameters, returnType) + + freshVariables.forEach(csBuilder::registerVariable) + csBuilder.addSubtypeConstraint(resolvedArgument.type, expectedType, ArgumentConstraintPosition(argument)) + csBuilder.addLambdaArgument(resolvedArgument) + + return null + } + + fun processCallableReferenceArgument( + callContext: KotlinCallContext, + kotlinCall: KotlinCall, + csBuilder: ConstraintSystemBuilder, + argument: CallableReferenceKotlinCallArgument, + expectedType: UnwrappedType + ): KotlinCallDiagnostic? { + val position = ArgumentConstraintPosition(argument) + + if (argument !is ChosenCallableReferenceDescriptor) { + val lhsType = argument.lhsType + if (lhsType != null) { + // todo: case with two receivers + val expectedReceiverType = expectedType.supertypes().firstOrNull { it.isFunctionType }?.arguments?.first()?.type?.unwrap() + if (expectedReceiverType != null) { + // (lhsType) -> .. <: (expectedReceiverType) -> ... => expectedReceiverType <: lhsType + csBuilder.addSubtypeConstraint(expectedReceiverType, lhsType, position) + } + } + + return null + } + + val descriptor = argument.candidate.descriptor + when (descriptor) { + is FunctionDescriptor -> { + // todo store resolved + val resolvedFunctionReference = callContext.callableReferenceResolver.resolveFunctionReference( + argument, kotlinCall, expectedType) + + csBuilder.addSubtypeConstraint(resolvedFunctionReference.reflectionType, expectedType, position) + return resolvedFunctionReference.argumentsMapping?.diagnostics?.let { + ErrorCallableMapping(resolvedFunctionReference) + } + } + is PropertyDescriptor -> { + + // todo store resolved + val resolvedPropertyReference = callContext.callableReferenceResolver.resolvePropertyReference(descriptor, + argument, kotlinCall, callContext.scopeTower.lexicalScope.ownerDescriptor) + csBuilder.addSubtypeConstraint(resolvedPropertyReference.reflectionType, expectedType, position) + } + else -> throw UnsupportedOperationException("Callable reference resolved to an unsupported descriptor: $descriptor") + } + return null + } +} + +internal fun checkExpressionArgument( + csBuilder: ConstraintSystemBuilder, + expressionArgument: ExpressionKotlinCallArgument, + expectedType: UnwrappedType, + isReceiver: Boolean +): KotlinCallDiagnostic? { + // todo run this approximation only once for call + val argumentType = expressionArgument.stableType + + fun unstableSmartCastOrSubtypeError( + unstableType: UnwrappedType?, expectedType: UnwrappedType, position: ArgumentConstraintPosition + ): KotlinCallDiagnostic? { + if (unstableType != null) { + if (csBuilder.addSubtypeConstraintIfCompatible(unstableType, expectedType, position)) { + return UnstableSmartCast(expressionArgument, unstableType) + } + } + csBuilder.addSubtypeConstraint(argumentType, expectedType, position) + return null + } + + val expectedNullableType = expectedType.makeNullableAsSpecified(true) + val position = ArgumentConstraintPosition(expressionArgument) + if (expressionArgument.isSafeCall) { + if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedNullableType, position)) { + return unstableSmartCastOrSubtypeError(expressionArgument.unstableType, expectedNullableType, position)?.let { return it } + } + return null + } + + if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedType, position)) { + if (!isReceiver) { + return unstableSmartCastOrSubtypeError(expressionArgument.unstableType, expectedType, position)?.let { return it } + } + + val unstableType = expressionArgument.unstableType + if (unstableType != null && csBuilder.addSubtypeConstraintIfCompatible(unstableType, expectedType, position)) { + return UnstableSmartCast(expressionArgument, unstableType) + } + else if (csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedNullableType, position)) { + return UnsafeCallError(expressionArgument) + } + else { + csBuilder.addSubtypeConstraint(argumentType, expectedType, position) + return null + } + } + + return null +} + +// if expression is not stable and has smart casts, then we create this type +private val ExpressionKotlinCallArgument.unstableType: UnwrappedType? + get() { + if (receiver.isStable || receiver.possibleTypes.isEmpty()) return null + return intersectWrappedTypes(receiver.possibleTypes + receiver.receiverValue.type) + } + +// with all smart casts if stable +internal val ExpressionKotlinCallArgument.stableType: UnwrappedType + get() { + if (!receiver.isStable || receiver.possibleTypes.isEmpty()) return receiver.receiverValue.type.unwrap() + return intersectWrappedTypes(receiver.possibleTypes + receiver.receiverValue.type) + } + + +internal fun checkSubCallArgument( + csBuilder: ConstraintSystemBuilder, + subCallArgument: SubKotlinCallArgument, + expectedType: UnwrappedType, + isReceiver: Boolean +): KotlinCallDiagnostic? { + val resolvedCall = subCallArgument.resolvedCall + val expectedNullableType = expectedType.makeNullableAsSpecified(true) + val position = ArgumentConstraintPosition(subCallArgument) + + csBuilder.addInnerCall(resolvedCall) + + // subArgument cannot has stable smartcast + val currentReturnType = subCallArgument.receiver.receiverValue.type.unwrap() + if (subCallArgument.isSafeCall) { + csBuilder.addSubtypeConstraint(currentReturnType, expectedNullableType, position) + return null + } + + if (isReceiver && !csBuilder.addSubtypeConstraintIfCompatible(currentReturnType, expectedType, position) && + csBuilder.addSubtypeConstraintIfCompatible(currentReturnType, expectedNullableType, position) + ) { + return UnsafeCallError(subCallArgument) + } + + csBuilder.addSubtypeConstraint(currentReturnType, expectedType, position) + return null +} + +internal fun KotlinCallArgument.getExpectedType(parameter: ValueParameterDescriptor) = + if (this.isSpread) { + parameter.type.unwrap() + } + else { + parameter.varargElementType?.unwrap() ?: parameter.type.unwrap() + } \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ExternalComponents.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ExternalComponents.kt new file mode 100644 index 00000000000..f3d80a275f8 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ExternalComponents.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.components + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.resolve.calls.model.KotlinCall +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallArgument +import org.jetbrains.kotlin.resolve.calls.model.LambdaKotlinCallArgument +import org.jetbrains.kotlin.types.UnwrappedType + +interface IsDescriptorFromSourcePredicate: (CallableDescriptor) -> Boolean + +interface CommonSupertypeCalculator: (Collection) -> UnwrappedType + +interface LambdaAnalyzer { + fun analyzeAndGetRelatedCalls( + topLevelCall: KotlinCall, + lambdaArgument: LambdaKotlinCallArgument, + receiverType: UnwrappedType?, + parameters: List, + expectedReturnType: UnwrappedType? // null means, that return type is not proper i.e. it depends on some type variables + ): List +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt new file mode 100644 index 00000000000..a4453f82ab6 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt @@ -0,0 +1,265 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.components + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor +import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder +import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintInjector +import org.jetbrains.kotlin.resolve.calls.inference.components.FixationOrderCalculator +import org.jetbrains.kotlin.resolve.calls.inference.components.ResultTypeResolver +import org.jetbrains.kotlin.resolve.calls.inference.model.ExpectedTypeConstraintPosition +import org.jetbrains.kotlin.resolve.calls.inference.model.LambdaTypeVariable +import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable +import org.jetbrains.kotlin.resolve.calls.inference.model.NotEnoughInformationForTypeParameter +import org.jetbrains.kotlin.resolve.calls.inference.returnTypeOrNothing +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability +import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateStatus +import org.jetbrains.kotlin.types.TypeSubstitutor +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.checker.KotlinTypeChecker +import org.jetbrains.kotlin.utils.SmartList +import org.jetbrains.kotlin.utils.addIfNotNull + +class KotlinCallCompleter( + val resultTypeResolver: ResultTypeResolver, + val fixationOrderCalculator: FixationOrderCalculator +) { + interface Context { + val innerCalls: List + val hasContradiction: Boolean + fun buildCurrentSubstitutor(): TypeSubstitutor + fun buildResultingSubstitutor(): TypeSubstitutor + val lambdaArguments: List + + // type can be proper if it not contains not fixed type variables + fun canBeProper(type: UnwrappedType): Boolean + fun asFixationOrderCalculatorContext(): FixationOrderCalculator.Context + fun asResultTypeResolverContext(): ResultTypeResolver.Context + + // mutable operations + fun asConstraintInjectorContext(): ConstraintInjector.Context + fun addError(error: KotlinCallDiagnostic) + fun fixVariable(variable: NewTypeVariable, resultType: UnwrappedType) + fun getBuilder(): ConstraintSystemBuilder + } + + fun transformWhenAmbiguity(candidate: KotlinResolutionCandidate): ResolvedKotlinCall = + toCompletedBaseResolvedCall(candidate.lastCall.constraintSystem.asCallCompleterContext(), candidate) + + fun completeCallIfNecessary( + candidate: KotlinResolutionCandidate, + expectedType: UnwrappedType?, + lambdaAnalyzer: LambdaAnalyzer + ): ResolvedKotlinCall { + val topLevelCall = + if (candidate is VariableAsFunctionKotlinResolutionCandidate) { + candidate.invokeCandidate + } + else { + candidate as SimpleKotlinResolutionCandidate + } + + if (topLevelCall.prepareForCompletion(expectedType)) { + val c = candidate.lastCall.constraintSystem.asCallCompleterContext() + + topLevelCall.competeCall(c, lambdaAnalyzer) + return toCompletedBaseResolvedCall(c, candidate) + } + + return ResolvedKotlinCall.OnlyResolvedKotlinCall(candidate) + } + + private fun toCompletedBaseResolvedCall( + c: Context, + candidate: KotlinResolutionCandidate + ): ResolvedKotlinCall.CompletedResolvedKotlinCall { + val currentSubstitutor = c.buildResultingSubstitutor() + val completedCall = candidate.toCompletedCall(currentSubstitutor) + val competedCalls = c.innerCalls.map { + it.candidate.toCompletedCall(currentSubstitutor) + } + return ResolvedKotlinCall.CompletedResolvedKotlinCall(completedCall, competedCalls) + } + + private fun KotlinResolutionCandidate.toCompletedCall(substitutor: TypeSubstitutor): CompletedKotlinCall { + if (this is VariableAsFunctionKotlinResolutionCandidate) { + val variable = resolvedVariable.toCompletedCall(substitutor) + val invoke = invokeCandidate.toCompletedCall(substitutor) + + return CompletedKotlinCall.VariableAsFunction(kotlinCall, variable, invoke) + } + return (this as SimpleKotlinResolutionCandidate).toCompletedCall(substitutor) + } + + private fun SimpleKotlinResolutionCandidate.toCompletedCall(substitutor: TypeSubstitutor): CompletedKotlinCall.Simple { + val resultingDescriptor = if (descriptorWithFreshTypes.typeParameters.isNotEmpty()) descriptorWithFreshTypes.substitute(substitutor)!! else descriptorWithFreshTypes + + val typeArguments = descriptorWithFreshTypes.typeParameters.map { substitutor.safeSubstitute(it.defaultType, Variance.INVARIANT).unwrap() } + + val status = computeStatus(this, resultingDescriptor) + return CompletedKotlinCall.Simple(kotlinCall, candidateDescriptor, resultingDescriptor, status, explicitReceiverKind, + dispatchReceiverArgument?.receiver, extensionReceiver?.receiver, typeArguments, argumentMappingByOriginal) + } + + private fun computeStatus(candidate: SimpleKotlinResolutionCandidate, resultingDescriptor: CallableDescriptor): ResolutionCandidateStatus { + val smartCasts = reportSmartCasts(candidate, resultingDescriptor).takeIf { it.isNotEmpty() } ?: return candidate.status + return ResolutionCandidateStatus(candidate.status.diagnostics + smartCasts) + } + + private fun createSmartCastDiagnostic(argument: KotlinCallArgument, expectedResultType: UnwrappedType): SmartCastDiagnostic? { + if (argument !is ExpressionKotlinCallArgument) return null + if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(argument.receiver.receiverValue.type, expectedResultType)) { + return SmartCastDiagnostic(argument, expectedResultType.unwrap()) + } + return null + } + + private fun reportSmartCastOnReceiver( + candidate: KotlinResolutionCandidate, + receiver: SimpleKotlinCallArgument?, + parameter: ReceiverParameterDescriptor? + ): SmartCastDiagnostic? { + if (receiver == null || parameter == null) return null + val expectedType = parameter.type.unwrap().let { if (receiver.isSafeCall) it.makeNullableAsSpecified(true) else it } + + val smartCastDiagnostic = createSmartCastDiagnostic(receiver, expectedType) ?: return null + + // todo may be we have smart cast to Int? + return smartCastDiagnostic.takeIf { + candidate.status.diagnostics.filterIsInstance().none { + it.receiver == receiver + } + && + candidate.status.diagnostics.filterIsInstance().none { + it.expressionArgument == receiver + } + } + } + + + private fun reportSmartCasts(candidate: SimpleKotlinResolutionCandidate, resultingDescriptor: CallableDescriptor): List = SmartList().apply { + addIfNotNull(reportSmartCastOnReceiver(candidate, candidate.extensionReceiver, resultingDescriptor.extensionReceiverParameter)) + addIfNotNull(reportSmartCastOnReceiver(candidate, candidate.dispatchReceiverArgument, resultingDescriptor.dispatchReceiverParameter)) + + for (parameter in resultingDescriptor.valueParameters) { + for (argument in candidate.argumentMappingByOriginal[parameter.original]?.arguments ?: continue) { + val smartCastDiagnostic = createSmartCastDiagnostic(argument, argument.getExpectedType(parameter)) ?: continue + + val thereIsUnstableSmartCastError = candidate.status.diagnostics.filterIsInstance().any { + it.expressionArgument == argument + } + + if (!thereIsUnstableSmartCastError) { + add(smartCastDiagnostic) + } + } + } + } + + // true if we should complete this call + private fun SimpleKotlinResolutionCandidate.prepareForCompletion(expectedType: UnwrappedType?): Boolean { + val returnType = descriptorWithFreshTypes.returnType?.unwrap() ?: return false + if (expectedType != null && !TypeUtils.noExpectedType(expectedType)) { + csBuilder.addSubtypeConstraint(returnType, expectedType, ExpectedTypeConstraintPosition(kotlinCall)) + } + + return expectedType != null || csBuilder.isProperType(returnType) + } + + private fun SimpleKotlinResolutionCandidate.competeCall(c: Context, lambdaAnalyzer: LambdaAnalyzer) { + while (!oneStepToEndOrLambda(c, lambdaAnalyzer)) { + // do nothing -- be happy + } + } + + // true if it is the end (happy or not) + private fun SimpleKotlinResolutionCandidate.oneStepToEndOrLambda(c: Context, lambdaAnalyzer: LambdaAnalyzer): Boolean { + if (c.hasContradiction) return true + + val lambda = c.lambdaArguments.find { canWeAnalyzeIt(c, it) } + if (lambda != null) { + analyzeLambda(c, lambdaAnalyzer, callContext, kotlinCall, lambda) + return false + } + + val completionOrder = fixationOrderCalculator.computeCompletionOrder(c.asFixationOrderCalculatorContext(), descriptorWithFreshTypes.returnTypeOrNothing) + for ((variableWithConstraints, direction) in completionOrder) { + if (c.hasContradiction) return true + val variable = variableWithConstraints.typeVariable + + val resultType = resultTypeResolver.findResultType(c.asResultTypeResolverContext(), variableWithConstraints, direction) + if (resultType == null) { + c.addError(NotEnoughInformationForTypeParameter(variable)) + break + } + c.fixVariable(variable, resultType) + + if (variable is LambdaTypeVariable) { + val resolvedLambda = c.lambdaArguments.find { it.argument == variable.lambdaArgument } ?: return true + if (canWeAnalyzeIt(c, resolvedLambda)) { + analyzeLambda(c, lambdaAnalyzer, callContext, kotlinCall, resolvedLambda) + return false + } + } + } + return true + } + + private fun analyzeLambda(c: Context, lambdaAnalyzer: LambdaAnalyzer, topLevelCallContext: KotlinCallContext, topLevelCall: KotlinCall, lambda: ResolvedLambdaArgument) { + val currentSubstitutor = c.buildCurrentSubstitutor() + fun substitute(type: UnwrappedType) = currentSubstitutor.safeSubstitute(type, Variance.INVARIANT).unwrap() + + val receiver = lambda.receiver?.let(::substitute) + val parameters = lambda.parameters.map(::substitute) + val expectedType = lambda.returnType.takeIf { c.canBeProper(it) }?.let(::substitute) + val callsFromLambda = lambdaAnalyzer.analyzeAndGetRelatedCalls(topLevelCall, lambda.argument, receiver, parameters, expectedType) + lambda.analyzed = true + + for (innerCall in callsFromLambda) { + // todo strange code -- why top-level kotlinCall? may be it isn't right outer call + CheckArguments.checkArgument(topLevelCallContext, topLevelCall, c.getBuilder(), innerCall, lambda.returnType) + } +// when (innerCall) { +// is ResolvedKotlinCall.CompletedResolvedKotlinCall -> { +// val returnType = innerCall.completedCall.lastCall.resultingDescriptor.returnTypeOrNothing +// constraintInjector.addInitialSubtypeConstraint(injectorContext, returnType, lambda.returnType, position) +// } +// is ResolvedKotlinCall.OnlyResolvedKotlinCall -> { +// // todo register call +// val returnType = innerCall.candidate.lastCall.descriptorWithFreshTypes.returnTypeOrNothing +// c.addInnerCall(innerCall) +// constraintInjector.addInitialSubtypeConstraint(injectorContext, returnType, lambda.returnType, position) +// } +// } + } + + private fun canWeAnalyzeIt(c: Context, lambda: ResolvedLambdaArgument): Boolean { + if (lambda.analyzed) return false + lambda.receiver?.let { + if (!c.canBeProper(it)) return false + } + return lambda.parameters.all { c.canBeProper(it) } + } +} + +class SmartCastDiagnostic(val expressionArgument: ExpressionKotlinCallArgument, val smartCastType: UnwrappedType): KotlinCallDiagnostic(ResolutionCandidateApplicability.RESOLVED) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(expressionArgument, this) +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/NewOverloadingConflictResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/NewOverloadingConflictResolver.kt new file mode 100644 index 00000000000..e335398f03f --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/NewOverloadingConflictResolver.kt @@ -0,0 +1,85 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.components + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintInjector +import org.jetbrains.kotlin.resolve.calls.inference.components.ResultTypeResolver +import org.jetbrains.kotlin.resolve.calls.inference.components.SimpleConstraintSystemImpl +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.results.FlatSignature +import org.jetbrains.kotlin.resolve.calls.results.FlatSignature.Companion.argumentValueType +import org.jetbrains.kotlin.resolve.calls.results.OverloadingConflictResolver +import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator +import org.jetbrains.kotlin.types.KotlinType +import java.util.* + +class NewOverloadingConflictResolver( + builtIns: KotlinBuiltIns, + specificityComparator: TypeSpecificityComparator, + isDescriptorFromSourcePredicate: IsDescriptorFromSourcePredicate, + constraintInjector: ConstraintInjector, + typeResolver: ResultTypeResolver +) : OverloadingConflictResolver( + builtIns, + specificityComparator, + { + (it as? VariableAsFunctionKotlinResolutionCandidate)?.invokeCandidate?.descriptorWithFreshTypes ?: + (it as SimpleKotlinResolutionCandidate).descriptorWithFreshTypes + }, + { SimpleConstraintSystemImpl(constraintInjector, typeResolver) }, + Companion::createFlatSignature, + { (it as? VariableAsFunctionKotlinResolutionCandidate)?.resolvedVariable }, + isDescriptorFromSourcePredicate +) { + + companion object { + private fun createFlatSignature(candidate: KotlinResolutionCandidate): FlatSignature { + val simpleCandidate = (candidate as? VariableAsFunctionKotlinResolutionCandidate)?.invokeCandidate ?: (candidate as SimpleKotlinResolutionCandidate) + + val originalDescriptor = simpleCandidate.descriptorWithFreshTypes.original + val originalValueParameters = originalDescriptor.valueParameters + + var numDefaults = 0 + val valueArgumentToParameterType = HashMap() + for ((valueParameter, resolvedValueArgument) in simpleCandidate.argumentMappingByOriginal) { + if (resolvedValueArgument is ResolvedCallArgument.DefaultArgument) { + numDefaults++ + } + else { + val originalValueParameter = originalValueParameters[valueParameter.index] + val parameterType = originalValueParameter.argumentValueType + for (valueArgument in resolvedValueArgument.arguments) { + valueArgumentToParameterType[valueArgument] = parameterType + } + } + } + + return FlatSignature.create(candidate, + originalDescriptor, + numDefaults, + listOfNotNull(originalDescriptor.extensionReceiverParameter?.type) + + simpleCandidate.kotlinCall.argumentsInParenthesis.map { valueArgumentToParameterType[it] } + + listOfNotNull(simpleCandidate.kotlinCall.externalArgument?.let { valueArgumentToParameterType[it] }) + ) + + } + } +} + + + diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt new file mode 100644 index 00000000000..bb6cf6c9c6d --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt @@ -0,0 +1,243 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.components + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.resolve.calls.components.TypeArgumentsToParametersMapper.TypeArgumentsMapping.NoExplicitArguments +import org.jetbrains.kotlin.resolve.calls.inference.model.DeclaredUpperBoundConstraintPosition +import org.jetbrains.kotlin.resolve.calls.inference.model.ExplicitTypeParameterConstraintPosition +import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableFromCallableDescriptor +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.smartcasts.getReceiverValueWithSmartCast +import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.* +import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability +import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability.IMPOSSIBLE_TO_GENERATE +import org.jetbrains.kotlin.resolve.calls.tower.VisibilityError +import org.jetbrains.kotlin.types.IndexedParametersSubstitution +import org.jetbrains.kotlin.types.TypeSubstitutor +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.typeUtil.asTypeProjection + + +internal object CheckVisibility : ResolutionPart { + override fun SimpleKotlinResolutionCandidate.process(): List { + val receiverValue = dispatchReceiverArgument?.receiver?.receiverValue + val invisibleMember = Visibilities.findInvisibleMember(receiverValue, candidateDescriptor, containingDescriptor) ?: return emptyList() + + if (dispatchReceiverArgument is ExpressionKotlinCallArgument) { + val smartCastReceiver = getReceiverValueWithSmartCast(receiverValue, dispatchReceiverArgument.stableType) + if (Visibilities.findInvisibleMember(smartCastReceiver, candidateDescriptor, containingDescriptor) == null) { + return listOf(SmartCastDiagnostic(dispatchReceiverArgument, dispatchReceiverArgument.stableType)) + } + } + + return listOf(VisibilityError(invisibleMember)) + } + + private val SimpleKotlinResolutionCandidate.containingDescriptor: DeclarationDescriptor get() = callContext.scopeTower.lexicalScope.ownerDescriptor +} + +internal object MapTypeArguments : ResolutionPart { + override fun SimpleKotlinResolutionCandidate.process(): List { + typeArgumentMappingByOriginal = callContext.typeArgumentsToParametersMapper.mapTypeArguments(kotlinCall, candidateDescriptor.original) + return typeArgumentMappingByOriginal.diagnostics + } +} + +internal object NoTypeArguments : ResolutionPart { + override fun SimpleKotlinResolutionCandidate.process(): List { + assert(kotlinCall.typeArguments.isEmpty()) { + "Variable call cannot has explicit type arguments: ${kotlinCall.typeArguments}. Call: $kotlinCall" + } + typeArgumentMappingByOriginal = NoExplicitArguments + return typeArgumentMappingByOriginal.diagnostics + } +} + +internal object MapArguments : ResolutionPart { + override fun SimpleKotlinResolutionCandidate.process(): List { + val mapping = callContext.argumentsToParametersMapper.mapArguments(kotlinCall, candidateDescriptor.original) + argumentMappingByOriginal = mapping.parameterToCallArgumentMap + return mapping.diagnostics + } +} + +internal object NoArguments : ResolutionPart { + override fun SimpleKotlinResolutionCandidate.process(): List { + assert(kotlinCall.argumentsInParenthesis.isEmpty()) { + "Variable call cannot has arguments: ${kotlinCall.argumentsInParenthesis}. Call: $kotlinCall" + } + assert(kotlinCall.externalArgument == null) { + "Variable call cannot has external argument: ${kotlinCall.externalArgument}. Call: $kotlinCall" + } + argumentMappingByOriginal = emptyMap() + return emptyList() + } +} + +internal object CreteDescriptorWithFreshTypeVariables : ResolutionPart { + override fun SimpleKotlinResolutionCandidate.process(): List { + if (candidateDescriptor.typeParameters.isEmpty()) { + descriptorWithFreshTypes = candidateDescriptor + return emptyList() + } + val typeParameters = candidateDescriptor.typeParameters + + val freshTypeVariables = typeParameters.map { TypeVariableFromCallableDescriptor(kotlinCall, it) } + val toFreshVariables = IndexedParametersSubstitution(typeParameters, + freshTypeVariables.map { it.defaultType.asTypeProjection() }).buildSubstitutor() + + for (freshVariable in freshTypeVariables) { + csBuilder.registerVariable(freshVariable) + } + + for (index in typeParameters.indices) { + val typeParameter = typeParameters[index] + val freshVariable = freshTypeVariables[index] + val position = DeclaredUpperBoundConstraintPosition(typeParameter) + + for (upperBound in typeParameter.upperBounds) { + csBuilder.addSubtypeConstraint(freshVariable.defaultType, upperBound.unwrap().substitute(toFreshVariables), position) + } + } + + // bad function -- error on declaration side + if (csBuilder.hasContradiction) { + descriptorWithFreshTypes = candidateDescriptor + return emptyList() + } + + // optimization + if (typeArgumentMappingByOriginal == NoExplicitArguments) { + descriptorWithFreshTypes = candidateDescriptor.safeSubstitute(toFreshVariables) + csBuilder.simplify().let { assert(it.isEmpty) { "Substitutor should be empty: $it, call: $kotlinCall" } } + return emptyList() + } + + // add explicit type parameter + for (index in typeParameters.indices) { + val typeParameter = typeParameters[index] + val typeArgument = typeArgumentMappingByOriginal.getTypeArgument(typeParameter) + + if (typeArgument is SimpleTypeArgument) { + val freshVariable = freshTypeVariables[index] + csBuilder.addEqualityConstraint(freshVariable.defaultType, typeArgument.type, ExplicitTypeParameterConstraintPosition(typeArgument)) + } + else { + assert(typeArgument == TypeArgumentPlaceholder) { + "Unexpected typeArgument: $typeArgument, ${typeArgument.javaClass.canonicalName}" + } + } + } + + /** + * Note: here we can fix also placeholders arguments. + * Example: + * fun , Y> foo() + * + * foo, *>() + */ + val toFixedTypeParameters = csBuilder.simplify() + // todo optimize -- composite substitutions before run safeSubstitute + descriptorWithFreshTypes = candidateDescriptor.safeSubstitute(toFreshVariables).safeSubstitute(toFixedTypeParameters) + + return emptyList() + } +} + +internal object CheckExplicitReceiverKindConsistency : ResolutionPart { + private fun SimpleKotlinResolutionCandidate.hasError(): Nothing = + error("Inconsistent call: $kotlinCall. \n" + + "Candidate: $candidateDescriptor, explicitReceiverKind: $explicitReceiverKind.\n" + + "Explicit receiver: ${kotlinCall.explicitReceiver}, dispatchReceiverForInvokeExtension: ${kotlinCall.dispatchReceiverForInvokeExtension}") + + override fun SimpleKotlinResolutionCandidate.process(): List { + when (explicitReceiverKind) { + NO_EXPLICIT_RECEIVER -> if (kotlinCall.explicitReceiver is SimpleKotlinCallArgument || kotlinCall.dispatchReceiverForInvokeExtension != null) hasError() + DISPATCH_RECEIVER, EXTENSION_RECEIVER -> if (kotlinCall.explicitReceiver == null || kotlinCall.dispatchReceiverForInvokeExtension != null) hasError() + BOTH_RECEIVERS -> if (kotlinCall.explicitReceiver == null || kotlinCall.dispatchReceiverForInvokeExtension == null) hasError() + } + return emptyList() + } +} + +internal object CheckReceivers : ResolutionPart { + private fun SimpleKotlinResolutionCandidate.checkReceiver( + receiverArgument: SimpleKotlinCallArgument?, + receiverParameter: ReceiverParameterDescriptor? + ): KotlinCallDiagnostic? { + if ((receiverArgument == null) != (receiverParameter == null)) { + error("Inconsistency receiver state for call $kotlinCall and candidate descriptor: $candidateDescriptor") + } + if (receiverArgument == null || receiverParameter == null) return null + + val expectedType = receiverParameter.type.unwrap() + + return when (receiverArgument) { + is ExpressionKotlinCallArgument -> checkExpressionArgument(csBuilder, receiverArgument, expectedType, isReceiver = true) + is SubKotlinCallArgument -> checkSubCallArgument(csBuilder, receiverArgument, expectedType, isReceiver = true) + else -> incorrectReceiver(receiverArgument) + } + } + + private fun incorrectReceiver(callReceiver: SimpleKotlinCallArgument): Nothing = + error("Incorrect receiver type: $callReceiver. Class name: ${callReceiver.javaClass.canonicalName}") + + override fun SimpleKotlinResolutionCandidate.process() = + listOfNotNull(checkReceiver(dispatchReceiverArgument, descriptorWithFreshTypes.dispatchReceiverParameter), + checkReceiver(extensionReceiver, descriptorWithFreshTypes.extensionReceiverParameter)) +} + + +fun D.safeSubstitute(substitutor: TypeSubstitutor): D = + @Suppress("UNCHECKED_CAST") (substitute(substitutor) as D) + +fun UnwrappedType.substitute(substitutor: TypeSubstitutor): UnwrappedType = substitutor.substitute(this, Variance.INVARIANT)!!.unwrap() + + +class UnstableSmartCast(val expressionArgument: ExpressionKotlinCallArgument, val targetType: UnwrappedType) : + KotlinCallDiagnostic(ResolutionCandidateApplicability.MAY_THROW_RUNTIME_ERROR) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(expressionArgument, this) +} + +class UnsafeCallError(val receiver: SimpleKotlinCallArgument) : KotlinCallDiagnostic(ResolutionCandidateApplicability.MAY_THROW_RUNTIME_ERROR) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallReceiver(receiver, this) +} + +class ExpectedLambdaParametersCountMismatch( + val lambdaArgument: LambdaKotlinCallArgument, + val expected: Int, + val actual: Int +) : KotlinCallDiagnostic(IMPOSSIBLE_TO_GENERATE) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(lambdaArgument, this) +} + +class UnexpectedReceiver(val functionExpression: FunctionExpression) : KotlinCallDiagnostic(IMPOSSIBLE_TO_GENERATE) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(functionExpression, this) +} + +class MissingReceiver(val functionExpression: FunctionExpression) : KotlinCallDiagnostic(IMPOSSIBLE_TO_GENERATE) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(functionExpression, this) +} + +class ErrorCallableMapping(val functionReference: ResolvedFunctionReference) : KotlinCallDiagnostic(IMPOSSIBLE_TO_GENERATE) { + override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(functionReference.argument, this) +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SpecialResolutionParts.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SpecialResolutionParts.kt new file mode 100644 index 00000000000..0ddfc097b8f --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SpecialResolutionParts.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.components + +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic +import org.jetbrains.kotlin.resolve.calls.model.ResolutionPart +import org.jetbrains.kotlin.resolve.calls.model.SimpleKotlinResolutionCandidate +import org.jetbrains.kotlin.resolve.calls.tower.InfixCallNoInfixModifier +import org.jetbrains.kotlin.resolve.calls.tower.InvokeConventionCallNoOperatorModifier + +object CheckInfixResolutionPart : ResolutionPart { + override fun SimpleKotlinResolutionCandidate.process(): List { + if (kotlinCall.isInfixCall && (candidateDescriptor !is FunctionDescriptor || !candidateDescriptor.isInfix)) { + return listOf(InfixCallNoInfixModifier) + } + + return emptyList() + } +} + +object CheckOperatorResolutionPart : ResolutionPart { + override fun SimpleKotlinResolutionCandidate.process(): List { + if (kotlinCall.isOperatorCall && (candidateDescriptor !is FunctionDescriptor || !candidateDescriptor.isOperator)) { + return listOf(InvokeConventionCallNoOperatorModifier) + } + + return emptyList() + } +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/TypeArgumentsToParametersMapper.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/TypeArgumentsToParametersMapper.kt new file mode 100644 index 00000000000..df8c413b879 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/TypeArgumentsToParametersMapper.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.components + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability + +class TypeArgumentsToParametersMapper { + + sealed class TypeArgumentsMapping(val diagnostics: List) { + + abstract fun getTypeArgument(typeParameterDescriptor: TypeParameterDescriptor): TypeArgument + + object NoExplicitArguments : TypeArgumentsMapping(emptyList()) { + override fun getTypeArgument(typeParameterDescriptor: TypeParameterDescriptor): TypeArgument { + return TypeArgumentPlaceholder + } + } + + class TypeArgumentsMappingImpl( + diagnostics: List, + private val typeParameterToArgumentMap: Map + ): TypeArgumentsMapping(diagnostics) { + override fun getTypeArgument(typeParameterDescriptor: TypeParameterDescriptor): TypeArgument { + return typeParameterToArgumentMap[typeParameterDescriptor] ?: + error("No argument for parameter: $typeParameterDescriptor. Reported diagnostics: $diagnostics") + } + } + } + + fun mapTypeArguments(call: KotlinCall, descriptor: CallableDescriptor): TypeArgumentsMapping { + if (call.typeArguments.isEmpty()) { + return TypeArgumentsMapping.NoExplicitArguments + } + + if (call.typeArguments.size != descriptor.typeParameters.size) { + return TypeArgumentsMapping.TypeArgumentsMappingImpl( + listOf(WrongCountOfTypeArguments(descriptor, call.typeArguments.size)), emptyMap()) + } + else { + val typeParameterToArgumentMap = descriptor.typeParameters.zip(call.typeArguments).associate { it } + return TypeArgumentsMapping.TypeArgumentsMappingImpl(listOf(), typeParameterToArgumentMap) + } + } + +} + +class WrongCountOfTypeArguments(val descriptor: CallableDescriptor, val currentCount: Int) : + KotlinCallDiagnostic(ResolutionCandidateApplicability.INAPPLICABLE) { + override fun report(reporter: DiagnosticReporter) = reporter.onTypeArguments(this) +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt new file mode 100644 index 00000000000..6ae6468cbf8 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.inference + +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintPosition +import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable +import org.jetbrains.kotlin.resolve.calls.model.ResolvedKotlinCall +import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaArgument +import org.jetbrains.kotlin.types.TypeSubstitutor +import org.jetbrains.kotlin.types.UnwrappedType + + +interface ConstraintSystemBuilder { + val hasContradiction: Boolean + + fun registerVariable(variable: NewTypeVariable) + + fun addSubtypeConstraint(lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition) + fun addEqualityConstraint(a: UnwrappedType, b: UnwrappedType, position: ConstraintPosition) + + fun addInnerCall(innerCall: ResolvedKotlinCall.OnlyResolvedKotlinCall) + fun addLambdaArgument(resolvedLambdaArgument: ResolvedLambdaArgument) + + fun addSubtypeConstraintIfCompatible(lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition): Boolean + + fun isProperType(type: UnwrappedType): Boolean + + /** + * This function removes variables for which we know exact type. + * @return substitutor from typeVariable to result + */ + fun simplify(): TypeSubstitutor +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt new file mode 100644 index 00000000000..81bf200e5f3 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.inference + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns +import org.jetbrains.kotlin.types.TypeConstructorSubstitution +import org.jetbrains.kotlin.types.TypeSubstitutor +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.typeUtil.asTypeProjection + +fun ConstraintStorage.buildCurrentSubstitutor() = TypeConstructorSubstitution.createByConstructorsMap(fixedTypeVariables.entries.associate { + it.key to it.value.asTypeProjection() +}).buildSubstitutor() + +val CallableDescriptor.returnTypeOrNothing: UnwrappedType + get() { + returnType?.let { return it.unwrap() } + + return builtIns.nothingType + } + +fun TypeSubstitutor.substitute(type: UnwrappedType): UnwrappedType = safeSubstitute(type, Variance.INVARIANT).unwrap() + diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/NewConstraintSystem.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/NewConstraintSystem.kt new file mode 100644 index 00000000000..879c72220ec --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/NewConstraintSystem.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.inference + +import org.jetbrains.kotlin.resolve.calls.components.KotlinCallCompleter +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic + +interface NewConstraintSystem { + val diagnostics: List + + fun getBuilder(): ConstraintSystemBuilder + + // after this method we shouldn't mutate system via ConstraintSystemBuilder + fun asReadOnlyStorage(): ConstraintStorage + fun asCallCompleterContext(): KotlinCallCompleter.Context +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt new file mode 100644 index 00000000000..11b04f95bd0 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt @@ -0,0 +1,161 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.inference.components + +import org.jetbrains.kotlin.types.TypeApproximator +import org.jetbrains.kotlin.types.TypeApproximatorConfiguration +import org.jetbrains.kotlin.resolve.calls.inference.model.* +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.checker.CaptureStatus +import org.jetbrains.kotlin.types.checker.NewCapturedType +import org.jetbrains.kotlin.types.checker.NewCapturedTypeConstructor +import org.jetbrains.kotlin.types.typeUtil.asTypeProjection +import org.jetbrains.kotlin.types.typeUtil.contains +import org.jetbrains.kotlin.utils.SmartSet +import org.jetbrains.kotlin.utils.addIfNotNull +import java.util.* + +// todo problem: intersection types in constrains: A <: Number, B <: Inv =>? B <: Inv +class ConstraintIncorporator(val typeApproximator: TypeApproximator) { + + interface Context { + val allTypeVariablesWithConstraints: Collection + + // if such type variable is fixed then it is error + fun getTypeVariable(typeConstructor: TypeConstructor): NewTypeVariable? + + fun getConstraintsForVariable(typeVariable: NewTypeVariable): Collection + + fun addNewIncorporatedConstraint(lowerType: UnwrappedType, upperType: UnwrappedType, position: IncorporationConstraintPosition) + } + + // \alpha is typeVariable, \beta -- other type variable registered in ConstraintStorage + fun incorporate(c: Context, typeVariable: NewTypeVariable, constraint: Constraint, position: IncorporationConstraintPosition) { + // we shouldn't incorporate recursive constraint -- It is too dangerous + if (constraint.type.contains { it.constructor == typeVariable.freshTypeConstructor }) return + + directWithVariable(c, typeVariable, constraint, position) + otherInsideMyConstraint(c, typeVariable, constraint, position) + insideOtherConstraint(c, typeVariable, constraint, position) + } + + // A <:(=) \alpha <:(=) B => A <: B + private fun directWithVariable(c: Context, typeVariable: NewTypeVariable, constraint: Constraint, position: IncorporationConstraintPosition) { + // \alpha <: constraint.type + if (constraint.kind != ConstraintKind.LOWER) { + c.getConstraintsForVariable(typeVariable).forEach { + if (it.kind != ConstraintKind.UPPER) { + c.addNewIncorporatedConstraint(it.type, constraint.type, position) + } + } + } + + // constraint.type <: \alpha + if (constraint.kind != ConstraintKind.UPPER) { + c.getConstraintsForVariable(typeVariable).forEach { + if (it.kind != ConstraintKind.LOWER) { + c.addNewIncorporatedConstraint(constraint.type, it.type, position) + } + } + } + } + + // \alpha <: Inv<\beta>, \beta <: Number => \alpha <: Inv + private fun otherInsideMyConstraint(c: Context, typeVariable: NewTypeVariable, constraint: Constraint, position: IncorporationConstraintPosition) { + val otherInMyConstraint = SmartSet.create() + constraint.type.contains { + otherInMyConstraint.addIfNotNull(c.getTypeVariable(it.constructor)) + false + } + + for (otherTypeVariable in otherInMyConstraint) { + // to avoid ConcurrentModificationException + val otherConstraints = ArrayList(c.getConstraintsForVariable(otherTypeVariable)) + for (otherConstraint in otherConstraints) { + generateNewConstraint(c, typeVariable, constraint, otherTypeVariable, otherConstraint, position) + } + } + } + + // \alpha <: Number, \beta <: Inv<\alpha> => \beta <: Inv + private fun insideOtherConstraint(c: Context, typeVariable: NewTypeVariable, constraint: Constraint, position: IncorporationConstraintPosition) { + for (typeVariableWithConstraint in c.allTypeVariablesWithConstraints) { + val constraintsWhichConstraintMyVariable = typeVariableWithConstraint.constraints.filter { + it.type.contains { it.constructor == typeVariable.freshTypeConstructor } + } + constraintsWhichConstraintMyVariable.forEach { + generateNewConstraint(c, typeVariableWithConstraint.typeVariable, it, typeVariable, constraint, position) + } + } + } + + private fun generateNewConstraint( + c: Context, + targetVariable: NewTypeVariable, + baseConstraint: Constraint, + otherVariable: NewTypeVariable, + otherConstraint: Constraint, + position: IncorporationConstraintPosition + ) { + val typeForApproximation = when (otherConstraint.kind) { + ConstraintKind.EQUALITY -> { + baseConstraint.type.substitute(otherVariable, otherConstraint.type) + } + ConstraintKind.UPPER -> { + val newCapturedTypeConstructor = NewCapturedTypeConstructor(TypeProjectionImpl(Variance.OUT_VARIANCE, otherConstraint.type), + listOf(otherConstraint.type)) + val temporaryCapturedType = NewCapturedType(CaptureStatus.FOR_INCORPORATION, + newCapturedTypeConstructor, + lowerType = null) + baseConstraint.type.substitute(otherVariable, temporaryCapturedType) + } + ConstraintKind.LOWER -> { + val newCapturedTypeConstructor = NewCapturedTypeConstructor(TypeProjectionImpl(Variance.IN_VARIANCE, otherConstraint.type), + emptyList()) + val temporaryCapturedType = NewCapturedType(CaptureStatus.FOR_INCORPORATION, + newCapturedTypeConstructor, + lowerType = otherConstraint.type) + baseConstraint.type.substitute(otherVariable, temporaryCapturedType) + } + } + + if (baseConstraint.kind != ConstraintKind.UPPER) { + c.addNewIncorporatedConstraint(approximateCapturedTypes(typeForApproximation, toSuper = false), targetVariable.defaultType, position) + } + if (baseConstraint.kind != ConstraintKind.LOWER) { + c.addNewIncorporatedConstraint(targetVariable.defaultType, approximateCapturedTypes(typeForApproximation, toSuper = true), position) + } + } + + private fun UnwrappedType.substitute(typeVariable: NewTypeVariable, value: UnwrappedType): UnwrappedType { + val substitutor = TypeSubstitutor.create(mapOf(typeVariable.freshTypeConstructor to value.asTypeProjection())) + val type = substitutor.substitute(this, Variance.INVARIANT) ?: error("Impossible to substitute in $this: $typeVariable -> $value") + return type.unwrap() + } + + private fun approximateCapturedTypes(type: UnwrappedType, toSuper: Boolean): UnwrappedType = + if (toSuper) typeApproximator.approximateToSuperType(type, CapturedTypesApproximatorConfiguration) ?: type + else typeApproximator.approximateToSubType(type, CapturedTypesApproximatorConfiguration) ?: type + + + private object CapturedTypesApproximatorConfiguration : TypeApproximatorConfiguration.AllFlexibleSameValue() { + override val allFlexible get() = true + override val capturedType get() = { it: NewCapturedType -> it.captureStatus != CaptureStatus.FOR_INCORPORATION } + override val intersection get() = IntersectionStrategy.ALLOWED + override val typeVariable: (TypeVariableTypeConstructor) -> Boolean get() = { true } + } +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt new file mode 100644 index 00000000000..3f10a0b572b --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt @@ -0,0 +1,172 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.inference.components + +import org.jetbrains.kotlin.resolve.calls.inference.model.* +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic +import org.jetbrains.kotlin.types.FlexibleType +import org.jetbrains.kotlin.types.SimpleType +import org.jetbrains.kotlin.types.TypeConstructor +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.checker.CaptureStatus +import org.jetbrains.kotlin.types.checker.NewCapturedType +import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker +import org.jetbrains.kotlin.types.typeUtil.contains +import java.util.* + +class ConstraintInjector(val constraintIncorporator: ConstraintIncorporator) { + private val ALLOWED_DEPTH_DELTA_FOR_INCORPORATION = 3 + + interface Context { + val allTypeVariables: Map + + var maxTypeDepthFromInitialConstraints: Int + val notFixedTypeVariables: MutableMap + + fun addInitialConstraint(initialConstraint: InitialConstraint) + fun addError(error: KotlinCallDiagnostic) + } + + fun addInitialSubtypeConstraint(c: Context, lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition) { + c.addInitialConstraint(InitialConstraint(lowerType, upperType, ConstraintKind.UPPER, position)) + updateAllowedTypeDepth(c, lowerType) + updateAllowedTypeDepth(c, upperType) + addSubTypeConstraintAndIncorporateIt(c, lowerType, upperType, position) + } + + fun addInitialEqualityConstraint(c: Context, a: UnwrappedType, b: UnwrappedType, position: ConstraintPosition) { + c.addInitialConstraint(InitialConstraint(a, b, ConstraintKind.EQUALITY, position)) + updateAllowedTypeDepth(c, a) + updateAllowedTypeDepth(c, b) + addSubTypeConstraintAndIncorporateIt(c, a, b, position) + addSubTypeConstraintAndIncorporateIt(c, b, a, position) + } + + + private fun addSubTypeConstraintAndIncorporateIt(c: Context, lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition) { + val incorporatePosition = IncorporationConstraintPosition(position) + val possibleNewConstraints = Stack>() + val typeCheckerContext = TypeCheckerContext(c, position, lowerType, upperType, possibleNewConstraints) + typeCheckerContext.runIsSubtypeOf(lowerType, upperType) + + while (possibleNewConstraints.isNotEmpty()) { + val (typeVariable, constraint) = possibleNewConstraints.pop() + val constraints = c.notFixedTypeVariables[typeVariable.freshTypeConstructor] ?: typeCheckerContext.fixedTypeVariable(typeVariable) + + // it is important, that we add constraint here(not inside TypeCheckerContext), because inside incorporation we read constraints + constraints.addConstraint(constraint)?.let { + constraintIncorporator.incorporate(typeCheckerContext, typeVariable, it, incorporatePosition) + } + } + } + + private fun updateAllowedTypeDepth(c: Context, initialType: UnwrappedType) { + c.maxTypeDepthFromInitialConstraints = Math.max(c.maxTypeDepthFromInitialConstraints, initialType.typeDepth()) + } + + private fun UnwrappedType.typeDepth() = + when (this) { + is SimpleType -> typeDepth() + is FlexibleType -> Math.max(lowerBound.typeDepth(), upperBound.typeDepth()) + } + + private fun SimpleType.typeDepth(): Int { + val maxInArguments = arguments.asSequence().map { + if (it.isStarProjection) 1 else it.type.unwrap().typeDepth() + }.max() ?: 0 + + return maxInArguments + 1 + } + + private fun Context.isAllowedType(type: UnwrappedType) = type.typeDepth() <= maxTypeDepthFromInitialConstraints + ALLOWED_DEPTH_DELTA_FOR_INCORPORATION + + private inner class TypeCheckerContext( + val c: Context, + val position: ConstraintPosition, + val baseLowerType: UnwrappedType, + val baseUpperType: UnwrappedType, + val possibleNewConstraints: MutableList> = ArrayList() + ) : TypeCheckerContextForConstraintSystem(), ConstraintIncorporator.Context { + + fun runIsSubtypeOf(lowerType: UnwrappedType, upperType: UnwrappedType) { + with(NewKotlinTypeChecker) { + if (!this@TypeCheckerContext.isSubtypeOf(lowerType, upperType)) { + // todo improve error reporting -- add information about base types + c.addError(NewConstraintError(lowerType, upperType, position)) + } + } + } + + // from TypeCheckerContextForConstraintSystem + override fun isMyTypeVariable(type: SimpleType): Boolean = c.allTypeVariables.containsKey(type.constructor) + override fun addUpperConstraint(typeVariable: TypeConstructor, superType: UnwrappedType) = + addConstraint(typeVariable, superType, ConstraintKind.UPPER) + + override fun addLowerConstraint(typeVariable: TypeConstructor, subType: UnwrappedType) = + addConstraint(typeVariable, subType, ConstraintKind.LOWER) + + private fun addConstraint(typeVariableConstructor: TypeConstructor, type: UnwrappedType, kind: ConstraintKind) { + val typeVariable = c.allTypeVariables[typeVariableConstructor] + ?: error("Should by type variableConstructor: $typeVariableConstructor. ${c.allTypeVariables.values}") + + if (type.contains { + val captureStatus = (it as? NewCapturedType)?.captureStatus + assert(captureStatus != CaptureStatus.FOR_INCORPORATION) { + "Captured type for incorporation shouldn't escape from incorporation: $type\n" + renderBaseConstraint() + } + captureStatus != null && captureStatus != CaptureStatus.FROM_EXPRESSION + }) { + c.addError(CapturedTypeFromSubtyping(typeVariable, type, position)) + return + } + + if (!c.isAllowedType(type)) return + + val newConstraint = Constraint(kind, type, position) + possibleNewConstraints.add(typeVariable to newConstraint) + } + + // from ConstraintIncorporator.Context + override fun addNewIncorporatedConstraint(lowerType: UnwrappedType, upperType: UnwrappedType, position: IncorporationConstraintPosition) { + if (c.isAllowedType(lowerType) && c.isAllowedType(upperType)) { + runIsSubtypeOf(lowerType, upperType) + } + } + + override val allTypeVariablesWithConstraints: Collection + get() = c.notFixedTypeVariables.values + + override fun getTypeVariable(typeConstructor: TypeConstructor): NewTypeVariable? { + val typeVariable = c.allTypeVariables[typeConstructor] + if (typeVariable != null && !c.notFixedTypeVariables.containsKey(typeConstructor)) { + fixedTypeVariable(typeVariable) + } + return typeVariable + } + + override fun getConstraintsForVariable(typeVariable: NewTypeVariable) = + c.notFixedTypeVariables[typeVariable.freshTypeConstructor]?.constraints + ?: fixedTypeVariable(typeVariable) + + fun fixedTypeVariable(variable: NewTypeVariable): Nothing { + error("Type variable $variable should not be fixed!\n" + + renderBaseConstraint()) + } + + private fun renderBaseConstraint() = "Base constraint: $baseLowerType <: $baseUpperType from position: $position" + } +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/FixationOrderCalculator.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/FixationOrderCalculator.kt new file mode 100644 index 00000000000..692acad0218 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/FixationOrderCalculator.kt @@ -0,0 +1,209 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.inference.components + +import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind +import org.jetbrains.kotlin.resolve.calls.inference.model.LambdaTypeVariable +import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints +import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaArgument +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker +import org.jetbrains.kotlin.types.checker.isIntersectionType +import org.jetbrains.kotlin.utils.DFS +import org.jetbrains.kotlin.utils.SmartList +import java.util.* + +private typealias Variable = VariableWithConstraints + +class FixationOrderCalculator { + enum class ResolveDirection { + TO_SUBTYPE, + TO_SUPERTYPE, + UNKNOWN + } + + data class NodeWithDirection(val variableWithConstraints: VariableWithConstraints, val direction: ResolveDirection) { + override fun toString() = "$variableWithConstraints to $direction" + } + + interface Context { + val notFixedTypeVariables: Map + val lambdaArguments: List + } + + fun computeCompletionOrder( + c: Context, + topReturnType: UnwrappedType + ): List = DependencyGraph(c).getCompletionOrder(topReturnType) + + private class DependencyGraph(val c: Context) { + private val directions = HashMap() + + // first in the list -- first fix + fun getCompletionOrder(topReturnType: UnwrappedType): List { + setupDirections(topReturnType) + + return topologicalOrderWith0Priority().map { NodeWithDirection(it, directions[it] ?: ResolveDirection.UNKNOWN) } + } + + private fun topologicalOrderWith0Priority(): List { + val handler = object : DFS.CollectingNodeHandler>(LinkedHashSet()) { + override fun afterChildren(current: Variable) { + // we have guaranty that from end of 0 edge there is no other edges with priority 0 + result.addAll(get0Edges(current)) + + result.add(current) + } + } + + for (typeVariable in c.notFixedTypeVariables.values) { + DFS.doDfs(typeVariable, DFS.Neighbors(this::getEdges), DFS.VisitedWithSet(), handler) + } + return handler.result().toList() + } + + + private fun setupDirections(topReturnType: UnwrappedType) { + topReturnType.visitType(ResolveDirection.TO_SUBTYPE) { variableWithConstraints, direction -> + enterToNode(variableWithConstraints, direction) + } + for (resolvedLambdaArgument in c.lambdaArguments) { + inner@ for (typeVariable in resolvedLambdaArgument.myTypeVariables) { + if (typeVariable.kind == LambdaTypeVariable.Kind.RETURN_TYPE) continue@inner + + c.notFixedTypeVariables[typeVariable.freshTypeConstructor]?.let { + enterToNode(it, ResolveDirection.TO_SUBTYPE) + } + } + } + } + + private fun enterToNode(variable: Variable, direction: ResolveDirection) { + if (direction == ResolveDirection.UNKNOWN) return + + val previous = directions[variable] + if (previous != null) { + if (previous != direction) { + directions[variable] = ResolveDirection.UNKNOWN + } + return + } + + directions[variable] = direction + + for ((otherVariable, otherDirection) in get12Edges(variable, direction)) { + enterToNode(otherVariable, otherDirection) + } + } + + private fun getEdges(variable: Variable): List { + val direction = directions[variable] ?: ResolveDirection.UNKNOWN + return get12Edges(variable, direction).map(NodeWithDirection::variableWithConstraints) + get0Edges(variable) + } + + /** + * Now we use only priority 0 and {1, 2}. + * Current vision of edge priority for type variable \alpha to variable \beta: + * 0 -- { \beta -> \alpha } i.e. return type depend of all parameters types of lambda + * 1 -- \alpha <: Inv<\beta> or \alpha >: Pair, Int> ot \alpha <: \beta & Any + * 2 -- \alpha <: \beta or \alpha >: \beta? + */ + private fun get12Edges(variableWithConstraints: Variable, direction: ResolveDirection, include2: Boolean = true): List { + fun isNotInterestingConstraint(direction: ResolveDirection, constraint: Constraint): Boolean { + return (direction == ResolveDirection.TO_SUBTYPE && constraint.kind == ConstraintKind.UPPER) || + (direction == ResolveDirection.TO_SUPERTYPE && constraint.kind == ConstraintKind.LOWER) + } + + val result = SmartList() + + for (constraint in variableWithConstraints.constraints) { + if (isNotInterestingConstraint(direction, constraint)) continue + + if (include2 || !c.notFixedTypeVariables.containsKey(constraint.type.constructor)) { // because we collect only type 1 of edges + constraint.type.visitType(direction) { variable, direction -> + result.add(NodeWithDirection(variable, direction)) + } + } + } + + return result + } + + private fun get0Edges(variable: Variable): List { + val typeVariable = variable.typeVariable + if (typeVariable !is LambdaTypeVariable || typeVariable.kind != LambdaTypeVariable.Kind.RETURN_TYPE) return emptyList() + + val resolvedLambdaArgument = c.lambdaArguments.find { it.argument == typeVariable.lambdaArgument } ?: + error("Missing resolved lambda argument for ${typeVariable.lambdaArgument}") + + return resolvedLambdaArgument.myTypeVariables.mapNotNull { + if (it.kind == LambdaTypeVariable.Kind.RETURN_TYPE) return@mapNotNull null + c.notFixedTypeVariables[it.freshTypeConstructor] + } + } + + + private fun UnwrappedType.visitType(startDirection: ResolveDirection, action: (variable: Variable, direction: ResolveDirection) -> Unit) = + when (this) { + is SimpleType -> visitType(startDirection, action) + is FlexibleType -> { + lowerBound.visitType(startDirection, action) + upperBound.visitType(startDirection, action) + } + } + + private fun SimpleType.visitType(startDirection: ResolveDirection, action: (variable: Variable, direction: ResolveDirection) -> Unit) { + if (isIntersectionType) { + constructor.supertypes.forEach { + it.unwrap().visitType(startDirection, action) + } + return + } + + if (arguments.isEmpty()) { + c.notFixedTypeVariables[constructor]?.let { + action(it, startDirection) + } + return + } + + val parameters = constructor.parameters + if (parameters.size != arguments.size) return // incorrect type + + fun ResolveDirection.opposite() = when (this) { + ResolveDirection.UNKNOWN -> ResolveDirection.UNKNOWN + ResolveDirection.TO_SUPERTYPE -> ResolveDirection.TO_SUBTYPE + ResolveDirection.TO_SUBTYPE -> ResolveDirection.TO_SUPERTYPE + } + + for ((argument, parameter) in arguments.zip(parameters)) { + if (argument.isStarProjection) continue + + val variance = NewKotlinTypeChecker.effectiveVariance(parameter.variance, argument.projectionKind) ?: Variance.INVARIANT + val innerDirection = when (variance) { + Variance.INVARIANT -> ResolveDirection.UNKNOWN + Variance.OUT_VARIANCE -> startDirection + Variance.IN_VARIANCE -> startDirection.opposite() + } + + argument.type.unwrap().visitType(innerDirection, action) + } + } + } + +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt new file mode 100644 index 00000000000..e4a26717f66 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt @@ -0,0 +1,108 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.inference.components + +import org.jetbrains.kotlin.resolve.calls.components.CommonSupertypeCalculator +import org.jetbrains.kotlin.resolve.calls.inference.components.FixationOrderCalculator.ResolveDirection +import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind +import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints +import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.checker.intersectTypes +import org.jetbrains.kotlin.types.singleBestRepresentative +import java.util.* + +class ResultTypeResolver(val commonSupertypeCalculator: CommonSupertypeCalculator) { + interface Context { + fun isProperType(type: UnwrappedType): Boolean + } + + fun findResultType(c: Context, variableWithConstraints: VariableWithConstraints, direction: ResolveDirection): UnwrappedType? { + findResultIfThereIsEqualsConstraint(c, variableWithConstraints, allowedFixToNotProperType = false)?.let { return it } + + if (direction == ResolveDirection.TO_SUBTYPE || direction == ResolveDirection.UNKNOWN) { + val lowerConstraints = variableWithConstraints.constraints.filter { it.kind == ConstraintKind.LOWER && c.isProperType(it.type) } + if (lowerConstraints.isNotEmpty()) { + return commonSupertypeCalculator(convertLowerTypesWithKnowledgeOfNumberTypes(lowerConstraints)) + } + } + + // direction == TO_LOWER or there is no LOWER bounds + val upperConstraints = variableWithConstraints.constraints.filter { it.kind == ConstraintKind.UPPER && c.isProperType(it.type) } + if (upperConstraints.isNotEmpty()) { + return intersectTypes(upperConstraints.map { it.type }) + } + + return null + } + + fun findResultIfThereIsEqualsConstraint( + c: Context, + variableWithConstraints: VariableWithConstraints, + allowedFixToNotProperType: Boolean = false + ): UnwrappedType? { + val properEqualsConstraint = variableWithConstraints.constraints.filter { + it.kind == ConstraintKind.EQUALITY && c.isProperType(it.type) + } + + if (properEqualsConstraint.isNotEmpty()) { + return properEqualsConstraint.map { it.type }.singleBestRepresentative()?.unwrap() + ?: properEqualsConstraint.first().type // seems like constraint system has contradiction + } + if (!allowedFixToNotProperType) return null + + val notProperEqualsConstraint = variableWithConstraints.constraints.filter { it.kind == ConstraintKind.EQUALITY } + + // may be we should just firstOrNull + return notProperEqualsConstraint.singleOrNull()?.type + } + + + private fun convertLowerTypesWithKnowledgeOfNumberTypes(lowerConstraints: Collection): Collection { + if (lowerConstraints.isEmpty()) return emptyList() + if (lowerConstraints.size == 1) return listOf(lowerConstraints.first().type) + + val (numberLowerBounds, generalLowerBounds) = lowerConstraints.map { it.type }.partition { it.constructor is IntegerValueTypeConstructor } + + val numberType = commonSupertypeForNumberTypes(numberLowerBounds) ?: return generalLowerBounds + return generalLowerBounds + numberType + } + + + private fun commonSupertypeForNumberTypes(numberLowerBounds: Collection): UnwrappedType? { + if (numberLowerBounds.isEmpty()) return null + val intersectionOfSupertypes = getIntersectionOfSupertypes(numberLowerBounds) + return TypeUtils.getDefaultPrimitiveNumberType(intersectionOfSupertypes)?.unwrap() ?: + commonSupertypeCalculator(numberLowerBounds) + } + + private fun getIntersectionOfSupertypes(types: Collection): Set { + val upperBounds = HashSet() + for (type in types) { + val supertypes = type.constructor.supertypes.map { it.unwrap() } + if (upperBounds.isEmpty()) { + upperBounds.addAll(supertypes) + } + else { + upperBounds.retainAll(supertypes) + } + } + return upperBounds + } +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/SimpleConstraintSystemImpl.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/SimpleConstraintSystemImpl.kt new file mode 100644 index 00000000000..04b24089e0d --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/SimpleConstraintSystemImpl.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.inference.components + +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallKind +import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder +import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl +import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition +import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableFromCallableDescriptor +import org.jetbrains.kotlin.resolve.calls.model.KotlinCall +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallArgument +import org.jetbrains.kotlin.resolve.calls.model.ReceiverKotlinCallArgument +import org.jetbrains.kotlin.resolve.calls.model.TypeArgument +import org.jetbrains.kotlin.resolve.calls.results.SimpleConstraintSystem +import org.jetbrains.kotlin.types.TypeConstructorSubstitution +import org.jetbrains.kotlin.types.TypeSubstitutor +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.typeUtil.asTypeProjection +import java.lang.UnsupportedOperationException + + +class SimpleConstraintSystemImpl(constraintInjector: ConstraintInjector, resultTypeResolver: ResultTypeResolver) : SimpleConstraintSystem { + val csBuilder: ConstraintSystemBuilder = NewConstraintSystemImpl(constraintInjector, resultTypeResolver).getBuilder() + + override fun registerTypeVariables(typeParameters: Collection): TypeSubstitutor { + val substitutionMap = typeParameters.associate { + val variable = TypeVariableFromCallableDescriptor(ThrowableKotlinCall, it) + csBuilder.registerVariable(variable) + + it.defaultType.constructor to variable.defaultType.asTypeProjection() + } + return TypeConstructorSubstitution.createByConstructorsMap(substitutionMap).buildSubstitutor() + } + + override fun addSubtypeConstraint(subType: UnwrappedType, superType: UnwrappedType) { + csBuilder.addSubtypeConstraint(subType, superType, SimpleConstraintSystemConstraintPosition) + } + + override fun hasContradiction() = csBuilder.hasContradiction + + private object ThrowableKotlinCall : KotlinCall { + override val callKind: KotlinCallKind get() = throw UnsupportedOperationException() + override val explicitReceiver: ReceiverKotlinCallArgument? get() = throw UnsupportedOperationException() + override val name: Name get() = throw UnsupportedOperationException() + override val typeArguments: List get() = throw UnsupportedOperationException() + override val argumentsInParenthesis: List get() = throw UnsupportedOperationException() + override val externalArgument: KotlinCallArgument? get() = throw UnsupportedOperationException() + override val isInfixCall: Boolean get() = throw UnsupportedOperationException() + override val isOperatorCall: Boolean get() = throw UnsupportedOperationException() + } +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeCheckerContextForConstraintSystem.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeCheckerContextForConstraintSystem.kt new file mode 100644 index 00000000000..8d5e180b83f --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/TypeCheckerContextForConstraintSystem.kt @@ -0,0 +1,137 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.inference.components + +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.checker.* +import org.jetbrains.kotlin.types.typeUtil.builtIns + +abstract class TypeCheckerContextForConstraintSystem : TypeCheckerContext(errorTypeEqualsToAnything = true, allowedTypeVariable = false) { + + abstract fun isMyTypeVariable(type: SimpleType): Boolean + + // super and sub type isSingleClassifierType + abstract fun addUpperConstraint(typeVariable: TypeConstructor, superType: UnwrappedType) + abstract fun addLowerConstraint(typeVariable: TypeConstructor, subType: UnwrappedType) + + override final fun addSubtypeConstraint(subType: UnwrappedType, superType: UnwrappedType): Boolean? { + assertInputTypes(subType, superType) + + var answer: Boolean? = null + + if (superType.anyBound(this::isMyTypeVariable)) { + answer = simplifyLowerConstraint(superType, subType) + } + + if (subType.anyBound(this::isMyTypeVariable)) { + return simplifyUpperConstraint(subType, superType) && (answer ?: true) + } + else { + return simplifyConstraintForPossibleIntersectionSubType(subType, superType) ?: answer + } + } + + /** + * Foo <: T! <=> Foo <: T? <=> Foo & Any <: T + * Foo <: T? <=> Foo & Any <: T + * Foo <: T -- leave as is + */ + fun simplifyLowerConstraint(typeVariable: UnwrappedType, subType: UnwrappedType): Boolean { + @Suppress("NAME_SHADOWING") + val typeVariable = typeVariable.upperIfFlexible() + + if (typeVariable.isMarkedNullable) { + addLowerConstraint(typeVariable.constructor, intersectTypes(listOf(subType, subType.builtIns.anyType))) + } + else { + addLowerConstraint(typeVariable.constructor, subType) + } + + return true + } + + /** + * T! <: Foo <=> T <: Foo + * T? <: Foo <=> T <: Foo && Nothing? <: Foo + * T <: Foo -- leave as is + */ + fun simplifyUpperConstraint(typeVariable: UnwrappedType, superType: UnwrappedType): Boolean { + @Suppress("NAME_SHADOWING") + val typeVariable = typeVariable.lowerIfFlexible() + + addUpperConstraint(typeVariable.constructor, superType) + + if (typeVariable.isMarkedNullable) { + // here is important that superType is singleClassifierType + return if (superType.anyBound(this::isMyTypeVariable)) { + simplifyLowerConstraint(superType, typeVariable) + } + else { + isSubtypeOfByTypeChecker(typeVariable.builtIns.nullableNothingType, superType) + } + } + + return true + } + + fun simplifyConstraintForPossibleIntersectionSubType(subType: UnwrappedType, superType: UnwrappedType): Boolean? { + @Suppress("NAME_SHADOWING") + val subType = subType.lowerIfFlexible() + + if (!subType.isIntersectionType) return null + + assert(!subType.isMarkedNullable) { "Intersection type should not be marked nullable!: $subType" } + + // TODO: may be we lose flexibility here + val subIntersectionTypes = (subType.constructor as IntersectionTypeConstructor).supertypes.map { it.lowerIfFlexible() } + + val typeVariables = subIntersectionTypes.filter(this::isMyTypeVariable).takeIf { it.isNotEmpty() } ?: return null + val notTypeVariables = subIntersectionTypes.filterNot(this::isMyTypeVariable) + + // todo: may be we can do better then that. + if (notTypeVariables.isNotEmpty() && NewKotlinTypeChecker.isSubtypeOf(intersectTypes(notTypeVariables), superType)) { + return true + } + + return typeVariables.all { simplifyUpperConstraint(it, superType) } + } + + private fun isSubtypeOfByTypeChecker(subType: UnwrappedType, superType: UnwrappedType) = + with(NewKotlinTypeChecker) { this@TypeCheckerContextForConstraintSystem.isSubtypeOf(subType, superType) } + + private fun assertInputTypes(subType: UnwrappedType, superType: UnwrappedType) { + fun correctSubType(subType: SimpleType) = subType.isSingleClassifierType || subType.isIntersectionType || isMyTypeVariable(subType) + fun correctSuperType(superType: SimpleType) = superType.isSingleClassifierType || isMyTypeVariable(superType) + + assert(subType.bothBounds(::correctSubType)) { + "Not singleClassifierType and not intersection subType: $subType" + } + assert(superType.bothBounds(::correctSuperType)) { + "Not singleClassifierType superType: $superType" + } + } + + private inline fun UnwrappedType.bothBounds(f: (SimpleType) -> Boolean) = when (this) { + is SimpleType -> f(this) + is FlexibleType -> f(lowerBound) && f(upperBound) + } + + private inline fun UnwrappedType.anyBound(f: (SimpleType) -> Boolean) = when (this) { + is SimpleType -> f(this) + is FlexibleType -> f(lowerBound) || f(upperBound) + } +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt new file mode 100644 index 00000000000..ca46a6e97d1 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.inference.model + +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability +import org.jetbrains.kotlin.types.UnwrappedType + + +sealed class ConstraintPosition + +class ExplicitTypeParameterConstraintPosition(val typeArgument: SimpleTypeArgument) : ConstraintPosition() { + override fun toString() = "TypeParameter $typeArgument" +} +class ExpectedTypeConstraintPosition(val topLevelCall: KotlinCall) : ConstraintPosition() { + override fun toString() = "ExpectedType for call $topLevelCall" +} +class DeclaredUpperBoundConstraintPosition(val typeParameterDescriptor: TypeParameterDescriptor) : ConstraintPosition() { + override fun toString() = "DeclaredUpperBound ${typeParameterDescriptor.name} from ${typeParameterDescriptor.containingDeclaration}" +} +class ArgumentConstraintPosition(val argument: KotlinCallArgument) : ConstraintPosition() { + override fun toString() = "Argument $argument" +} +class FixVariableConstraintPosition(val variable: NewTypeVariable) : ConstraintPosition() { + override fun toString() = "Fix variable $variable" +} + +class IncorporationConstraintPosition(val from: ConstraintPosition) : ConstraintPosition() { + override fun toString() = "Incorporate $from" +} + +@Deprecated("Should be used only in SimpleConstraintSystemImpl") +object SimpleConstraintSystemConstraintPosition : ConstraintPosition() + + +class NewConstraintError(val lowerType: UnwrappedType, val upperType: UnwrappedType, val position: ConstraintPosition): + KotlinCallDiagnostic(ResolutionCandidateApplicability.INAPPLICABLE) { + override fun report(reporter: DiagnosticReporter) = reporter.constraintError(this) +} + +class CapturedTypeFromSubtyping(val typeVariable: NewTypeVariable, val constraintType: UnwrappedType, val position: ConstraintPosition) : + KotlinCallDiagnostic(ResolutionCandidateApplicability.INAPPLICABLE) { + override fun report(reporter: DiagnosticReporter) = reporter.constraintError(this) +} + +class NotEnoughInformationForTypeParameter(val typeVariable: NewTypeVariable) : KotlinCallDiagnostic(ResolutionCandidateApplicability.INAPPLICABLE) { + override fun report(reporter: DiagnosticReporter) = reporter.constraintError(this) +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt new file mode 100644 index 00000000000..da10af33336 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt @@ -0,0 +1,134 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.inference.model + +import org.jetbrains.kotlin.resolve.calls.inference.substitute +import org.jetbrains.kotlin.resolve.calls.model.ResolvedKotlinCall +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic +import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaArgument +import org.jetbrains.kotlin.types.TypeConstructor +import org.jetbrains.kotlin.types.TypeSubstitutor +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.checker.KotlinTypeChecker + +/** + * Every type variable can be in the following states: + * - not fixed => there is several constraints for this type variable(possible no one). + * for this type variable we have VariableWithConstraints in map notFixedTypeVariables + * - fixed to proper type or not proper type. For such type variable there is no VariableWithConstraints in notFixedTypeVariables. + * Also we should guaranty that there is no other constraints in other VariableWithConstraints which depends on this fixed type variable. + * + * Note: fixedTypeVariables can contains a proper and not proper type. + * + * Fixing procedure(to proper types). First of all we should determinate fixing order. + * After it, for every type variable we do the following: + * - determinate result proper type + * - add equality constraint, for example: T = Int + * - run incorporation and generate all new constraints + * - after is we remove VariableWithConstraints for type variable T from map notFixedTypeVariables + * - also we remove all constraint in other variable which contains T + * - add result type to fixedTypeVariables. + * + * Note fixing procedure to not proper type the same. The only difference in determination result type. + * + */ + +interface ConstraintStorage { + val allTypeVariables: Map + val notFixedTypeVariables: Map + val initialConstraints: List + val maxTypeDepthFromInitialConstraints: Int + val errors: List + val fixedTypeVariables: Map + val lambdaArguments: List + val innerCalls: List + + object Empty : ConstraintStorage { + override val allTypeVariables: Map get() = emptyMap() + override val notFixedTypeVariables: Map get() = emptyMap() + override val initialConstraints: List get() = emptyList() + override val maxTypeDepthFromInitialConstraints: Int get() = 1 + override val errors: List get() = emptyList() + override val fixedTypeVariables: Map get() = emptyMap() + override val lambdaArguments: List get() = emptyList() + override val innerCalls: List get() = emptyList() + } +} + +enum class ConstraintKind { + LOWER, + UPPER, + EQUALITY +} + +class Constraint( + val kind: ConstraintKind, + val type: UnwrappedType, // flexible types here is allowed + val position: ConstraintPosition, + val typeHashCode: Int = type.hashCode() +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other?.javaClass != javaClass) return false + + other as Constraint + + if (typeHashCode != other.typeHashCode) return false + if (kind != other.kind) return false + if (position != other.position) return false + if (type != other.type) return false + + return true + } + + override fun hashCode() = typeHashCode + + override fun toString() = "$kind($type) from $position" +} + +interface VariableWithConstraints { + val typeVariable: NewTypeVariable + val constraints: List +} + +class InitialConstraint( + val a: UnwrappedType, + val b: UnwrappedType, + val constraintKind: ConstraintKind, // see [checkConstraint] + val position: ConstraintPosition +) { + override fun toString(): String { + val sign = + when (constraintKind) { + ConstraintKind.EQUALITY -> "==" + ConstraintKind.LOWER -> ":>" + ConstraintKind.UPPER -> "<:" + } + return "$a $sign $b from $position" + } +} + +fun InitialConstraint.checkConstraint(substitutor: TypeSubstitutor): Boolean { + val newA = substitutor.substitute(a) + val newB = substitutor.substitute(a) + val typeChecker = KotlinTypeChecker.DEFAULT + return when (constraintKind) { + ConstraintKind.EQUALITY -> typeChecker.equalTypes(newA, newB) + ConstraintKind.UPPER -> typeChecker.isSubtypeOf(newA, newB) + ConstraintKind.LOWER -> typeChecker.isSubtypeOf(newB, newA) + } +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt new file mode 100644 index 00000000000..c51002a0e8b --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt @@ -0,0 +1,97 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.inference.model + +import org.jetbrains.kotlin.resolve.calls.model.ResolvedKotlinCall +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic +import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaArgument +import org.jetbrains.kotlin.types.TypeConstructor +import org.jetbrains.kotlin.types.UnwrappedType +import java.util.* + + +class MutableVariableWithConstraints( + override val typeVariable: NewTypeVariable, + constraints: Collection = emptyList() +) : VariableWithConstraints { + override val constraints: List get() = mutableConstraints + private val mutableConstraints = MyArrayList(constraints) + + // return new actual constraint, if this constraint is new + fun addConstraint(constraint: Constraint): Constraint? { + val previousConstraintWithSameType = constraints.filter { it.typeHashCode == constraint.typeHashCode && it.type == constraint.type } + + if (previousConstraintWithSameType.any { newConstraintIsUseless(it.kind, constraint.kind) }) { + return null + } + + val actualConstraint = if (previousConstraintWithSameType.isNotEmpty()) { + // i.e. previous is LOWER and new is UPPER or opposite situation + Constraint(ConstraintKind.EQUALITY, constraint.type, constraint.position, constraint.typeHashCode) + } + else { + constraint + } + mutableConstraints.add(actualConstraint) + return actualConstraint + } + + fun removeLastConstraints(shouldRemove: (Constraint) -> Boolean) { + mutableConstraints.removeLast(shouldRemove) + } + + // todo optimize it! + fun removeConstrains(shouldRemove: (Constraint) -> Boolean) { + val newConstraints = mutableConstraints.filter { !shouldRemove(it) } + mutableConstraints.clear() + mutableConstraints.addAll(newConstraints) + } + + private fun newConstraintIsUseless(oldKind: ConstraintKind, newKind: ConstraintKind) = + when (oldKind) { + ConstraintKind.EQUALITY -> true + ConstraintKind.LOWER -> newKind == ConstraintKind.LOWER + ConstraintKind.UPPER -> newKind == ConstraintKind.UPPER + } + + private class MyArrayList(c: Collection): ArrayList(c) { + fun removeLast(predicate: (E) -> Boolean) { + val newSize = indexOfLast { !predicate(it) } + 1 + + if (newSize != size) { + removeRange(newSize, size) + } + } + } + + override fun toString(): String { + return "Constraints for $typeVariable" + } + +} + +// todo may be we should use LinkedHasMap +class MutableConstraintStorage : ConstraintStorage { + override val allTypeVariables: MutableMap = HashMap() + override val notFixedTypeVariables: MutableMap = HashMap() + override val initialConstraints: MutableList = ArrayList() + override var maxTypeDepthFromInitialConstraints: Int = 1 + override val errors: MutableList = ArrayList() + override val fixedTypeVariables: MutableMap = HashMap() + override val lambdaArguments: MutableList = ArrayList() + override val innerCalls: MutableList = ArrayList() +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt new file mode 100644 index 00000000000..ffd350aea13 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt @@ -0,0 +1,254 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.inference.model + +import org.jetbrains.kotlin.resolve.calls.components.KotlinCallCompleter +import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder +import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem +import org.jetbrains.kotlin.resolve.calls.inference.buildCurrentSubstitutor +import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintInjector +import org.jetbrains.kotlin.resolve.calls.inference.components.FixationOrderCalculator +import org.jetbrains.kotlin.resolve.calls.inference.components.ResultTypeResolver +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic +import org.jetbrains.kotlin.resolve.calls.model.ResolvedKotlinCall +import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaArgument +import org.jetbrains.kotlin.resolve.calls.tower.isSuccess +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.typeUtil.asTypeProjection +import org.jetbrains.kotlin.types.typeUtil.contains +import java.util.* + +class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val resultTypeResolver: ResultTypeResolver): + NewConstraintSystem, + ConstraintSystemBuilder, + ConstraintInjector.Context, + ResultTypeResolver.Context, + KotlinCallCompleter.Context, + FixationOrderCalculator.Context +{ + val storage = MutableConstraintStorage() + private var state = State.BUILDING + + private enum class State { + BUILDING, + FREEZED, + COMPLETION + } + + private fun checkState(vararg allowedState: State) { + assert(state in allowedState) { + "State $state is not allowed. AllowedStates: ${allowedState.joinToString()}" + } + } + + override val diagnostics: List + get() = storage.errors + + override fun getBuilder() = apply { checkState(State.BUILDING, State.COMPLETION) } + + override fun asConstraintInjectorContext() = apply { checkState(State.BUILDING, State.COMPLETION) } + + override fun asReadOnlyStorage(): ConstraintStorage { + checkState(State.BUILDING, State.FREEZED) + state = State.FREEZED + return storage + } + + override fun asCallCompleterContext(): KotlinCallCompleter.Context { + checkState(State.BUILDING, State.COMPLETION) + state = State.COMPLETION + return this + } + + // ConstraintSystemBuilder + override fun registerVariable(variable: NewTypeVariable) { + checkState(State.BUILDING, State.COMPLETION) + + storage.allTypeVariables[variable.freshTypeConstructor] = variable + storage.notFixedTypeVariables[variable.freshTypeConstructor] = MutableVariableWithConstraints(variable) + } + + override fun addSubtypeConstraint(lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition) = + constraintInjector.addInitialSubtypeConstraint(apply { checkState(State.BUILDING, State.COMPLETION) }, lowerType, upperType, position) + + override fun addEqualityConstraint(a: UnwrappedType, b: UnwrappedType, position: ConstraintPosition) = + constraintInjector.addInitialEqualityConstraint(apply { checkState(State.BUILDING, State.COMPLETION) }, a, b, position) + + override fun addLambdaArgument(resolvedLambdaArgument: ResolvedLambdaArgument) { + checkState(State.BUILDING, State.COMPLETION) + storage.lambdaArguments.add(resolvedLambdaArgument) + } + + override fun addSubtypeConstraintIfCompatible(lowerType: UnwrappedType, upperType: UnwrappedType, position: ConstraintPosition): Boolean { + checkState(State.BUILDING, State.COMPLETION) + + if (hasContradiction) return false + addSubtypeConstraint(lowerType, upperType, position) + if (!hasContradiction) return true + + val shouldRemove = { c: Constraint -> c.position === position || + (c.position is IncorporationConstraintPosition && c.position.from === position) } + + for (variableWithConstraint in storage.notFixedTypeVariables.values) { + variableWithConstraint.removeLastConstraints(shouldRemove) + } + storage.errors.clear() + storage.initialConstraints.removeAt(storage.initialConstraints.lastIndex) + + return false + } + + private fun getVariablesForFixation(): Map { + val fixedVariables = LinkedHashMap() + + for (variableWithConstrains in storage.notFixedTypeVariables.values) { + val resultType = resultTypeResolver.findResultIfThereIsEqualsConstraint(apply { checkState(State.BUILDING) }, variableWithConstrains, + allowedFixToNotProperType = false) + if (resultType != null) { + fixedVariables[variableWithConstrains.typeVariable] = resultType + } + } + return fixedVariables + } + + override fun simplify(): TypeSubstitutor { + checkState(State.BUILDING) + + var fixedVariables = getVariablesForFixation() + while (fixedVariables.isNotEmpty()) { + for ((variable, resultType) in fixedVariables) { + fixVariable(variable, resultType) + } + fixedVariables = getVariablesForFixation() + } + + return storage.buildCurrentSubstitutor() + } + + // ConstraintSystemBuilder, KotlinCallCompleter.Context + override val hasContradiction: Boolean + get() = diagnostics.any { !it.candidateApplicability.isSuccess }.apply { checkState(State.BUILDING, State.COMPLETION) } + + override fun addInnerCall(innerCall: ResolvedKotlinCall.OnlyResolvedKotlinCall) { + checkState(State.BUILDING, State.COMPLETION) + storage.innerCalls.add(innerCall) + + val otherSystem = innerCall.candidate.lastCall.constraintSystem.asReadOnlyStorage() + storage.allTypeVariables.putAll(otherSystem.allTypeVariables) + for ((variable, constraints) in otherSystem.notFixedTypeVariables) { + notFixedTypeVariables[variable] = MutableVariableWithConstraints(constraints.typeVariable, constraints.constraints) + } + storage.initialConstraints.addAll(otherSystem.initialConstraints) + storage.maxTypeDepthFromInitialConstraints = Math.max(storage.maxTypeDepthFromInitialConstraints, otherSystem.maxTypeDepthFromInitialConstraints) + storage.errors.addAll(otherSystem.errors) + storage.fixedTypeVariables.putAll(otherSystem.fixedTypeVariables) + storage.lambdaArguments.addAll(otherSystem.lambdaArguments) + storage.innerCalls.addAll(otherSystem.innerCalls) + } + + + // ResultTypeResolver.Context, ConstraintSystemBuilder + override fun isProperType(type: UnwrappedType): Boolean { + checkState(State.BUILDING, State.COMPLETION) + return !type.contains { + storage.allTypeVariables.containsKey(it.constructor) + } + } + + // ConstraintInjector.Context + override val allTypeVariables: Map get() { + checkState(State.BUILDING, State.COMPLETION) + return storage.allTypeVariables + } + + override var maxTypeDepthFromInitialConstraints: Int + get() = storage.maxTypeDepthFromInitialConstraints + set(value) { + checkState(State.BUILDING, State.COMPLETION) + storage.maxTypeDepthFromInitialConstraints = value + } + + override fun addInitialConstraint(initialConstraint: InitialConstraint) { + checkState(State.BUILDING, State.COMPLETION) + storage.initialConstraints.add(initialConstraint) + } + + // ConstraintInjector.Context, FixationOrderCalculator.Context + override val notFixedTypeVariables: MutableMap get() { + checkState(State.BUILDING, State.COMPLETION) + return storage.notFixedTypeVariables + } + + // ConstraintInjector.Context, KotlinCallCompleter.Context + override fun addError(error: KotlinCallDiagnostic) { + checkState(State.BUILDING, State.COMPLETION) + storage.errors.add(error) + } + + // FixationOrderCalculator.Context, KotlinCallCompleter.Context + override val lambdaArguments: List get() { + checkState(State.COMPLETION) + return storage.lambdaArguments + } + + // KotlinCallCompleter.Context + override fun asResultTypeResolverContext() = apply { checkState(State.COMPLETION) } + + override fun asFixationOrderCalculatorContext() = apply { checkState(State.COMPLETION) } + + override fun fixVariable(variable: NewTypeVariable, resultType: UnwrappedType) { + checkState(State.BUILDING, State.COMPLETION) + + constraintInjector.addInitialEqualityConstraint(this, variable.defaultType, resultType, FixVariableConstraintPosition(variable)) + notFixedTypeVariables.remove(variable.freshTypeConstructor) + + for (variableWithConstraint in notFixedTypeVariables.values) { + variableWithConstraint.removeConstrains { + it.type.contains { it.constructor == variable.freshTypeConstructor } + } + } + + storage.fixedTypeVariables[variable.freshTypeConstructor] = resultType + } + + override val innerCalls: List get() { + checkState(State.COMPLETION) + return storage.innerCalls + } + + override fun canBeProper(type: UnwrappedType): Boolean { + checkState(State.COMPLETION) + return !type.contains { storage.notFixedTypeVariables.containsKey(it.constructor) } + } + + override fun buildCurrentSubstitutor(): TypeSubstitutor { + checkState(State.COMPLETION) + return storage.buildCurrentSubstitutor() + } + + override fun buildResultingSubstitutor(): TypeSubstitutor { + checkState(State.COMPLETION) + val currentSubstitutorMap = storage.fixedTypeVariables.entries.associate { + it.key to it.value.asTypeProjection() + } + val uninferredSubstitutorMap = storage.notFixedTypeVariables.entries.associate { (freshTypeConstructor, typeVariable) -> + freshTypeConstructor to ErrorUtils.createErrorTypeWithCustomConstructor("Uninferred type", typeVariable.typeVariable.freshTypeConstructor).asTypeProjection() + } + + return TypeConstructorSubstitution.createByConstructorsMap(currentSubstitutorMap + uninferredSubstitutorMap).buildSubstitutor() + } +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/TypeVariable.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/TypeVariable.kt new file mode 100644 index 00000000000..9c0219fd7b4 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/TypeVariable.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.inference.model + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.ClassifierDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.resolve.calls.model.KotlinCall +import org.jetbrains.kotlin.resolve.calls.model.LambdaKotlinCallArgument +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.checker.NewTypeVariableConstructor + + +class TypeVariableTypeConstructor(private val builtIns: KotlinBuiltIns, val debugName: String): TypeConstructor, NewTypeVariableConstructor { + override fun getParameters(): List = emptyList() + override fun getSupertypes(): Collection = emptyList() + override fun isFinal(): Boolean = false + override fun isDenotable(): Boolean = false + override fun getDeclarationDescriptor(): ClassifierDescriptor? = null + + override fun getBuiltIns() = builtIns + + override fun toString() = "TypeVariable($debugName)" +} + +sealed class NewTypeVariable(builtIns: KotlinBuiltIns, name: String) { + val freshTypeConstructor: TypeConstructor = TypeVariableTypeConstructor(builtIns, name) + + val defaultType: SimpleType = KotlinTypeFactory.simpleType( + Annotations.EMPTY, freshTypeConstructor, arguments = emptyList(), + nullable = false, memberScope = ErrorUtils.createErrorScope("Type variable", true)) + + override fun toString() = freshTypeConstructor.toString() +} + +class TypeVariableFromCallableDescriptor( + val call: KotlinCall, + val originalTypeParameter: TypeParameterDescriptor +) : NewTypeVariable(originalTypeParameter.builtIns, originalTypeParameter.name.identifier) + +class LambdaTypeVariable( + val lambdaArgument: LambdaKotlinCallArgument, + val kind: Kind, + builtIns: KotlinBuiltIns +) : NewTypeVariable(builtIns, createDebugName(lambdaArgument, kind)) { + enum class Kind { + RECEIVER, + PARAMETER, + RETURN_TYPE + } +} + +private fun createDebugName(lambdaArgument: LambdaKotlinCallArgument, kind: LambdaTypeVariable.Kind): String { + val text = lambdaArgument.toString().let { it.substring(0..(Math.min(20, it.lastIndex))) } + return when (kind) { + LambdaTypeVariable.Kind.RECEIVER -> "Receiver[$text]" + LambdaTypeVariable.Kind.PARAMETER -> "Parameter[$text]" + LambdaTypeVariable.Kind.RETURN_TYPE -> "Result[$text]" + } +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ArgumentsImpl.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ArgumentsImpl.kt new file mode 100644 index 00000000000..ff4be7522ee --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ArgumentsImpl.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.model + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo +import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver +import org.jetbrains.kotlin.types.checker.prepareArgumentTypeRegardingCaptureTypes + + +class FakeKotlinCallArgumentForCallableReference( + val callableReference: ChosenCallableReferenceDescriptor, + val index: Int +) : KotlinCallArgument { + override val isSpread: Boolean get() = false + override val argumentName: Name? get() = null +} + +class ReceiverExpressionKotlinCallArgument private constructor( + override val receiver: ReceiverValueWithSmartCastInfo, + override val isSafeCall: Boolean = false, + val isVariableReceiverForInvoke: Boolean = false +) : ExpressionKotlinCallArgument { + override val isSpread: Boolean get() = false + override val argumentName: Name? get() = null + override fun toString() = "$receiver" + if(isSafeCall) "?" else "" + + companion object { + // we create ReceiverArgument and fix capture types + operator fun invoke( + receiver: ReceiverValueWithSmartCastInfo, + isSafeCall: Boolean = false, + isVariableReceiverForInvoke: Boolean = false + ): ReceiverExpressionKotlinCallArgument { + val newType = prepareArgumentTypeRegardingCaptureTypes(receiver.receiverValue.type.unwrap()) + val newReceiver = if (newType != null) { + ReceiverValueWithSmartCastInfo(receiver.receiverValue.replaceType(newType), receiver.possibleTypes, receiver.isStable) + } else receiver + + return ReceiverExpressionKotlinCallArgument(newReceiver, isSafeCall, isVariableReceiverForInvoke) + } + } +} + +class EmptyLabeledReturn(builtIns: KotlinBuiltIns) : ExpressionKotlinCallArgument { + override val isSpread: Boolean get() = false + override val argumentName: Name? get() = null + override val receiver = ReceiverValueWithSmartCastInfo(TransientReceiver(builtIns.unitType), emptySet(), true) + override val isSafeCall: Boolean get() = false +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/Diagnostics.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/Diagnostics.kt new file mode 100644 index 00000000000..887d8716c55 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/Diagnostics.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.model + +import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability +import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability.INAPPLICABLE +import org.jetbrains.kotlin.types.KotlinType + +abstract class KotlinCallDiagnostic(val candidateApplicability: ResolutionCandidateApplicability) { + abstract fun report(reporter: DiagnosticReporter) +} + +interface DiagnosticReporter { + fun onExplicitReceiver(diagnostic: KotlinCallDiagnostic) + + fun onCall(diagnostic: KotlinCallDiagnostic) + + fun onTypeArguments(diagnostic: KotlinCallDiagnostic) + + fun onCallName(diagnostic: KotlinCallDiagnostic) + + fun onTypeArgument(typeArgument: TypeArgument, diagnostic: KotlinCallDiagnostic) + + fun onCallReceiver(callReceiver: SimpleKotlinCallArgument, diagnostic: KotlinCallDiagnostic) + + fun onCallArgument(callArgument: KotlinCallArgument, diagnostic: KotlinCallDiagnostic) + fun onCallArgumentName(callArgument: KotlinCallArgument, diagnostic: KotlinCallDiagnostic) + fun onCallArgumentSpread(callArgument: KotlinCallArgument, diagnostic: KotlinCallDiagnostic) + + fun constraintError(diagnostic: KotlinCallDiagnostic) +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCall.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCall.kt new file mode 100644 index 00000000000..967d05af3f0 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCall.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.model + +import org.jetbrains.kotlin.name.Name + + +interface KotlinCall { + val callKind: KotlinCallKind + + val explicitReceiver: ReceiverKotlinCallArgument? + + // a.(foo)() -- (foo) is dispatchReceiverForInvoke + val dispatchReceiverForInvokeExtension: SimpleKotlinCallArgument? get() = null + + val name: Name + + val typeArguments: List + + val argumentsInParenthesis: List + + val externalArgument: KotlinCallArgument? + + val isInfixCall: Boolean + val isOperatorCall: Boolean +} + +private fun SimpleKotlinCallArgument.checkReceiverInvariants() { + assert(!isSpread) { + "Receiver cannot be a spread: $this" + } + assert(argumentName == null) { + "Argument name should be null for receiver: $this, but it is $argumentName" + } +} + +fun KotlinCall.checkCallInvariants() { + assert(explicitReceiver !is LambdaKotlinCallArgument && explicitReceiver !is CallableReferenceKotlinCallArgument) { + "Lambda argument or callable reference is not allowed as explicit receiver: $explicitReceiver" + } + + (explicitReceiver as? SimpleKotlinCallArgument)?.checkReceiverInvariants() + dispatchReceiverForInvokeExtension?.checkReceiverInvariants() + + if (callKind != KotlinCallKind.FUNCTION) { + assert(externalArgument == null) { + "External argument is not allowed not for function call: $externalArgument." + } + assert(argumentsInParenthesis.isEmpty()) { + "Arguments in parenthesis should be empty for not function call: $this " + } + assert(dispatchReceiverForInvokeExtension == null) { + "Dispatch receiver for invoke should be null for not function call: $dispatchReceiverForInvokeExtension" + } + } + else { + assert(externalArgument == null || !externalArgument!!.isSpread) { + "External argument cannot nave spread element: $externalArgument" + } + + assert(externalArgument?.argumentName == null) { + "Illegal external argument with name: $externalArgument" + } + + assert(dispatchReceiverForInvokeExtension == null || !dispatchReceiverForInvokeExtension!!.isSafeCall) { + "Dispatch receiver for invoke cannot be safe: $dispatchReceiverForInvokeExtension" + } + } +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallArguments.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallArguments.kt new file mode 100644 index 00000000000..bed7b68c267 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallArguments.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.model + +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage +import org.jetbrains.kotlin.resolve.calls.tower.CandidateWithBoundDispatchReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.DetailedReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.QualifierReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo +import org.jetbrains.kotlin.types.UnwrappedType + + +interface ReceiverKotlinCallArgument { + val receiver: DetailedReceiver +} + +class QualifierReceiverKotlinCallArgument(override val receiver: QualifierReceiver) : ReceiverKotlinCallArgument { + override fun toString() = "$receiver" +} + +interface KotlinCallArgument { + val isSpread: Boolean + val argumentName: Name? +} + +interface SimpleKotlinCallArgument : KotlinCallArgument, ReceiverKotlinCallArgument { + override val receiver: ReceiverValueWithSmartCastInfo + + val isSafeCall: Boolean +} + +interface ExpressionKotlinCallArgument : SimpleKotlinCallArgument + +interface SubKotlinCallArgument : SimpleKotlinCallArgument { + val resolvedCall: ResolvedKotlinCall.OnlyResolvedKotlinCall +} + +interface LambdaKotlinCallArgument : KotlinCallArgument { + override val isSpread: Boolean + get() = false + + /** + * parametersTypes == null means, that there is no declared arguments + * null inside array means that this type is not declared explicitly + */ + val parametersTypes: Array? +} + +interface FunctionExpression : LambdaKotlinCallArgument { + override val parametersTypes: Array + + // null means that there function can not have receiver + val receiverType: UnwrappedType? + + // null means that return type is not declared, for fun(){ ... } returnType == Unit + val returnType: UnwrappedType? +} + +interface CallableReferenceKotlinCallArgument : KotlinCallArgument { + override val isSpread: Boolean + get() = false + + // Foo::bar lhsType = Foo. For a::bar where a is expression, this type is null + val lhsType: UnwrappedType? + + val constraintStorage: ConstraintStorage +} + +interface ChosenCallableReferenceDescriptor : CallableReferenceKotlinCallArgument { + val candidate: CandidateWithBoundDispatchReceiver + + val extensionReceiver: ReceiverValueWithSmartCastInfo? +} + + +interface TypeArgument + +// todo allow '_' in frontend +object TypeArgumentPlaceholder : TypeArgument + +interface SimpleTypeArgument: TypeArgument { + val type: UnwrappedType +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinResolverContext.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinResolverContext.kt new file mode 100644 index 00000000000..9f480f25fdc --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinResolverContext.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.model + +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.resolve.calls.components.CheckArguments +import org.jetbrains.kotlin.resolve.calls.components.LambdaAnalyzer +import org.jetbrains.kotlin.resolve.calls.components.* +import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintInjector +import org.jetbrains.kotlin.resolve.calls.inference.components.ResultTypeResolver +import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind +import org.jetbrains.kotlin.resolve.calls.tower.CandidateFactory +import org.jetbrains.kotlin.resolve.calls.tower.CandidateWithBoundDispatchReceiver +import org.jetbrains.kotlin.resolve.calls.tower.ImplicitScopeTower +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo +import org.jetbrains.kotlin.types.ErrorUtils +import org.jetbrains.kotlin.types.TypeSubstitutor + + +class KotlinCallContext( + val scopeTower: ImplicitScopeTower, + val lambdaAnalyzer: LambdaAnalyzer, + val argumentsToParametersMapper: ArgumentsToParametersMapper, + val typeArgumentsToParametersMapper: TypeArgumentsToParametersMapper, + val resultTypeResolver: ResultTypeResolver, + val callableReferenceResolver: CallableReferenceResolver, + val constraintInjector: ConstraintInjector +) + +class SimpleCandidateFactory(val callContext: KotlinCallContext, val kotlinCall: KotlinCall): CandidateFactory { + + // todo: try something else, because current method is ugly and unstable + private fun createReceiverArgument( + explicitReceiver: ReceiverKotlinCallArgument?, + fromResolution: ReceiverValueWithSmartCastInfo? + ): SimpleKotlinCallArgument? = + explicitReceiver as? SimpleKotlinCallArgument ?: // qualifier receiver cannot be safe + fromResolution?.let { ReceiverExpressionKotlinCallArgument(it, isSafeCall = false) } // todo smartcast implicit this + + override fun createCandidate( + towerCandidate: CandidateWithBoundDispatchReceiver, + explicitReceiverKind: ExplicitReceiverKind, + extensionReceiver: ReceiverValueWithSmartCastInfo? + ): SimpleKotlinResolutionCandidate { + val dispatchArgumentReceiver = createReceiverArgument(kotlinCall.getExplicitDispatchReceiver(explicitReceiverKind), + towerCandidate.dispatchReceiver) + val extensionArgumentReceiver = createReceiverArgument(kotlinCall.getExplicitExtensionReceiver(explicitReceiverKind), extensionReceiver) + + if (ErrorUtils.isError(towerCandidate.descriptor)) { + return ErrorKotlinResolutionCandidate(callContext, kotlinCall, explicitReceiverKind, dispatchArgumentReceiver, extensionArgumentReceiver, towerCandidate.descriptor) + } + + return SimpleKotlinResolutionCandidate(callContext, kotlinCall, explicitReceiverKind, dispatchArgumentReceiver, extensionArgumentReceiver, + towerCandidate.descriptor, towerCandidate.diagnostics) + } +} + +enum class KotlinCallKind(vararg resolutionPart: ResolutionPart) { + VARIABLE( + CheckVisibility, + CheckInfixResolutionPart, + CheckOperatorResolutionPart, + NoTypeArguments, + NoArguments, + CreteDescriptorWithFreshTypeVariables, + CheckExplicitReceiverKindConsistency, + CheckReceivers + ), + FUNCTION( + CheckVisibility, + MapTypeArguments, + MapArguments, + CreteDescriptorWithFreshTypeVariables, + CheckExplicitReceiverKindConsistency, + CheckReceivers, + CheckArguments + ), + UNSUPPORTED(); + + val resolutionSequence = resolutionPart.asList() +} + +class GivenCandidate( + val descriptor: FunctionDescriptor, + val dispatchReceiver: ReceiverValueWithSmartCastInfo?, + val knownTypeParametersResultingSubstitutor: TypeSubstitutor? +) + diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionCandidate.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionCandidate.kt new file mode 100644 index 00000000000..001f6eab950 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionCandidate.kt @@ -0,0 +1,145 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.model + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.renderer.DescriptorRenderer +import org.jetbrains.kotlin.resolve.calls.components.TypeArgumentsToParametersMapper +import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder +import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem +import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl +import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind +import org.jetbrains.kotlin.resolve.calls.tower.Candidate +import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateStatus +import org.jetbrains.kotlin.resolve.calls.tower.isSuccess +import java.util.* + + +interface ResolutionPart { + fun SimpleKotlinResolutionCandidate.process(): List +} + +sealed class KotlinResolutionCandidate : Candidate { + abstract val kotlinCall: KotlinCall + + abstract val lastCall: SimpleKotlinResolutionCandidate +} + +class VariableAsFunctionKotlinResolutionCandidate( + override val kotlinCall: KotlinCall, + val resolvedVariable: SimpleKotlinResolutionCandidate, + val invokeCandidate: SimpleKotlinResolutionCandidate +) : KotlinResolutionCandidate() { + override val isSuccessful: Boolean get() = resolvedVariable.isSuccessful && invokeCandidate.isSuccessful + override val status: ResolutionCandidateStatus + get() = ResolutionCandidateStatus(resolvedVariable.status.diagnostics + invokeCandidate.status.diagnostics) + + override val lastCall: SimpleKotlinResolutionCandidate get() = invokeCandidate +} + +sealed class AbstractSimpleKotlinResolutionCandidate( + val constraintSystem: NewConstraintSystem, + initialDiagnostics: Collection = emptyList() +) : KotlinResolutionCandidate() { + override val isSuccessful: Boolean + get() { + process(stopOnFirstError = true) + return !hasErrors + } + + private var _status: ResolutionCandidateStatus? = null + + override val status: ResolutionCandidateStatus + get() { + if (_status == null) { + process(stopOnFirstError = false) + _status = ResolutionCandidateStatus(diagnostics + constraintSystem.diagnostics) + } + return _status!! + } + + private val diagnostics = ArrayList() + protected var step = 0 + private set + + protected var hasErrors = false + private set + + private fun process(stopOnFirstError: Boolean) { + while (step < resolutionSequence.size && (!stopOnFirstError || !hasErrors)) { + addDiagnostics(resolutionSequence[step].run { lastCall.process() }) + step++ + } + } + + private fun addDiagnostics(diagnostics: Collection) { + hasErrors = hasErrors || diagnostics.any { !it.candidateApplicability.isSuccess } || + constraintSystem.diagnostics.any { !it.candidateApplicability.isSuccess } + this.diagnostics.addAll(diagnostics) + } + + init { + addDiagnostics(initialDiagnostics) + } + + + abstract val resolutionSequence: List +} + +open class SimpleKotlinResolutionCandidate( + val callContext: KotlinCallContext, + override val kotlinCall: KotlinCall, + val explicitReceiverKind: ExplicitReceiverKind, + val dispatchReceiverArgument: SimpleKotlinCallArgument?, + val extensionReceiver: SimpleKotlinCallArgument?, + val candidateDescriptor: CallableDescriptor, + initialDiagnostics: Collection +) : AbstractSimpleKotlinResolutionCandidate(NewConstraintSystemImpl(callContext.constraintInjector, callContext.resultTypeResolver), initialDiagnostics) { + val csBuilder: ConstraintSystemBuilder get() = constraintSystem.getBuilder() + + lateinit var typeArgumentMappingByOriginal: TypeArgumentsToParametersMapper.TypeArgumentsMapping + lateinit var argumentMappingByOriginal: Map + lateinit var descriptorWithFreshTypes: CallableDescriptor + + override val lastCall: SimpleKotlinResolutionCandidate get() = this + override val resolutionSequence: List get() = kotlinCall.callKind.resolutionSequence + + override fun toString(): String { + val descriptor = DescriptorRenderer.COMPACT.render(candidateDescriptor) + val okOrFail = if (hasErrors) "FAIL" else "OK" + val step = "$step/${resolutionSequence.size}" + return "$okOrFail($step): $descriptor" + } +} + +class ErrorKotlinResolutionCandidate( + callContext: KotlinCallContext, + kotlinCall: KotlinCall, + explicitReceiverKind: ExplicitReceiverKind, + dispatchReceiverArgument: SimpleKotlinCallArgument?, + extensionReceiver: SimpleKotlinCallArgument?, + candidateDescriptor: CallableDescriptor +) : SimpleKotlinResolutionCandidate(callContext, kotlinCall, explicitReceiverKind, dispatchReceiverArgument, extensionReceiver, candidateDescriptor, listOf()) { + override val resolutionSequence: List get() = emptyList() + + init { + typeArgumentMappingByOriginal = TypeArgumentsToParametersMapper.TypeArgumentsMapping.NoExplicitArguments + argumentMappingByOriginal = emptyMap() + descriptorWithFreshTypes = candidateDescriptor + } +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolvedCallElements.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolvedCallElements.kt new file mode 100644 index 00000000000..d2594f352ca --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolvedCallElements.kt @@ -0,0 +1,97 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.model + +import org.jetbrains.kotlin.builtins.createFunctionType +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.resolve.calls.components.ArgumentsToParametersMapper +import org.jetbrains.kotlin.resolve.calls.inference.model.LambdaTypeVariable +import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable +import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.SimpleType +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.typeUtil.builtIns + + +sealed class ArgumentWithPostponeResolution { + abstract val outerCall: KotlinCall + abstract val argument: KotlinCallArgument + abstract val myTypeVariables: Collection + abstract val inputType: Collection // parameters and implicit receiver + abstract val outputType: UnwrappedType? + + var analyzed: Boolean = false +} + +class ResolvedLambdaArgument( + override val outerCall: KotlinCall, + override val argument: LambdaKotlinCallArgument, + override val myTypeVariables: Collection, + val receiver: UnwrappedType?, + val parameters: List, + val returnType: UnwrappedType +) : ArgumentWithPostponeResolution() { + val type: SimpleType = createFunctionType(returnType.builtIns, Annotations.EMPTY, receiver, parameters, null, returnType) // todo support annotations + + override val inputType: Collection get() = receiver?.let { parameters + it } ?: parameters + override val outputType: UnwrappedType get() = returnType +} + + +class ResolvedPropertyReference( + val outerCall: KotlinCall, + val argument: ChosenCallableReferenceDescriptor, + val reflectionType: UnwrappedType +) { + val boundDispatchReceiver: ReceiverValue? get() = argument.candidate.dispatchReceiver?.receiverValue?.takeIf { it !is MockReceiverForCallableReference } + val boundExtensionReceiver: ReceiverValue? get() = argument.extensionReceiver?.receiverValue?.takeIf { it !is MockReceiverForCallableReference } +} + +class ResolvedFunctionReference( + val outerCall: KotlinCall, + val argument: ChosenCallableReferenceDescriptor, + val reflectionType: UnwrappedType, + val argumentsMapping: ArgumentsToParametersMapper.ArgumentMapping? +) { + val boundDispatchReceiver: ReceiverValue? get() = argument.candidate.dispatchReceiver?.receiverValue?.takeIf { it !is MockReceiverForCallableReference } + val boundExtensionReceiver: ReceiverValue? get() = argument.extensionReceiver?.receiverValue?.takeIf { it !is MockReceiverForCallableReference } +} + + +fun KotlinCall.getExplicitDispatchReceiver(explicitReceiverKind: ExplicitReceiverKind) = when (explicitReceiverKind) { + ExplicitReceiverKind.DISPATCH_RECEIVER -> explicitReceiver + ExplicitReceiverKind.BOTH_RECEIVERS -> dispatchReceiverForInvokeExtension + else -> null +} + +fun KotlinCall.getExplicitExtensionReceiver(explicitReceiverKind: ExplicitReceiverKind) = when (explicitReceiverKind) { + ExplicitReceiverKind.EXTENSION_RECEIVER, ExplicitReceiverKind.BOTH_RECEIVERS -> explicitReceiver + else -> null +} + +class MockReceiverForCallableReference(val lhsOrDeclaredType: UnwrappedType) : ReceiverValue { + override fun getType() = lhsOrDeclaredType + override fun replaceType(newType: KotlinType) = MockReceiverForCallableReference(newType.unwrap()) +} + +val ChosenCallableReferenceDescriptor.dispatchNotBoundReceiver : UnwrappedType? + get() = (candidate.dispatchReceiver?.receiverValue as? MockReceiverForCallableReference)?.lhsOrDeclaredType + +val ChosenCallableReferenceDescriptor.extensionNotBoundReceiver : UnwrappedType? + get() = (extensionReceiver as? MockReceiverForCallableReference)?.lhsOrDeclaredType diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolvedKotlinCall.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolvedKotlinCall.kt new file mode 100644 index 00000000000..b68029e180d --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolvedKotlinCall.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.model + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.resolve.calls.inference.returnTypeOrNothing +import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind +import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateStatus +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo +import org.jetbrains.kotlin.types.UnwrappedType + +sealed class ResolvedKotlinCall { + class CompletedResolvedKotlinCall( + val completedCall: CompletedKotlinCall, + val allInnerCalls: Collection + ): ResolvedKotlinCall() + + class OnlyResolvedKotlinCall( + val candidate: KotlinResolutionCandidate + ) : ResolvedKotlinCall() { + val currentReturnType: UnwrappedType = candidate.lastCall.descriptorWithFreshTypes.returnTypeOrNothing + } +} + +sealed class CompletedKotlinCall { + abstract val resolutionStatus: ResolutionCandidateStatus + + class Simple( + val kotlinCall: KotlinCall, + val candidateDescriptor: CallableDescriptor, + val resultingDescriptor: CallableDescriptor, + override val resolutionStatus: ResolutionCandidateStatus, + val explicitReceiverKind: ExplicitReceiverKind, + val dispatchReceiver: ReceiverValueWithSmartCastInfo?, + val extensionReceiver: ReceiverValueWithSmartCastInfo?, + val typeArguments: List, + val argumentMappingByOriginal: Map + ): CompletedKotlinCall() + + class VariableAsFunction( + val kotlinCall: KotlinCall, + val variableCall: Simple, + val invokeCall: Simple + ): CompletedKotlinCall() { + + override val resolutionStatus: ResolutionCandidateStatus = + ResolutionCandidateStatus(variableCall.resolutionStatus.diagnostics + invokeCall.resolutionStatus.diagnostics) + } +} + +sealed class ResolvedCallArgument { + abstract val arguments: List + + object DefaultArgument : ResolvedCallArgument() { + override val arguments: List + get() = emptyList() + + } + + class SimpleArgument(val callArgument: KotlinCallArgument): ResolvedCallArgument() { + override val arguments: List + get() = listOf(callArgument) + + } + + class VarargArgument(override val arguments: List): ResolvedCallArgument() +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt index 04af32698a8..afcb08f68cb 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt @@ -32,7 +32,7 @@ import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils import java.util.* -class OverloadingConflictResolver( +open class OverloadingConflictResolver( private val builtIns: KotlinBuiltIns, private val specificityComparator: TypeSpecificityComparator, private val getResultingDescriptor: (C) -> CallableDescriptor, diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/ImplicitScopeTower.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/ImplicitScopeTower.kt index cd308fb9373..730ce7de1b3 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/ImplicitScopeTower.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/ImplicitScopeTower.kt @@ -21,6 +21,9 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic +import org.jetbrains.kotlin.resolve.calls.model.DiagnosticReporter +import org.jetbrains.kotlin.resolve.calls.tower.ResolutionCandidateApplicability.* import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.SyntheticScopes @@ -59,9 +62,9 @@ interface CandidateWithBoundDispatchReceiver { fun copy(newDescriptor: CallableDescriptor): CandidateWithBoundDispatchReceiver } -data class ResolutionCandidateStatus(val diagnostics: List) { - val resultingApplicability: ResolutionCandidateApplicability = diagnostics.asSequence().map { it.candidateLevel }.max() - ?: ResolutionCandidateApplicability.RESOLVED +data class ResolutionCandidateStatus(val diagnostics: List) { + val resultingApplicability: ResolutionCandidateApplicability = diagnostics.asSequence().map { it.candidateApplicability }.max() + ?: RESOLVED } enum class ResolutionCandidateApplicability { @@ -77,22 +80,31 @@ enum class ResolutionCandidateApplicability { HIDDEN, // removed from resolve } -abstract class ResolutionDiagnostic(val candidateLevel: ResolutionCandidateApplicability) +abstract class ResolutionDiagnostic(candidateApplicability: ResolutionCandidateApplicability): KotlinCallDiagnostic(candidateApplicability) { + override fun report(reporter: DiagnosticReporter) { + // do nothing + } +} // todo error for this access from nested class -class VisibilityError(val invisibleMember: DeclarationDescriptorWithVisibility): ResolutionDiagnostic(ResolutionCandidateApplicability.RUNTIME_ERROR) -class NestedClassViaInstanceReference(val classDescriptor: ClassDescriptor): ResolutionDiagnostic(ResolutionCandidateApplicability.IMPOSSIBLE_TO_GENERATE) -class InnerClassViaStaticReference(val classDescriptor: ClassDescriptor): ResolutionDiagnostic(ResolutionCandidateApplicability.IMPOSSIBLE_TO_GENERATE) -class UnsupportedInnerClassCall(val message: String): ResolutionDiagnostic(ResolutionCandidateApplicability.IMPOSSIBLE_TO_GENERATE) -class UsedSmartCastForDispatchReceiver(val smartCastType: KotlinType): ResolutionDiagnostic(ResolutionCandidateApplicability.RESOLVED) +class VisibilityError(val invisibleMember: DeclarationDescriptorWithVisibility): ResolutionDiagnostic(RUNTIME_ERROR) { + override fun report(reporter: DiagnosticReporter) { + reporter.onCall(this) + } +} -object ErrorDescriptorDiagnostic : ResolutionDiagnostic(ResolutionCandidateApplicability.RESOLVED) // todo discuss and change to INAPPLICABLE -object LowPriorityDescriptorDiagnostic : ResolutionDiagnostic(ResolutionCandidateApplicability.RESOLVED_LOW_PRIORITY) -object DynamicDescriptorDiagnostic: ResolutionDiagnostic(ResolutionCandidateApplicability.RESOLVED_LOW_PRIORITY) -object UnstableSmartCastDiagnostic: ResolutionDiagnostic(ResolutionCandidateApplicability.MAY_THROW_RUNTIME_ERROR) -object HiddenExtensionRelatedToDynamicTypes : ResolutionDiagnostic(ResolutionCandidateApplicability.HIDDEN) -object HiddenDescriptor: ResolutionDiagnostic(ResolutionCandidateApplicability.HIDDEN) +class NestedClassViaInstanceReference(val classDescriptor: ClassDescriptor): ResolutionDiagnostic(IMPOSSIBLE_TO_GENERATE) +class InnerClassViaStaticReference(val classDescriptor: ClassDescriptor): ResolutionDiagnostic(IMPOSSIBLE_TO_GENERATE) +class UnsupportedInnerClassCall(val message: String): ResolutionDiagnostic(IMPOSSIBLE_TO_GENERATE) +class UsedSmartCastForDispatchReceiver(val smartCastType: KotlinType): ResolutionDiagnostic(RESOLVED) -object InvokeConventionCallNoOperatorModifier : ResolutionDiagnostic(ResolutionCandidateApplicability.CONVENTION_ERROR) -object InfixCallNoInfixModifier : ResolutionDiagnostic(ResolutionCandidateApplicability.CONVENTION_ERROR) -object DeprecatedUnaryPlusAsPlus : ResolutionDiagnostic(ResolutionCandidateApplicability.CONVENTION_ERROR) +object ErrorDescriptorDiagnostic : ResolutionDiagnostic(RESOLVED) // todo discuss and change to INAPPLICABLE +object LowPriorityDescriptorDiagnostic : ResolutionDiagnostic(RESOLVED_LOW_PRIORITY) +object DynamicDescriptorDiagnostic: ResolutionDiagnostic(RESOLVED_LOW_PRIORITY) +object UnstableSmartCastDiagnostic: ResolutionDiagnostic(MAY_THROW_RUNTIME_ERROR) +object HiddenExtensionRelatedToDynamicTypes: ResolutionDiagnostic(HIDDEN) +object HiddenDescriptor: ResolutionDiagnostic(HIDDEN) + +object InvokeConventionCallNoOperatorModifier : ResolutionDiagnostic(CONVENTION_ERROR) +object InfixCallNoInfixModifier : ResolutionDiagnostic(CONVENTION_ERROR) +object DeprecatedUnaryPlusAsPlus : ResolutionDiagnostic(CONVENTION_ERROR) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/TowerLevels.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/TowerLevels.kt index 2a00d263d8e..263c00897d0 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/TowerLevels.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/TowerLevels.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve.calls.tower import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.calls.USE_NEW_INFERENCE import org.jetbrains.kotlin.resolve.calls.smartcasts.getReceiverValueWithSmartCast import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForTypeAliasObject @@ -63,13 +64,15 @@ internal abstract class AbstractScopeTowerLevel( if (descriptor.hasLowPriorityInOverloadResolution()) diagnostics.add(LowPriorityDescriptorDiagnostic) if (dispatchReceiverSmartCastType != null) diagnostics.add(UsedSmartCastForDispatchReceiver(dispatchReceiverSmartCastType)) - val shouldSkipVisibilityCheck = scopeTower.isDebuggerContext - if (!shouldSkipVisibilityCheck) { - Visibilities.findInvisibleMember( - getReceiverValueWithSmartCast(dispatchReceiver?.receiverValue, dispatchReceiverSmartCastType), - descriptor, - scopeTower.lexicalScope.ownerDescriptor - )?.let { diagnostics.add(VisibilityError(it)) } + if (!USE_NEW_INFERENCE) { + val shouldSkipVisibilityCheck = scopeTower.isDebuggerContext + if (!shouldSkipVisibilityCheck) { + Visibilities.findInvisibleMember( + getReceiverValueWithSmartCast(dispatchReceiver?.receiverValue, dispatchReceiverSmartCastType), + descriptor, + scopeTower.lexicalScope.ownerDescriptor + )?.let { diagnostics.add(VisibilityError(it)) } + } } } return CandidateWithBoundDispatchReceiverImpl(dispatchReceiver, descriptor, diagnostics) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/TowerResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/TowerResolver.kt index 9d6436072e4..01da1e18767 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/TowerResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/TowerResolver.kt @@ -41,6 +41,7 @@ interface CandidateFactory { interface CandidateFactoryProviderForInvoke { + // variable here is resolved, invoke -- only chosen fun transformCandidate(variable: C, invoke: C): C fun factoryForVariable(stripExplicitReceiver: Boolean): CandidateFactory diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/util/FakeCallableDescriptorForObject.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/util/FakeCallableDescriptorForObject.kt index e5325be02bf..babf235a267 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/util/FakeCallableDescriptorForObject.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/util/FakeCallableDescriptorForObject.kt @@ -72,5 +72,6 @@ open class FakeCallableDescriptorForObject( override fun hashCode() = classDescriptor.hashCode() override fun getContainingDeclaration() = classDescriptor.getClassObjectReferenceTarget().containingDeclaration - override fun substitute(substitutor: TypeSubstitutor) = TODO("Substitution of FakeCallableDescriptorForObject is not supported") + + override fun substitute(substitutor: TypeSubstitutor) = this } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/receivers/QualifierReceiver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/receivers/QualifierReceiver.kt index ce8dde58a71..595bb67abac 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/receivers/QualifierReceiver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/receivers/QualifierReceiver.kt @@ -27,8 +27,9 @@ class ReceiverValueWithSmartCastInfo( val receiverValue: ReceiverValue, val possibleTypes: Set, // doesn't include receiver.type val isStable: Boolean -): DetailedReceiver - +): DetailedReceiver { + override fun toString() = receiverValue.toString() +} interface QualifierReceiver : Receiver, DetailedReceiver { val descriptor: DeclarationDescriptor diff --git a/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt b/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt new file mode 100644 index 00000000000..6270df53451 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt @@ -0,0 +1,343 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.types + +import org.jetbrains.kotlin.types.TypeApproximatorConfiguration.IntersectionStrategy.* +import org.jetbrains.kotlin.resolve.calls.components.CommonSupertypeCalculator +import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableTypeConstructor +import org.jetbrains.kotlin.types.checker.NewCapturedType +import org.jetbrains.kotlin.types.checker.NewCapturedTypeConstructor +import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker +import org.jetbrains.kotlin.types.checker.intersectTypes +import org.jetbrains.kotlin.types.typeUtil.* + + +open class TypeApproximatorConfiguration { + enum class IntersectionStrategy { + ALLOWED, + TO_FIRST, + TO_COMMON_SUPERTYPE + } + open val flexible get() = false // simple flexible types (FlexibleTypeImpl) + open val dynamic get() = false // DynamicType + open val rawType get() = false // RawTypeImpl + open val errorType get() = false + open val intersection: IntersectionStrategy = TO_COMMON_SUPERTYPE + + open val typeVariable: (TypeVariableTypeConstructor) -> Boolean = { false } + open val capturedType: (NewCapturedType) -> Boolean = { false } // true means that this type we can leave as is + + abstract class AllFlexibleSameValue : TypeApproximatorConfiguration() { + abstract val allFlexible: Boolean + + override val flexible get() = allFlexible + override val dynamic get() = allFlexible + override val rawType get() = allFlexible + } + + object LocalDeclaration : AllFlexibleSameValue() { + override val allFlexible get() = true + override val intersection get() = ALLOWED + override val errorType get() = true + } + + object PublicDeclaration : AllFlexibleSameValue() { + override val allFlexible get() = true + } +} + +class TypeApproximator(private val commonSupertypeCalculator: CommonSupertypeCalculator) { + private val referenceApproximateToSuperType = this::approximateToSuperType + private val referenceApproximateToSubType = this::approximateToSubType + + + // null means that this input type is the result, i.e. input type not contains not-allowed kind of types + // type <: resultType + fun approximateToSuperType(type: UnwrappedType, conf: TypeApproximatorConfiguration): UnwrappedType? { + if (type is TypeUtils.SpecialType) return null + return approximateTo(NewKotlinTypeChecker.transformToNewType(type), conf, FlexibleType::upperBound, referenceApproximateToSuperType) + } + + // resultType <: type + fun approximateToSubType(type: UnwrappedType, conf: TypeApproximatorConfiguration): UnwrappedType? { + if (type is TypeUtils.SpecialType) return null + return approximateTo(NewKotlinTypeChecker.transformToNewType(type), conf, FlexibleType::lowerBound, referenceApproximateToSubType) + } + + // comments for case bound = upperBound, approximateTo = toSuperType + private fun approximateTo( + type: UnwrappedType, + conf: TypeApproximatorConfiguration, + bound: FlexibleType.() -> SimpleType, + approximateTo: (SimpleType, TypeApproximatorConfiguration) -> UnwrappedType? + ): UnwrappedType? { + when (type) { + is SimpleType -> return approximateTo(type, conf) + is FlexibleType -> { + if (type is DynamicType) { + return if (conf.dynamic) null else type.bound() + } + else if (type is RawType) { + return if (conf.rawType) null else type.bound() + } + + assert(type is FlexibleTypeImpl) { + "Unexpected subclass of FlexibleType: ${type::class.java.canonicalName}, type = $type" + } + + if (conf.flexible) { + /** + * Let inputType = L_1..U_1; resultType = L_2..U_2 + * We should create resultType such as inputType <: resultType. + * It means that if A <: inputType, then A <: U_1. And, because inputType <: resultType, + * A <: resultType => A <: U_2. I.e. for every type A such A <: U_1, A <: U_2 => U_1 <: U_2. + * + * Similar for L_1 <: L_2: Let B : resultType <: B. L_2 <: B and L_1 <: B. + * I.e. for every type B such as L_2 <: B, L_1 <: B. For example B = L_2. + */ + + val lowerResult = approximateTo(type.lowerBound, conf) + val upperResult = approximateTo(type.upperBound, conf) + if (lowerResult == null && upperResult == null) return null + + /** + * If C <: L..U then C <: L. + * inputType.lower <: lowerResult => inputType.lower <: lowerResult?.lowerIfFlexible() + * i.e. this type is correct. We use this type, because this type more flexible. + * + * If U_1 <: U_2.lower .. U_2.upper, then we know only that U_1 <: U_2.upper. + */ + return FlexibleTypeImpl(lowerResult?.lowerIfFlexible() ?: type.lowerBound, + upperResult?.upperIfFlexible() ?: type.upperBound) + } + else { + return type.bound().let { approximateTo(it, conf) ?: it } + } + } + } + } + + private fun approximateIntersectionType(type: SimpleType, conf: TypeApproximatorConfiguration, toSuper: Boolean): UnwrappedType? { + val typeConstructor = type.constructor + assert(typeConstructor is IntersectionTypeConstructor) { + "Should be intersection type: $type, typeConstructor class: ${typeConstructor::class.java.canonicalName}" + } + assert(typeConstructor.supertypes.isNotEmpty()) { + "Supertypes for intersection type should not be empty: $type" + } + + var thereIsApproximation = false + val newTypes = typeConstructor.supertypes.map { + val newType = if (toSuper) approximateToSuperType(it.unwrap(), conf) else approximateToSubType(it.unwrap(), conf) + if (newType != null) { + thereIsApproximation = true + newType + } else it.unwrap() + } + + /** + * For case ALLOWED: + * A <: A', B <: B' => A & B <: A' & B' + * + * For other case -- it's impossible to find some type except Nothing as subType for intersection type. + */ + val baseResult = when (conf.intersection) { + ALLOWED -> if (!thereIsApproximation) return null else intersectTypes(newTypes) + TO_FIRST -> if (toSuper) newTypes.first() else return type.defaultResult(toSuper = false) + // commonSupertypeCalculator should handle flexible types correctly + TO_COMMON_SUPERTYPE -> if (toSuper) commonSupertypeCalculator(newTypes) else return type.defaultResult(toSuper = false) + } + + return if (type.isMarkedNullable) baseResult.makeNullableAsSpecified(true) else baseResult + } + + private fun approximateCapturedType(type: NewCapturedType, conf: TypeApproximatorConfiguration, toSuper: Boolean): UnwrappedType? { + val supertypes = type.constructor.supertypes + val baseSuperType = when (supertypes.size) { + 0 -> type.builtIns.nullableAnyType // Let C = in Int, then superType for C and C? is Any? + 1 -> supertypes.single() + else -> intersectTypes(supertypes) + } + val baseSubType = type.lowerType ?: type.builtIns.nothingType + + if (conf.capturedType(type)) { + /** + * Here everything is ok if bounds for this captured type should not be approximated. + * But. If such bounds contains some unauthorized types, then we cannot leave this captured type "as is". + * And we cannot create new capture type, because meaning of new captured type is not clear. + * So, we will just approximate such types + * + * todo handle flexible types + */ + if (approximateToSuperType(baseSuperType, conf) == null && approximateToSubType(baseSubType, conf) == null) { + return null + } + } + val baseResult = if (toSuper) approximateToSuperType(baseSuperType, conf) ?: baseSuperType else approximateToSubType(baseSubType, conf) ?: baseSubType + + // C = in Int, Int <: C => Int? <: C? + // C = out Number, C <: Number => C? <: Number? + return if (type.isMarkedNullable) baseResult.makeNullableAsSpecified(true) else baseResult + } + + private fun approximateToSuperType(type: SimpleType, conf: TypeApproximatorConfiguration) = approximateTo(type, conf, toSuper = true) + private fun approximateToSubType(type: SimpleType, conf: TypeApproximatorConfiguration) = approximateTo(type, conf, toSuper = false) + + private fun approximateTo(type: SimpleType, conf: TypeApproximatorConfiguration, toSuper: Boolean): UnwrappedType? { + if (type.isError) { + // todo -- fix builtIns. Now builtIns here is DefaultBuiltIns + return if (conf.errorType) null else type.defaultResult(toSuper) + } + + if (type.arguments.isNotEmpty()) { + return approximateParametrizedType(type, conf, toSuper) + } + + val typeConstructor = type.constructor + + if (typeConstructor is NewCapturedTypeConstructor) { + assert(type is NewCapturedType) { // KT-16147 + "Type is inconsistent -- somewhere we create type with typeConstructor = $typeConstructor " + + "and class: ${type::class.java.canonicalName}. type.toString() = $type" + } + return approximateCapturedType(type as NewCapturedType, conf, toSuper) + } + + if (typeConstructor is IntersectionTypeConstructor) { + return approximateIntersectionType(type, conf, toSuper) + } + + if (typeConstructor is TypeVariableTypeConstructor) { + return if (conf.typeVariable(typeConstructor)) null else type.defaultResult(toSuper) + } + + return null // simple classifier type + } + + private fun isApproximateDirectionToSuper(effectiveVariance: Variance, toSuper: Boolean) = + when (effectiveVariance) { + Variance.OUT_VARIANCE -> toSuper + Variance.IN_VARIANCE -> !toSuper + Variance.INVARIANT -> throw AssertionError("Incorrect variance $effectiveVariance") + } + + private fun approximateParametrizedType(type: SimpleType, conf: TypeApproximatorConfiguration, toSuper: Boolean): SimpleType? { + val parameters = type.constructor.parameters + val arguments = type.arguments + if (parameters.size != arguments.size) { + return if (conf.errorType) { + ErrorUtils.createErrorType("Inconsistent type: $type (parameters.size = ${parameters.size}, arguments.size = ${arguments.size})") + } + else type.defaultResult(toSuper) + } + + val newArguments = arrayOfNulls(arguments.size) + + loop@ for (index in arguments.indices) { + val parameter = parameters[index] + val argument = arguments[index] + + if (argument.isStarProjection) continue + + val argumentType = argument.type.unwrap() + val effectiveVariance = NewKotlinTypeChecker.effectiveVariance(parameter.variance, argument.projectionKind) + when (effectiveVariance) { + null -> { + return if (conf.errorType) { + ErrorUtils.createErrorType("Inconsistent type: $type ($index parameter has declared variance: ${parameter.variance}, " + + "but argument variance is ${argument.projectionKind})") + } else type.defaultResult(toSuper) + } + Variance.OUT_VARIANCE, Variance.IN_VARIANCE -> { + /** + * Out <: Out + * Inv <: Inv + + * In <: In + * Inv <: Inv + */ + val approximatedArgument = argumentType.let { + if (isApproximateDirectionToSuper(effectiveVariance, toSuper)) approximateToSuperType(it, conf) else approximateToSubType(it, conf) + } ?: continue@loop + + if (parameter.variance == Variance.INVARIANT) { + newArguments[index] = TypeProjectionImpl(effectiveVariance, approximatedArgument) + } else { + newArguments[index] = approximatedArgument.asTypeProjection() + } + } + Variance.INVARIANT -> { + if (!toSuper) { + // Inv cannot be approximated to subType + val toSubType = approximateToSubType(argumentType, conf) ?: continue@loop + + // Inv is supertype for Inv + if (!NewKotlinTypeChecker.equalTypes(argumentType, toSubType)) return type.defaultResult(toSuper) + + newArguments[index] = argumentType.asTypeProjection() + continue@loop + } + + /** + * Example with non-trivial both type approximations: + * Inv> where C = in Int + * Inv> <: Inv> + * Inv> <: Inv> + * + * So such case is rare and we will chose Inv> for now. + * + * Note that for case Inv we will chose Inv, because it is more informative then Inv. + * May be we should do the same for deeper types, but not now. + */ + if (argumentType is NewCapturedType) { + val subType = approximateToSubType(argumentType, conf) ?: continue@loop + if (!subType.isTrivialSub()) { + newArguments[index] = TypeProjectionImpl(Variance.IN_VARIANCE, subType) + continue@loop + } + } + + val approximatedSuperType = approximateToSuperType(argumentType, conf) ?: continue@loop // null means that this type we can leave as is + if (approximatedSuperType.isTrivialSuper()) { + val approximatedSubType = approximateToSubType(argumentType, conf) ?: continue@loop // seems like this is never null + if (!approximatedSubType.isTrivialSub()) { + newArguments[index] = TypeProjectionImpl(Variance.IN_VARIANCE, approximatedSubType) + continue@loop + } + } + + newArguments[index] = TypeProjectionImpl(Variance.OUT_VARIANCE, approximatedSuperType) + } + } + } + + if (newArguments.all { it == null }) return null + + val newArgumentsList = arguments.mapIndexed { index, oldArgument -> newArguments[index] ?: oldArgument } + return type.replace(newArgumentsList) + } + + private fun SimpleType.defaultResult(toSuper: Boolean) = if (toSuper) builtIns.nullableAnyType else { + if (isMarkedNullable) builtIns.nullableNothingType else builtIns.nothingType + } + + // Any? or Any! + private fun UnwrappedType.isTrivialSuper() = upperIfFlexible().isNullableAny() + + // Nothing or Nothing! + private fun UnwrappedType.isTrivialSub() = lowerIfFlexible().isNothing() +} \ No newline at end of file diff --git a/compiler/tests-common/org/jetbrains/kotlin/checkers/AbstractDiagnosticsTest.kt b/compiler/tests-common/org/jetbrains/kotlin/checkers/AbstractDiagnosticsTest.kt index 62ff18aa1fd..fbc31856855 100644 --- a/compiler/tests-common/org/jetbrains/kotlin/checkers/AbstractDiagnosticsTest.kt +++ b/compiler/tests-common/org/jetbrains/kotlin/checkers/AbstractDiagnosticsTest.kt @@ -52,6 +52,7 @@ import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.platform.JvmBuiltIns import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.* +import org.jetbrains.kotlin.resolve.calls.USE_NEW_INFERENCE import org.jetbrains.kotlin.resolve.calls.model.MutableResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver @@ -501,8 +502,10 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() { val lineAndColumn = DiagnosticUtils.getLineAndColumnInPsiFile(element.containingFile, element.textRange) - assertTrue("Resolved call for '${element.text}'$lineAndColumn is not completed", - (resolvedCall as MutableResolvedCall<*>).isCompleted) + if (!USE_NEW_INFERENCE) { + assertTrue("Resolved call for '${element.text}'$lineAndColumn is not completed", + (resolvedCall as MutableResolvedCall<*>).isCompleted) + } } checkResolvedCallsInDiagnostics(bindingContext) @@ -532,6 +535,7 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() { private fun assertResolvedCallsAreCompleted(diagnostic: Diagnostic, resolvedCalls: Collection>) { val element = diagnostic.psiElement val lineAndColumn = DiagnosticUtils.getLineAndColumnInPsiFile(element.containingFile, element.textRange) + if (USE_NEW_INFERENCE) return assertTrue("Resolved calls stored in ${diagnostic.factory.name}\nfor '${element.text}'$lineAndColumn are not completed", resolvedCalls.all { (it as MutableResolvedCall<*>).isCompleted }) diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/ErrorUtils.java b/core/descriptors/src/org/jetbrains/kotlin/types/ErrorUtils.java index cf6f3d63d15..5a9a92b5ddc 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/ErrorUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/types/ErrorUtils.java @@ -230,14 +230,14 @@ public class ErrorUtils { @Nullable @Override public ClassifierDescriptor getContributedClassifier(@NotNull Name name, @NotNull LookupLocation location) { - throw new IllegalStateException(); + throw new IllegalStateException(debugMessage+", required name: " + name); } @NotNull @Override @SuppressWarnings({"unchecked"}) // KT-9898 Impossible implement kotlin interface from java public Collection getContributedVariables(@NotNull Name name, @NotNull LookupLocation location) { - throw new IllegalStateException(); + throw new IllegalStateException(debugMessage+", required name: " + name); } @NotNull @@ -246,7 +246,7 @@ public class ErrorUtils { // method is covariantly overridden in Kotlin, but collections in Java are invariant @SuppressWarnings({"unchecked"}) public Collection getContributedFunctions(@NotNull Name name, @NotNull LookupLocation location) { - throw new IllegalStateException(); + throw new IllegalStateException(debugMessage+", required name: " + name); } @NotNull @@ -254,7 +254,7 @@ public class ErrorUtils { public Collection getContributedDescriptors( @NotNull DescriptorKindFilter kindFilter, @NotNull Function1 nameFilter ) { - throw new IllegalStateException(); + throw new IllegalStateException(debugMessage); } @NotNull diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/IntersectionType.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/IntersectionType.kt index 01cfbc42956..7101a1eca34 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/IntersectionType.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/IntersectionType.kt @@ -18,6 +18,10 @@ package org.jetbrains.kotlin.types.checker import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.types.* +import java.util.* +import kotlin.collections.HashSet + +fun intersectWrappedTypes(types: Collection) = intersectTypes(types.map { it.unwrap() }) fun intersectTypes(types: List): UnwrappedType { when (types.size) { @@ -58,7 +62,92 @@ fun intersectTypes(types: List): UnwrappedType { // types.size >= 2 // It is incorrect see to nullability here, because of KT-12684 private fun intersectTypes(types: List): SimpleType { - val constructor = IntersectionTypeConstructor(types) - return KotlinTypeFactory.simpleType(Annotations.EMPTY, constructor, listOf(), false, constructor.createScopeForKotlinType()) + return TypeIntersector.intersectTypes(types) } +object TypeIntersector { + + internal fun intersectTypes(types: List): SimpleType { + assert(types.size > 1) { + "Size should be at least 2, but it is ${types.size}" + } + val inputTypes = ArrayList() + for (type in types) { + if (type.constructor is IntersectionTypeConstructor) { + inputTypes.addAll(type.constructor.supertypes.map { + it.upperIfFlexible().let { if (type.isMarkedNullable) it.makeNullableAsSpecified(true) else it } + }) + } + else { + inputTypes.add(type) + } + } + val resultNullability = inputTypes.fold(ResultNullability.START, ResultNullability::combine) + /** + * resultNullability. Value description: + * ACCEPT_NULL means that all types marked nullable + * NOT_NULL means that there is one type which is subtype of Any => all types can be marked not nullable + * UNKNOWN means, that we do not know, i.e. more precisely, all singleClassifier types marked nullable if any, + * and other types is captured types or type parameters without not-null upper bound. Example: `String? & T` such types we should leave as is. + */ + val correctNullability = inputTypes.mapTo(HashSet()) { + if (resultNullability == ResultNullability.NOT_NULL) it.makeNullableAsSpecified(false) else it + } + + return intersectTypesWithoutIntersectionType(correctNullability) + } + + // nullability here is correct + private fun intersectTypesWithoutIntersectionType(inputTypes: Set): SimpleType { + // Any and Nothing should leave + // Note that duplicates should be dropped because we have Set here. + val filteredSupertypes = inputTypes.filterNot { upper -> + inputTypes.any { upper != it && NewKotlinTypeChecker.isSubtypeOf(it, upper) } + } + + assert(filteredSupertypes.isNotEmpty()) { + "This collections cannot be empty! input types: ${inputTypes.joinToString()}" + } + + if (filteredSupertypes.size < 2) return filteredSupertypes.single() + + val constructor = IntersectionTypeConstructor(inputTypes) + return KotlinTypeFactory.simpleType(Annotations.EMPTY, constructor, listOf(), false, constructor.createScopeForKotlinType()) + } + + /** + * Let T is type parameter with upper bound Any?. resultNullability(String? & T) = UNKNOWN => String? & T + */ + private enum class ResultNullability { + START { + override fun combine(nextType: UnwrappedType) = nextType.resultNullability + }, + ACCEPT_NULL { + override fun combine(nextType: UnwrappedType) = nextType.resultNullability + }, + // example: type parameter without not-null supertype + UNKNOWN { + override fun combine(nextType: UnwrappedType) = + nextType.resultNullability.let { + if (it == ACCEPT_NULL) this else it + } + }, + NOT_NULL { + override fun combine(nextType: UnwrappedType) = this + }; + + abstract fun combine(nextType: UnwrappedType): ResultNullability + + protected val UnwrappedType.resultNullability: ResultNullability + get() { + if (isMarkedNullable) return ACCEPT_NULL + + if (NullabilityChecker.isSubtypeOfAny(this)) { + return NOT_NULL + } + else { + return UNKNOWN + } + } + } +} \ No newline at end of file diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewCapturedType.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewCapturedType.kt index af96cc984aa..23f9f1a51d1 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewCapturedType.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewCapturedType.kt @@ -26,13 +26,52 @@ import org.jetbrains.kotlin.types.typeUtil.asTypeProjection import org.jetbrains.kotlin.types.typeUtil.builtIns import org.jetbrains.kotlin.utils.DO_NOTHING_2 +// if input type is capturedType, then we approximate it to UpperBound +// null means that type should be leaved as is +fun prepareArgumentTypeRegardingCaptureTypes(argumentType: UnwrappedType): UnwrappedType? { + val simpleType = NewKotlinTypeChecker.transformToNewType(argumentType.lowerIfFlexible()) + if (simpleType.constructor is IntersectionTypeConstructor){ + var changed = false + val preparedSuperTypes = simpleType.constructor.supertypes.map { + prepareArgumentTypeRegardingCaptureTypes(it.unwrap())?.apply { changed = true } ?: it.unwrap() + } + if (!changed) return null + return intersectTypes(preparedSuperTypes).makeNullableAsSpecified(simpleType.isMarkedNullable) + } + if (simpleType is NewCapturedType) { + // todo may be we should respect flexible capture types also... + return simpleType.constructor.supertypes.takeIf { it.isNotEmpty() }?.let(::intersectTypes) ?: argumentType.builtIns.nullableAnyType + } + return captureFromExpression(simpleType) +} + +fun captureFromExpression(type: UnwrappedType): UnwrappedType? = when (type) { + is SimpleType -> captureFromExpression(type) + // i.e. if there is nothing to capture -- no changes, if there is something -- use lowerBound as base type + is FlexibleType -> captureFromExpression(type.lowerBound) +} + +fun captureFromExpression(type: SimpleType): UnwrappedType? { + val typeConstructor = type.constructor + if (typeConstructor is IntersectionTypeConstructor) { + var changed = false + val capturedSupertypes = typeConstructor.supertypes.map { + captureFromExpression(it.unwrap())?.apply { changed = true } ?: it.unwrap() + } + if (!changed) return null + return intersectTypes(capturedSupertypes).makeNullableAsSpecified(type.isMarkedNullable) + } + return captureFromArguments(type, CaptureStatus.FROM_EXPRESSION) +} + +// this function suppose that input type is simple classifier type fun captureFromArguments( type: SimpleType, status: CaptureStatus, acceptNewCapturedType: ((argumentIndex: Int, NewCapturedType) -> Unit) = DO_NOTHING_2 -): SimpleType { +): SimpleType? { val arguments = type.arguments - if (arguments.all { it.projectionKind == Variance.INVARIANT }) return type + if (arguments.all { it.projectionKind == Variance.INVARIANT }) return null val newArguments = arguments.map { projection -> @@ -68,6 +107,7 @@ fun captureFromArguments( enum class CaptureStatus { FOR_SUBTYPING, + FOR_INCORPORATION, FROM_EXPRESSION } @@ -86,7 +126,7 @@ class NewCapturedType( override val annotations: Annotations = Annotations.EMPTY, override val isMarkedNullable: Boolean = false ): SimpleType() { - constructor(captureStatus: CaptureStatus, lowerType: UnwrappedType?, projection: TypeProjection) : + internal constructor(captureStatus: CaptureStatus, lowerType: UnwrappedType?, projection: TypeProjection) : this(captureStatus, NewCapturedTypeConstructor(projection), lowerType) override val arguments: List get() = listOf() diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt index 0e2ca146fb7..9546d45e3b8 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt @@ -250,7 +250,7 @@ object NewKotlinTypeChecker : KotlinTypeChecker { var result: MutableList? = null anySupertype(baseType, { false }) { - val current = captureFromArguments(it, CaptureStatus.FOR_SUBTYPING) + val current = captureFromArguments(it, CaptureStatus.FOR_SUBTYPING) ?: it when { areEqualTypeConstructors(current.constructor, constructor) -> { @@ -290,7 +290,7 @@ object NewKotlinTypeChecker : KotlinTypeChecker { return if (allPureSupertypes.isNotEmpty()) allPureSupertypes else supertypes } - private fun effectiveVariance(declared: Variance, useSite: Variance): Variance? { + fun effectiveVariance(declared: Variance, useSite: Variance): Variance? { if (declared == Variance.INVARIANT) return useSite if (useSite == Variance.INVARIANT) return declared @@ -341,12 +341,15 @@ object NullabilityChecker { fun isPossibleSubtype(context: TypeCheckerContext, subType: SimpleType, superType: SimpleType): Boolean = context.runIsPossibleSubtype(subType, superType) + fun isSubtypeOfAny(type: UnwrappedType): Boolean = + TypeCheckerContext(false).hasNotNullSupertype(type.lowerIfFlexible(), SupertypesPolicy.LowerIfFlexible) + private fun TypeCheckerContext.runIsPossibleSubtype(subType: SimpleType, superType: SimpleType): Boolean { // it makes for case String? & Any <: String assert(subType.isIntersectionType || subType.isSingleClassifierType || subType.isAllowedTypeVariable) { "Not singleClassifierType superType: $superType" } - assert(superType.isSingleClassifierType || subType.isAllowedTypeVariable) { + assert(superType.isSingleClassifierType || superType.isAllowedTypeVariable) { "Not singleClassifierType superType: $superType" } @@ -391,7 +394,7 @@ object NullabilityChecker { /** * ClassType means that type constructor for this type is type for real class or interface */ -private val SimpleType.isClassType: Boolean get() = constructor.declarationDescriptor is ClassDescriptor +val SimpleType.isClassType: Boolean get() = constructor.declarationDescriptor is ClassDescriptor /** * SingleClassifierType is one of the following types: @@ -401,10 +404,10 @@ private val SimpleType.isClassType: Boolean get() = constructor.declarationDescr * * Such types can contains error types in our arguments, but type constructor isn't errorTypeConstructor */ -private val SimpleType.isSingleClassifierType: Boolean +val SimpleType.isSingleClassifierType: Boolean get() = !isError && constructor.declarationDescriptor !is TypeAliasDescriptor && (constructor.declarationDescriptor != null || this is CapturedType || this is NewCapturedType) -private val SimpleType.isIntersectionType: Boolean +val SimpleType.isIntersectionType: Boolean get() = constructor is IntersectionTypeConstructor \ No newline at end of file diff --git a/core/util.runtime/src/org/jetbrains/kotlin/utils/DFS.java b/core/util.runtime/src/org/jetbrains/kotlin/utils/DFS.java index 287628976ed..66cbbc62cdd 100644 --- a/core/util.runtime/src/org/jetbrains/kotlin/utils/DFS.java +++ b/core/util.runtime/src/org/jetbrains/kotlin/utils/DFS.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -91,7 +91,7 @@ public class DFS { return topologicalOrder(nodes, neighbors, new VisitedWithSet()); } - private static void doDfs(@NotNull N current, @NotNull Neighbors neighbors, @NotNull Visited visited, @NotNull NodeHandler handler) { + public static void doDfs(@NotNull N current, @NotNull Neighbors neighbors, @NotNull Visited visited, @NotNull NodeHandler handler) { if (!visited.checkAndMarkVisited(current)) return; if (!handler.beforeChildren(current)) return;