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