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