From 517c24c8f6e20cda3dc53cd07a83e031d2765879 Mon Sep 17 00:00:00 2001 From: Svetlana Isakova Date: Thu, 11 Jul 2013 13:57:13 +0400 Subject: [PATCH] added UNKNOWN_EXPECTED_TYPE, NumberValueTypeConstructor analyze function arguments with UNKNOWN_EXPECTED_TYPE (for several times in different traces yet) for number literals create type with NumberValueTypeConstructor with exact value, transform it to the corresponding type when resolve arguments (expected type is known) doesn't work properly for if, elvis operator, etc. (throws exceptions) --- .../resolve/calls/ArgumentTypeResolver.java | 15 +-- .../lang/resolve/calls/CandidateResolver.java | 7 +- .../CompileTimeConstantResolver.java | 13 ++- .../lang/resolve/constants/ConstantUtils.java | 39 ++++++++ .../constants/NumberValueTypeConstructor.java | 93 +++++++++++++++++++ .../jetbrains/jet/lang/types/TypeUtils.java | 28 ++++-- .../BasicExpressionTypingVisitor.java | 14 +++ 7 files changed, 184 insertions(+), 25 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/resolve/constants/NumberValueTypeConstructor.java diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ArgumentTypeResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ArgumentTypeResolver.java index 346ad0fc621..8ea35542bec 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ArgumentTypeResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ArgumentTypeResolver.java @@ -24,17 +24,15 @@ import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.jet.lang.diagnostics.Errors; import org.jetbrains.jet.lang.psi.*; -import org.jetbrains.jet.lang.resolve.BindingTrace; -import org.jetbrains.jet.lang.resolve.TemporaryBindingTrace; -import org.jetbrains.jet.lang.resolve.TypeResolver; +import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.resolve.calls.context.CallResolutionContext; import org.jetbrains.jet.lang.resolve.calls.context.CheckValueArgumentsMode; import org.jetbrains.jet.lang.resolve.calls.context.ResolutionContext; import org.jetbrains.jet.lang.resolve.calls.context.ResolveMode; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallImpl; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument; +import org.jetbrains.jet.lang.resolve.constants.*; import org.jetbrains.jet.lang.resolve.scopes.JetScope; -import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.jet.lang.types.ErrorUtils; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.JetTypeInfo; @@ -50,7 +48,6 @@ import java.util.Map; import java.util.Set; import static org.jetbrains.jet.lang.resolve.BindingContextUtils.getRecordedTypeInfo; -import static org.jetbrains.jet.lang.resolve.BindingContextUtils.recordExpressionType; import static org.jetbrains.jet.lang.resolve.calls.CallResolverUtil.*; import static org.jetbrains.jet.lang.resolve.calls.CallResolverUtil.ResolveArgumentsMode.RESOLVE_FUNCTION_ARGUMENTS; import static org.jetbrains.jet.lang.resolve.calls.CallResolverUtil.ResolveArgumentsMode.SKIP_FUNCTION_ARGUMENTS; @@ -73,7 +70,10 @@ public class ArgumentTypeResolver { this.expressionTypingServices = expressionTypingServices; } - public static boolean isSubtypeOfForArgumentType(@NotNull JetType subtype, @NotNull JetType supertype) { + public static boolean isSubtypeOfForArgumentType( + @NotNull JetType subtype, + @NotNull JetType supertype + ) { if (subtype == PLACEHOLDER_FUNCTION_TYPE) { return isFunctionOrErrorType(supertype) || KotlinBuiltIns.getInstance().isAny(supertype); //todo function type extends } @@ -180,8 +180,9 @@ public class ArgumentTypeResolver { if (recordedTypeInfo != null) { return recordedTypeInfo; } - ResolutionContext newContext = context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceResolveMode(ResolveMode.NESTED_CALL); + ResolutionContext newContext = context.replaceExpectedType(TypeUtils.UNKNOWN_EXPECTED_TYPE).replaceResolveMode(ResolveMode.NESTED_CALL); JetTypeInfo result = expressionTypingServices.getTypeInfo(expression, newContext); + if (traceToCommitForCall != null) { traceToCommitForCall.commit(); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java index d289fbdbdd3..72ab3c4fe8f 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java @@ -630,7 +630,7 @@ public class CandidateResolver { resultingType = type; } else { - resultingType = autocastValueArgumentTypeIfPossible(expression, expectedType, type, trace, candidateCall.getDataFlowInfo()); + resultingType = autocastValueArgumentTypeIfPossible(expression, expectedType, type, newContext); if (resultingType == null) { resultingType = type; resultStatus = OTHER_ERROR; @@ -649,11 +649,10 @@ public class CandidateResolver { @NotNull JetExpression expression, @NotNull JetType expectedType, @NotNull JetType actualType, - @NotNull BindingTrace trace, - @NotNull DataFlowInfo dataFlowInfo + @NotNull ResolutionContext context ) { ExpressionReceiver receiverToCast = new ExpressionReceiver(expression, actualType); - List variants = AutoCastUtils.getAutoCastVariants(trace.getBindingContext(), dataFlowInfo, receiverToCast); + List variants = AutoCastUtils.getAutoCastVariants(context.trace.getBindingContext(), context.dataFlowInfo, receiverToCast); for (ReceiverValue receiverValue : variants) { JetType possibleType = receiverValue.getType(); if (ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, expectedType)) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/constants/CompileTimeConstantResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/constants/CompileTimeConstantResolver.java index b208022a393..13ee1bb0a88 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/constants/CompileTimeConstantResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/constants/CompileTimeConstantResolver.java @@ -40,8 +40,12 @@ public class CompileTimeConstantResolver { @NotNull public CompileTimeConstant getIntegerValue(@NotNull String text, @NotNull JetType expectedType) { + return getIntegerValue(parseLongValue(text), expectedType); + } + + @NotNull + public CompileTimeConstant getIntegerValue(@Nullable Long value, @NotNull JetType expectedType) { if (noExpectedType(expectedType)) { - Long value = parseLongValue(text); if (value == null) { return OUT_OF_RANGE; } @@ -80,16 +84,15 @@ public class CompileTimeConstantResolver { JetType intType = builtIns.getIntType(); JetType longType = builtIns.getLongType(); if (typeChecker.isSubtypeOf(intType, expectedType)) { - return getIntegerValue(text, intType); + return getIntegerValue(value, intType); } else if (typeChecker.isSubtypeOf(longType, expectedType)) { - return getIntegerValue(text, longType); + return getIntegerValue(value, longType); } else { return new ErrorValue("An integer literal does not conform to the expected type " + expectedType); } } - Long value = parseLongValue(text); if (value != null && lowerBound <= value && value <= upperBound) { return create.apply(value); @@ -98,7 +101,7 @@ public class CompileTimeConstantResolver { } @Nullable - private static Long parseLongValue(String text) { + public static Long parseLongValue(String text) { try { long value; if (text.startsWith("0x") || text.startsWith("0X")) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/constants/ConstantUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/constants/ConstantUtils.java index d65639bf492..0488c32a5ad 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/constants/ConstantUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/constants/ConstantUtils.java @@ -16,16 +16,26 @@ package org.jetbrains.jet.lang.resolve.constants; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.psi.JetQualifiedExpression; import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.BindingTrace; +import org.jetbrains.jet.lang.resolve.calls.context.ResolutionContext; import org.jetbrains.jet.lang.resolve.name.Name; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.JetTypeInfo; +import org.jetbrains.jet.lang.types.checker.JetTypeChecker; import org.jetbrains.jet.lang.types.expressions.OperatorConventions; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; +import static org.jetbrains.jet.lang.diagnostics.Errors.ERROR_COMPILE_TIME_VALUE; +import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType; +import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.getDefaultType; import static org.jetbrains.jet.lang.types.expressions.OperatorConventions.*; import static org.jetbrains.jet.lang.types.expressions.OperatorConventions.BYTE; import static org.jetbrains.jet.lang.types.expressions.OperatorConventions.INT; @@ -63,4 +73,33 @@ public class ConstantUtils { } } } + + public static JetType updateConstantValueType( + @NotNull NumberValueTypeConstructor numberValueTypeConstructor, + @NotNull JetType expectedType, + @NotNull JetExpression expression, + @NotNull ResolutionContext context + ) { + CompileTimeConstantResolver compileTimeConstantResolver = new CompileTimeConstantResolver(); + CompileTimeConstant value = compileTimeConstantResolver.getIntegerValue(numberValueTypeConstructor.getValue(), expectedType); + JetTypeInfo recordedTypeInfo = BindingContextUtils.getRecordedTypeInfo(expression, context.trace.getBindingContext()); + assert recordedTypeInfo != null : "Expression " + expression + " should have been analyzed"; + JetScope resolutionScope = context.trace.get(BindingContext.RESOLUTION_SCOPE, expression); + assert resolutionScope != null : "Expression " + expression + " should have been analyzed"; + + JetType type; + if (value instanceof ErrorValue) { + ErrorValue errorValue = (ErrorValue) value; + type = getDefaultType(expression.getNode().getElementType()); + if (!noExpectedType(context.expectedType) && JetTypeChecker.INSTANCE.isSubtypeOf(type, context.expectedType)) { + context.trace.report(ERROR_COMPILE_TIME_VALUE.on(expression, errorValue.getMessage())); + } + } + else { + type = value.getType(KotlinBuiltIns.getInstance()); + } + BindingContextUtils.recordExpressionType(expression, context.trace, resolutionScope, + JetTypeInfo.create(type, recordedTypeInfo.getDataFlowInfo())); + return type; + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/constants/NumberValueTypeConstructor.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/constants/NumberValueTypeConstructor.java new file mode 100644 index 00000000000..5c8ce8038b2 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/constants/NumberValueTypeConstructor.java @@ -0,0 +1,93 @@ +/* + * Copyright 2010-2013 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.jet.lang.resolve.constants; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor; +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; +import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.TypeConstructor; +import org.jetbrains.jet.lang.types.TypeProjection; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class NumberValueTypeConstructor implements TypeConstructor { + private final Long value; + + public NumberValueTypeConstructor(Long value) { + this.value = value; + } + + public Long getValue() { + return value; + } + + @NotNull + @Override + public List getParameters() { + throw new IllegalStateException(); + } + + @NotNull + @Override + public Collection getSupertypes() { + throw new IllegalStateException(); + } + + @Override + public boolean isSealed() { + throw new IllegalStateException(); + } + + @Override + public boolean isDenotable() { + return false; + } + + @Nullable + @Override + public ClassifierDescriptor getDeclarationDescriptor() { + throw new IllegalStateException(); + } + + @Override + public List getAnnotations() { + throw new IllegalStateException(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + NumberValueTypeConstructor type = (NumberValueTypeConstructor) o; + + if (!value.equals(type.value)) return false; + + return true; + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeUtils.java index 0a19e607c20..a87f22e1388 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeUtils.java @@ -38,43 +38,53 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import java.util.*; public class TypeUtils { - public static final JetType NO_EXPECTED_TYPE = new JetType() { + public static class SpecialType implements JetType { + private final String name; + + public SpecialType(String name) { + this.name = name; + } + @NotNull @Override public TypeConstructor getConstructor() { - throw new IllegalStateException(); + throw new IllegalStateException(name); } @NotNull @Override public List getArguments() { - throw new IllegalStateException(); + throw new IllegalStateException(name); } @Override public boolean isNullable() { - throw new IllegalStateException(); + throw new IllegalStateException(name); } @NotNull @Override public JetScope getMemberScope() { - throw new IllegalStateException(); + throw new IllegalStateException(name); } @Override public List getAnnotations() { - throw new IllegalStateException(); + throw new IllegalStateException(name); } @Override public String toString() { - return "NO_EXPECTED_TYPE"; + return name; } - }; + } + + public static final JetType NO_EXPECTED_TYPE = new SpecialType("NO_EXPECTED_TYPE"); + + public static final JetType UNKNOWN_EXPECTED_TYPE = new SpecialType("UNKNOWN_EXPECTED_TYPE"); public static boolean noExpectedType(@NotNull JetType type) { - return type == NO_EXPECTED_TYPE; + return type == NO_EXPECTED_TYPE || type == UNKNOWN_EXPECTED_TYPE; } @NotNull diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java index 52d87dee014..4b0e35cf90c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java @@ -67,6 +67,7 @@ import static org.jetbrains.jet.lang.resolve.BindingContext.*; import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getStaticNestedClassesScope; import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue.NO_RECEIVER; import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE; +import static org.jetbrains.jet.lang.types.TypeUtils.UNKNOWN_EXPECTED_TYPE; import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType; import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.*; @@ -109,6 +110,19 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance(); CompileTimeConstantResolver compileTimeConstantResolver = context.getCompileTimeConstantResolver(); + if (context.expectedType == UNKNOWN_EXPECTED_TYPE) { + if (elementType == JetNodeTypes.INTEGER_CONSTANT) { + Long longValue = CompileTimeConstantResolver.parseLongValue(text); + if (longValue != null) { + JetTypeImpl numberValueType = new JetTypeImpl( + Collections.emptyList(), new NumberValueTypeConstructor(longValue), false, + Collections.emptyList(), + ErrorUtils.createErrorScope("Scope for number value type (" + longValue + ")", true)); + return JetTypeInfo.create(numberValueType, context.dataFlowInfo); + } + } + } + CompileTimeConstant value; if (elementType == JetNodeTypes.INTEGER_CONSTANT) { value = compileTimeConstantResolver.getIntegerValue(text, context.expectedType);