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    }