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