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