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)
This commit is contained in:
svtk
2011-08-17 12:21:26 +04:00
parent fabf7aab5f
commit 9ebffafd89
4 changed files with 96 additions and 100 deletions
@@ -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<JetType> 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;
}
-58
View File
@@ -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 => <error>a</error>
}
fun illegalWhenBlock(a: Any): Int {
when(a) {
is Int => return a
is String => return <error>a</error>
}
}
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, <error>a</error>)
}
if (a is String) {
val s: (Any, String) = (a, a)
}
fun illegalTupleReturnType(): (Any, String) = (<error>a</error>, <error>a</error>)
if (a is String) {
fun legalTupleReturnType(): (Any, String) = (a, a)
}
val illegalFunctionLiteral: Function0<Int> = <error>{ <error>a</error> }</error>
val illegalReturnValueInFunctionLiteral: Function0<Int> = { (): Int => <error>a</error> }
if (a is Int) {
val legalFunctionLiteral: Function0<Int> = { a }
val alsoLegalFunctionLiteral: Function0<Int> = { (): Int => a }
}
}
fun returnFunctionLiteralBlock(a: Any?): Function0<Int> {
if (a is Int) return { a }
else return { 1 }
}
fun returnFunctionLiteral(a: Any?): Function0<Int> =
if (a is Int) { (): Int => a }
else { () => 1 }
fun illegalTupleReturnType(a: Any): (Any, String) = (a, <error>a</error>)
fun declarationInsidePattern(x: (Any, Any)): String = when(x) { is (val a is String, *) => a; else => "something" }
@@ -0,0 +1,11 @@
namespace qualified_expressions
fun test(s: String?) {
val a: Int = <error>s?.length</error>
val b: Int? = s?.length
val c: Int = s?.length ?: -11
val d: Int = s?.length ?: <error>"empty"</error>
val e: String = <error>s?.length</error> ?: "empty"
val f: Int = s?.length ?: b ?: 1
val g: Int? = e? startsWith("s")?.length
}
+64 -1
View File
@@ -152,4 +152,67 @@ fun getStringLength(obj : Any) : Char? {
if (obj !is String)
return null
return <info>obj</info>.get(0) // no cast to String is needed
}
}
fun toInt(i: Int?): Int = if (i != null) <info descr="Automatically cast to Int">i</info> else 0
fun illegalWhenBody(a: Any): Int = when(a) {
is Int => <info descr="Automatically cast to Int">a</info>
is String => <error>a</error>
}
fun illegalWhenBlock(a: Any): Int {
when(a) {
is Int => return <info descr="Automatically cast to Int">a</info>
is String => return <error>a</error>
}
}
fun declarations(a: Any?) {
if (a is String) {
val p4: (Int, String) = (2, <info descr="Automatically cast to String">a</info>)
}
if (a is String?) {
if (a != null) {
val s: String = <info descr="Automatically cast to String">a</info>
}
}
if (a != null) {
if (a is String?) {
val s: String = <info descr="Automatically cast to String">a</info>
}
}
}
fun vars(a: Any?) {
var b: Int = 0
if (a is Int) {
b = <info descr="Automatically cast to Int">a</info>
}
}
fun tuples(a: Any?) {
if (a != null) {
val s: (Any, String) = (<info descr="Automatically cast to Any">a</info>, <error>a</error>)
}
if (a is String) {
val s: (Any, String) = (<info descr="Automatically cast to Any">a</info>, <info descr="Automatically cast to String">a</info>)
}
fun illegalTupleReturnType(): (Any, String) = (<error>a</error>, <error>a</error>)
if (a is String) {
fun legalTupleReturnType(): (Any, String) = (<info descr="Automatically cast to Any">a</info>, <info descr="Automatically cast to String">a</info>)
}
val illegalFunctionLiteral: Function0<Int> = <error>{ <error>a</error> }</error>
val illegalReturnValueInFunctionLiteral: Function0<Int> = { (): Int => <error>a</error> }
if (a is Int) {
val legalFunctionLiteral: Function0<Int> = { <info descr="Automatically cast to Int">a</info> }
val alsoLegalFunctionLiteral: Function0<Int> = { (): Int => <info descr="Automatically cast to Int">a</info> }
}
}
fun returnFunctionLiteralBlock(a: Any?): Function0<Int> {
if (a is Int) return { <info descr="Automatically cast to Int">a</info> }
else return { 1 }
}
fun returnFunctionLiteral(a: Any?): Function0<Int> =
if (a is Int) { (): Int => <info descr="Automatically cast to Int">a</info> }
else { () => 1 }
fun illegalTupleReturnType(a: Any): (Any, String) = (a, <error>a</error>)
fun declarationInsidePattern(x: (Any, Any)): String = when(x) { is (val a is String, *) => <info descr="Automatically cast to String">a</info>; else => "something" }