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