From 1662c5cb7513efb25d455ed8ee357eb9deacfc89 Mon Sep 17 00:00:00 2001 From: Mikhael Bogdanov Date: Mon, 18 Feb 2013 18:31:40 +0400 Subject: [PATCH] New diagnostic error for ambiguous object expression type --- .../jet/lang/diagnostics/Errors.java | 2 + .../rendering/DefaultErrorMessages.java | 2 + .../jet/lang/resolve/DescriptorResolver.java | 75 +++++-- .../jet/lang/types/DeferredType.java | 1 + .../expressions/ExpressionTypingServices.java | 1 + .../ambiguousObjectExpressionType.kt | 117 ++++++++++ .../unambiguousObjectExpressionType.kt | 200 ++++++++++++++++++ .../checkers/JetDiagnosticsTestGenerated.java | 10 + .../jet/codegen/OuterClassGenTest.java | 6 +- 9 files changed, 395 insertions(+), 19 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/declarationChecks/ambiguousObjectExpressionType.kt create mode 100644 compiler/testData/diagnostics/tests/declarationChecks/unambiguousObjectExpressionType.kt 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 d91ae3d5224..1d1d144e9ea 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -229,6 +229,8 @@ public interface Errors { DiagnosticFactory2 MANY_IMPL_MEMBER_NOT_IMPLEMENTED = DiagnosticFactory2.create(ERROR); + DiagnosticFactory1> AMBIGUOUS_ANONYMOUS_TYPE_INFERRED = DiagnosticFactory1.create(ERROR, NAMED_ELEMENT); + // Property-specific DiagnosticFactory2 VAR_OVERRIDDEN_BY_VAL = 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 d5c06f6338d..9dc7dac4680 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 @@ -441,6 +441,8 @@ public class DefaultErrorMessages { "Names of the parameter #{1} conflict in the following members of supertypes: ''{0}''" + "This may cause problems when calling this function with named arguments.", commaSeparated(TO_STRING), TO_STRING); + MAP.put(AMBIGUOUS_ANONYMOUS_TYPE_INFERRED, "Right-hand side has anonymous type. Please specify type explicitly", TO_STRING); + MAP.setImmutable(); for (Field field : Errors.class.getFields()) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorResolver.java index b21bd9be6e5..09079621ad8 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorResolver.java @@ -245,10 +245,10 @@ public class DescriptorResolver { @NotNull public SimpleFunctionDescriptor resolveFunctionDescriptor( - DeclarationDescriptor containingDescriptor, - final JetScope scope, - final JetNamedFunction function, - final BindingTrace trace + @NotNull DeclarationDescriptor containingDescriptor, + @NotNull final JetScope scope, + @NotNull final JetNamedFunction function, + @NotNull final BindingTrace trace ) { final SimpleFunctionDescriptorImpl functionDescriptor = new SimpleFunctionDescriptorImpl( containingDescriptor, @@ -296,7 +296,8 @@ public class DescriptorResolver { @Override protected JetType compute() { //JetFlowInformationProvider flowInformationProvider = computeFlowData(function, bodyExpression); - return expressionTypingServices.inferFunctionReturnType(scope, function, functionDescriptor, trace); + JetType type = expressionTypingServices.inferFunctionReturnType(scope, function, functionDescriptor, trace); + return transformAnonymousTypeIfNeeded(functionDescriptor, function, type, trace); } }); } @@ -736,7 +737,7 @@ public class DescriptorResolver { ); JetType type = - getVariableType(scope, variable, dataFlowInfo, false, trace); // For a local variable the type must not be deferred + getVariableType(propertyDescriptor, scope, variable, dataFlowInfo, false, trace); // For a local variable the type must not be deferred ReceiverParameterDescriptor receiverParameter = ((ScriptDescriptor) containingDeclaration).getThisAsReceiverParameter(); propertyDescriptor.setType(type, Collections.emptyList(), receiverParameter, (JetType) null); @@ -748,7 +749,7 @@ public class DescriptorResolver { resolveLocalVariableDescriptorWithType(containingDeclaration, variable, null, trace); JetType type = - getVariableType(scope, variable, dataFlowInfo, false, trace); // For a local variable the type must not be deferred + getVariableType(variableDescriptor, scope, variable, dataFlowInfo, false, trace); // For a local variable the type must not be deferred variableDescriptor.setOutType(type); return variableDescriptor; } @@ -948,7 +949,7 @@ public class DescriptorResolver { JetScope propertyScope = getPropertyDeclarationInnerScope(propertyDescriptor, scope, typeParameterDescriptors, NO_RECEIVER_PARAMETER, trace); - JetType type = getVariableType(propertyScope, property, DataFlowInfo.EMPTY, true, trace); + JetType type = getVariableType(propertyDescriptor, propertyScope, property, DataFlowInfo.EMPTY, true, trace); propertyDescriptor.setType(type, typeParameterDescriptors, getExpectedThisObjectIfNeeded(containingDeclaration), receiverDescriptor); @@ -980,6 +981,7 @@ public class DescriptorResolver { @NotNull private JetType getVariableType( + @NotNull final VariableDescriptor variableDescriptor, @NotNull final JetScope scope, @NotNull final JetVariableDeclaration variable, @NotNull final DataFlowInfo dataFlowInfo, @@ -997,17 +999,18 @@ public class DescriptorResolver { return ErrorUtils.createErrorType("No type, no body"); } else { - RecursionIntolerantLazyValue lazyValue = new RecursionIntolerantLazyValueWithDefault(ErrorUtils.createErrorType("Recursive dependency")) { - @Override - protected JetType compute() { - return expressionTypingServices.safeGetType(scope, initializer, TypeUtils.NO_EXPECTED_TYPE, dataFlowInfo, trace); - } - }; if (notLocal) { - return DeferredType.create(trace, lazyValue); + return DeferredType.create(trace, new RecursionIntolerantLazyValueWithDefault(ErrorUtils.createErrorType("Recursive dependency")) { + @Override + protected JetType compute() { + JetType type = resolveInitializerType(scope, initializer, dataFlowInfo, trace); + + return transformAnonymousTypeIfNeeded(variableDescriptor, variable, type, trace); + } + }); } else { - return lazyValue.get(); + return resolveInitializerType(scope, initializer, dataFlowInfo, trace); } } } @@ -1016,6 +1019,46 @@ public class DescriptorResolver { } } + @Nullable + private JetType transformAnonymousTypeIfNeeded( + @NotNull DeclarationDescriptorWithVisibility descriptor, + @NotNull JetNamedDeclaration declaration, + @NotNull JetType type, + @NotNull final BindingTrace trace + ) { + ClassifierDescriptor classifierDescriptor = type.getConstructor().getDeclarationDescriptor(); + boolean isAnonymous = classifierDescriptor != null && classifierDescriptor.getName().isSpecial() && !ErrorUtils.isErrorType(type); + if (!isAnonymous) { + return type; + } + + boolean definedInClass = DescriptorUtils.getParentOfType(descriptor, ClassDescriptor.class) != null; + boolean isLocal = descriptor.getContainingDeclaration() instanceof CallableDescriptor; + Visibility visibility = descriptor.getVisibility(); + boolean transformNeeded = !isLocal && !visibility.isPublicAPI() + && !(definedInClass && Visibilities.PRIVATE.equals(visibility)); + if (transformNeeded) { + if (type.getConstructor().getSupertypes().size() == 1) { + assert type.getArguments().isEmpty() : "Object expression couldn't have any type parameters!"; + return type.getConstructor().getSupertypes().iterator().next(); + } + else { + trace.report(AMBIGUOUS_ANONYMOUS_TYPE_INFERRED.on(declaration, type.getConstructor().getSupertypes())); + } + } + return type; + } + + @NotNull + private JetType resolveInitializerType( + @NotNull JetScope scope, + @NotNull JetExpression initializer, + @NotNull final DataFlowInfo dataFlowInfo, + @NotNull BindingTrace trace + ) { + return expressionTypingServices.safeGetType(scope, initializer, TypeUtils.NO_EXPECTED_TYPE, dataFlowInfo, trace); + } + @Nullable private PropertySetterDescriptor resolvePropertySetterDescriptor( @NotNull JetScope scope, diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/DeferredType.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/DeferredType.java index 9ac1cdf7032..f43b5a98f96 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/DeferredType.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/DeferredType.java @@ -46,6 +46,7 @@ public class DeferredType implements JetType { return lazyValue.isComputed(); } + @NotNull public JetType getActualType() { return lazyValue.get(); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java index cadd815a870..0e74040c951 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java @@ -117,6 +117,7 @@ public class ExpressionTypingServices { this.typeResolver = typeResolver; } + @NotNull public JetType safeGetType(@NotNull JetScope scope, @NotNull JetExpression expression, @NotNull JetType expectedType, @NotNull DataFlowInfo dataFlowInfo, @NotNull BindingTrace trace) { JetType type = getType(scope, expression, expectedType, dataFlowInfo, trace); if (type != null) { diff --git a/compiler/testData/diagnostics/tests/declarationChecks/ambiguousObjectExpressionType.kt b/compiler/testData/diagnostics/tests/declarationChecks/ambiguousObjectExpressionType.kt new file mode 100644 index 00000000000..9e233f03f45 --- /dev/null +++ b/compiler/testData/diagnostics/tests/declarationChecks/ambiguousObjectExpressionType.kt @@ -0,0 +1,117 @@ +trait MyTrait { + fun f1() {} +} + +open class MyClass { + fun f2() {} +} + + +class Foo { + + private val privateProperty = object : MyClass(), MyTrait {} + + { + privateProperty.f1() + privateProperty.f2() + } + + protected val protectedProperty = object : MyClass(), MyTrait {} + + val internalProperty = object : MyClass(), MyTrait {} + + internal val internal2Property = object : MyClass(), MyTrait {} + + public val publicProperty = object : MyClass(), MyTrait {} + + + private fun privateFunction() = object : MyClass(), MyTrait {} + + { + privateFunction().f1() + privateFunction().f2() + } + + protected fun protectedFunction() = object : MyClass(), MyTrait {} + + fun internalFunction() = object : MyClass(), MyTrait {} + + internal fun internal2Function() = object : MyClass(), MyTrait {} + + public fun publicFunction() = object : MyClass(), MyTrait {} + + + + class FooInner { + private val privatePropertyInner = object : MyClass(), MyTrait {} + + { + privatePropertyInner.f1() + privatePropertyInner.f2() + } + + protected val protectedProperty = object : MyClass(), MyTrait {} + + val internalProperty = object : MyClass(), MyTrait {} + + internal val internal2Property = object : MyClass(), MyTrait {} + + public val publicProperty = object : MyClass(), MyTrait {} + + + private fun privateFunctionInner() = object : MyClass(), MyTrait {} + + { + privateFunctionInner().f1() + privateFunctionInner().f2() + } + + protected fun protectedFunction() = object : MyClass(), MyTrait {} + + fun internalFunction() = object : MyClass(), MyTrait {} + + internal fun internal2Function() = object : MyClass(), MyTrait {} + + public fun publicFunction() = object : MyClass(), MyTrait {} + + } + + fun foo() { + val localVar = object : MyClass(), MyTrait {} + localVar.f1() + localVar.f2() + + fun foo2() = object : MyClass(), MyTrait {} + foo2().f1() + foo2().f2() + } + +} + +private val packagePrivateProperty = object : MyClass(), MyTrait {} + +protected val packageProtectedProperty = object : MyClass(), MyTrait {} + +val packageInternalProperty = object : MyClass(), MyTrait {} + +internal val packageInternal2Property = object : MyClass(), MyTrait {} + +public val packagePublicProperty = object : MyClass(), MyTrait {} + +protected fun packageProtectedFunction() = object : MyClass(), MyTrait {} + +fun packageInternalFunction() = object : MyClass(), MyTrait {} + +internal fun packageInternal2Function() = object : MyClass(), MyTrait {} + +public fun packagePublicFunction() = object : MyClass(), MyTrait {} + +fun fooPackage() { + val packageLocalVar = object : MyClass(), MyTrait {} + packageLocalVar.f1() + packageLocalVar.f2() + + fun fooPackageLocal() = object : MyClass(), MyTrait {} + fooPackageLocal().f1() + fooPackageLocal().f2() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/declarationChecks/unambiguousObjectExpressionType.kt b/compiler/testData/diagnostics/tests/declarationChecks/unambiguousObjectExpressionType.kt new file mode 100644 index 00000000000..d899c9045e0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/declarationChecks/unambiguousObjectExpressionType.kt @@ -0,0 +1,200 @@ +open class MyClass { + fun f1() {} +} + + +class Foo { + + protected val protectedProperty = object : MyClass() {} + + public val publicProperty = object : MyClass() {} + + protected val protected2Property : MyClass = object : MyClass() {fun invisible() {}} + + public val public2Property : MyClass = object : MyClass() {fun invisible() {}} + + private val privateProperty = object : MyClass() {fun visible() {}} + + val internalProperty = object : MyClass() { fun invisible() {}} + + internal val internal2Property = object : MyClass() {fun invisible() {}} + + + fun testProperties() { + privateProperty.f1() + internalProperty.f1() + internal2Property.f1() + protected2Property.f1() + public2Property.f1() + + privateProperty.visible() + protected2Property.invisible() + public2Property.invisible() + internalProperty.invisible() + internal2Property.invisible() + } + + + protected fun protectedFunction() = object : MyClass() {} + + public fun publicFunction() = object : MyClass() {} + + protected fun protected2Function(): MyClass = object : MyClass() {fun visible() {}} + + public fun public2Function(): MyClass = object : MyClass() {fun visible() {}} + + private fun privateFunction() = object : MyClass() {fun visible() {}} + + fun internalFunction() = object : MyClass() {fun invisible() {}} + + internal fun internal2Function() = object : MyClass() {fun invisible() {}} + + + fun testFunctions() { + privateFunction().f1() + internalFunction().f1() + internal2Function().f1() + public2Function().f1() + protected2Function().f1() + + privateFunction().visible() + internalFunction().invisible() + internal2Function().invisible() + public2Function().invisible() + protected2Function().invisible() + } + + + class FooInner { + + public val publicProperty = object : MyClass() {} + + protected val protectedProperty = object : MyClass() {} + + protected val protected2Property : MyClass = object : MyClass() {fun invisible() {}} + + public val public2Property : MyClass = object : MyClass() {fun invisible() {}} + + private val privateProperty = object : MyClass() {fun visible() {}} + + val internalProperty = object : MyClass() { fun invisible() {}} + + internal val internal2Property = object : MyClass() {fun invisible() {}} + + + fun testProperties() { + privateProperty.f1() + internalProperty.f1() + internal2Property.f1() + protected2Property.f1() + public2Property.f1() + + privateProperty.visible() + protected2Property.invisible() + public2Property.invisible() + internalProperty.invisible() + internal2Property.invisible() + } + + + protected fun protectedFunction() = object : MyClass() {} + + public fun publicFunction() = object : MyClass() {} + + protected fun protected2Function(): MyClass = object : MyClass() {fun visible() {}} + + public fun public2Function(): MyClass = object : MyClass() {fun visible() {}} + + private fun privateFunction() = object : MyClass() {fun visible() {}} + + fun internalFunction() = object : MyClass() {fun invisible() {}} + + internal fun internal2Function() = object : MyClass() {fun invisible() {}} + + + fun testFunctions() { + privateFunction().f1() + internalFunction().f1() + internal2Function().f1() + public2Function().f1() + protected2Function().f1() + + privateFunction().visible() + internalFunction().invisible() + internal2Function().invisible() + public2Function().invisible() + protected2Function().invisible() + } + } + + fun foo() { + val localVar = object : MyClass() {} + localVar.f1() + fun foo2() = object : MyClass() {} + foo2().f1() + } + +} + +protected val packageProtectedProperty = object : MyClass() {} + +public val packagePublicProperty = object : MyClass() {} + +public val packagePublic2Property : MyClass = object : MyClass() {fun invisible() {}} + +private val packagePrivateProperty = object : MyClass() {fun invisible() {}} + +val packageInternalProperty = object : MyClass() {fun invisible() {}} + +internal val packageInternal2Property = object : MyClass() {fun invisible() {}} + + +fun testProperties() { + packagePrivateProperty.f1() + packageInternalProperty.f1() + packageInternal2Property.f1() + packagePublic2Property.f1() + + packagePrivateProperty.invisible() + packageInternalProperty.invisible() + packageInternal2Property.invisible() + packagePublic2Property.invisible() +} + + +private fun privateFunction() = object : MyClass() {fun invisible() {}} + +protected fun protectedFunction() = object : MyClass() {} + +public fun publicFunction() = object : MyClass() {} + +public fun public2Function() : MyClass = object : MyClass() {fun invisible() {}} + +fun internalFunction() = object : MyClass() {fun invisible() {}} + +internal fun internal2Function() = object : MyClass() {fun invisible() {}} + + + +fun testFunctions() { + privateFunction().f1() + internalFunction().f1() + internal2Function().f1() + public2Function().f1() + + privateFunction().invisible() + internalFunction().invisible() + internal2Function().invisible() + public2Function().invisible() +} + +fun fooPackage() { + val packageLocalVar = object : MyClass() {fun visible() {}} + packageLocalVar.f1() + packageLocalVar.visible() + + fun fooPackageLocal() = object : MyClass() {fun visible() {}} + fooPackageLocal().f1() + fooPackageLocal().visible() +} + diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index 51cba9de7e7..dd9540a289a 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -1453,6 +1453,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/declarationChecks"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("ambiguousObjectExpressionType.kt") + public void testAmbiguousObjectExpressionType() throws Exception { + doTest("compiler/testData/diagnostics/tests/declarationChecks/ambiguousObjectExpressionType.kt"); + } + @TestMetadata("ComponentFunctionReturnTypeMismatch.kt") public void testComponentFunctionReturnTypeMismatch() throws Exception { doTest("compiler/testData/diagnostics/tests/declarationChecks/ComponentFunctionReturnTypeMismatch.kt"); @@ -1548,6 +1553,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage doTest("compiler/testData/diagnostics/tests/declarationChecks/RedeclarationsInMultiDecl.kt"); } + @TestMetadata("unambiguousObjectExpressionType.kt") + public void testUnambiguousObjectExpressionType() throws Exception { + doTest("compiler/testData/diagnostics/tests/declarationChecks/unambiguousObjectExpressionType.kt"); + } + @TestMetadata("valVarFunctionParameter.kt") public void testValVarFunctionParameter() throws Exception { doTest("compiler/testData/diagnostics/tests/declarationChecks/valVarFunctionParameter.kt"); diff --git a/compiler/tests/org/jetbrains/jet/codegen/OuterClassGenTest.java b/compiler/tests/org/jetbrains/jet/codegen/OuterClassGenTest.java index 75d227d450f..4d29af05208 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/OuterClassGenTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/OuterClassGenTest.java @@ -92,15 +92,15 @@ public class OuterClassGenTest extends CodegenTestCase { private void checkInfo(ClassReader kotlinReader, ClassReader javaReader) { - String [] kotlinInfo = getOuterClasInfo(kotlinReader); - String [] javaInfo = getOuterClasInfo(javaReader); + String [] kotlinInfo = getOuterClassInfo(kotlinReader); + String [] javaInfo = getOuterClassInfo(javaReader); for (int i = 0; i < kotlinInfo.length; i++) { String info = kotlinInfo[i]; assertEquals("Error in enclosingMethodInfo info for: " + kotlinReader.getClassName() + " class in " + INFO_PARTS[i] + " part", javaInfo[i], info); } } - public String [] getOuterClasInfo(ClassReader reader) { + public String [] getOuterClassInfo(ClassReader reader) { final String [] info = new String [3]; reader.accept(new ClassVisitor(Opcodes.ASM4) { @Override