Skip to content

Commit 353c4a9

Browse files
committed
A solution for KT-6283 and KT-6284. #KT-6283 Fixed. #KT-6284 Fixed.
DataFlowInfo contains now jumpPossible to determine an opportunity of a jump out of a loop. A local descendant of JetTypeInfo added to save separately current data flow info and jump point data flow info.
1 parent 50d4642 commit 353c4a9

File tree

4 files changed

+121
-21
lines changed

4 files changed

+121
-21
lines changed

compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowInfo.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,19 @@
2626

2727
public interface DataFlowInfo {
2828
DataFlowInfo EMPTY = new DelegatingDataFlowInfo(null, ImmutableMap.<DataFlowValue, Nullability>of(),
29-
DelegatingDataFlowInfo.newTypeInfo());
29+
DelegatingDataFlowInfo.newTypeInfo(), false);
3030

3131
@NotNull
3232
Map<DataFlowValue, Nullability> getCompleteNullabilityInfo();
3333

3434
@NotNull
3535
SetMultimap<DataFlowValue, JetType> getCompleteTypeInfo();
3636

37+
/**
38+
* Returns true if jump out of a loop is possible up to this point
39+
*/
40+
boolean isJumpPossible();
41+
3742
@NotNull
3843
Nullability getNullability(@NotNull DataFlowValue key);
3944

@@ -54,4 +59,10 @@ public interface DataFlowInfo {
5459

5560
@NotNull
5661
DataFlowInfo or(@NotNull DataFlowInfo other);
62+
63+
/**
64+
* Returns a new data flow info with jump out of a loop possible
65+
*/
66+
@NotNull
67+
DataFlowInfo jump();
5768
}

compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DelegatingDataFlowInfo.java

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
/* package */ class DelegatingDataFlowInfo implements DataFlowInfo {
3232
private static final ImmutableMap<DataFlowValue,Nullability> EMPTY_NULLABILITY_INFO = ImmutableMap.of();
3333
private static final SetMultimap<DataFlowValue, JetType> EMPTY_TYPE_INFO = newTypeInfo();
34+
private static final DataFlowInfo EMPTY_INFO_WITH_JUMP = new DelegatingDataFlowInfo(null, EMPTY_NULLABILITY_INFO,
35+
EMPTY_TYPE_INFO, true);
3436

3537
@Nullable
3638
private final DataFlowInfo parent;
@@ -42,14 +44,18 @@
4244
@NotNull
4345
private final SetMultimap<DataFlowValue, JetType> typeInfo;
4446

47+
private final boolean jumpPossible;
48+
4549
/* package */ DelegatingDataFlowInfo(
4650
@Nullable DataFlowInfo parent,
4751
@NotNull ImmutableMap<DataFlowValue, Nullability> nullabilityInfo,
48-
@NotNull SetMultimap<DataFlowValue, JetType> typeInfo
52+
@NotNull SetMultimap<DataFlowValue, JetType> typeInfo,
53+
boolean jumpPossible
4954
) {
5055
this.parent = parent;
5156
this.nullabilityInfo = nullabilityInfo;
5257
this.typeInfo = typeInfo;
58+
this.jumpPossible = jumpPossible;
5359
}
5460

5561
@Override
@@ -84,6 +90,11 @@ public SetMultimap<DataFlowValue, JetType> getCompleteTypeInfo() {
8490
return result;
8591
}
8692

93+
@Override
94+
public boolean isJumpPossible() {
95+
return jumpPossible;
96+
}
97+
8798
@Override
8899
@NotNull
89100
public Nullability getNullability(@NotNull DataFlowValue key) {
@@ -142,7 +153,8 @@ public DataFlowInfo equate(@NotNull DataFlowValue a, @NotNull DataFlowValue b) {
142153
: new DelegatingDataFlowInfo(
143154
this,
144155
ImmutableMap.copyOf(builder),
145-
newTypeInfo.isEmpty() ? EMPTY_TYPE_INFO : newTypeInfo
156+
newTypeInfo.isEmpty() ? EMPTY_TYPE_INFO : newTypeInfo,
157+
jumpPossible
146158
);
147159
}
148160

@@ -176,7 +188,7 @@ public DataFlowInfo disequate(@NotNull DataFlowValue a, @NotNull DataFlowValue b
176188
boolean changed = false;
177189
changed |= putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB.invert()));
178190
changed |= putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA.invert()));
179-
return changed ? new DelegatingDataFlowInfo(this, ImmutableMap.copyOf(builder), EMPTY_TYPE_INFO) : this;
191+
return changed ? new DelegatingDataFlowInfo(this, ImmutableMap.copyOf(builder), EMPTY_TYPE_INFO, jumpPossible) : this;
180192
}
181193

182194
@Override
@@ -187,7 +199,7 @@ public DataFlowInfo establishSubtyping(@NotNull DataFlowValue value, @NotNull Je
187199
ImmutableMap<DataFlowValue, Nullability> newNullabilityInfo =
188200
type.isMarkedNullable() ? EMPTY_NULLABILITY_INFO : ImmutableMap.of(value, NOT_NULL);
189201
SetMultimap<DataFlowValue, JetType> newTypeInfo = ImmutableSetMultimap.of(value, type);
190-
return new DelegatingDataFlowInfo(this, newNullabilityInfo, newTypeInfo);
202+
return new DelegatingDataFlowInfo(this, newNullabilityInfo, newTypeInfo, jumpPossible);
191203
}
192204

193205
@NotNull
@@ -214,10 +226,11 @@ public DataFlowInfo and(@NotNull DataFlowInfo otherInfo) {
214226
SetMultimap<DataFlowValue, JetType> myTypeInfo = getCompleteTypeInfo();
215227
SetMultimap<DataFlowValue, JetType> otherTypeInfo = other.getCompleteTypeInfo();
216228
if (nullabilityMapBuilder.isEmpty() && containsAll(myTypeInfo, otherTypeInfo)) {
217-
return this;
229+
return otherInfo.isJumpPossible() ? jump(): this;
218230
}
219231

220-
return new DelegatingDataFlowInfo(this, ImmutableMap.copyOf(nullabilityMapBuilder), otherTypeInfo);
232+
return new DelegatingDataFlowInfo(this, ImmutableMap.copyOf(nullabilityMapBuilder), otherTypeInfo,
233+
jumpPossible || otherInfo.isJumpPossible());
221234
}
222235

223236
private static boolean containsAll(SetMultimap<DataFlowValue, JetType> first, SetMultimap<DataFlowValue, JetType> second) {
@@ -253,10 +266,17 @@ public DataFlowInfo or(@NotNull DataFlowInfo otherInfo) {
253266
}
254267

255268
if (nullabilityMapBuilder.isEmpty() && newTypeInfo.isEmpty()) {
256-
return EMPTY;
269+
return (jumpPossible || otherInfo.isJumpPossible()) ? EMPTY_INFO_WITH_JUMP : EMPTY;
257270
}
258271

259-
return new DelegatingDataFlowInfo(null, ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo);
272+
return new DelegatingDataFlowInfo(null, ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo, jumpPossible || otherInfo.isJumpPossible());
273+
}
274+
275+
@NotNull
276+
@Override
277+
public DataFlowInfo jump() {
278+
return isJumpPossible() ? this :
279+
(this == EMPTY ? EMPTY_INFO_WITH_JUMP : new DelegatingDataFlowInfo(parent, nullabilityInfo, typeInfo, true));
260280
}
261281

262282
@NotNull

compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.intellij.psi.util.PsiTreeUtil;
2323
import org.jetbrains.annotations.NotNull;
2424
import org.jetbrains.annotations.Nullable;
25+
import org.jetbrains.kotlin.JetNodeTypes;
2526
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
2627
import org.jetbrains.kotlin.descriptors.*;
2728
import org.jetbrains.kotlin.diagnostics.Errors;
@@ -139,10 +140,14 @@ public JetTypeInfo visitIfExpression(JetIfExpression ifExpression, ExpressionTyp
139140
resultDataFlowInfo = thenDataFlowInfo.or(elseDataFlowInfo);
140141
}
141142
else if (thenType == null || (jumpInThen && !jumpInElse)) {
142-
resultDataFlowInfo = elseDataFlowInfo;
143+
resultDataFlowInfo = elseDataFlowInfo.jump();
143144
}
144145
else if (elseType == null || (jumpInElse && !jumpInThen)) {
145-
resultDataFlowInfo = thenDataFlowInfo;
146+
resultDataFlowInfo = thenDataFlowInfo.jump();
147+
}
148+
// || jumpInElse: it is always false after jumpInThen check
149+
else if (jumpInThen) {
150+
resultDataFlowInfo = thenDataFlowInfo.or(elseDataFlowInfo).jump();
146151
}
147152
else {
148153
resultDataFlowInfo = thenDataFlowInfo.or(elseDataFlowInfo);
@@ -169,7 +174,7 @@ private JetTypeInfo getTypeInfoWhenOnlyOneBranchIsPresent(
169174
JetType type = typeInfo.getType();
170175
DataFlowInfo dataFlowInfo;
171176
if (type != null && KotlinBuiltIns.isNothing(type)) {
172-
dataFlowInfo = otherInfo;
177+
dataFlowInfo = otherInfo.jump();
173178
} else {
174179
dataFlowInfo = typeInfo.getDataFlowInfo().or(otherInfo);
175180
}
@@ -182,6 +187,11 @@ public JetTypeInfo visitWhileExpression(@NotNull JetWhileExpression expression,
182187
return visitWhileExpression(expression, context, false);
183188
}
184189

190+
private static boolean isTrueConstant(JetExpression condition) {
191+
return (condition != null && condition.getNode().getElementType() == JetNodeTypes.BOOLEAN_CONSTANT &&
192+
"true".equals(condition.getText()));
193+
}
194+
185195
public JetTypeInfo visitWhileExpression(JetWhileExpression expression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) {
186196
if (!isStatement) return DataFlowUtils.illegalStatementType(expression, contextWithExpectedType, facade);
187197

@@ -191,17 +201,27 @@ public JetTypeInfo visitWhileExpression(JetWhileExpression expression, Expressio
191201
DataFlowInfo dataFlowInfo = checkCondition(context.scope, condition, context);
192202

193203
JetExpression body = expression.getBody();
204+
// Special case: while (true)
205+
// In this case we must record data flow information at the nearest break / continue and
206+
// .and it with entrance data flow information, because while body until break is executed at least once in this case
207+
// See KT-6284
208+
ExpressionTypingServices.LoopJetTypeInfo bodyTypeInfo = null;
194209
if (body != null) {
195210
WritableScopeImpl scopeToExtend = newWritableScopeImpl(context, "Scope extended in while's condition");
196211
DataFlowInfo conditionInfo = DataFlowUtils.extractDataFlowInfoFromCondition(condition, true, context).and(dataFlowInfo);
197-
components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
212+
bodyTypeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
198213
scopeToExtend, Collections.singletonList(body),
199214
CoercionStrategy.NO_COERCION, context.replaceDataFlowInfo(conditionInfo));
200215
}
201216

202217
if (!containsJumpOutOfLoop(expression, context)) {
203218
dataFlowInfo = DataFlowUtils.extractDataFlowInfoFromCondition(condition, false, context).and(dataFlowInfo);
204219
}
220+
221+
// While (true) only should be considered here
222+
if (bodyTypeInfo != null && isTrueConstant(condition)) {
223+
dataFlowInfo = dataFlowInfo.and(bodyTypeInfo.getJumpFlowInfo());
224+
}
205225
return DataFlowUtils.checkType(components.builtIns.getUnitType(), expression, contextWithExpectedType, dataFlowInfo);
206226
}
207227

@@ -255,13 +275,17 @@ public JetTypeInfo visitDoWhileExpression(JetDoWhileExpression expression, Expre
255275
contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT);
256276
JetExpression body = expression.getBody();
257277
JetScope conditionScope = context.scope;
278+
// Here we must record data flow information at the end of the body (or at the first jump, to be precise) and
279+
// .and it with entrance data flow information, because do-while body is executed at least once
280+
// See KT-6283
281+
ExpressionTypingServices.LoopJetTypeInfo bodyTypeInfo = null;
258282
if (body instanceof JetFunctionLiteralExpression) {
259283
JetFunctionLiteralExpression function = (JetFunctionLiteralExpression) body;
260284
JetFunctionLiteral functionLiteral = function.getFunctionLiteral();
261285
if (!functionLiteral.hasParameterSpecification()) {
262286
WritableScope writableScope = newWritableScopeImpl(context, "do..while body scope");
263287
conditionScope = writableScope;
264-
components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
288+
bodyTypeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
265289
writableScope, functionLiteral.getBodyExpression().getStatements(), CoercionStrategy.NO_COERCION, context);
266290
context.trace.record(BindingContext.BLOCK, function);
267291
}
@@ -279,7 +303,7 @@ else if (body != null) {
279303
else {
280304
block = Collections.<JetElement>singletonList(body);
281305
}
282-
components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
306+
bodyTypeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
283307
writableScope, block, CoercionStrategy.NO_COERCION, context);
284308
}
285309
JetExpression condition = expression.getCondition();
@@ -291,6 +315,9 @@ else if (body != null) {
291315
else {
292316
dataFlowInfo = context.dataFlowInfo;
293317
}
318+
if (bodyTypeInfo != null) {
319+
dataFlowInfo = dataFlowInfo.and(bodyTypeInfo.getJumpFlowInfo());
320+
}
294321
return DataFlowUtils.checkType(components.builtIns.getUnitType(), expression, contextWithExpectedType, dataFlowInfo);
295322
}
296323

@@ -512,13 +539,13 @@ private static boolean isClassInitializer(@NotNull Pair<FunctionDescriptor, PsiE
512539
@Override
513540
public JetTypeInfo visitBreakExpression(@NotNull JetBreakExpression expression, ExpressionTypingContext context) {
514541
LabelResolver.INSTANCE.resolveControlLabel(expression, context);
515-
return DataFlowUtils.checkType(components.builtIns.getNothingType(), expression, context, context.dataFlowInfo);
542+
return DataFlowUtils.checkType(components.builtIns.getNothingType(), expression, context, context.dataFlowInfo.jump());
516543
}
517544

518545
@Override
519546
public JetTypeInfo visitContinueExpression(@NotNull JetContinueExpression expression, ExpressionTypingContext context) {
520547
LabelResolver.INSTANCE.resolveControlLabel(expression, context);
521-
return DataFlowUtils.checkType(components.builtIns.getNothingType(), expression, context, context.dataFlowInfo);
548+
return DataFlowUtils.checkType(components.builtIns.getNothingType(), expression, context, context.dataFlowInfo.jump());
522549
}
523550

524551
@NotNull

compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingServices.java

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -303,24 +303,55 @@ public JetType getBodyExpressionType(
303303
}
304304
}
305305

306+
/**
307+
* A local descendant of JetTypeInfo. Stores simultaneously current data flow info
308+
* and jump point data flow info. For example:
309+
* do {
310+
* x!!.foo()
311+
* if (bar()) break;
312+
* y!!.gav()
313+
* } while (bis())
314+
* At the end current data flow info is x != null && y != null, but jump data flow info is x != null only.
315+
*/
316+
/*package*/ class LoopJetTypeInfo extends JetTypeInfo {
317+
318+
private DataFlowInfo jumpFlowInfo;
319+
320+
private LoopJetTypeInfo(@Nullable JetType type, @NotNull DataFlowInfo dataFlowInfo) {
321+
this(type, dataFlowInfo, dataFlowInfo);
322+
}
323+
324+
private LoopJetTypeInfo(@Nullable JetType type, @NotNull DataFlowInfo dataFlowInfo, @NotNull DataFlowInfo jumpFlowInfo) {
325+
super(type, dataFlowInfo);
326+
this.jumpFlowInfo = jumpFlowInfo;
327+
}
328+
329+
DataFlowInfo getJumpFlowInfo() {
330+
return jumpFlowInfo;
331+
}
332+
}
333+
306334
/**
307335
* Visits block statements propagating data flow information from the first to the last.
308-
* Determines block returned type and data flow information at the end of the block.
336+
* Determines block returned type and data flow information at the end of the block AND
337+
* at the nearest jump point from the block beginning.
309338
*/
310-
/*package*/ JetTypeInfo getBlockReturnedTypeWithWritableScope(
339+
/*package*/ LoopJetTypeInfo getBlockReturnedTypeWithWritableScope(
311340
@NotNull WritableScope scope,
312341
@NotNull List<? extends JetElement> block,
313342
@NotNull CoercionStrategy coercionStrategyForLastExpression,
314343
@NotNull ExpressionTypingContext context
315344
) {
316345
if (block.isEmpty()) {
317-
return JetTypeInfo.create(builtIns.getUnitType(), context.dataFlowInfo);
346+
return new LoopJetTypeInfo(builtIns.getUnitType(), context.dataFlowInfo);
318347
}
319348

320349
ExpressionTypingInternals blockLevelVisitor = ExpressionTypingVisitorDispatcher.createForBlock(expressionTypingComponents, scope);
321350
ExpressionTypingContext newContext = context.replaceScope(scope).replaceExpectedType(NO_EXPECTED_TYPE);
322351

323352
JetTypeInfo result = JetTypeInfo.create(null, context.dataFlowInfo);
353+
// Jump point data flow info
354+
DataFlowInfo beforeJumpInfo = newContext.dataFlowInfo;
324355
for (Iterator<? extends JetElement> iterator = block.iterator(); iterator.hasNext(); ) {
325356
JetElement statement = iterator.next();
326357
if (!(statement instanceof JetExpression)) {
@@ -340,10 +371,21 @@ public JetType getBodyExpressionType(
340371
DataFlowInfo newDataFlowInfo = result.getDataFlowInfo();
341372
if (newDataFlowInfo != context.dataFlowInfo) {
342373
newContext = newContext.replaceDataFlowInfo(newDataFlowInfo);
374+
// We take current data flow info if jump there is not possible
375+
if (!newDataFlowInfo.isJumpPossible()) {
376+
beforeJumpInfo = newDataFlowInfo;
377+
}
378+
else if (result instanceof LoopJetTypeInfo) {
379+
LoopJetTypeInfo loopJetTypeInfo = (LoopJetTypeInfo)result;
380+
// We take jump point data flow info if jump (another) is not possible before
381+
if (!loopJetTypeInfo.getJumpFlowInfo().isJumpPossible()) {
382+
beforeJumpInfo = loopJetTypeInfo.getJumpFlowInfo();
383+
}
384+
}
343385
}
344386
blockLevelVisitor = ExpressionTypingVisitorDispatcher.createForBlock(expressionTypingComponents, scope);
345387
}
346-
return result;
388+
return new LoopJetTypeInfo(result.getType(), result.getDataFlowInfo(), beforeJumpInfo);
347389
}
348390

349391
private JetTypeInfo getTypeOfLastExpressionInBlock(

0 commit comments

Comments
 (0)