001 /*
002 * Copyright 2010-2015 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.kotlin.types.expressions;
018
019 import com.google.common.collect.Sets;
020 import com.intellij.openapi.util.Ref;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.annotations.Nullable;
023 import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
024 import org.jetbrains.kotlin.cfg.WhenChecker;
025 import org.jetbrains.kotlin.diagnostics.Errors;
026 import org.jetbrains.kotlin.psi.*;
027 import org.jetbrains.kotlin.resolve.BindingContext;
028 import org.jetbrains.kotlin.resolve.PossiblyBareType;
029 import org.jetbrains.kotlin.resolve.TypeResolutionContext;
030 import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
031 import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue;
032 import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory;
033 import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
034 import org.jetbrains.kotlin.resolve.scopes.WritableScope;
035 import org.jetbrains.kotlin.types.*;
036 import org.jetbrains.kotlin.types.checker.JetTypeChecker;
037 import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryPackage;
038
039 import java.util.Collections;
040 import java.util.Set;
041
042 import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean;
043 import static org.jetbrains.kotlin.diagnostics.Errors.*;
044 import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT;
045 import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
046 import static org.jetbrains.kotlin.types.TypeUtils.isIntersectionEmpty;
047 import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.newWritableScopeImpl;
048
049 public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
050 protected PatternMatchingTypingVisitor(@NotNull ExpressionTypingInternals facade) {
051 super(facade);
052 }
053
054 @Override
055 public JetTypeInfo visitIsExpression(@NotNull JetIsExpression expression, ExpressionTypingContext contextWithExpectedType) {
056 ExpressionTypingContext context = contextWithExpectedType
057 .replaceExpectedType(NO_EXPECTED_TYPE)
058 .replaceContextDependency(INDEPENDENT);
059 JetExpression leftHandSide = expression.getLeftHandSide();
060 JetTypeInfo typeInfo = facade.safeGetTypeInfo(leftHandSide, context.replaceScope(context.scope));
061 JetType knownType = typeInfo.getType();
062 if (expression.getTypeReference() != null) {
063 DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(leftHandSide, knownType, context);
064 DataFlowInfo conditionInfo = checkTypeForIs(context, knownType, expression.getTypeReference(), dataFlowValue).thenInfo;
065 DataFlowInfo newDataFlowInfo = conditionInfo.and(typeInfo.getDataFlowInfo());
066 context.trace.record(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression, newDataFlowInfo);
067 }
068 return DataFlowUtils.checkType(typeInfo.replaceType(components.builtIns.getBooleanType()), expression, contextWithExpectedType);
069 }
070
071 @Override
072 public JetTypeInfo visitWhenExpression(@NotNull JetWhenExpression expression, ExpressionTypingContext context) {
073 return visitWhenExpression(expression, context, false);
074 }
075
076 public JetTypeInfo visitWhenExpression(JetWhenExpression expression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) {
077 DataFlowUtils.recordExpectedType(contextWithExpectedType.trace, expression, contextWithExpectedType.expectedType);
078
079 ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT);
080 // TODO :change scope according to the bound value in the when header
081 JetExpression subjectExpression = expression.getSubjectExpression();
082
083 JetType subjectType;
084 boolean loopBreakContinuePossible = false;
085 if (subjectExpression == null) {
086 subjectType = ErrorUtils.createErrorType("Unknown type");
087 }
088 else {
089 JetTypeInfo typeInfo = facade.safeGetTypeInfo(subjectExpression, context);
090 loopBreakContinuePossible = typeInfo.getJumpOutPossible();
091 subjectType = typeInfo.getType();
092 context = context.replaceDataFlowInfo(typeInfo.getDataFlowInfo());
093 }
094 DataFlowValue subjectDataFlowValue = subjectExpression != null
095 ? DataFlowValueFactory.createDataFlowValue(subjectExpression, subjectType, context)
096 : DataFlowValue.NULL;
097
098 // TODO : exhaustive patterns
099
100 Set<JetType> expressionTypes = Sets.newHashSet();
101 DataFlowInfo commonDataFlowInfo = null;
102 DataFlowInfo elseDataFlowInfo = context.dataFlowInfo;
103 for (JetWhenEntry whenEntry : expression.getEntries()) {
104 DataFlowInfos infosForCondition = getDataFlowInfosForEntryCondition(
105 whenEntry, context.replaceDataFlowInfo(elseDataFlowInfo), subjectExpression, subjectType, subjectDataFlowValue);
106 elseDataFlowInfo = elseDataFlowInfo.and(infosForCondition.elseInfo);
107
108 JetExpression bodyExpression = whenEntry.getExpression();
109 if (bodyExpression != null) {
110 WritableScope scopeToExtend = newWritableScopeImpl(context, "Scope extended in when entry");
111 ExpressionTypingContext newContext = contextWithExpectedType
112 .replaceScope(scopeToExtend).replaceDataFlowInfo(infosForCondition.thenInfo).replaceContextDependency(INDEPENDENT);
113 CoercionStrategy coercionStrategy = isStatement ? CoercionStrategy.COERCION_TO_UNIT : CoercionStrategy.NO_COERCION;
114 JetTypeInfo typeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
115 scopeToExtend, Collections.singletonList(bodyExpression), coercionStrategy, newContext);
116 loopBreakContinuePossible |= typeInfo.getJumpOutPossible();
117 JetType type = typeInfo.getType();
118 if (type != null) {
119 expressionTypes.add(type);
120 }
121 if (commonDataFlowInfo == null) {
122 commonDataFlowInfo = typeInfo.getDataFlowInfo();
123 }
124 else {
125 commonDataFlowInfo = commonDataFlowInfo.or(typeInfo.getDataFlowInfo());
126 }
127 }
128 }
129
130 if (commonDataFlowInfo == null) {
131 commonDataFlowInfo = context.dataFlowInfo;
132 }
133 else if (expression.getElseExpression() == null && !WhenChecker.isWhenExhaustive(expression, context.trace)) {
134 // Without else expression in non-exhaustive when, we *must* take initial data flow info into account,
135 // because data flow can bypass all when branches in this case
136 commonDataFlowInfo = commonDataFlowInfo.or(context.dataFlowInfo);
137 }
138
139 return TypeInfoFactoryPackage.createTypeInfo(expressionTypes.isEmpty() ? null : DataFlowUtils.checkType(
140 DataFlowUtils.checkImplicitCast(
141 CommonSupertypes.commonSupertype(expressionTypes), expression,
142 contextWithExpectedType, isStatement),
143 expression, contextWithExpectedType),
144 commonDataFlowInfo,
145 loopBreakContinuePossible,
146 contextWithExpectedType.dataFlowInfo);
147 }
148
149 @NotNull
150 private DataFlowInfos getDataFlowInfosForEntryCondition(
151 @NotNull JetWhenEntry whenEntry,
152 @NotNull ExpressionTypingContext context,
153 @Nullable JetExpression subjectExpression,
154 @NotNull JetType subjectType,
155 @NotNull DataFlowValue subjectDataFlowValue
156 ) {
157 if (whenEntry.isElse()) {
158 return new DataFlowInfos(context.dataFlowInfo);
159 }
160
161 DataFlowInfos infos = null;
162 ExpressionTypingContext contextForCondition = context;
163 for (JetWhenCondition condition : whenEntry.getConditions()) {
164 DataFlowInfos conditionInfos = checkWhenCondition(subjectExpression, subjectType, condition,
165 contextForCondition, subjectDataFlowValue);
166 if (infos != null) {
167 infos = new DataFlowInfos(infos.thenInfo.or(conditionInfos.thenInfo), infos.elseInfo.and(conditionInfos.elseInfo));
168 }
169 else {
170 infos = conditionInfos;
171 }
172 contextForCondition = contextForCondition.replaceDataFlowInfo(conditionInfos.elseInfo);
173 }
174 return infos != null ? infos : new DataFlowInfos(context.dataFlowInfo);
175 }
176
177 private DataFlowInfos checkWhenCondition(
178 @Nullable final JetExpression subjectExpression,
179 final JetType subjectType,
180 JetWhenCondition condition,
181 final ExpressionTypingContext context,
182 final DataFlowValue subjectDataFlowValue
183 ) {
184 final Ref<DataFlowInfos> newDataFlowInfo = new Ref<DataFlowInfos>(noChange(context));
185 condition.accept(new JetVisitorVoid() {
186 @Override
187 public void visitWhenConditionInRange(@NotNull JetWhenConditionInRange condition) {
188 JetExpression rangeExpression = condition.getRangeExpression();
189 if (rangeExpression == null) return;
190 if (subjectExpression == null) {
191 context.trace.report(EXPECTED_CONDITION.on(condition));
192 DataFlowInfo dataFlowInfo = facade.getTypeInfo(rangeExpression, context).getDataFlowInfo();
193 newDataFlowInfo.set(new DataFlowInfos(dataFlowInfo, dataFlowInfo));
194 return;
195 }
196 ValueArgument argumentForSubject = CallMaker.makeExternalValueArgument(subjectExpression);
197 JetTypeInfo typeInfo = facade.checkInExpression(condition, condition.getOperationReference(),
198 argumentForSubject, rangeExpression, context);
199 DataFlowInfo dataFlowInfo = typeInfo.getDataFlowInfo();
200 newDataFlowInfo.set(new DataFlowInfos(dataFlowInfo, dataFlowInfo));
201 JetType type = typeInfo.getType();
202 if (type == null || !isBoolean(type)) {
203 context.trace.report(TYPE_MISMATCH_IN_RANGE.on(condition));
204 }
205 }
206
207 @Override
208 public void visitWhenConditionIsPattern(@NotNull JetWhenConditionIsPattern condition) {
209 if (subjectExpression == null) {
210 context.trace.report(EXPECTED_CONDITION.on(condition));
211 }
212 if (condition.getTypeReference() != null) {
213 DataFlowInfos result = checkTypeForIs(context, subjectType, condition.getTypeReference(), subjectDataFlowValue);
214 if (condition.isNegated()) {
215 newDataFlowInfo.set(new DataFlowInfos(result.elseInfo, result.thenInfo));
216 }
217 else {
218 newDataFlowInfo.set(result);
219 }
220 }
221 }
222
223 @Override
224 public void visitWhenConditionWithExpression(@NotNull JetWhenConditionWithExpression condition) {
225 JetExpression expression = condition.getExpression();
226 if (expression != null) {
227 newDataFlowInfo.set(checkTypeForExpressionCondition(context, expression, subjectType, subjectExpression == null,
228 subjectDataFlowValue));
229 }
230 }
231
232 @Override
233 public void visitJetElement(@NotNull JetElement element) {
234 context.trace.report(UNSUPPORTED.on(element, getClass().getCanonicalName()));
235 }
236 });
237 return newDataFlowInfo.get();
238 }
239
240 private static class DataFlowInfos {
241 private final DataFlowInfo thenInfo;
242 private final DataFlowInfo elseInfo;
243
244 private DataFlowInfos(DataFlowInfo thenInfo, DataFlowInfo elseInfo) {
245 this.thenInfo = thenInfo;
246 this.elseInfo = elseInfo;
247 }
248
249 private DataFlowInfos(DataFlowInfo info) {
250 this(info, info);
251 }
252 }
253
254 private DataFlowInfos checkTypeForExpressionCondition(
255 ExpressionTypingContext context,
256 JetExpression expression,
257 JetType subjectType,
258 boolean conditionExpected,
259 DataFlowValue subjectDataFlowValue
260 ) {
261 if (expression == null) {
262 return noChange(context);
263 }
264 JetTypeInfo typeInfo = facade.getTypeInfo(expression, context);
265 JetType type = typeInfo.getType();
266 if (type == null) {
267 return noChange(context);
268 }
269 context = context.replaceDataFlowInfo(typeInfo.getDataFlowInfo());
270 if (conditionExpected) {
271 JetType booleanType = components.builtIns.getBooleanType();
272 if (!JetTypeChecker.DEFAULT.equalTypes(booleanType, type)) {
273 context.trace.report(TYPE_MISMATCH_IN_CONDITION.on(expression, type));
274 }
275 else {
276 DataFlowInfo ifInfo = DataFlowUtils.extractDataFlowInfoFromCondition(expression, true, context);
277 DataFlowInfo elseInfo = DataFlowUtils.extractDataFlowInfoFromCondition(expression, false, context);
278 return new DataFlowInfos(ifInfo, elseInfo);
279 }
280 return noChange(context);
281 }
282 checkTypeCompatibility(context, type, subjectType, expression);
283 DataFlowValue expressionDataFlowValue =
284 DataFlowValueFactory.createDataFlowValue(expression, type, context);
285 DataFlowInfos result = noChange(context);
286 result = new DataFlowInfos(
287 result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue),
288 result.elseInfo.disequate(subjectDataFlowValue, expressionDataFlowValue)
289 );
290 return result;
291 }
292
293 private DataFlowInfos checkTypeForIs(
294 ExpressionTypingContext context,
295 JetType subjectType,
296 JetTypeReference typeReferenceAfterIs,
297 DataFlowValue subjectDataFlowValue
298 ) {
299 if (typeReferenceAfterIs == null) {
300 return noChange(context);
301 }
302 TypeResolutionContext typeResolutionContext = new TypeResolutionContext(context.scope, context.trace, true, /*allowBareTypes=*/ true);
303 PossiblyBareType possiblyBareTarget = components.typeResolver.resolvePossiblyBareType(typeResolutionContext, typeReferenceAfterIs);
304 JetType targetType = TypeReconstructionUtil.reconstructBareType(typeReferenceAfterIs, possiblyBareTarget, subjectType, context.trace, components.builtIns);
305
306 if (TypesPackage.isDynamic(targetType)) {
307 context.trace.report(DYNAMIC_NOT_ALLOWED.on(typeReferenceAfterIs));
308 }
309
310 if (!subjectType.isMarkedNullable() && targetType.isMarkedNullable()) {
311 JetTypeElement element = typeReferenceAfterIs.getTypeElement();
312 assert element instanceof JetNullableType : "element must be instance of " + JetNullableType.class.getName();
313 JetNullableType nullableType = (JetNullableType) element;
314 context.trace.report(Errors.USELESS_NULLABLE_CHECK.on(nullableType));
315 }
316 checkTypeCompatibility(context, targetType, subjectType, typeReferenceAfterIs);
317 if (CastDiagnosticsUtil.isCastErased(subjectType, targetType, JetTypeChecker.DEFAULT)) {
318 context.trace.report(Errors.CANNOT_CHECK_FOR_ERASED.on(typeReferenceAfterIs, targetType));
319 }
320 return new DataFlowInfos(context.dataFlowInfo.establishSubtyping(subjectDataFlowValue, targetType), context.dataFlowInfo);
321 }
322
323 private static DataFlowInfos noChange(ExpressionTypingContext context) {
324 return new DataFlowInfos(context.dataFlowInfo, context.dataFlowInfo);
325 }
326
327 /*
328 * (a: SubjectType) is Type
329 */
330 private static void checkTypeCompatibility(
331 @NotNull ExpressionTypingContext context,
332 @Nullable JetType type,
333 @NotNull JetType subjectType,
334 @NotNull JetElement reportErrorOn
335 ) {
336 // TODO : Take smart casts into account?
337 if (type == null) {
338 return;
339 }
340 if (isIntersectionEmpty(type, subjectType)) {
341 context.trace.report(INCOMPATIBLE_TYPES.on(reportErrorOn, type, subjectType));
342 return;
343 }
344
345 // check if the pattern is essentially a 'null' expression
346 if (KotlinBuiltIns.isNullableNothing(type) && !TypeUtils.isNullableType(subjectType)) {
347 context.trace.report(SENSELESS_NULL_IN_WHEN.on(reportErrorOn));
348 }
349 }
350 }