From 9ebffafd893652a540d9dbe2079847fa8e0f8f97 Mon Sep 17 00:00:00 2001 From: svtk Date: Wed, 17 Aug 2011 12:21:26 +0400 Subject: [PATCH] added expected type for assignment and elvis operator changed return type for safe operator (now it works as a?.b is Nullable type, constructions like a?.b.c need another parsing) --- .../jet/lang/types/JetTypeInferrer.java | 62 ++++++------------ idea/testData/checker/AutomaticCasts.jet | 58 ----------------- .../testData/checker/QualifiedExpressions.jet | 11 ++++ idea/testData/checker/infos/Autocasts.jet | 65 ++++++++++++++++++- 4 files changed, 96 insertions(+), 100 deletions(-) delete mode 100644 idea/testData/checker/AutomaticCasts.jet create mode 100644 idea/testData/checker/QualifiedExpressions.jet diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index f31c6f9e014..4c201d48c4c 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -757,29 +757,6 @@ public class JetTypeInferrer { return expressionType; } -// @Nullable -// private JetType enrichType(@NotNull JetExpression expression, @Nullable JetType initialType, @NotNull TypeInferenceContext context) { -// if (initialType == null || context.expectedType == null || context.expectedType == NO_EXPECTED_TYPE) { -// return initialType; -// } -// VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(expression, context); -// if (variableDescriptor == null) return initialType; -// JetType enrichedType = null; -// List possibleTypes = Lists.newArrayList(context.dataFlowInfo.getPossibleTypes(variableDescriptor)); -// Collections.reverse(possibleTypes); -// for (JetType possibleType: possibleTypes) { -// if (semanticServices.getTypeChecker().isSubtypeOf(possibleType, context.expectedType)) { -// enrichedType = possibleType; -// break; -// } -// } -// if (enrichedType == null) { -// enrichedType = context.dataFlowInfo.getOutType(variableDescriptor); -// } -// if (enrichedType == null) return initialType; -// return enrichedType; -// } - @Nullable private JetType checkEnrichedType(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull TypeInferenceContext context) { if (expressionType == null || context.expectedType == null || context.expectedType == NO_EXPECTED_TYPE || @@ -808,7 +785,7 @@ public class JetTypeInferrer { if (!semanticServices.getTypeChecker().isSubtypeOf(enrichedType, context.expectedType)) { context.trace.getErrorHandler().typeMismatch(expression, context.expectedType, expressionType); } else { - context.trace.recordAutoCast(expression, enrichedType); + context.trace.recordAutoCast(expression, context.expectedType); } return enrichedType; } @@ -885,12 +862,14 @@ public class JetTypeInferrer { return new TypeInferenceContext(trace, scope, services, preferBlock, newDataFlowInfo, expectedType, expectedReturnType); } - public TypeInferenceContext replaceExpectedType(@NotNull JetType newExpectedType) { + public TypeInferenceContext replaceExpectedType(@Nullable JetType newExpectedType) { + if (newExpectedType == null) return replaceExpectedType(NO_EXPECTED_TYPE); if (expectedType == newExpectedType) return this; return new TypeInferenceContext(trace, scope, services, preferBlock, dataFlowInfo, newExpectedType, expectedReturnType); } - public TypeInferenceContext replaceExpectedReturnType(@NotNull JetType newExpectedReturnType) { + public TypeInferenceContext replaceExpectedReturnType(@Nullable JetType newExpectedReturnType) { + if (newExpectedReturnType == null) return replaceExpectedReturnType(NO_EXPECTED_TYPE); if (expectedReturnType == newExpectedReturnType) return this; return new TypeInferenceContext(trace, scope, services, preferBlock, dataFlowInfo, expectedType, newExpectedReturnType); } @@ -2119,13 +2098,13 @@ public class JetTypeInferrer { ErrorHandlerWithRegions errorHandler = context.trace.getErrorHandler(); errorHandler.openRegion(); JetType selectorReturnType = getSelectorReturnType(receiverType, selectorExpression, context); - -// if (expression instanceof JetSafeQualifiedExpression) { -// if (!selectorReturnType.isNullable()) { -// TypeUtils.makeNullable(selectorReturnType); -// } -// } + //TODO move further + if (expression.getOperationSign() == JetTokens.SAFE_ACCESS) { + if (selectorReturnType != null && !selectorReturnType.isNullable() && !JetStandardClasses.isUnit(selectorReturnType)) { + selectorReturnType = TypeUtils.makeNullable(selectorReturnType); + } + } if (selectorReturnType != null) { errorHandler.closeAndCommitCurrentRegion(); } @@ -2401,13 +2380,14 @@ public class JetTypeInferrer { } else if (operationType == JetTokens.ELVIS) { JetType leftType = getType(context.scope, left, false, context); - JetType rightType = right == null ? null : getType(context.scope, right, false, context); + JetType rightType = right == null ? null : getType(context.scope, right, false, contextWithExpectedType); if (leftType != null) { if (!leftType.isNullable()) { context.trace.getErrorHandler().genericWarning(left.getNode(), "Elvis operator (?:) is always returns the left operand of non-nullable type " + leftType); } if (rightType != null) { - result = TypeUtils.makeNullableAsSpecified(semanticServices.getTypeChecker().commonSupertype(leftType, rightType), rightType.isNullable()); + context.services.checkType(TypeUtils.makeNullableAsSpecified(leftType, rightType.isNullable()), left, contextWithExpectedType); + return TypeUtils.makeNullableAsSpecified(semanticServices.getTypeChecker().commonSupertype(leftType, rightType), rightType.isNullable()); } } } @@ -2745,14 +2725,14 @@ public class JetTypeInferrer { JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) deparenthesized; return resolveArrayAccessToLValue(arrayAccessExpression, right, expression.getOperationReference(), context); } - JetType leftType = getType(scope, left, false, context); + JetType leftType = getType(scope, left, false, context.replaceExpectedType(NO_EXPECTED_TYPE)); if (right != null) { - JetType rightType = getType(scope, right, false, context); - if (rightType != null && - leftType != null && - !semanticServices.getTypeChecker().isConvertibleTo(rightType, leftType)) { - context.trace.getErrorHandler().typeMismatch(right, leftType, rightType); - } + JetType rightType = getType(scope, right, false, context.replaceExpectedType(leftType)); +// if (rightType != null && +// leftType != null && +// !semanticServices.getTypeChecker().isConvertibleTo(rightType, leftType)) { +// context.trace.getErrorHandler().typeMismatch(right, leftType, rightType); +// } } return null; } diff --git a/idea/testData/checker/AutomaticCasts.jet b/idea/testData/checker/AutomaticCasts.jet deleted file mode 100644 index 36667a7cbdc..00000000000 --- a/idea/testData/checker/AutomaticCasts.jet +++ /dev/null @@ -1,58 +0,0 @@ -namespace automatic_casts - -fun toInt(i: Int?): Int = if (i != null) i else 0 -fun illegalWhenBody(a: Any): Int = when(a) { - is Int => a - is String => a -} -fun illegalWhenBlock(a: Any): Int { - when(a) { - is Int => return a - is String => return a - } -} -fun declarations(a: Any?) { - if (a is String) { - val p4: (Int, String) = (2, a) - } - if (a is String?) { - if (a != null) { - val s: String = a - } - } - if (a != null) { - if (a is String?) { - val s: String = a - } - } -} -fun tuples(a: Any?) { - if (a != null) { - val s: (Any, String) = (a, a) - } - if (a is String) { - val s: (Any, String) = (a, a) - } - fun illegalTupleReturnType(): (Any, String) = (a, a) - if (a is String) { - fun legalTupleReturnType(): (Any, String) = (a, a) - } - val illegalFunctionLiteral: Function0 = { a } - val illegalReturnValueInFunctionLiteral: Function0 = { (): Int => a } - - if (a is Int) { - val legalFunctionLiteral: Function0 = { a } - val alsoLegalFunctionLiteral: Function0 = { (): Int => a } - } -} -fun returnFunctionLiteralBlock(a: Any?): Function0 { - if (a is Int) return { a } - else return { 1 } -} -fun returnFunctionLiteral(a: Any?): Function0 = - if (a is Int) { (): Int => a } - else { () => 1 } - -fun illegalTupleReturnType(a: Any): (Any, String) = (a, a) - -fun declarationInsidePattern(x: (Any, Any)): String = when(x) { is (val a is String, *) => a; else => "something" } \ No newline at end of file diff --git a/idea/testData/checker/QualifiedExpressions.jet b/idea/testData/checker/QualifiedExpressions.jet new file mode 100644 index 00000000000..0ef138d7cd5 --- /dev/null +++ b/idea/testData/checker/QualifiedExpressions.jet @@ -0,0 +1,11 @@ +namespace qualified_expressions + +fun test(s: String?) { + val a: Int = s?.length + val b: Int? = s?.length + val c: Int = s?.length ?: -11 + val d: Int = s?.length ?: "empty" + val e: String = s?.length ?: "empty" + val f: Int = s?.length ?: b ?: 1 + val g: Int? = e? startsWith("s")?.length +} \ No newline at end of file diff --git a/idea/testData/checker/infos/Autocasts.jet b/idea/testData/checker/infos/Autocasts.jet index cd930a4f366..48ef4191b47 100644 --- a/idea/testData/checker/infos/Autocasts.jet +++ b/idea/testData/checker/infos/Autocasts.jet @@ -152,4 +152,67 @@ fun getStringLength(obj : Any) : Char? { if (obj !is String) return null return obj.get(0) // no cast to String is needed -} \ No newline at end of file +} + +fun toInt(i: Int?): Int = if (i != null) i else 0 +fun illegalWhenBody(a: Any): Int = when(a) { + is Int => a + is String => a +} +fun illegalWhenBlock(a: Any): Int { + when(a) { + is Int => return a + is String => return a + } +} +fun declarations(a: Any?) { + if (a is String) { + val p4: (Int, String) = (2, a) + } + if (a is String?) { + if (a != null) { + val s: String = a + } + } + if (a != null) { + if (a is String?) { + val s: String = a + } + } +} +fun vars(a: Any?) { + var b: Int = 0 + if (a is Int) { + b = a + } +} +fun tuples(a: Any?) { + if (a != null) { + val s: (Any, String) = (a, a) + } + if (a is String) { + val s: (Any, String) = (a, a) + } + fun illegalTupleReturnType(): (Any, String) = (a, a) + if (a is String) { + fun legalTupleReturnType(): (Any, String) = (a, a) + } + val illegalFunctionLiteral: Function0 = { a } + val illegalReturnValueInFunctionLiteral: Function0 = { (): Int => a } + + if (a is Int) { + val legalFunctionLiteral: Function0 = { a } + val alsoLegalFunctionLiteral: Function0 = { (): Int => a } + } +} +fun returnFunctionLiteralBlock(a: Any?): Function0 { + if (a is Int) return { a } + else return { 1 } +} +fun returnFunctionLiteral(a: Any?): Function0 = + if (a is Int) { (): Int => a } + else { () => 1 } + +fun illegalTupleReturnType(a: Any): (Any, String) = (a, a) + +fun declarationInsidePattern(x: (Any, Any)): String = when(x) { is (val a is String, *) => a; else => "something" } \ No newline at end of file