KT-4198 On covariant projection, do not throw whole var's away
#KT-4198 Fixed
This commit is contained in:
@@ -443,6 +443,7 @@ public interface Errors {
|
|||||||
DiagnosticFactory0<JetFunctionLiteralExpression> UNUSED_FUNCTION_LITERAL = DiagnosticFactory0.create(WARNING);
|
DiagnosticFactory0<JetFunctionLiteralExpression> UNUSED_FUNCTION_LITERAL = DiagnosticFactory0.create(WARNING);
|
||||||
|
|
||||||
DiagnosticFactory1<JetExpression, DeclarationDescriptor> VAL_REASSIGNMENT = DiagnosticFactory1.create(ERROR);
|
DiagnosticFactory1<JetExpression, DeclarationDescriptor> VAL_REASSIGNMENT = DiagnosticFactory1.create(ERROR);
|
||||||
|
DiagnosticFactory1<JetExpression, DeclarationDescriptor> SETTER_PROJECTED_OUT = DiagnosticFactory1.create(ERROR);
|
||||||
|
|
||||||
DiagnosticFactory1<JetExpression, DeclarationDescriptor> INITIALIZATION_BEFORE_DECLARATION = DiagnosticFactory1.create(ERROR);
|
DiagnosticFactory1<JetExpression, DeclarationDescriptor> INITIALIZATION_BEFORE_DECLARATION = DiagnosticFactory1.create(ERROR);
|
||||||
|
|
||||||
|
|||||||
+1
@@ -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(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(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(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(INITIALIZATION_BEFORE_DECLARATION, "Variable cannot be initialized before declaration", NAME);
|
||||||
MAP.put(VARIABLE_EXPECTED, "Variable expected");
|
MAP.put(VARIABLE_EXPECTED, "Variable expected");
|
||||||
|
|||||||
+10
-1
@@ -758,8 +758,17 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
}
|
}
|
||||||
if (canBeThis && expression instanceof JetThisExpression) return;
|
if (canBeThis && expression instanceof JetThisExpression) return;
|
||||||
VariableDescriptor variable = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), expression, true);
|
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) {
|
if (variable == null) {
|
||||||
trace.report(VARIABLE_EXPECTED.on(expression != null ? expression : expressionWithParenthesis));
|
trace.report(VARIABLE_EXPECTED.on(reportOn));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
trait Tr<T> {
|
||||||
|
var v: Tr<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(t: Tr<*>) {
|
||||||
|
t.v = t
|
||||||
|
val v = TypeOf(t.v)
|
||||||
|
v: TypeOf<Tr<*>>
|
||||||
|
}
|
||||||
|
|
||||||
|
class TypeOf<T>(t: T)
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
trait Tr<T> {
|
||||||
|
var v: T
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(t: Tr<*>) {
|
||||||
|
<!SETTER_PROJECTED_OUT!>t.v<!> = null!!
|
||||||
|
val v = TypeOf(t.v)
|
||||||
|
v: TypeOf<Any?>
|
||||||
|
}
|
||||||
|
|
||||||
|
class TypeOf<T>(t: T)
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
trait Tr<T> {
|
||||||
|
var v: T
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(t: Tr<out String>) {
|
||||||
|
<!SETTER_PROJECTED_OUT!>t.v<!> += null!!
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
trait Tr<T> {
|
||||||
|
var v: T
|
||||||
|
}
|
||||||
|
|
||||||
|
class C {
|
||||||
|
fun plusAssign(<!UNUSED_PARAMETER!>c<!>: C) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(t: Tr<out C>) {
|
||||||
|
// No error because no real assignment happens
|
||||||
|
t.v += null!!
|
||||||
|
}
|
||||||
@@ -2860,7 +2860,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
|||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/diagnostics/tests/generics")
|
@TestMetadata("compiler/testData/diagnostics/tests/generics")
|
||||||
@InnerTestClasses({Generics.TpAsReified.class})
|
@InnerTestClasses({Generics.TpAsReified.class, Generics.VarProjection.class})
|
||||||
public static class Generics extends AbstractDiagnosticsTestWithEagerResolve {
|
public static class Generics extends AbstractDiagnosticsTestWithEagerResolve {
|
||||||
public void testAllFilesPresentInGenerics() throws Exception {
|
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);
|
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() {
|
public static Test innerSuite() {
|
||||||
TestSuite suite = new TestSuite("Generics");
|
TestSuite suite = new TestSuite("Generics");
|
||||||
suite.addTestSuite(Generics.class);
|
suite.addTestSuite(Generics.class);
|
||||||
suite.addTestSuite(TpAsReified.class);
|
suite.addTestSuite(TpAsReified.class);
|
||||||
|
suite.addTestSuite(VarProjection.class);
|
||||||
return suite;
|
return suite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,16 @@ public interface PropertyDescriptor extends VariableDescriptor, CallableMemberDe
|
|||||||
@Nullable
|
@Nullable
|
||||||
PropertySetterDescriptor getSetter();
|
PropertySetterDescriptor getSetter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In the following case, the setter is projected out:
|
||||||
|
*
|
||||||
|
* trait Tr<T> { var v: T }
|
||||||
|
* fun test(tr: Tr<out String>) {
|
||||||
|
* tr.v = null!! // the assignment is illegal, although a read would be fine
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
boolean isSetterProjectedOut();
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
List<PropertyAccessorDescriptor> getAccessors();
|
List<PropertyAccessorDescriptor> getAccessors();
|
||||||
|
|
||||||
|
|||||||
+20
-1
@@ -30,6 +30,7 @@ import org.jetbrains.jet.lang.types.DescriptorSubstitutor;
|
|||||||
import org.jetbrains.jet.lang.types.JetType;
|
import org.jetbrains.jet.lang.types.JetType;
|
||||||
import org.jetbrains.jet.lang.types.TypeSubstitutor;
|
import org.jetbrains.jet.lang.types.TypeSubstitutor;
|
||||||
import org.jetbrains.jet.lang.types.Variance;
|
import org.jetbrains.jet.lang.types.Variance;
|
||||||
|
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -49,6 +50,7 @@ public class PropertyDescriptorImpl extends VariableDescriptorImpl implements Pr
|
|||||||
private List<TypeParameterDescriptor> typeParameters;
|
private List<TypeParameterDescriptor> typeParameters;
|
||||||
private PropertyGetterDescriptorImpl getter;
|
private PropertyGetterDescriptorImpl getter;
|
||||||
private PropertySetterDescriptor setter;
|
private PropertySetterDescriptor setter;
|
||||||
|
private boolean setterProjectedOut;
|
||||||
|
|
||||||
private PropertyDescriptorImpl(
|
private PropertyDescriptorImpl(
|
||||||
@Nullable PropertyDescriptor original,
|
@Nullable PropertyDescriptor original,
|
||||||
@@ -125,6 +127,10 @@ public class PropertyDescriptorImpl extends VariableDescriptorImpl implements Pr
|
|||||||
this.setter = setter;
|
this.setter = setter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSetterProjectedOut(boolean setterProjectedOut) {
|
||||||
|
this.setterProjectedOut = setterProjectedOut;
|
||||||
|
}
|
||||||
|
|
||||||
public void setVisibility(@NotNull Visibility visibility) {
|
public void setVisibility(@NotNull Visibility visibility) {
|
||||||
this.visibility = visibility;
|
this.visibility = visibility;
|
||||||
}
|
}
|
||||||
@@ -182,6 +188,11 @@ public class PropertyDescriptorImpl extends VariableDescriptorImpl implements Pr
|
|||||||
return setter;
|
return setter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSetterProjectedOut() {
|
||||||
|
return setterProjectedOut;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public List<PropertyAccessorDescriptor> getAccessors() {
|
public List<PropertyAccessorDescriptor> getAccessors() {
|
||||||
@@ -253,7 +264,15 @@ public class PropertyDescriptorImpl extends VariableDescriptorImpl implements Pr
|
|||||||
if (newSetter != null) {
|
if (newSetter != null) {
|
||||||
List<ValueParameterDescriptor> substitutedValueParameters = FunctionDescriptorImpl.getSubstitutedValueParameters(newSetter, setter, substitutor);
|
List<ValueParameterDescriptor> substitutedValueParameters = FunctionDescriptorImpl.getSubstitutedValueParameters(newSetter, setter, substitutor);
|
||||||
if (substitutedValueParameters == null) {
|
if (substitutedValueParameters == null) {
|
||||||
return null;
|
// The setter is projected out, e.g. in this case:
|
||||||
|
// trait Tr<T> { var v: T }
|
||||||
|
// fun test(tr: Tr<out Any?>) { ... }
|
||||||
|
// 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.<ValueParameterDescriptor>singletonList(
|
||||||
|
PropertySetterDescriptorImpl.createSetterParameter(newSetter, KotlinBuiltIns.getInstance().getNothingType())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (substitutedValueParameters.size() != 1) {
|
if (substitutedValueParameters.size() != 1) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
|
|||||||
+10
-1
@@ -67,7 +67,16 @@ public class PropertySetterDescriptorImpl extends PropertyAccessorDescriptorImpl
|
|||||||
|
|
||||||
public void initializeDefault() {
|
public void initializeDefault() {
|
||||||
assert parameter == null;
|
assert parameter == null;
|
||||||
parameter = new ValueParameterDescriptorImpl(this, 0, Collections.<AnnotationDescriptor>emptyList(), Name.special("<set-?>"), 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.<AnnotationDescriptor>emptyList(), Name.special("<set-?>"), type, false, null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
Reference in New Issue
Block a user