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