From 25d26fe6fde4da66188c19aaadd4fef8e95d40e9 Mon Sep 17 00:00:00 2001 From: svtk Date: Thu, 10 Nov 2011 22:13:52 +0400 Subject: [PATCH] KT-235 Illegal assignment return type --- .../jet/lang/diagnostics/Errors.java | 1 + .../ExpressionTypingVisitorForStatements.java | 37 +++++++++----- .../full/FunctionReturnTypes.jet | 2 +- .../quick/regressions/kt235.jet | 49 +++++++++++++++++++ 4 files changed, 75 insertions(+), 14 deletions(-) create mode 100644 compiler/testData/checkerWithErrorTypes/quick/regressions/kt235.jet diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java index 3d39c74cb01..07bf0723459 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -215,6 +215,7 @@ public interface Errors { SimpleDiagnosticFactory NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY = SimpleDiagnosticFactory.create(ERROR, "A 'return' expression required in a function with a block body ('{...}')"); ParameterizedDiagnosticFactory1 RETURN_TYPE_MISMATCH = ParameterizedDiagnosticFactory1.create(ERROR, "This function must return a value of type {0}"); ParameterizedDiagnosticFactory1 EXPECTED_TYPE_MISMATCH = ParameterizedDiagnosticFactory1.create(ERROR, "Expected a value of type {0}"); + ParameterizedDiagnosticFactory1 ASSIGNMENT_TYPE_MISMATCH = ParameterizedDiagnosticFactory1.create(ERROR, "Expected a value of type {0}. Assignment operation is not an expression, so it does not return any value"); ParameterizedDiagnosticFactory1 UPPER_BOUND_VIOLATED = ParameterizedDiagnosticFactory1.create(ERROR, "An upper bound {0} is violated"); // TODO : Message ParameterizedDiagnosticFactory1 FINAL_CLASS_OBJECT_UPPER_BOUND = ParameterizedDiagnosticFactory1.create(ERROR, "{0} is a final type, and thus a class object cannot extend it"); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java index a320b6d6da4..718f668f1fe 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java @@ -4,16 +4,15 @@ import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.diagnostics.Errors; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; -import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.TemporaryBindingTrace; import org.jetbrains.jet.lang.resolve.TopDownAnalyzer; import org.jetbrains.jet.lang.resolve.calls.CallMaker; import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; import org.jetbrains.jet.lang.resolve.scopes.WritableScope; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; -import org.jetbrains.jet.lang.types.ErrorUtils; import org.jetbrains.jet.lang.types.JetStandardClasses; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.TypeUtils; @@ -35,7 +34,7 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV } @Nullable - private JetType checkExpectedType(JetExpression expression, ExpressionTypingContext context) { + private JetType checkExpectedType(@NotNull JetExpression expression, @NotNull ExpressionTypingContext context) { if (context.expectedType != TypeUtils.NO_EXPECTED_TYPE) { if (JetStandardClasses.isUnit(context.expectedType)) { return JetStandardClasses.getUnitType(); @@ -45,6 +44,16 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV return null; } + @Nullable + private JetType checkAssignmentType(@Nullable JetType assignmentType, @NotNull JetBinaryExpression expression, @NotNull ExpressionTypingContext context) { + if (assignmentType != null && !JetStandardClasses.isUnit(assignmentType) && context.expectedType != TypeUtils.NO_EXPECTED_TYPE && + TypeUtils.equalTypes(context.expectedType, assignmentType)) { + context.trace.report(Errors.ASSIGNMENT_TYPE_MISMATCH.on(expression, context.expectedType)); + return null; + } + return checkExpectedType(expression, context); + } + @Override public JetType visitObjectDeclaration(JetObjectDeclaration declaration, ExpressionTypingContext context) { TopDownAnalyzer.processObject(context.semanticServices, context.trace, scope, scope.getContainingDeclaration(), declaration); @@ -108,7 +117,8 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV } @Override - protected JetType visitAssignmentOperation(JetBinaryExpression expression, ExpressionTypingContext context) { + protected JetType visitAssignmentOperation(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) { + ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceExpectedReturnType(TypeUtils.NO_EXPECTED_TYPE); // If there's += (or similar op) defined as such, we just call it. JetSimpleNameExpression operationSign = expression.getOperationReference(); IElementType operationType = operationSign.getReferencedNameElementType(); @@ -126,8 +136,8 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left; resolveArrayAccessToLValue(arrayAccessExpression, expression.getRight(), operationSign, context); } - JetType typeForBinaryCall = getTypeForBinaryCall(scope, counterpartName, context, expression); - if (typeForBinaryCall != null) { + assignmentOperationType = getTypeForBinaryCall(scope, counterpartName, context, expression); + if (assignmentOperationType != null) { context.trace.record(BindingContext.VARIABLE_REASSIGNMENT, expression); ExpressionTypingUtils.checkWrappingInRef(expression.getLeft(), context); } @@ -136,20 +146,21 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV temporaryBindingTrace.commit(); } checkLValue(context.trace, expression.getLeft()); - return checkExpectedType(expression, context); + return checkAssignmentType(assignmentOperationType, expression, contextWithExpectedType); } @Override - protected JetType visitAssignment(JetBinaryExpression expression, ExpressionTypingContext context) { + protected JetType visitAssignment(JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) { + ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceExpectedReturnType(TypeUtils.NO_EXPECTED_TYPE); JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft()); JetExpression right = expression.getRight(); if (left instanceof JetArrayAccessExpression) { JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left; - JetType resultType = resolveArrayAccessToLValue(arrayAccessExpression, right, expression.getOperationReference(), context); + JetType assignmentType = resolveArrayAccessToLValue(arrayAccessExpression, right, expression.getOperationReference(), context); checkLValue(context.trace, arrayAccessExpression); - return resultType; + return checkAssignmentType(assignmentType, expression, contextWithExpectedType); } - JetType leftType = facade.getType(expression.getLeft(), context.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceScope(scope)); + JetType leftType = facade.getType(expression.getLeft(), context.replaceScope(scope)); if (right != null) { JetType rightType = facade.getType(right, context.replaceExpectedType(leftType).replaceScope(scope)); } @@ -168,7 +179,7 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV } ExpressionTypingUtils.checkWrappingInRef(simpleName, context); } - return checkExpectedType(expression, context); + return checkExpectedType(expression, contextWithExpectedType); } private JetType resolveArrayAccessToLValue(JetArrayAccessExpression arrayAccessExpression, JetExpression rightHandSide, JetSimpleNameExpression operationSign, ExpressionTypingContext context) { @@ -196,7 +207,7 @@ public class ExpressionTypingVisitorForStatements extends BasicExpressionTypingV // else { // context.trace.record(REFERENCE_TARGET, operationSign, setFunctionDescriptor); // } - return DataFlowUtils.checkType(setFunctionDescriptor.getReturnType(), arrayAccessExpression, context); + return setFunctionDescriptor.getReturnType(); } @Override diff --git a/compiler/testData/checkerWithErrorTypes/full/FunctionReturnTypes.jet b/compiler/testData/checkerWithErrorTypes/full/FunctionReturnTypes.jet index 311a3b16aa6..95dd6c17306 100644 --- a/compiler/testData/checkerWithErrorTypes/full/FunctionReturnTypes.jet +++ b/compiler/testData/checkerWithErrorTypes/full/FunctionReturnTypes.jet @@ -185,7 +185,7 @@ fun testFunctionLiterals() { val endsWithReAssignment = { () : Int => var x = 1 - x += 333 + x += 333 } val endsWithFunDeclaration : fun() : String = { diff --git a/compiler/testData/checkerWithErrorTypes/quick/regressions/kt235.jet b/compiler/testData/checkerWithErrorTypes/quick/regressions/kt235.jet new file mode 100644 index 00000000000..5e4f6a0cd9b --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/quick/regressions/kt235.jet @@ -0,0 +1,49 @@ +//KT-235 Illegal assignment return type + +namespace kt235 + +fun main(args: Array) { + val array = MyArray() + val f = { (): String => + array[2] = 23 //error: Type mismatch: inferred type is Int (!!!) but String was expected + } + val g = {(): String => + var x = 1 + x += 2 //no error, but it should be here + } + val h = {(): String => + var x = 1 + x = 2 //the same + } + val array1 = MyArray1() + val x = { (): String => + array1[2] = 23 + } + + val fi = { (): Int => + array[2] = 23 + } + val gi = {(): Int => + var x = 1 + x += 21 + } + + var m: MyNumber = MyNumber() + val a = { (): MyNumber => + m++ + } +} + +class MyArray() { + fun get(i: Int): Int = 1 + fun set(i: Int, value: Int): Int = 1 +} + +class MyArray1() { + fun get(i: Int): Int = 1 + fun set(i: Int, value: Int) {} +} + +class MyNumber() { + fun inc(): MyNumber = MyNumber() +} \ No newline at end of file