From 0c79949cf145f79638c7c11d34860a43aeb08006 Mon Sep 17 00:00:00 2001 From: Stanislav Erokhin Date: Wed, 12 Apr 2017 15:23:36 +0300 Subject: [PATCH] [NI] Implement NewTypeSubstitutor --- .../calls/components/KotlinCallCompleter.kt | 16 +-- .../calls/components/ResolutionParts.kt | 3 +- .../inference/ConstraintSystemBuilder.kt | 4 +- .../resolve/calls/inference/InferenceUtils.kt | 22 ++-- .../components/NewTypeSubstitutor.kt | 123 ++++++++++++++++++ .../model/NewConstraintSystemImpl.kt | 21 ++- 6 files changed, 159 insertions(+), 30 deletions(-) create mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/NewTypeSubstitutor.kt 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 index 0ba8df0d969..a422233c9ad 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt @@ -21,19 +21,19 @@ 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.NewTypeSubstitutor 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.inference.substitute 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 @@ -45,8 +45,8 @@ class KotlinCallCompleter( interface Context { val innerCalls: List val hasContradiction: Boolean - fun buildCurrentSubstitutor(): TypeSubstitutor - fun buildResultingSubstitutor(): TypeSubstitutor + fun buildCurrentSubstitutor(): NewTypeSubstitutor + fun buildResultingSubstitutor(): NewTypeSubstitutor val lambdaArguments: List // type can be proper if it not contains not fixed type variables @@ -100,7 +100,7 @@ class KotlinCallCompleter( return ResolvedKotlinCall.CompletedResolvedKotlinCall(completedCall, competedCalls) } - private fun KotlinResolutionCandidate.toCompletedCall(substitutor: TypeSubstitutor): CompletedKotlinCall { + private fun KotlinResolutionCandidate.toCompletedCall(substitutor: NewTypeSubstitutor): CompletedKotlinCall { if (this is VariableAsFunctionKotlinResolutionCandidate) { val variable = resolvedVariable.toCompletedCall(substitutor) val invoke = invokeCandidate.toCompletedCall(substitutor) @@ -110,10 +110,10 @@ class KotlinCallCompleter( return (this as SimpleKotlinResolutionCandidate).toCompletedCall(substitutor) } - private fun SimpleKotlinResolutionCandidate.toCompletedCall(substitutor: TypeSubstitutor): CompletedKotlinCall.Simple { + private fun SimpleKotlinResolutionCandidate.toCompletedCall(substitutor: NewTypeSubstitutor): 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 typeArguments = descriptorWithFreshTypes.typeParameters.map { substitutor.safeSubstitute(it.defaultType) } val status = computeStatus(this, resultingDescriptor) return CompletedKotlinCall.Simple(kotlinCall, candidateDescriptor, resultingDescriptor, status, explicitReceiverKind, @@ -226,7 +226,7 @@ class KotlinCallCompleter( 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() + fun substitute(type: UnwrappedType) = currentSubstitutor.safeSubstitute(type) val receiver = lambda.receiver?.let(::substitute) val parameters = lambda.parameters.map(::substitute) 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 index 9f0423bff51..48788edc830 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.resolve.calls.components.TypeArgumentsToParametersMa 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.inference.substitute import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.smartcasts.getReceiverValueWithSmartCast import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.* @@ -164,7 +165,7 @@ internal object CreteDescriptorWithFreshTypeVariables : ResolutionPart { */ val toFixedTypeParameters = csBuilder.simplify() // todo optimize -- composite substitutions before run safeSubstitute - descriptorWithFreshTypes = candidateDescriptor.safeSubstitute(toFreshVariables).safeSubstitute(toFixedTypeParameters) + descriptorWithFreshTypes = candidateDescriptor.substitute(toFreshVariables)!!.substitute(toFixedTypeParameters)!! return emptyList() } 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 index 6ae6468cbf8..57a32f4e518 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilder.kt @@ -16,11 +16,11 @@ package org.jetbrains.kotlin.resolve.calls.inference +import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor 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 @@ -43,5 +43,5 @@ interface ConstraintSystemBuilder { * This function removes variables for which we know exact type. * @return substitutor from typeVariable to result */ - fun simplify(): TypeSubstitutor + fun simplify(): NewTypeSubstitutor } 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 index 81bf200e5f3..50af0bbfe02 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt @@ -17,17 +17,15 @@ package org.jetbrains.kotlin.resolve.calls.inference import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor +import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutorByConstructorMap 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 +import org.jetbrains.kotlin.types.* -fun ConstraintStorage.buildCurrentSubstitutor() = TypeConstructorSubstitution.createByConstructorsMap(fixedTypeVariables.entries.associate { - it.key to it.value.asTypeProjection() -}).buildSubstitutor() +fun ConstraintStorage.buildCurrentSubstitutor() = NewTypeSubstitutorByConstructorMap(fixedTypeVariables.entries.associate { + it.key to it.value +}) val CallableDescriptor.returnTypeOrNothing: UnwrappedType get() { @@ -38,3 +36,11 @@ val CallableDescriptor.returnTypeOrNothing: UnwrappedType fun TypeSubstitutor.substitute(type: UnwrappedType): UnwrappedType = safeSubstitute(type, Variance.INVARIANT).unwrap() +fun CallableDescriptor.substitute(substitutor: NewTypeSubstitutor): CallableDescriptor? { + val wrappedSubstitution = object : TypeSubstitution() { + override fun get(key: KotlinType): TypeProjection? = null + override fun prepareTopLevelType(topLevelType: KotlinType, position: Variance) = substitutor.safeSubstitute(topLevelType.unwrap()) + } + return substitute(TypeSubstitutor.create(wrappedSubstitution)) +} + diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/NewTypeSubstitutor.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/NewTypeSubstitutor.kt new file mode 100644 index 00000000000..33cd7711ad7 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/NewTypeSubstitutor.kt @@ -0,0 +1,123 @@ +/* + * 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.inference.components + +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.checker.NewCapturedType +import org.jetbrains.kotlin.types.checker.NewCapturedTypeConstructor +import org.jetbrains.kotlin.types.checker.intersectTypes + +interface NewTypeSubstitutor { + fun substituteNotNullTypeWithConstructor(constructor: TypeConstructor): UnwrappedType? + val isEmpty: Boolean + + fun safeSubstitute(type: UnwrappedType): UnwrappedType = substitute(type) ?: type + + // null means that this type isn't changed + fun substitute(type: UnwrappedType): UnwrappedType? = + when (type) { + is SimpleType -> substitute(type) + is FlexibleType -> if (type is DynamicType || type is RawType) { + null + } + else { + val lowerBound = substitute(type.lowerBound) + val upperBound = substitute(type.upperBound) + if (lowerBound == null && upperBound == null) { + null + } + else { + // todo discuss lowerIfFlexible and upperIfFlexible + FlexibleTypeImpl(lowerBound?.lowerIfFlexible() ?: type.lowerBound, upperBound?.upperIfFlexible() ?: type.upperBound) + } + } + } + + fun substitute(type: SimpleType): UnwrappedType? { + if (type.isError) return null + + if (type.arguments.isNotEmpty()) { + return substituteParametrizedType(type) + } + + 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" + } + val lower = (type as NewCapturedType).lowerType?.let { substitute(it) } + if (lower != null) throw IllegalStateException("Illegal type substitutor: $this, " + + "because for captured type '$type' lower type approximation should be null, but it is: '$lower'," + + "original lower type: '${type.lowerType}") + + type.constructor.supertypes.forEach { supertype -> + substitute(supertype)?.let { + throw IllegalStateException("Illegal type substitutor: $this, " + + "because for captured type '$type' supertype approximation should be null, but it is: '$supertype'," + + "original supertype: '$supertype'") + } + } + + return null + } + + if (typeConstructor is IntersectionTypeConstructor) { + var thereIsChanges = false + val newTypes = typeConstructor.supertypes.map { + substitute(it.unwrap())?.apply { thereIsChanges = true } ?: it.unwrap() + } + if (!thereIsChanges) return null + return intersectTypes(newTypes).let { if (type.isMarkedNullable) it.makeNullableAsSpecified(true) else it } + } + + // simple classifier type + val replacement = substituteNotNullTypeWithConstructor(typeConstructor) ?: return null + + return if (type.isMarkedNullable) replacement.makeNullableAsSpecified(true) else replacement + } + + private fun substituteParametrizedType(type: SimpleType): UnwrappedType? { + val parameters = type.constructor.parameters + val arguments = type.arguments + if (parameters.size != arguments.size) { + // todo error type or exception? + return ErrorUtils.createErrorType("Inconsistent type: $type (parameters.size = ${parameters.size}, arguments.size = ${arguments.size})") + } + val newArguments = arrayOfNulls(arguments.size) + + for (index in arguments.indices) { + val argument = arguments[index] + + if (argument.isStarProjection) continue + val substitutedArgumentType = substitute(argument.type.unwrap()) ?: continue + + newArguments[index] = TypeProjectionImpl(argument.projectionKind, substitutedArgumentType) + } + + if (newArguments.all { it == null }) return null + + val newArgumentsList = arguments.mapIndexed { index, oldArgument -> newArguments[index] ?: oldArgument } + return type.replace(newArgumentsList) + } +} + +class NewTypeSubstitutorByConstructorMap(val map: Map) : NewTypeSubstitutor { + override val isEmpty get() = map.isEmpty() + override fun substituteNotNullTypeWithConstructor(constructor: TypeConstructor): UnwrappedType? = map[constructor] +} \ 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 index ffd350aea13..9da40d7cef3 100644 --- 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 @@ -20,15 +20,14 @@ 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.inference.components.* 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.ErrorUtils +import org.jetbrains.kotlin.types.TypeConstructor +import org.jetbrains.kotlin.types.UnwrappedType import org.jetbrains.kotlin.types.typeUtil.contains import java.util.* @@ -125,7 +124,7 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re return fixedVariables } - override fun simplify(): TypeSubstitutor { + override fun simplify(): NewTypeSubstitutor { checkState(State.BUILDING) var fixedVariables = getVariablesForFixation() @@ -235,20 +234,20 @@ class NewConstraintSystemImpl(val constraintInjector: ConstraintInjector, val re return !type.contains { storage.notFixedTypeVariables.containsKey(it.constructor) } } - override fun buildCurrentSubstitutor(): TypeSubstitutor { + override fun buildCurrentSubstitutor(): NewTypeSubstitutor { checkState(State.COMPLETION) return storage.buildCurrentSubstitutor() } - override fun buildResultingSubstitutor(): TypeSubstitutor { + override fun buildResultingSubstitutor(): NewTypeSubstitutor { checkState(State.COMPLETION) val currentSubstitutorMap = storage.fixedTypeVariables.entries.associate { - it.key to it.value.asTypeProjection() + it.key to it.value } val uninferredSubstitutorMap = storage.notFixedTypeVariables.entries.associate { (freshTypeConstructor, typeVariable) -> - freshTypeConstructor to ErrorUtils.createErrorTypeWithCustomConstructor("Uninferred type", typeVariable.typeVariable.freshTypeConstructor).asTypeProjection() + freshTypeConstructor to ErrorUtils.createErrorTypeWithCustomConstructor("Uninferred type", typeVariable.typeVariable.freshTypeConstructor) } - return TypeConstructorSubstitution.createByConstructorsMap(currentSubstitutorMap + uninferredSubstitutorMap).buildSubstitutor() + return NewTypeSubstitutorByConstructorMap(currentSubstitutorMap + uninferredSubstitutorMap) } } \ No newline at end of file