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 cf920c2d04e..e8221c91901 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -32,6 +32,7 @@ import org.jetbrains.jet.lexer.JetTokens; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Collection; +import java.util.List; import static org.jetbrains.jet.lang.diagnostics.PositioningStrategies.*; import static org.jetbrains.jet.lang.diagnostics.Severity.ERROR; @@ -494,6 +495,10 @@ public interface Errors { DiagnosticFactory3 RESULT_TYPE_MISMATCH = DiagnosticFactory3.create(ERROR); SimpleDiagnosticFactory TYPE_MISMATCH_IN_RANGE = new SimpleDiagnosticFactory(ERROR, WHEN_CONDITION_IN_RANGE); + DiagnosticFactory1 EXPECTED_PARAMETER_TYPE_MISMATCH = DiagnosticFactory1.create(ERROR); + DiagnosticFactory1 EXPECTED_RETURN_TYPE_MISMATCH = DiagnosticFactory1.create(ERROR); + DiagnosticFactory2> EXPECTED_PARAMETERS_NUMBER_MISMATCH = DiagnosticFactory2.create(ERROR, FUNCTION_LITERAL_PARAMETERS); + DiagnosticFactory2 INCOMPATIBLE_TYPES = DiagnosticFactory2.create(ERROR); // Context tracking diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/PositioningStrategies.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/PositioningStrategies.java index 1802ec1601f..daf126b58df 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/PositioningStrategies.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/PositioningStrategies.java @@ -354,6 +354,18 @@ public class PositioningStrategies { } }; + public static final PositioningStrategy FUNCTION_LITERAL_PARAMETERS = new PositioningStrategy() { + @NotNull + @Override + public List mark(@NotNull JetFunctionLiteral functionLiteral) { + JetParameterList valueParameterList = functionLiteral.getValueParameterList(); + if (valueParameterList != null) { + return markElement(valueParameterList); + } + return markNode(functionLiteral.getOpenBraceNode()); + } + }; + private PositioningStrategies() { } } \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java index fc55fca7d23..cf82feae819 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java @@ -31,7 +31,6 @@ import org.jetbrains.jet.renderer.DescriptorRenderer; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Collection; -import java.util.Iterator; import static org.jetbrains.jet.lang.diagnostics.Errors.*; import static org.jetbrains.jet.lang.diagnostics.rendering.Renderers.*; @@ -254,6 +253,11 @@ public class DefaultErrorMessages { MAP.put(EXPECTED_TYPE_MISMATCH, "Expected a value of type {0}", RENDER_TYPE); MAP.put(ASSIGNMENT_TYPE_MISMATCH, "Expected a value of type {0}. Assignment operation is not an expression, so it does not return any value", RENDER_TYPE); + + MAP.put(EXPECTED_PARAMETER_TYPE_MISMATCH, "Expected parameter of type {0}", RENDER_TYPE); + MAP.put(EXPECTED_RETURN_TYPE_MISMATCH, "Expected return type {0}", RENDER_TYPE); + MAP.put(EXPECTED_PARAMETERS_NUMBER_MISMATCH, "Expected {0,choice,0#no parameters|1#one parameter of type|1<{0,number,integer} parameters of types} {1}", null, RENDER_COLLECTION_OF_TYPES); + MAP.put(IMPLICIT_CAST_TO_UNIT_OR_ANY, "Type was casted to ''{0}''. Please specify ''{0}'' as expected type, if you mean such cast", RENDER_TYPE); MAP.put(EXPRESSION_EXPECTED, "{0} is not an expression, and only expressions are allowed here", new Renderer() { @@ -332,22 +336,7 @@ public class DefaultErrorMessages { MAP.put(CANNOT_CHECK_FOR_ERASED, "Cannot check for instance of erased type: {0}", RENDER_TYPE); MAP.put(UNCHECKED_CAST, "Unchecked cast: {0} to {1}", RENDER_TYPE, RENDER_TYPE); - MAP.put(INCONSISTENT_TYPE_PARAMETER_VALUES, "Type parameter {0} of ''{1}'' has inconsistent values: {2}", NAME, NAME, - new Renderer>() { - @NotNull - @Override - public String render(@NotNull Collection types) { - StringBuilder builder = new StringBuilder(); - for (Iterator iterator = types.iterator(); iterator.hasNext(); ) { - JetType jetType = iterator.next(); - builder.append(jetType); - if (iterator.hasNext()) { - builder.append(", "); - } - } - return builder.toString(); - } - }); + MAP.put(INCONSISTENT_TYPE_PARAMETER_VALUES, "Type parameter {0} of ''{1}'' has inconsistent values: {2}", NAME, NAME, RENDER_COLLECTION_OF_TYPES); MAP.put(EQUALITY_NOT_APPLICABLE, "Operator ''{0}'' cannot be applied to ''{1}'' and ''{2}''", new Renderer() { @NotNull diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/Renderers.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/Renderers.java index f8b008f7d4e..35eadeaea2a 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/Renderers.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/Renderers.java @@ -310,6 +310,23 @@ public class Renderers { } }; + public static final Renderer> RENDER_COLLECTION_OF_TYPES = new Renderer>() { + @NotNull + @Override + public String render(@NotNull Collection types) { + StringBuilder builder = new StringBuilder(); + for (Iterator iterator = types.iterator(); iterator.hasNext(); ) { + JetType jetType = iterator.next(); + builder.append(jetType); + if (iterator.hasNext()) { + builder.append(", "); + } + } + return builder.toString(); + } + }; + + private Renderers() { } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ClosureExpressionsTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ClosureExpressionsTypingVisitor.java index 0581c8f1ac1..84f48bc73b3 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ClosureExpressionsTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ClosureExpressionsTypingVisitor.java @@ -26,6 +26,7 @@ import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.types.*; +import org.jetbrains.jet.lang.types.checker.JetTypeChecker; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import org.jetbrains.jet.util.lazy.LazyValueWithDefault; import org.jetbrains.jet.util.slicedmap.WritableSlice; @@ -33,8 +34,9 @@ import org.jetbrains.jet.util.slicedmap.WritableSlice; import java.util.Collections; import java.util.List; -import static org.jetbrains.jet.lang.diagnostics.Errors.CANNOT_INFER_PARAMETER_TYPE; +import static org.jetbrains.jet.lang.diagnostics.Errors.*; import static org.jetbrains.jet.lang.resolve.BindingContext.*; +import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE; /** * @author abreslav @@ -94,7 +96,7 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor { if (bodyExpression == null) return null; JetType expectedType = context.expectedType; - boolean functionTypeExpected = expectedType != TypeUtils.NO_EXPECTED_TYPE && KotlinBuiltIns.getInstance().isFunctionType(expectedType); + boolean functionTypeExpected = expectedType != NO_EXPECTED_TYPE && KotlinBuiltIns.getInstance().isFunctionType(expectedType); SimpleFunctionDescriptorImpl functionDescriptor = createFunctionDescriptor(expression, context, functionTypeExpected); @@ -106,18 +108,24 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor { ReceiverParameterDescriptor receiverParameter = functionDescriptor.getReceiverParameter(); JetType receiver = DescriptorUtils.getReceiverParameterType(receiverParameter); - JetType returnType = TypeUtils.NO_EXPECTED_TYPE; + JetType returnType = NO_EXPECTED_TYPE; JetScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(context.scope, functionDescriptor, context.trace); JetTypeReference returnTypeRef = functionLiteral.getReturnTypeRef(); TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(context.trace, "trace to resolve function literal expression", expression); + JetType expectedReturnType = functionTypeExpected ? KotlinBuiltIns.getInstance().getReturnTypeFromFunctionType(expectedType) : null; if (returnTypeRef != null) { returnType = context.expressionTypingServices.getTypeResolver().resolveType(context.scope, returnTypeRef, context.trace, true); context.expressionTypingServices.checkFunctionReturnType(expression, context.replaceScope(functionInnerScope). replaceExpectedType(returnType).replaceBindingTrace(temporaryTrace), temporaryTrace); + if (functionTypeExpected) { + if (!JetTypeChecker.INSTANCE.isSubtypeOf(expectedReturnType, returnType)) { + temporaryTrace.report(EXPECTED_RETURN_TYPE_MISMATCH.on(returnTypeRef, expectedReturnType)); + } + } } else { if (functionTypeExpected) { - returnType = KotlinBuiltIns.getInstance().getReturnTypeFromFunctionType(expectedType); + returnType = expectedReturnType; } returnType = context.expressionTypingServices.getBlockReturnedType(functionInnerScope, bodyExpression, CoercionStrategy.COERCION_TO_UNIT, context.replaceExpectedType(returnType).replaceBindingTrace(temporaryTrace), temporaryTrace).getType(); @@ -130,17 +138,21 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor { } }, true); JetType safeReturnType = returnType == null ? ErrorUtils.createErrorType("") : returnType; - functionDescriptor.setReturnType(safeReturnType); if (!functionLiteral.hasDeclaredReturnType() && functionTypeExpected) { - JetType expectedReturnType = KotlinBuiltIns.getInstance().getReturnTypeFromFunctionType(expectedType); if (KotlinBuiltIns.getInstance().isUnit(expectedReturnType)) { - functionDescriptor.setReturnType(KotlinBuiltIns.getInstance().getUnitType()); - return DataFlowUtils.checkType(KotlinBuiltIns.getInstance().getFunctionType(Collections.emptyList(), receiver, parameterTypes, KotlinBuiltIns.getInstance().getUnitType()), expression, context, context.dataFlowInfo); + safeReturnType = KotlinBuiltIns.getInstance().getUnitType(); } - } - return DataFlowUtils.checkType(KotlinBuiltIns.getInstance().getFunctionType(Collections.emptyList(), receiver, parameterTypes, safeReturnType), expression, context, context.dataFlowInfo); + functionDescriptor.setReturnType(safeReturnType); + + JetType resultType = KotlinBuiltIns.getInstance().getFunctionType( + Collections.emptyList(), receiver, parameterTypes, safeReturnType); + if (expectedType != NO_EXPECTED_TYPE && KotlinBuiltIns.getInstance().isFunctionType(expectedType)) { + // all checks were done before + return JetTypeInfo.create(resultType, context.dataFlowInfo); + } + return DataFlowUtils.checkType(resultType, expression, context, context.dataFlowInfo); } private SimpleFunctionDescriptorImpl createFunctionDescriptor(JetFunctionLiteralExpression expression, ExpressionTypingContext context, boolean functionTypeExpected) { @@ -185,7 +197,8 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor { ? KotlinBuiltIns.getInstance().getValueParameters(functionDescriptor, context.expectedType) : null; - boolean hasDeclaredValueParameters = functionLiteral.getValueParameterList() != null; + JetParameterList valueParameterList = functionLiteral.getValueParameterList(); + boolean hasDeclaredValueParameters = valueParameterList != null; if (functionTypeExpected && !hasDeclaredValueParameters && expectedValueParameters.size() == 1) { ValueParameterDescriptor valueParameterDescriptor = expectedValueParameters.get(0); ValueParameterDescriptor it = new ValueParameterDescriptorImpl( @@ -195,17 +208,36 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor { context.trace.record(AUTO_CREATED_IT, it); } else { + if (expectedValueParameters != null && declaredValueParameters.size() != expectedValueParameters.size()) { + List expectedParameterTypes = Lists.newArrayList(); + for (ValueParameterDescriptor parameter : expectedValueParameters) { + expectedParameterTypes.add(parameter.getType()); + } + context.trace.report(EXPECTED_PARAMETERS_NUMBER_MISMATCH.on(functionLiteral, expectedParameterTypes.size(), expectedParameterTypes)); + } for (int i = 0; i < declaredValueParameters.size(); i++) { JetParameter declaredParameter = declaredValueParameters.get(i); JetTypeReference typeReference = declaredParameter.getTypeReference(); + JetType expectedType; + if (expectedValueParameters != null && i < expectedValueParameters.size()) { + expectedType = expectedValueParameters.get(i).getType(); + } + else { + expectedType = null; + } JetType type; if (typeReference != null) { type = context.expressionTypingServices.getTypeResolver().resolveType(context.scope, typeReference, context.trace, true); + if (expectedType != null) { + if (!JetTypeChecker.INSTANCE.isSubtypeOf(type, expectedType)) { + context.trace.report(EXPECTED_PARAMETER_TYPE_MISMATCH.on(declaredParameter, expectedType)); + } + } } else { - if (expectedValueParameters != null && i < expectedValueParameters.size()) { - type = expectedValueParameters.get(i).getType(); + if (expectedType != null) { + type = expectedType; } else { context.trace.report(CANNOT_INFER_PARAMETER_TYPE.on(declaredParameter)); diff --git a/compiler/testData/diagnostics/tests/AutoCreatedIt.kt b/compiler/testData/diagnostics/tests/AutoCreatedIt.kt index 6d9b64596a2..5e0f4ce9e08 100644 --- a/compiler/testData/diagnostics/tests/AutoCreatedIt.kt +++ b/compiler/testData/diagnostics/tests/AutoCreatedIt.kt @@ -2,17 +2,17 @@ fun text() { "direct:a" to "mock:a" "direct:a" on {it.body == ""} to "mock:a" "direct:a" on {it -> it.body == ""} to "mock:a" - bar {1} - bar {it + 1} + bar {1} + bar {it + 1} bar {it, it1 -> it} bar1 {1} bar1 {it + 1} - bar2 {} + bar2 {} bar2 {1} bar2 {it} - bar2 {it -> it} + bar2 {it -> it} } fun bar(f : (Int, Int) -> Int) {} diff --git a/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.kt b/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.kt index b0b6b230000..4f07578fc2c 100644 --- a/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.kt +++ b/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.kt @@ -34,8 +34,8 @@ fun main(args : Array) { foo2()({}) foo2(){} (foo2()){} - (foo2()){x -> } - foo2()({x -> }) + (foo2()){x -> } + foo2()({x -> }) val a = fooT1(1)() a : Int diff --git a/compiler/testData/diagnostics/tests/functionLiterals/ExpectedParameterTypeMismatchVariance.kt b/compiler/testData/diagnostics/tests/functionLiterals/ExpectedParameterTypeMismatchVariance.kt new file mode 100644 index 00000000000..26796bbb4d8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/functionLiterals/ExpectedParameterTypeMismatchVariance.kt @@ -0,0 +1,19 @@ +package a + +trait Super +trait Trait : Super +class Sub : Trait + +fun foo(f: (Trait) -> Trait) = f + +fun test(s: Sub) { + foo { + (t: Super): Sub -> s + } + foo { + (t: Trait): Trait -> s + } + foo { + (t: Sub): Super -> s + } +} diff --git a/compiler/testData/diagnostics/tests/functionLiterals/ExpectedParametersTypesMismatch.kt b/compiler/testData/diagnostics/tests/functionLiterals/ExpectedParametersTypesMismatch.kt new file mode 100644 index 00000000000..5543fdb078b --- /dev/null +++ b/compiler/testData/diagnostics/tests/functionLiterals/ExpectedParametersTypesMismatch.kt @@ -0,0 +1,47 @@ +package a + +fun foo0(f: () -> String) = f +fun foo1(f: (Int) -> String) = f +fun foo2(f: (Int, String) -> String) = f + +fun test1() { + foo0 { + "" + } + foo0 { + (s: String) -> "" + } + foo0 { + x, y -> "" + } + foo0 { + (): Int -> 42 + } + + foo1 { + "" + } + foo1 { + (s: String) -> "" + } + foo1 { + x, y -> "" + } + foo1 { + (): Int -> 42 + } + + + foo2 { + "" + } + foo2 { + (s: String) -> "" + } + foo2 { + x -> "" + } + foo2 { + (): Int -> 42 + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/regressions/kt352.kt b/compiler/testData/diagnostics/tests/regressions/kt352.kt index 71a37104fef..fb34818a755 100644 --- a/compiler/testData/diagnostics/tests/regressions/kt352.kt +++ b/compiler/testData/diagnostics/tests/regressions/kt352.kt @@ -2,18 +2,18 @@ package kt352 -val f : (Any) -> Unit = { () : Unit -> } //type mismatch +val f : (Any) -> Unit = { () : Unit -> } //type mismatch fun foo() { - val f : (Any) -> Unit = { () : Unit -> } //!!! no error + val f : (Any) -> Unit = { () : Unit -> } //!!! no error } class A() { - val f : (Any) -> Unit = { () : Unit -> } //type mismatch + val f : (Any) -> Unit = { () : Unit -> } //type mismatch } //more tests -val g : () -> Unit = { (): Int -> 42 } +val g : () -> Unit = { (): Int -> 42 } val h : () -> Unit = { doSmth() } @@ -24,4 +24,4 @@ val testIt : (Any) -> Unit = { if (it is String) { doSmth(it) } -} +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index 86efe992c16..c7a6c8d1b33 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -1791,6 +1791,16 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/functionLiterals"), "kt", true); } + @TestMetadata("ExpectedParameterTypeMismatchVariance.kt") + public void testExpectedParameterTypeMismatchVariance() throws Exception { + doTest("compiler/testData/diagnostics/tests/functionLiterals/ExpectedParameterTypeMismatchVariance.kt"); + } + + @TestMetadata("ExpectedParametersTypesMismatch.kt") + public void testExpectedParametersTypesMismatch() throws Exception { + doTest("compiler/testData/diagnostics/tests/functionLiterals/ExpectedParametersTypesMismatch.kt"); + } + @TestMetadata("kt2906.kt") public void testKt2906() throws Exception { doTest("compiler/testData/diagnostics/tests/functionLiterals/kt2906.kt");