From d9568ef88c690633b13cbb438edf5e7a0e2531a8 Mon Sep 17 00:00:00 2001 From: Svetlana Isakova Date: Thu, 21 May 2015 22:54:56 +0300 Subject: [PATCH] Added special type constructor for function/ extension function that stores an information about declared argument types --- .../resolve/calls/ArgumentTypeResolver.java | 43 ++++++----- .../resolve/calls/CandidateResolver.java | 30 +------- .../kotlin/types/JetTypeCheckerTest.java | 4 +- .../calls/inference/ConstraintSystemImpl.kt | 77 +++++++++---------- .../jetbrains/kotlin/types/ErrorUtils.java | 67 +++++++++++++++- 5 files changed, 132 insertions(+), 89 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java index 9bec24d0f0d..b3b81a31614 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.annotations.Annotations; import org.jetbrains.kotlin.diagnostics.Errors; import org.jetbrains.kotlin.psi.*; +import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage; import org.jetbrains.kotlin.resolve.*; import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext; import org.jetbrains.kotlin.resolve.calls.context.CheckValueArgumentsMode; @@ -36,11 +37,11 @@ import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluat import org.jetbrains.kotlin.resolve.scopes.JetScope; import org.jetbrains.kotlin.resolve.scopes.receivers.QualifierReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; +import org.jetbrains.kotlin.types.ErrorUtils; import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.types.TypeUtils; import org.jetbrains.kotlin.types.checker.JetTypeChecker; import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices; -import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage; import org.jetbrains.kotlin.types.expressions.JetTypeInfo; import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryPackage; @@ -54,7 +55,9 @@ import static org.jetbrains.kotlin.resolve.calls.CallResolverUtil.ResolveArgumen import static org.jetbrains.kotlin.resolve.calls.CallResolverUtil.ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS; import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.DEPENDENT; import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT; -import static org.jetbrains.kotlin.types.TypeUtils.*; +import static org.jetbrains.kotlin.resolve.calls.inference.InferencePackage.createCorrespondingFunctionTypeForFunctionPlaceholder; +import static org.jetbrains.kotlin.types.TypeUtils.DONT_CARE; +import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE; public class ArgumentTypeResolver { @@ -81,21 +84,21 @@ public class ArgumentTypeResolver { @NotNull JetType actualType, @NotNull JetType expectedType ) { - if (actualType == PLACEHOLDER_FUNCTION_TYPE) { - return isFunctionOrErrorType(expectedType) || KotlinBuiltIns.isAnyOrNullableAny(expectedType); //todo function type extends + if (ErrorUtils.isFunctionPlaceholder(actualType)) { + JetType functionType = createCorrespondingFunctionTypeForFunctionPlaceholder(actualType, expectedType); + return JetTypeChecker.DEFAULT.isSubtypeOf(functionType, expectedType); } return JetTypeChecker.DEFAULT.isSubtypeOf(actualType, expectedType); } - private static boolean isFunctionOrErrorType(@NotNull JetType supertype) { - return KotlinBuiltIns.isFunctionOrExtensionFunctionType(supertype) || supertype.isError(); - } - public void checkTypesWithNoCallee(@NotNull CallResolutionContext context) { checkTypesWithNoCallee(context, SHAPE_FUNCTION_ARGUMENTS); } - public void checkTypesWithNoCallee(@NotNull CallResolutionContext context, @NotNull ResolveArgumentsMode resolveFunctionArgumentBodies) { + public void checkTypesWithNoCallee( + @NotNull CallResolutionContext context, + @NotNull ResolveArgumentsMode resolveFunctionArgumentBodies + ) { if (context.checkArguments == CheckValueArgumentsMode.DISABLED) return; for (ValueArgument valueArgument : context.call.getValueArguments()) { @@ -222,27 +225,31 @@ public class ArgumentTypeResolver { @Nullable public JetType getShapeTypeOfFunctionLiteral( - @NotNull JetFunction functionLiteral, + @NotNull JetFunction function, @NotNull JetScope scope, @NotNull BindingTrace trace, boolean expectedTypeIsUnknown ) { - if (functionLiteral.getValueParameterList() == null) { - return expectedTypeIsUnknown ? PLACEHOLDER_FUNCTION_TYPE : builtIns.getFunctionType( - Annotations.EMPTY, null, Collections.emptyList(), DONT_CARE); + boolean isFunctionLiteral = function instanceof JetFunctionLiteral; + if (function.getValueParameterList() == null && isFunctionLiteral) { + return expectedTypeIsUnknown + ? ErrorUtils.createFunctionPlaceholderType(Collections.emptyList()) + : builtIns.getFunctionType(Annotations.EMPTY, null, Collections.emptyList(), DONT_CARE); } - List valueParameters = functionLiteral.getValueParameters(); + List valueParameters = function.getValueParameters(); TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create( trace, "trace to resolve function literal parameter types"); List parameterTypes = Lists.newArrayList(); for (JetParameter parameter : valueParameters) { parameterTypes.add(resolveTypeRefWithDefault(parameter.getTypeReference(), scope, temporaryTrace, DONT_CARE)); } - JetType returnType = resolveTypeRefWithDefault(functionLiteral.getTypeReference(), scope, temporaryTrace, DONT_CARE); + JetType returnType = resolveTypeRefWithDefault(function.getTypeReference(), scope, temporaryTrace, DONT_CARE); assert returnType != null; - JetType receiverType = resolveTypeRefWithDefault(functionLiteral.getReceiverTypeReference(), scope, temporaryTrace, null); - return builtIns.getFunctionType(Annotations.EMPTY, receiverType, parameterTypes, - returnType); + JetType receiverType = resolveTypeRefWithDefault(function.getReceiverTypeReference(), scope, temporaryTrace, null); + + return expectedTypeIsUnknown && isFunctionLiteral + ? ErrorUtils.createFunctionPlaceholderType(parameterTypes) + : builtIns.getFunctionType(Annotations.EMPTY, receiverType, parameterTypes, returnType); } @Nullable diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java index 206e659b564..90c2af91d0e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java @@ -58,7 +58,6 @@ import static org.jetbrains.kotlin.resolve.calls.CallResolverUtil.ResolveArgumen import static org.jetbrains.kotlin.resolve.calls.CallResolverUtil.ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS; import static org.jetbrains.kotlin.resolve.calls.CallTransformer.CallForImplicitInvoke; import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT; -import static org.jetbrains.kotlin.resolve.calls.inference.InferencePackage.createCorrespondingExtensionFunctionTypeIfNecessary; import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.RECEIVER_POSITION; import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.VALUE_PARAMETER_POSITION; import static org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.*; @@ -534,18 +533,18 @@ public class CandidateResolver { ArgumentMatchStatus matchStatus = ArgumentMatchStatus.SUCCESS; JetType resultingType = type; - if (type == null || (type.isError() && type != PLACEHOLDER_FUNCTION_TYPE)) { + if (type == null || (type.isError() && !ErrorUtils.isFunctionPlaceholder(type))) { matchStatus = ArgumentMatchStatus.ARGUMENT_HAS_NO_TYPE; } else if (!noExpectedType(expectedType)) { if (!ArgumentTypeResolver.isSubtypeOfForArgumentType(type, expectedType)) { - JetType morePreciseType = makeMorePreciseType(type, expression, newContext); - if (morePreciseType == null) { + JetType smartCast = smartCastValueArgumentTypeIfPossible(expression, newContext.expectedType, type, newContext); + if (smartCast == null) { resultStatus = OTHER_ERROR; matchStatus = ArgumentMatchStatus.TYPE_MISMATCH; } else { - resultingType = morePreciseType; + resultingType = smartCast; } } else if (ErrorUtils.containsUninferredParameter(expectedType)) { @@ -559,27 +558,6 @@ public class CandidateResolver { return new ValueArgumentsCheckingResult(resultStatus, argumentTypes); } - @Nullable - private static > JetType makeMorePreciseType( - @NotNull JetType type, - @NotNull JetExpression expression, - @NotNull CallResolutionContext context - ) { - JetType smartCastType = smartCastValueArgumentTypeIfPossible(expression, context.expectedType, type, context); - if (smartCastType != null) { - return smartCastType; - } - // function literal without declaring receiver type { x -> ... } - // can be considered as extension function if one is expected - if (ArgumentTypeResolver.isFunctionLiteralArgument(expression, context)) { - JetType extensionFunctionType = createCorrespondingExtensionFunctionTypeIfNecessary(type, context.expectedType); - if (ArgumentTypeResolver.isSubtypeOfForArgumentType(extensionFunctionType, context.expectedType)) { - return extensionFunctionType; - } - } - return null; - } - @Nullable private static JetType smartCastValueArgumentTypeIfPossible( @NotNull JetExpression expression, diff --git a/compiler/tests/org/jetbrains/kotlin/types/JetTypeCheckerTest.java b/compiler/tests/org/jetbrains/kotlin/types/JetTypeCheckerTest.java index 148fde19803..aeaf7f8ef2b 100644 --- a/compiler/tests/org/jetbrains/kotlin/types/JetTypeCheckerTest.java +++ b/compiler/tests/org/jetbrains/kotlin/types/JetTypeCheckerTest.java @@ -382,8 +382,8 @@ public class JetTypeCheckerTest extends JetLiteFixture { public void testBlocks() throws Exception { assertType("if (1) {val a = 1; a} else {null}", "Int?"); assertType("if (1) {() -> val a = 1; a} else {() -> null}", "Function0"); - assertType("if (1) {() -> val a = 1; a; var b : Boolean; b} else null", "Function0?"); - assertType("if (1) {() -> val a = 1; a; var b = a; b} else null", "Function0?"); + assertType("if (1) (fun (): Boolean { val a = 1; a; var b : Boolean; return b }) else null", "Function0?"); + assertType("if (1) (fun (): Int { val a = 1; a; var b = a; return b }) else null", "Function0?"); } public void testNew() throws Exception { diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt index 3e9d9860d58..10d6697dac7 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt @@ -24,8 +24,10 @@ import org.jetbrains.kotlin.types.TypeUtils.DONT_CARE import org.jetbrains.kotlin.types.TypeProjectionImpl import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.types.ErrorUtils +import org.jetbrains.kotlin.types.ErrorUtils.FunctionPlaceholderTypeConstructor import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure import org.jetbrains.kotlin.types.checker.TypeCheckingProcedureCallbacks @@ -42,6 +44,7 @@ import org.jetbrains.kotlin.types.getCustomTypeVariable import org.jetbrains.kotlin.types.isFlexible import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.Bound import org.jetbrains.kotlin.types.TypeSubstitution +import org.jetbrains.kotlin.types.checker.JetTypeChecker public class ConstraintSystemImpl : ConstraintSystem { @@ -234,7 +237,7 @@ public class ConstraintSystemImpl : ConstraintSystem { return true } - if (type == null || (type.isError() && type != TypeUtils.PLACEHOLDER_FUNCTION_TYPE)) { + if (type == null || (type.isError() && !ErrorUtils.isFunctionPlaceholder(type))) { errors.add(ErrorInConstrainingType(constraintPosition)) return true } @@ -251,30 +254,22 @@ public class ConstraintSystemImpl : ConstraintSystem { if (isErrorOrSpecialType(subType, constraintPosition) || isErrorOrSpecialType(superType, constraintPosition)) return if (subType == null || superType == null) return - assert(superType != TypeUtils.PLACEHOLDER_FUNCTION_TYPE) { + assert(!ErrorUtils.isFunctionPlaceholder(superType)) { "The type for " + constraintPosition + " shouldn't be a placeholder for function type" } - if (subType == TypeUtils.PLACEHOLDER_FUNCTION_TYPE) { - if (!KotlinBuiltIns.isFunctionOrExtensionFunctionType(superType)) { - if (isMyTypeVariable(superType)) { - // a constraint binds type parameter and any function type, so there is no new info and no error - return - } - errors.add(TypeConstructorMismatch(constraintPosition)) + // function literal { x -> ... } goes without declaring receiver type + // and can be considered as extension function if one is expected + val newSubType = if (constraintKind == SUB_TYPE && ErrorUtils.isFunctionPlaceholder(subType)) { + if (isMyTypeVariable(superType)) { + // the constraint binds type parameter and a function type, + // we don't add it without knowing whether it's a function type or an extension function type + return } - return - } - - // todo temporary hack - // function literal without declaring receiver type { x -> ... } - // can be considered as extension function if one is expected - // (special type constructor for function/ extension function should be introduced like PLACEHOLDER_FUNCTION_TYPE) - val newSubType = if (constraintKind == SUB_TYPE) { - createCorrespondingExtensionFunctionTypeIfNecessary(subType, superType) + createCorrespondingFunctionTypeForFunctionPlaceholder(subType, superType) } else { - subType : JetType + subType } fun simplifyConstraint(subType: JetType, superType: JetType) { @@ -422,31 +417,29 @@ public class ConstraintSystemImpl : ConstraintSystem { override fun getCurrentSubstitutor() = replaceUninferredBy(TypeUtils.DONT_CARE).setApproximateCapturedTypes() } -fun createCorrespondingExtensionFunctionTypeIfNecessary(functionType: JetType, expectedType: JetType): JetType { - if (KotlinBuiltIns.isFunctionType(functionType) && KotlinBuiltIns.isExtensionFunctionType(expectedType)) { - return createCorrespondingExtensionFunctionType(functionType, DONT_CARE) +fun createCorrespondingFunctionTypeForFunctionPlaceholder( + functionPlaceholder: JetType, + expectedType: JetType +): JetType { + assert(ErrorUtils.isFunctionPlaceholder(functionPlaceholder)) { "Function placeholder type expected: $functionPlaceholder" } + val functionPlaceholderTypeConstructor = functionPlaceholder.getConstructor() as FunctionPlaceholderTypeConstructor + val declaredArgumentTypes = functionPlaceholderTypeConstructor.getArgumentTypes() + + val isExtension = KotlinBuiltIns.isExtensionFunctionType(expectedType) + val newArgumentTypes = if (declaredArgumentTypes.isEmpty()) { + val typeParamSize = expectedType.getConstructor().getParameters().size() + // the first parameter is receiver (if present), the last one is return type, + // the remaining are function arguments + val functionArgumentsSize = if (isExtension) typeParamSize - 2 else typeParamSize - 1 + val result = arrayListOf() + (1..functionArgumentsSize).forEach { result.add(DONT_CARE) } + result } - return functionType -} - -private fun createCorrespondingExtensionFunctionType(functionType: JetType, receiverType: JetType): JetType { - assert(KotlinBuiltIns.isFunctionType(functionType)) - - val typeArguments = functionType.getArguments() - assert(!typeArguments.isEmpty()) - - val arguments = ArrayList() - // excluding the last type argument of the function type, which is the return type - var index = 0 - val lastIndex = typeArguments.size() - 1 - for (typeArgument in typeArguments) { - if (index < lastIndex) { - arguments.add(typeArgument.getType()) - } - index++ + else { + declaredArgumentTypes } - val returnType = typeArguments.get(lastIndex).getType() - return KotlinBuiltIns.getInstance().getFunctionType(functionType.getAnnotations(), receiverType, arguments, returnType) + val receiverType = if (isExtension) DONT_CARE else null + return KotlinBuiltIns.getInstance().getFunctionType(Annotations.EMPTY, receiverType, newArgumentTypes, DONT_CARE) } private fun TypeSubstitutor.setApproximateCapturedTypes(): TypeSubstitutor { diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/ErrorUtils.java b/core/descriptors/src/org/jetbrains/kotlin/types/ErrorUtils.java index 46c39185472..328d2c1ad54 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/ErrorUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/types/ErrorUtils.java @@ -496,7 +496,7 @@ public class ErrorUtils { private final TypeParameterDescriptor typeParameterDescriptor; private final TypeConstructor errorTypeConstructor; - public UninferredParameterTypeConstructor(@NotNull TypeParameterDescriptor descriptor) { + private UninferredParameterTypeConstructor(@NotNull TypeParameterDescriptor descriptor) { typeParameterDescriptor = descriptor; errorTypeConstructor = createErrorTypeConstructorWithCustomDebugName("CANT_INFER_TYPE_PARAMETER: " + descriptor.getName()); } @@ -541,5 +541,70 @@ public class ErrorUtils { } } + public static boolean isFunctionPlaceholder(@Nullable JetType type) { + return type != null && type.getConstructor() instanceof FunctionPlaceholderTypeConstructor; + } + + @NotNull + public static JetType createFunctionPlaceholderType(@NotNull List argumentTypes) { + return new ErrorTypeImpl( + new FunctionPlaceholderTypeConstructor(argumentTypes), + createErrorScope("Scope for function placeholder type")); + } + + public static class FunctionPlaceholderTypeConstructor implements TypeConstructor { + private final TypeConstructor errorTypeConstructor; + private final List argumentTypes; + + private FunctionPlaceholderTypeConstructor(@NotNull List argumentTypes) { + errorTypeConstructor = createErrorTypeConstructorWithCustomDebugName("PLACEHOLDER_FUNCTION_TYPE" + argumentTypes); + this.argumentTypes = argumentTypes; + } + + @NotNull + public List getArgumentTypes() { + return argumentTypes; + } + + @NotNull + @Override + public List getParameters() { + return errorTypeConstructor.getParameters(); + } + + @NotNull + @Override + public Collection getSupertypes() { + return errorTypeConstructor.getSupertypes(); + } + + @Override + public boolean isFinal() { + return errorTypeConstructor.isFinal(); + } + + @Override + public boolean isDenotable() { + return errorTypeConstructor.isDenotable(); + } + + @Nullable + @Override + public ClassifierDescriptor getDeclarationDescriptor() { + return errorTypeConstructor.getDeclarationDescriptor(); + } + + @NotNull + @Override + public Annotations getAnnotations() { + return errorTypeConstructor.getAnnotations(); + } + + @Override + public String toString() { + return errorTypeConstructor.toString(); + } + } + private ErrorUtils() {} }