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