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