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 5f3a601b45d..b3ee8b7f91d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -443,6 +443,7 @@ public interface Errors { DiagnosticFactory0 UNUSED_FUNCTION_LITERAL = DiagnosticFactory0.create(WARNING); DiagnosticFactory1 VAL_REASSIGNMENT = DiagnosticFactory1.create(ERROR); + DiagnosticFactory1 SETTER_PROJECTED_OUT = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 INITIALIZATION_BEFORE_DECLARATION = DiagnosticFactory1.create(ERROR); 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 f346b9548a3..d61518689d5 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 @@ -175,6 +175,7 @@ public class DefaultErrorMessages { MAP.put(UNUSED_FUNCTION_LITERAL, "The function literal is unused. If you mean block, you can use 'run { ... }'"); MAP.put(VAL_REASSIGNMENT, "Val cannot be reassigned", NAME); + MAP.put(SETTER_PROJECTED_OUT, "Setter for ''{0}'' is removed by type projection", NAME); MAP.put(INVISIBLE_SETTER, "Cannot assign to ''{0}'': the setter is ''{1}'' in ''{2}''", NAME, TO_STRING, NAME); MAP.put(INITIALIZATION_BEFORE_DECLARATION, "Variable cannot be initialized before declaration", NAME); MAP.put(VARIABLE_EXPECTED, "Variable expected"); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java index fce534db341..6a650bd68c0 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java @@ -758,8 +758,17 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } if (canBeThis && expression instanceof JetThisExpression) return; VariableDescriptor variable = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), expression, true); + + JetExpression reportOn = expression != null ? expression : expressionWithParenthesis; + if (variable instanceof PropertyDescriptor) { + PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variable; + if (propertyDescriptor.isSetterProjectedOut()) { + trace.report(SETTER_PROJECTED_OUT.on(reportOn, propertyDescriptor)); + } + } + if (variable == null) { - trace.report(VARIABLE_EXPECTED.on(expression != null ? expression : expressionWithParenthesis)); + trace.report(VARIABLE_EXPECTED.on(reportOn)); } } diff --git a/compiler/testData/diagnostics/tests/generics/varProjection/setterNotProjectedOutAssign.kt b/compiler/testData/diagnostics/tests/generics/varProjection/setterNotProjectedOutAssign.kt new file mode 100644 index 00000000000..e082d821eb4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/varProjection/setterNotProjectedOutAssign.kt @@ -0,0 +1,11 @@ +trait Tr { + var v: Tr +} + +fun test(t: Tr<*>) { + t.v = t + val v = TypeOf(t.v) + v: TypeOf> +} + +class TypeOf(t: T) \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutAssign.kt b/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutAssign.kt new file mode 100644 index 00000000000..58b1910333a --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutAssign.kt @@ -0,0 +1,11 @@ +trait Tr { + var v: T +} + +fun test(t: Tr<*>) { + t.v = null!! + val v = TypeOf(t.v) + v: TypeOf +} + +class TypeOf(t: T) \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutNoPlusAssign.kt b/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutNoPlusAssign.kt new file mode 100644 index 00000000000..36d8d429d53 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutNoPlusAssign.kt @@ -0,0 +1,7 @@ +trait Tr { + var v: T +} + +fun test(t: Tr) { + t.v += null!! +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutPlusAssignDefined.kt b/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutPlusAssignDefined.kt new file mode 100644 index 00000000000..3d5065d918f --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutPlusAssignDefined.kt @@ -0,0 +1,12 @@ +trait Tr { + var v: T +} + +class C { + fun plusAssign(c: C) {} +} + +fun test(t: Tr) { + // No error because no real assignment happens + t.v += null!! +} \ 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 b222706d282..beff12c40ba 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -2860,7 +2860,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage } @TestMetadata("compiler/testData/diagnostics/tests/generics") - @InnerTestClasses({Generics.TpAsReified.class}) + @InnerTestClasses({Generics.TpAsReified.class, Generics.VarProjection.class}) public static class Generics extends AbstractDiagnosticsTestWithEagerResolve { public void testAllFilesPresentInGenerics() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/generics"), Pattern.compile("^(.+)\\.kt$"), true); @@ -2949,10 +2949,39 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage } + @TestMetadata("compiler/testData/diagnostics/tests/generics/varProjection") + public static class VarProjection extends AbstractDiagnosticsTestWithEagerResolve { + public void testAllFilesPresentInVarProjection() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/generics/varProjection"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("setterNotProjectedOutAssign.kt") + public void testSetterNotProjectedOutAssign() throws Exception { + doTest("compiler/testData/diagnostics/tests/generics/varProjection/setterNotProjectedOutAssign.kt"); + } + + @TestMetadata("setterProjectedOutAssign.kt") + public void testSetterProjectedOutAssign() throws Exception { + doTest("compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutAssign.kt"); + } + + @TestMetadata("setterProjectedOutNoPlusAssign.kt") + public void testSetterProjectedOutNoPlusAssign() throws Exception { + doTest("compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutNoPlusAssign.kt"); + } + + @TestMetadata("setterProjectedOutPlusAssignDefined.kt") + public void testSetterProjectedOutPlusAssignDefined() throws Exception { + doTest("compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutPlusAssignDefined.kt"); + } + + } + public static Test innerSuite() { TestSuite suite = new TestSuite("Generics"); suite.addTestSuite(Generics.class); suite.addTestSuite(TpAsReified.class); + suite.addTestSuite(VarProjection.class); return suite; } } diff --git a/core/descriptors/src/org/jetbrains/jet/lang/descriptors/PropertyDescriptor.java b/core/descriptors/src/org/jetbrains/jet/lang/descriptors/PropertyDescriptor.java index b648a39db6b..46aa56d9785 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/descriptors/PropertyDescriptor.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/descriptors/PropertyDescriptor.java @@ -30,6 +30,16 @@ public interface PropertyDescriptor extends VariableDescriptor, CallableMemberDe @Nullable PropertySetterDescriptor getSetter(); + /** + * In the following case, the setter is projected out: + * + * trait Tr { var v: T } + * fun test(tr: Tr) { + * tr.v = null!! // the assignment is illegal, although a read would be fine + * } + */ + boolean isSetterProjectedOut(); + @NotNull List getAccessors(); diff --git a/core/descriptors/src/org/jetbrains/jet/lang/descriptors/impl/PropertyDescriptorImpl.java b/core/descriptors/src/org/jetbrains/jet/lang/descriptors/impl/PropertyDescriptorImpl.java index 38a474e1ebf..71e578b3142 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/descriptors/impl/PropertyDescriptorImpl.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/descriptors/impl/PropertyDescriptorImpl.java @@ -30,6 +30,7 @@ import org.jetbrains.jet.lang.types.DescriptorSubstitutor; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.TypeSubstitutor; import org.jetbrains.jet.lang.types.Variance; +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import java.util.Collections; import java.util.List; @@ -49,6 +50,7 @@ public class PropertyDescriptorImpl extends VariableDescriptorImpl implements Pr private List typeParameters; private PropertyGetterDescriptorImpl getter; private PropertySetterDescriptor setter; + private boolean setterProjectedOut; private PropertyDescriptorImpl( @Nullable PropertyDescriptor original, @@ -125,6 +127,10 @@ public class PropertyDescriptorImpl extends VariableDescriptorImpl implements Pr this.setter = setter; } + public void setSetterProjectedOut(boolean setterProjectedOut) { + this.setterProjectedOut = setterProjectedOut; + } + public void setVisibility(@NotNull Visibility visibility) { this.visibility = visibility; } @@ -182,6 +188,11 @@ public class PropertyDescriptorImpl extends VariableDescriptorImpl implements Pr return setter; } + @Override + public boolean isSetterProjectedOut() { + return setterProjectedOut; + } + @Override @NotNull public List getAccessors() { @@ -253,7 +264,15 @@ public class PropertyDescriptorImpl extends VariableDescriptorImpl implements Pr if (newSetter != null) { List substitutedValueParameters = FunctionDescriptorImpl.getSubstitutedValueParameters(newSetter, setter, substitutor); if (substitutedValueParameters == null) { - return null; + // The setter is projected out, e.g. in this case: + // trait Tr { var v: T } + // fun test(tr: Tr) { ... } + // we want to tell the user that although the property is declared as a var, + // it can not be assigned to because of the projection + substitutedDescriptor.setSetterProjectedOut(true); + substitutedValueParameters = Collections.singletonList( + PropertySetterDescriptorImpl.createSetterParameter(newSetter, KotlinBuiltIns.getInstance().getNothingType()) + ); } if (substitutedValueParameters.size() != 1) { throw new IllegalStateException(); diff --git a/core/descriptors/src/org/jetbrains/jet/lang/descriptors/impl/PropertySetterDescriptorImpl.java b/core/descriptors/src/org/jetbrains/jet/lang/descriptors/impl/PropertySetterDescriptorImpl.java index a9cf0d86443..f06ec7ef988 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/descriptors/impl/PropertySetterDescriptorImpl.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/descriptors/impl/PropertySetterDescriptorImpl.java @@ -67,7 +67,16 @@ public class PropertySetterDescriptorImpl extends PropertyAccessorDescriptorImpl public void initializeDefault() { assert parameter == null; - parameter = new ValueParameterDescriptorImpl(this, 0, Collections.emptyList(), Name.special(""), getCorrespondingProperty().getReturnType(), false, null); + parameter = createSetterParameter(this, getCorrespondingProperty().getReturnType()); + } + + public static ValueParameterDescriptorImpl createSetterParameter( + @NotNull PropertySetterDescriptor setterDescriptor, + @NotNull JetType type + ) { + return new ValueParameterDescriptorImpl( + setterDescriptor, 0, Collections.emptyList(), Name.special(""), type, false, null + ); } @NotNull