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.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
061 public 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 }