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.Lists; 020import com.intellij.openapi.project.Project; 021import com.intellij.openapi.util.Pair; 022import com.intellij.psi.PsiElement; 023import com.intellij.psi.tree.IElementType; 024import com.intellij.psi.tree.TokenSet; 025import com.intellij.psi.util.PsiTreeUtil; 026import org.jetbrains.annotations.NotNull; 027import org.jetbrains.annotations.Nullable; 028import org.jetbrains.jet.JetNodeTypes; 029import org.jetbrains.jet.lang.descriptors.*; 030import org.jetbrains.jet.lang.diagnostics.AbstractDiagnosticFactory; 031import org.jetbrains.jet.lang.diagnostics.Diagnostic; 032import org.jetbrains.jet.lang.diagnostics.Errors; 033import org.jetbrains.jet.lang.psi.*; 034import org.jetbrains.jet.lang.resolve.*; 035import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; 036import org.jetbrains.jet.lang.resolve.calls.context.ExpressionPosition; 037import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition; 038import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem; 039import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl; 040import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintsUtil; 041import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; 042import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults; 043import org.jetbrains.jet.lang.resolve.calls.util.CallMaker; 044import org.jetbrains.jet.lang.resolve.name.FqName; 045import org.jetbrains.jet.lang.resolve.name.Name; 046import org.jetbrains.jet.lang.resolve.scopes.JetScope; 047import org.jetbrains.jet.lang.resolve.scopes.WritableScope; 048import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl; 049import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; 050import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue; 051import org.jetbrains.jet.lang.types.*; 052import org.jetbrains.jet.lang.types.checker.JetTypeChecker; 053import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 054import org.jetbrains.jet.util.slicedmap.WritableSlice; 055 056import java.util.*; 057 058import static org.jetbrains.jet.lang.diagnostics.Errors.*; 059import static org.jetbrains.jet.lang.resolve.BindingContext.*; 060 061public class ExpressionTypingUtils { 062 063 private ExpressionTypingUtils() { 064 } 065 066 public static final JetType CANT_INFER_LAMBDA_PARAM_TYPE = ErrorUtils.createErrorType("Cannot be inferred"); 067 068 @Nullable 069 protected static ExpressionReceiver getExpressionReceiver(@NotNull JetExpression expression, @Nullable JetType type) { 070 if (type == null) return null; 071 return new ExpressionReceiver(expression, type); 072 } 073 074 @Nullable 075 protected static ExpressionReceiver getExpressionReceiver(@NotNull ExpressionTypingFacade facade, @NotNull JetExpression expression, ExpressionTypingContext context) { 076 return getExpressionReceiver(expression, facade.getTypeInfo(expression, context).getType()); 077 } 078 079 @NotNull 080 protected static ExpressionReceiver safeGetExpressionReceiver(@NotNull ExpressionTypingFacade facade, @NotNull JetExpression expression, ExpressionTypingContext context) { 081 return new ExpressionReceiver(expression, facade.safeGetTypeInfo(expression, context).getType()); 082 } 083 084 @NotNull 085 public static WritableScopeImpl newWritableScopeImpl(ExpressionTypingContext context, @NotNull String scopeDebugName) { 086 WritableScopeImpl scope = new WritableScopeImpl( 087 context.scope, context.scope.getContainingDeclaration(), new TraceBasedRedeclarationHandler(context.trace), scopeDebugName); 088 scope.changeLockLevel(WritableScope.LockLevel.BOTH); 089 return scope; 090 } 091 092 public static boolean isBoolean(@NotNull JetType type) { 093 return JetTypeChecker.INSTANCE.isSubtypeOf(type, KotlinBuiltIns.getInstance().getBooleanType()); 094 } 095 096 public static boolean ensureBooleanResult(JetExpression operationSign, Name name, JetType resultType, ExpressionTypingContext context) { 097 return ensureBooleanResultWithCustomSubject(operationSign, resultType, "'" + name + "'", context); 098 } 099 100 public static boolean ensureBooleanResultWithCustomSubject(JetExpression operationSign, JetType resultType, String subjectName, ExpressionTypingContext context) { 101 if (resultType != null) { 102 // TODO : Relax? 103 if (!isBoolean(resultType)) { 104 context.trace.report(RESULT_TYPE_MISMATCH.on(operationSign, subjectName, KotlinBuiltIns.getInstance().getBooleanType(), resultType)); 105 return false; 106 } 107 } 108 return true; 109 } 110 111 @NotNull 112 public static JetType getDefaultType(IElementType constantType) { 113 if (constantType == JetNodeTypes.INTEGER_CONSTANT) { 114 return KotlinBuiltIns.getInstance().getIntType(); 115 } 116 else if (constantType == JetNodeTypes.FLOAT_CONSTANT) { 117 return KotlinBuiltIns.getInstance().getDoubleType(); 118 } 119 else if (constantType == JetNodeTypes.BOOLEAN_CONSTANT) { 120 return KotlinBuiltIns.getInstance().getBooleanType(); 121 } 122 else if (constantType == JetNodeTypes.CHARACTER_CONSTANT) { 123 return KotlinBuiltIns.getInstance().getCharType(); 124 } 125 else if (constantType == JetNodeTypes.NULL) { 126 return KotlinBuiltIns.getInstance().getNullableNothingType(); 127 } 128 else { 129 throw new IllegalArgumentException("Unsupported constant type: " + constantType); 130 } 131 } 132 133 public static boolean isTypeFlexible(@Nullable JetExpression expression) { 134 if (expression == null) return false; 135 136 return TokenSet.create( 137 JetNodeTypes.INTEGER_CONSTANT, 138 JetNodeTypes.FLOAT_CONSTANT 139 ).contains(expression.getNode().getElementType()); 140 } 141 142 private static boolean isCapturedInInline( 143 @NotNull BindingContext context, 144 @NotNull DeclarationDescriptor scopeContainer, 145 @NotNull DeclarationDescriptor variableParent 146 ) { 147 PsiElement scopeDeclaration = BindingContextUtils.descriptorToDeclaration(context, scopeContainer); 148 if (!(scopeDeclaration instanceof JetFunctionLiteral)) { 149 return false; 150 } 151 152 PsiElement parent = scopeDeclaration.getParent(); 153 assert parent instanceof JetFunctionLiteralExpression : "parent of JetFunctionLiteral is " + parent; 154 JetCallExpression callExpression = getCallExpression((JetFunctionLiteralExpression) parent); 155 if (callExpression == null) { 156 return false; 157 } 158 159 ResolvedCall<? extends CallableDescriptor> call = context.get(BindingContext.RESOLVED_CALL, callExpression.getCalleeExpression()); 160 if (call == null) { 161 return false; 162 } 163 164 CallableDescriptor callable = call.getResultingDescriptor(); 165 if (callable instanceof SimpleFunctionDescriptor && ((SimpleFunctionDescriptor) callable).isInline()) { 166 DeclarationDescriptor scopeContainerParent = scopeContainer.getContainingDeclaration(); 167 assert scopeContainerParent != null : "parent is null for " + scopeContainer; 168 return scopeContainerParent == variableParent || isCapturedInInline(context, scopeContainerParent, variableParent); 169 } 170 else { 171 return false; 172 } 173 } 174 175 @Nullable 176 private static JetCallExpression getCallExpression(@NotNull JetFunctionLiteralExpression functionLiteralExpression) { 177 PsiElement parent = functionLiteralExpression.getParent(); 178 if (parent instanceof JetValueArgument) { 179 // foo({ ... }) or foo(f = { ... }) 180 181 PsiElement valueArgumentList = parent.getParent(); 182 assert valueArgumentList instanceof JetValueArgumentList : "parent of value argument is " + valueArgumentList; 183 184 if (valueArgumentList.getParent() instanceof JetCallExpression) { // may be argument list of annotation 185 return (JetCallExpression) valueArgumentList.getParent(); 186 } 187 } 188 else if (parent instanceof JetCallExpression) { 189 // foo { ... } 190 191 return (JetCallExpression) parent; 192 } 193 return null; 194 } 195 public static void checkCapturingInClosure(JetSimpleNameExpression expression, BindingTrace trace, JetScope scope) { 196 VariableDescriptor variable = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), expression, true); 197 if (variable != null) { 198 DeclarationDescriptor variableParent = variable.getContainingDeclaration(); 199 DeclarationDescriptor scopeContainer = scope.getContainingDeclaration(); 200 if (scopeContainer != variableParent && variableParent instanceof CallableDescriptor) { 201 if (trace.get(CAPTURED_IN_CLOSURE, variable) != CaptureKind.NOT_INLINE) { 202 boolean inline = isCapturedInInline(trace.getBindingContext(), scopeContainer, variableParent); 203 trace.record(CAPTURED_IN_CLOSURE, variable, inline ? CaptureKind.INLINE_ONLY : CaptureKind.NOT_INLINE); 204 } 205 } 206 } 207 } 208 209 public static boolean isVariableIterable(@NotNull ExpressionTypingServices expressionTypingServices, 210 @NotNull Project project, @NotNull VariableDescriptor variableDescriptor, @NotNull JetScope scope) { 211 JetExpression expression = JetPsiFactory.createExpression(project, "fake"); 212 ExpressionReceiver expressionReceiver = new ExpressionReceiver(expression, variableDescriptor.getType()); 213 ExpressionTypingContext context = ExpressionTypingContext.newContext( 214 expressionTypingServices, 215 new BindingTraceContext(), 216 scope, 217 DataFlowInfo.EMPTY, 218 TypeUtils.NO_EXPECTED_TYPE, 219 ExpressionPosition.FREE 220 ); 221 return ControlStructureTypingVisitor.checkIterableConvention(expressionReceiver, context) != null; 222 } 223 224 /** 225 * Check that function or property with the given qualified name can be resolved in given scope and called on given receiver 226 * 227 * @param callableFQN 228 * @param project 229 * @param scope 230 * @return 231 */ 232 public static List<CallableDescriptor> canFindSuitableCall( 233 @NotNull FqName callableFQN, 234 @NotNull Project project, 235 @NotNull JetExpression receiverExpression, 236 @NotNull JetType receiverType, 237 @NotNull JetScope scope, 238 @NotNull ModuleDescriptor module 239 ) { 240 JetImportDirective importDirective = JetPsiFactory.createImportDirective(project, callableFQN.asString()); 241 242 Collection<? extends DeclarationDescriptor> declarationDescriptors = new QualifiedExpressionResolver() 243 .analyseImportReference(importDirective, scope, new BindingTraceContext(), module); 244 245 List<CallableDescriptor> callableExtensionDescriptors = new ArrayList<CallableDescriptor>(); 246 ReceiverValue receiverValue = new ExpressionReceiver(receiverExpression, receiverType); 247 248 for (DeclarationDescriptor declarationDescriptor : declarationDescriptors) { 249 if (declarationDescriptor instanceof CallableDescriptor) { 250 CallableDescriptor callableDescriptor = (CallableDescriptor) declarationDescriptor; 251 252 if (checkIsExtensionCallable(receiverValue, callableDescriptor)) { 253 callableExtensionDescriptors.add(callableDescriptor); 254 } 255 } 256 } 257 258 return callableExtensionDescriptors; 259 } 260 261 /* 262 * Checks if receiver declaration could be resolved to call expected receiver. 263 */ 264 public static boolean checkIsExtensionCallable ( 265 @NotNull ReceiverValue receiverArgument, 266 @NotNull CallableDescriptor callableDescriptor 267 ) { 268 JetType type = receiverArgument.getType(); 269 270 if (type instanceof NamespaceType) { 271 // This fake class ruins standard algorithms 272 return false; 273 } 274 275 if (checkReceiverResolution(receiverArgument, type, callableDescriptor)) return true; 276 if (type.isNullable()) { 277 JetType notNullableType = TypeUtils.makeNotNullable(type); 278 if (checkReceiverResolution(receiverArgument, notNullableType, callableDescriptor)) return true; 279 } 280 return false; 281 } 282 283 private static boolean checkReceiverResolution ( 284 @NotNull ReceiverValue receiverArgument, 285 @NotNull JetType receiverType, 286 @NotNull CallableDescriptor callableDescriptor 287 ) { 288 ReceiverParameterDescriptor receiverParameter = callableDescriptor.getReceiverParameter(); 289 290 if (!receiverArgument.exists() && receiverParameter == null) { 291 // Both receivers do not exist 292 return true; 293 } 294 295 if (!(receiverArgument.exists() && receiverParameter != null)) { 296 return false; 297 } 298 299 Set<Name> typeNamesInReceiver = collectUsedTypeNames(receiverParameter.getType()); 300 301 ConstraintSystem constraintSystem = new ConstraintSystemImpl(); 302 for (TypeParameterDescriptor typeParameterDescriptor : callableDescriptor.getTypeParameters()) { 303 if (typeNamesInReceiver.contains(typeParameterDescriptor.getName())) { 304 constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); 305 } 306 } 307 308 constraintSystem.addSubtypeConstraint(receiverType, receiverParameter.getType(), ConstraintPosition.RECEIVER_POSITION); 309 return constraintSystem.isSuccessful() && ConstraintsUtil.checkBoundsAreSatisfied(constraintSystem, true); 310 } 311 312 private static Set<Name> collectUsedTypeNames(@NotNull JetType jetType) { 313 Set<Name> typeNames = new HashSet<Name>(); 314 315 ClassifierDescriptor descriptor = jetType.getConstructor().getDeclarationDescriptor(); 316 if (descriptor != null) { 317 typeNames.add(descriptor.getName()); 318 } 319 320 for (TypeProjection argument : jetType.getArguments()) { 321 typeNames.addAll(collectUsedTypeNames(argument.getType())); 322 } 323 324 return typeNames; 325 } 326 327 @NotNull 328 public static OverloadResolutionResults<FunctionDescriptor> resolveFakeCall( 329 @NotNull ExpressionTypingContext context, 330 @NotNull ReceiverValue receiver, 331 @NotNull Name name, 332 @NotNull JetType... argumentTypes 333 ) { 334 TemporaryBindingTrace traceWithFakeArgumentInfo = TemporaryBindingTrace.create(context.trace, "trace to store fake argument for", 335 name); 336 List<JetExpression> fakeArguments = Lists.newArrayList(); 337 for (JetType type : argumentTypes) { 338 fakeArguments.add(createFakeExpressionOfType(context.expressionTypingServices.getProject(), traceWithFakeArgumentInfo, 339 "fakeArgument" + fakeArguments.size(), type)); 340 } 341 return makeAndResolveFakeCall(receiver, context.replaceBindingTrace(traceWithFakeArgumentInfo), fakeArguments, name).getSecond(); 342 } 343 344 public static JetExpression createFakeExpressionOfType( 345 @NotNull Project project, 346 @NotNull BindingTrace trace, 347 @NotNull String argumentName, 348 @NotNull JetType argumentType 349 ) { 350 JetExpression fakeExpression = JetPsiFactory.createExpression(project, argumentName); 351 trace.record(EXPRESSION_TYPE, fakeExpression, argumentType); 352 trace.record(PROCESSED, fakeExpression); 353 return fakeExpression; 354 } 355 356 @NotNull 357 public static OverloadResolutionResults<FunctionDescriptor> resolveFakeCall( 358 @NotNull ExpressionTypingContext context, 359 @NotNull ReceiverValue receiver, 360 @NotNull Name name 361 ) { 362 return makeAndResolveFakeCall(receiver, context, Collections.<JetExpression>emptyList(), name).getSecond(); 363 } 364 365 @NotNull 366 public static Pair<Call, OverloadResolutionResults<FunctionDescriptor>> makeAndResolveFakeCall( 367 @NotNull ReceiverValue receiver, 368 @NotNull ExpressionTypingContext context, 369 @NotNull List<JetExpression> valueArguments, 370 @NotNull Name name 371 ) { 372 final JetReferenceExpression fake = JetPsiFactory.createSimpleName(context.expressionTypingServices.getProject(), "fake"); 373 TemporaryBindingTrace fakeTrace = TemporaryBindingTrace.create(context.trace, "trace to resolve fake call for", name); 374 Call call = CallMaker.makeCallWithExpressions(fake, receiver, null, fake, valueArguments); 375 OverloadResolutionResults<FunctionDescriptor> results = 376 context.replaceBindingTrace(fakeTrace).resolveCallWithGivenName(call, fake, name); 377 if (results.isSuccess()) { 378 fakeTrace.commit(new TraceEntryFilter() { 379 @Override 380 public boolean accept(@NotNull WritableSlice<?, ?> slice, Object key) { 381 // excluding all entries related to fake expression 382 return key != fake; 383 } 384 }, false); 385 } 386 return Pair.create(call, results); 387 } 388 389 public static void defineLocalVariablesFromMultiDeclaration( 390 @NotNull WritableScope writableScope, 391 @NotNull JetMultiDeclaration multiDeclaration, 392 @NotNull ReceiverValue receiver, 393 @NotNull JetExpression reportErrorsOn, 394 @NotNull ExpressionTypingContext context 395 ) { 396 int componentIndex = 1; 397 for (JetMultiDeclarationEntry entry : multiDeclaration.getEntries()) { 398 Name componentName = Name.identifier(DescriptorResolver.COMPONENT_FUNCTION_NAME_PREFIX + componentIndex); 399 componentIndex++; 400 401 JetType expectedType = getExpectedTypeForComponent(context, entry); 402 OverloadResolutionResults<FunctionDescriptor> results = 403 resolveFakeCall(context.replaceExpectedType(expectedType), receiver, componentName); 404 405 JetType componentType = null; 406 if (results.isSuccess()) { 407 context.trace.record(COMPONENT_RESOLVED_CALL, entry, results.getResultingCall()); 408 componentType = results.getResultingDescriptor().getReturnType(); 409 if (componentType != null && expectedType != TypeUtils.NO_EXPECTED_TYPE 410 && !JetTypeChecker.INSTANCE.isSubtypeOf(componentType, expectedType)) { 411 412 context.trace.report( 413 COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH.on(reportErrorsOn, componentName, componentType, expectedType)); 414 } 415 } 416 else if (results.isAmbiguity()) { 417 context.trace.report(COMPONENT_FUNCTION_AMBIGUITY.on(reportErrorsOn, componentName, results.getResultingCalls())); 418 } 419 else { 420 context.trace.report(COMPONENT_FUNCTION_MISSING.on(reportErrorsOn, componentName, receiver.getType())); 421 } 422 if (componentType == null) { 423 componentType = ErrorUtils.createErrorType(componentName + "() return type"); 424 } 425 VariableDescriptor variableDescriptor = context.expressionTypingServices.getDescriptorResolver(). 426 resolveLocalVariableDescriptorWithType(writableScope, entry, componentType, context.trace); 427 428 VariableDescriptor olderVariable = writableScope.getLocalVariable(variableDescriptor.getName()); 429 checkVariableShadowing(context, variableDescriptor, olderVariable); 430 431 writableScope.addVariableDescriptor(variableDescriptor); 432 } 433 } 434 435 public static void checkVariableShadowing(@NotNull ExpressionTypingContext context, @NotNull VariableDescriptor variableDescriptor, VariableDescriptor oldDescriptor) { 436 if (oldDescriptor != null && DescriptorUtils.isLocal(variableDescriptor.getContainingDeclaration(), oldDescriptor)) { 437 PsiElement declaration = BindingContextUtils.descriptorToDeclaration(context.trace.getBindingContext(), variableDescriptor); 438 if (declaration != null) { 439 context.trace.report(Errors.NAME_SHADOWING.on(declaration, variableDescriptor.getName().asString())); 440 } 441 } 442 } 443 444 @NotNull 445 private static JetType getExpectedTypeForComponent(ExpressionTypingContext context, JetMultiDeclarationEntry entry) { 446 JetTypeReference entryTypeRef = entry.getTypeRef(); 447 if (entryTypeRef != null) { 448 return context.expressionTypingServices.getTypeResolver().resolveType(context.scope, entryTypeRef, context.trace, true); 449 } 450 else { 451 return TypeUtils.NO_EXPECTED_TYPE; 452 } 453 } 454 455 public static ObservableBindingTrace makeTraceInterceptingTypeMismatch(@NotNull BindingTrace trace, @NotNull final JetElement expressionToWatch, @NotNull final boolean[] mismatchFound) { 456 return new ObservableBindingTrace(trace) { 457 458 @Override 459 public void report(@NotNull Diagnostic diagnostic) { 460 AbstractDiagnosticFactory factory = diagnostic.getFactory(); 461 if ((factory == TYPE_MISMATCH || factory == ERROR_COMPILE_TIME_VALUE) 462 && diagnostic.getPsiElement() == expressionToWatch) { 463 mismatchFound[0] = true; 464 } 465 if (TYPE_INFERENCE_ERRORS.contains(factory) && 466 PsiTreeUtil.isAncestor(expressionToWatch, diagnostic.getPsiElement(), false)) { 467 mismatchFound[0] = true; 468 } 469 super.report(diagnostic); 470 } 471 }; 472 } 473 474 @NotNull 475 public static JetTypeInfo getTypeInfoOrNullType( 476 @Nullable JetExpression expression, 477 @NotNull ExpressionTypingContext context, 478 @NotNull ExpressionTypingInternals facade 479 ) { 480 return expression != null 481 ? facade.getTypeInfo(expression, context) 482 : JetTypeInfo.create(null, context.dataFlowInfo); 483 } 484}