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