From 38fc57ae426d04198489aabb32782087e66c599a Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 21 Jun 2012 21:17:13 +0400 Subject: [PATCH 1/4] Pass DataFlowInfo through when-conditions --- .../expressions/PatternMatchingTypingVisitor.java | 12 ++++++++---- .../diagnostics/tests/dataFlowInfoTraversal/When.jet | 2 +- .../diagnostics/tests/regressions/Jet169.jet | 2 +- idea/testData/checker/regression/Jet169.jet | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java index f43a469a2f5..5d384c0623c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java @@ -116,9 +116,6 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { if (newDataFlowInfo == null) { newDataFlowInfo = context.dataFlowInfo; } - else { - newDataFlowInfo = newDataFlowInfo.and(context.dataFlowInfo); - } } JetExpression bodyExpression = whenEntry.getExpression(); if (bodyExpression != null) { @@ -259,14 +256,21 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { JetExpression expression = pattern.getExpression(); if (expression == null) return; JetType type = facade.getTypeInfo(expression, context.replaceScope(scopeToExtend)).getType(); + if (type == null) return; if (conditionExpected) { JetType booleanType = JetStandardLibrary.getInstance().getBooleanType(); - if (type != null && !JetTypeChecker.INSTANCE.equalTypes(booleanType, type)) { + if (!JetTypeChecker.INSTANCE.equalTypes(booleanType, type)) { context.trace.report(TYPE_MISMATCH_IN_CONDITION.on(pattern, type)); } return; } checkTypeCompatibility(type, subjectType, pattern); + DataFlowInfo dataFlowInfo = context.dataFlowInfo; + DataFlowValue expressionDataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(expression, type, context.trace.getBindingContext()); + for (DataFlowValue subjectVariable : subjectVariables) { + dataFlowInfo = dataFlowInfo.equate(subjectVariable, expressionDataFlowValue); + } + result.set(dataFlowInfo); } @Override diff --git a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/When.jet b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/When.jet index 5582e7b2346..06b49dbf4d5 100644 --- a/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/When.jet +++ b/compiler/testData/diagnostics/tests/dataFlowInfoTraversal/When.jet @@ -11,7 +11,7 @@ fun foo() { } when (x) { - 0 -> { if (x == null) return } + 0 -> { if (x == null) return } else -> { if (x == null) return } } bar(x) diff --git a/compiler/testData/diagnostics/tests/regressions/Jet169.jet b/compiler/testData/diagnostics/tests/regressions/Jet169.jet index 9b806fd22c1..9761d8ec20f 100644 --- a/compiler/testData/diagnostics/tests/regressions/Jet169.jet +++ b/compiler/testData/diagnostics/tests/regressions/Jet169.jet @@ -1,7 +1,7 @@ fun set(key : String, value : String) { val a : String? = "" when (a) { - "" -> a.get(0) + "" -> a.get(0) is String, is Any -> a.compareTo("") else -> a.toString() } diff --git a/idea/testData/checker/regression/Jet169.jet b/idea/testData/checker/regression/Jet169.jet index 35ace683078..e38e1eb9a05 100644 --- a/idea/testData/checker/regression/Jet169.jet +++ b/idea/testData/checker/regression/Jet169.jet @@ -1,7 +1,7 @@ fun set(key : String, value : String) { val a : String? = "" when (a) { - "" -> a.get(0) + "" -> a.get(0) is String, is Any -> a.compareTo("") else -> a.toString() } From b5ba7123d580b60f45c5c96e84b0350dff7b5431 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 21 Jun 2012 22:36:25 +0400 Subject: [PATCH 2/4] KT-2146 Nullability casts in when. #KT-2146 Fixed --- .../PatternMatchingTypingVisitor.java | 49 +++++++++++-------- .../tests/nullabilityAndAutoCasts/kt2146.jet | 9 ++++ 2 files changed, 38 insertions(+), 20 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/nullabilityAndAutoCasts/kt2146.jet diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java index 5d384c0623c..aff2b589b24 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java @@ -18,6 +18,7 @@ package org.jetbrains.jet.lang.types.expressions; import com.google.common.collect.Sets; import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -61,7 +62,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { if (pattern != null) { WritableScopeImpl scopeToExtend = newWritableScopeImpl(context, "Scope extended in 'is'"); DataFlowValue dataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(leftHandSide, knownType, context.trace.getBindingContext()); - newDataFlowInfo = checkPatternType(pattern, knownType, false, scopeToExtend, context, dataFlowValue); + newDataFlowInfo = checkPatternType(pattern, knownType, false, scopeToExtend, context, dataFlowValue).first; context.patternsToDataFlowInfo.put(pattern, newDataFlowInfo); context.patternsToBoundVariableLists.put(pattern, scopeToExtend.getDeclaredVariables()); } @@ -87,31 +88,37 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { Set expressionTypes = Sets.newHashSet(); DataFlowInfo commonDataFlowInfo = null; + DataFlowInfo elseDataFlowInfo = context.dataFlowInfo; for (JetWhenEntry whenEntry : expression.getEntries()) { JetWhenCondition[] conditions = whenEntry.getConditions(); DataFlowInfo newDataFlowInfo; WritableScope scopeToExtend; - if (conditions.length == 1) { + if (whenEntry.isElse()) { + scopeToExtend = newWritableScopeImpl(context, "Scope extended in when-else entry"); + newDataFlowInfo = elseDataFlowInfo; + } + else if (conditions.length == 1) { scopeToExtend = newWritableScopeImpl(context, "Scope extended in when entry"); newDataFlowInfo = context.dataFlowInfo; JetWhenCondition condition = conditions[0]; if (condition != null) { - newDataFlowInfo = checkWhenCondition(subjectExpression, subjectExpression == null, subjectType, condition, scopeToExtend, context, variableDescriptor); + Pair infos = checkWhenCondition(subjectExpression, subjectExpression == null, subjectType, condition, scopeToExtend, context, variableDescriptor); + newDataFlowInfo = infos.first; + elseDataFlowInfo = elseDataFlowInfo.and(infos.second); } } else { scopeToExtend = newWritableScopeImpl(context, "pattern matching"); // We don't write to this scope newDataFlowInfo = null; for (JetWhenCondition condition : conditions) { - DataFlowInfo dataFlowInfo = checkWhenCondition( - subjectExpression, subjectExpression == null, subjectType, condition, - newWritableScopeImpl(context, ""), context, variableDescriptor); + Pair infos = checkWhenCondition(subjectExpression, subjectExpression == null, subjectType, condition, newWritableScopeImpl(context, ""), context, variableDescriptor); if (newDataFlowInfo == null) { - newDataFlowInfo = dataFlowInfo; + newDataFlowInfo = infos.first; } else { - newDataFlowInfo = newDataFlowInfo.or(dataFlowInfo); + newDataFlowInfo = newDataFlowInfo.or(infos.first); } + elseDataFlowInfo = elseDataFlowInfo.and(infos.second); } if (newDataFlowInfo == null) { newDataFlowInfo = context.dataFlowInfo; @@ -145,8 +152,8 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { return JetTypeInfo.create(null, commonDataFlowInfo); } - private DataFlowInfo checkWhenCondition(@Nullable final JetExpression subjectExpression, final boolean expectedCondition, final JetType subjectType, JetWhenCondition condition, final WritableScope scopeToExtend, final ExpressionTypingContext context, final DataFlowValue... subjectVariables) { - final DataFlowInfo[] newDataFlowInfo = new DataFlowInfo[]{context.dataFlowInfo}; + private Pair checkWhenCondition(@Nullable final JetExpression subjectExpression, final boolean expectedCondition, final JetType subjectType, JetWhenCondition condition, final WritableScope scopeToExtend, final ExpressionTypingContext context, final DataFlowValue... subjectVariables) { + final Ref> newDataFlowInfo = new Ref>(Pair.create(context.dataFlowInfo, context.dataFlowInfo)); condition.accept(new JetVisitorVoid() { @Override @@ -170,7 +177,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { context.trace.report(EXPECTED_CONDITION.on(condition)); } if (pattern != null) { - newDataFlowInfo[0] = checkPatternType(pattern, subjectType, subjectExpression == null, scopeToExtend, context, subjectVariables); + newDataFlowInfo.set(checkPatternType(pattern, subjectType, subjectExpression == null, scopeToExtend, context, subjectVariables)); } } @@ -178,7 +185,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { public void visitWhenConditionWithExpression(JetWhenConditionWithExpression condition) { JetPattern pattern = condition.getPattern(); if (pattern != null) { - newDataFlowInfo[0] = checkPatternType(pattern, subjectType, subjectExpression == null, scopeToExtend, context, subjectVariables); + newDataFlowInfo.set(checkPatternType(pattern, subjectType, subjectExpression == null, scopeToExtend, context, subjectVariables)); } } @@ -187,13 +194,13 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { context.trace.report(UNSUPPORTED.on(element, getClass().getCanonicalName())); } }); - return newDataFlowInfo[0]; + return newDataFlowInfo.get(); } - private DataFlowInfo checkPatternType(@NotNull JetPattern pattern, @NotNull final JetType subjectType, final boolean conditionExpected, + private Pair checkPatternType(@NotNull JetPattern pattern, @NotNull final JetType subjectType, final boolean conditionExpected, @NotNull final WritableScope scopeToExtend, final ExpressionTypingContext context, @NotNull final DataFlowValue... subjectVariables ) { - final Ref result = new Ref(context.dataFlowInfo); + final Ref> result = new Ref>(Pair.create(context.dataFlowInfo, context.dataFlowInfo)); pattern.accept(new JetVisitorVoid() { @Override public void visitTypePattern(JetTypePattern typePattern) { @@ -201,7 +208,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { if (typeReference == null) return; JetType type = context.expressionTypingServices.getTypeResolver().resolveType(context.scope, typeReference, context.trace, true); checkTypeCompatibility(type, subjectType, typePattern); - result.set(context.dataFlowInfo.establishSubtyping(subjectVariables, type)); + result.set(Pair.create(context.dataFlowInfo.establishSubtyping(subjectVariables, type), context.dataFlowInfo)); } @Override @@ -226,7 +233,8 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { JetPattern entryPattern = entry.getPattern(); if (entryPattern != null) { - result.set(result.get().and(checkPatternType(entryPattern, type, false, scopeToExtend, context))); + Pair dataFlowInfos = checkPatternType(entryPattern, type, false, scopeToExtend, context); + result.set(Pair.create(result.get().first.and(dataFlowInfos.first), context.dataFlowInfo)); } } } @@ -265,12 +273,13 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { return; } checkTypeCompatibility(type, subjectType, pattern); - DataFlowInfo dataFlowInfo = context.dataFlowInfo; DataFlowValue expressionDataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(expression, type, context.trace.getBindingContext()); for (DataFlowValue subjectVariable : subjectVariables) { - dataFlowInfo = dataFlowInfo.equate(subjectVariable, expressionDataFlowValue); + result.set(Pair.create( + result.get().first.equate(subjectVariable, expressionDataFlowValue), + result.get().second.disequate(subjectVariable, expressionDataFlowValue) + )); } - result.set(dataFlowInfo); } @Override diff --git a/compiler/testData/diagnostics/tests/nullabilityAndAutoCasts/kt2146.jet b/compiler/testData/diagnostics/tests/nullabilityAndAutoCasts/kt2146.jet new file mode 100644 index 00000000000..e5e640267ec --- /dev/null +++ b/compiler/testData/diagnostics/tests/nullabilityAndAutoCasts/kt2146.jet @@ -0,0 +1,9 @@ +//KT-2146 Nullability casts in when. +package kt2146 + +fun f(s : Int?) : Int { + return when (s) { + null -> 3 + else -> s // type mismatch + } +} From 3f4e2514ea090043abe96430023d38e244f75c7b Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Fri, 22 Jun 2012 13:18:45 +0400 Subject: [PATCH 3/4] extract DataFlowInfo from when-condition + more tests for kt2146 --- .../PatternMatchingTypingVisitor.java | 8 ++++- .../tests/nullabilityAndAutoCasts/kt2146.jet | 35 +++++++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java index aff2b589b24..f637d1ddd55 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java @@ -263,13 +263,19 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { public void visitExpressionPattern(JetExpressionPattern pattern) { JetExpression expression = pattern.getExpression(); if (expression == null) return; - JetType type = facade.getTypeInfo(expression, context.replaceScope(scopeToExtend)).getType(); + JetTypeInfo typeInfo = facade.getTypeInfo(expression, context.replaceScope(scopeToExtend)); + JetType type = typeInfo.getType(); if (type == null) return; if (conditionExpected) { JetType booleanType = JetStandardLibrary.getInstance().getBooleanType(); if (!JetTypeChecker.INSTANCE.equalTypes(booleanType, type)) { context.trace.report(TYPE_MISMATCH_IN_CONDITION.on(pattern, type)); } + else { + DataFlowInfo ifInfo = DataFlowUtils.extractDataFlowInfoFromCondition(expression, true, scopeToExtend, context); + DataFlowInfo elseInfo = DataFlowUtils.extractDataFlowInfoFromCondition(expression, false, null, context); + result.set(Pair.create(ifInfo, elseInfo)); + } return; } checkTypeCompatibility(type, subjectType, pattern); diff --git a/compiler/testData/diagnostics/tests/nullabilityAndAutoCasts/kt2146.jet b/compiler/testData/diagnostics/tests/nullabilityAndAutoCasts/kt2146.jet index e5e640267ec..579f1095ba5 100644 --- a/compiler/testData/diagnostics/tests/nullabilityAndAutoCasts/kt2146.jet +++ b/compiler/testData/diagnostics/tests/nullabilityAndAutoCasts/kt2146.jet @@ -1,9 +1,40 @@ //KT-2146 Nullability casts in when. package kt2146 -fun f(s : Int?) : Int { +fun f1(s: Int?): Int { return when (s) { null -> 3 - else -> s // type mismatch + else -> s + } +} + +fun f2(s: Int?): Int { + return when (s) { + is 4 -> s + is null -> s + else -> s + } +} + +fun f3(s: Int?): Int { + return when (s) { + is Int -> s + else -> s + } +} + +fun f4(s: Int?): Int { + return when { + s == 4 -> s + s == null -> s + else -> s + } +} + +fun f5(s: Int?): Int { + return when (s) { + s!! -> s + s -> s + else -> 0 } } From 28b150892a59e34ce26108406514b562acc83a9c Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Fri, 22 Jun 2012 15:51:13 +0400 Subject: [PATCH 4/4] "!is" now works correctly in when-clauses --- .../PatternMatchingTypingVisitor.java | 8 +++++++- .../tests/nullabilityAndAutoCasts/kt2146.jet | 17 +++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java index f637d1ddd55..f595a08fdc5 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java @@ -177,7 +177,13 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { context.trace.report(EXPECTED_CONDITION.on(condition)); } if (pattern != null) { - newDataFlowInfo.set(checkPatternType(pattern, subjectType, subjectExpression == null, scopeToExtend, context, subjectVariables)); + Pair result = checkPatternType(pattern, subjectType, subjectExpression == null, scopeToExtend, context, subjectVariables); + if (condition.isNegated()) { + newDataFlowInfo.set(Pair.create(result.second, result.first)); + } + else { + newDataFlowInfo.set(result); + } } } diff --git a/compiler/testData/diagnostics/tests/nullabilityAndAutoCasts/kt2146.jet b/compiler/testData/diagnostics/tests/nullabilityAndAutoCasts/kt2146.jet index 579f1095ba5..acf19b90e48 100644 --- a/compiler/testData/diagnostics/tests/nullabilityAndAutoCasts/kt2146.jet +++ b/compiler/testData/diagnostics/tests/nullabilityAndAutoCasts/kt2146.jet @@ -10,8 +10,7 @@ fun f1(s: Int?): Int { fun f2(s: Int?): Int { return when (s) { - is 4 -> s - is null -> s + !is Int -> s else -> s } } @@ -38,3 +37,17 @@ fun f5(s: Int?): Int { else -> 0 } } + +fun f6(s: Int?): Int { + return when { + s is Int -> s + else -> s + } +} + +fun f7(s: Int?): Int { + return when { + s !is Int -> s + else -> s + } +}