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.resolve;
018
019 import com.google.common.collect.Lists;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.jet.lang.descriptors.*;
023 import org.jetbrains.jet.lang.diagnostics.rendering.Renderers;
024 import org.jetbrains.jet.lang.psi.*;
025 import org.jetbrains.jet.lang.resolve.calls.CallResolver;
026 import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
027 import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition;
028 import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem;
029 import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemCompleter;
030 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
031 import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
032 import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
033 import org.jetbrains.jet.lang.resolve.name.Name;
034 import org.jetbrains.jet.lang.resolve.scopes.JetScope;
035 import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
036 import org.jetbrains.jet.lang.types.DeferredType;
037 import org.jetbrains.jet.lang.types.JetType;
038 import org.jetbrains.jet.lang.types.TypeUtils;
039 import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
040 import org.jetbrains.jet.lang.types.expressions.ExpressionTypingContext;
041 import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices;
042 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
043 import org.jetbrains.jet.util.slicedmap.WritableSlice;
044
045 import javax.inject.Inject;
046 import java.util.List;
047
048 import static org.jetbrains.jet.lang.diagnostics.Errors.*;
049 import static org.jetbrains.jet.lang.psi.PsiPackage.JetPsiFactory;
050 import static org.jetbrains.jet.lang.resolve.BindingContext.*;
051 import static org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage.getCalleeExpressionIfAny;
052 import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
053 import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
054 import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.createFakeExpressionOfType;
055
056 public class DelegatedPropertyResolver {
057
058 @NotNull
059 private ExpressionTypingServices expressionTypingServices;
060
061 @NotNull
062 private CallResolver callResolver;
063
064 private static final String PD_METHOD_NAME = "propertyDelegated";
065
066 @Inject
067 public void setExpressionTypingServices(@NotNull ExpressionTypingServices expressionTypingServices) {
068 this.expressionTypingServices = expressionTypingServices;
069 }
070
071 @Inject
072 public void setCallResolver(@NotNull CallResolver callResolver) {
073 this.callResolver = callResolver;
074 }
075
076 @Nullable
077 public JetType getDelegatedPropertyGetMethodReturnType(
078 @NotNull PropertyDescriptor propertyDescriptor,
079 @NotNull JetExpression delegateExpression,
080 @NotNull JetType delegateType,
081 @NotNull BindingTrace trace,
082 @NotNull JetScope scope
083 ) {
084 resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, scope, true);
085 ResolvedCall<FunctionDescriptor> resolvedCall =
086 trace.getBindingContext().get(DELEGATED_PROPERTY_RESOLVED_CALL, propertyDescriptor.getGetter());
087 return resolvedCall != null ? resolvedCall.getResultingDescriptor().getReturnType() : null;
088 }
089
090 public void resolveDelegatedPropertyGetMethod(
091 @NotNull PropertyDescriptor propertyDescriptor,
092 @NotNull JetExpression delegateExpression,
093 @NotNull JetType delegateType,
094 @NotNull BindingTrace trace,
095 @NotNull JetScope scope
096 ) {
097 JetType returnType = getDelegatedPropertyGetMethodReturnType(
098 propertyDescriptor, delegateExpression, delegateType, trace, scope);
099 JetType propertyType = propertyDescriptor.getType();
100
101 /* Do not check return type of get() method of delegate for properties with DeferredType because property type is taken from it */
102 if (!(propertyType instanceof DeferredType) && returnType != null && !JetTypeChecker.DEFAULT.isSubtypeOf(returnType, propertyType)) {
103 Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, propertyDescriptor.getGetter());
104 assert call != null : "Call should exists for " + propertyDescriptor.getGetter();
105 trace.report(DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH
106 .on(delegateExpression, renderCall(call, trace.getBindingContext()), propertyDescriptor.getType(), returnType));
107 }
108 }
109
110 public void resolveDelegatedPropertySetMethod(
111 @NotNull PropertyDescriptor propertyDescriptor,
112 @NotNull JetExpression delegateExpression,
113 @NotNull JetType delegateType,
114 @NotNull BindingTrace trace,
115 @NotNull JetScope scope
116 ) {
117 resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, scope, false);
118 }
119
120 @NotNull
121 private static JetExpression createExpressionForPropertyMetadata(
122 @NotNull JetPsiFactory psiFactory,
123 @NotNull PropertyDescriptor propertyDescriptor
124 ) {
125 return psiFactory.createExpression(KotlinBuiltIns.getInstance().getPropertyMetadataImpl().getName().asString() +
126 "(\"" +
127 propertyDescriptor.getName().asString() +
128 "\")");
129 }
130
131 public void resolveDelegatedPropertyPDMethod(
132 @NotNull PropertyDescriptor propertyDescriptor,
133 @NotNull JetExpression delegateExpression,
134 @NotNull JetType delegateType,
135 @NotNull BindingTrace trace,
136 @NotNull JetScope scope
137 ) {
138 TemporaryBindingTrace traceToResolvePDMethod = TemporaryBindingTrace.create(trace, "Trace to resolve propertyDelegated method in delegated property");
139 ExpressionTypingContext context = ExpressionTypingContext.newContext(
140 expressionTypingServices, traceToResolvePDMethod, scope,
141 DataFlowInfo.EMPTY, TypeUtils.NO_EXPECTED_TYPE);
142
143 List<JetExpression> arguments = Lists.newArrayList();
144 JetPsiFactory psiFactory = JetPsiFactory(delegateExpression);
145 arguments.add(createExpressionForPropertyMetadata(psiFactory, propertyDescriptor));
146 Name functionName = Name.identifier(PD_METHOD_NAME);
147 JetReferenceExpression fakeCalleeExpression = psiFactory.createSimpleName(functionName.asString());
148 ExpressionReceiver receiver = new ExpressionReceiver(delegateExpression, delegateType);
149 Call call = CallMaker.makeCallWithExpressions(fakeCalleeExpression, receiver, null, fakeCalleeExpression, arguments, Call.CallType.DEFAULT);
150
151 OverloadResolutionResults<FunctionDescriptor> functionResults =
152 callResolver.resolveCallWithGivenName(context, call, fakeCalleeExpression, functionName);
153
154 if (!functionResults.isSuccess()) {
155 String expectedFunction = renderCall(call, traceToResolvePDMethod.getBindingContext());
156 if (functionResults.isIncomplete() || functionResults.isSingleResult() ||
157 functionResults.getResultCode() == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES) {
158 trace.report(DELEGATE_PD_METHOD_NONE_APPLICABLE.on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
159 } else if (functionResults.isAmbiguity()) {
160 trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY
161 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
162 }
163 return;
164 }
165
166 trace.record(DELEGATED_PROPERTY_PD_RESOLVED_CALL, propertyDescriptor, functionResults.getResultingCall());
167 }
168
169 /* Resolve get() or set() methods from delegate */
170 private void resolveDelegatedPropertyConventionMethod(
171 @NotNull PropertyDescriptor propertyDescriptor,
172 @NotNull JetExpression delegateExpression,
173 @NotNull JetType delegateType,
174 @NotNull BindingTrace trace,
175 @NotNull JetScope scope,
176 boolean isGet
177 ) {
178 PropertyAccessorDescriptor accessor = isGet ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
179 assert accessor != null : "Delegated property should have getter/setter " + propertyDescriptor + " " + delegateExpression.getText();
180
181 if (trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, accessor) != null) return;
182
183 OverloadResolutionResults<FunctionDescriptor> functionResults = getDelegatedPropertyConventionMethod(
184 propertyDescriptor, delegateExpression, delegateType, trace, scope, isGet);
185 Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, accessor);
186 assert call != null : "'getDelegatedPropertyConventionMethod' didn't record a call";
187
188 if (!functionResults.isSuccess()) {
189 String expectedFunction = renderCall(call, trace.getBindingContext());
190 if (functionResults.isIncomplete()) {
191 trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType));
192 }
193 else if (functionResults.isSingleResult() ||
194 functionResults.getResultCode() == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES) {
195 trace.report(DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE
196 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
197 }
198 else if (functionResults.isAmbiguity()) {
199 trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY
200 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
201 }
202 else {
203 trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType));
204 }
205 return;
206 }
207
208 trace.record(DELEGATED_PROPERTY_RESOLVED_CALL, accessor, functionResults.getResultingCall());
209 }
210
211 /* Resolve get() or set() methods from delegate */
212 public OverloadResolutionResults<FunctionDescriptor> getDelegatedPropertyConventionMethod(
213 @NotNull PropertyDescriptor propertyDescriptor,
214 @NotNull JetExpression delegateExpression,
215 @NotNull JetType delegateType,
216 @NotNull BindingTrace trace,
217 @NotNull JetScope scope,
218 boolean isGet
219 ) {
220 PropertyAccessorDescriptor accessor = isGet ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
221 assert accessor != null : "Delegated property should have getter/setter " + propertyDescriptor + " " + delegateExpression.getText();
222
223 ExpressionTypingContext context = ExpressionTypingContext.newContext(
224 expressionTypingServices, trace, scope,
225 DataFlowInfo.EMPTY, TypeUtils.NO_EXPECTED_TYPE);
226
227 boolean hasThis = propertyDescriptor.getReceiverParameter() != null || propertyDescriptor.getExpectedThisObject() != null;
228
229 List<JetExpression> arguments = Lists.newArrayList();
230 JetPsiFactory psiFactory = JetPsiFactory(delegateExpression);
231 arguments.add(psiFactory.createExpression(hasThis ? "this" : "null"));
232
233 arguments.add(createExpressionForPropertyMetadata(psiFactory, propertyDescriptor));
234
235 if (!isGet) {
236 JetReferenceExpression fakeArgument = (JetReferenceExpression) createFakeExpressionOfType(expressionTypingServices.getProject(), trace,
237 "fakeArgument" + arguments.size(),
238 propertyDescriptor.getType());
239 arguments.add(fakeArgument);
240 List<ValueParameterDescriptor> valueParameters = accessor.getValueParameters();
241 trace.record(REFERENCE_TARGET, fakeArgument, valueParameters.get(0));
242 }
243
244 Name functionName = Name.identifier(isGet ? "get" : "set");
245 JetReferenceExpression fakeCalleeExpression = psiFactory.createSimpleName(functionName.asString());
246
247 ExpressionReceiver receiver = new ExpressionReceiver(delegateExpression, delegateType);
248 Call call = CallMaker.makeCallWithExpressions(fakeCalleeExpression, receiver, null, fakeCalleeExpression, arguments, Call.CallType.DEFAULT);
249 trace.record(BindingContext.DELEGATED_PROPERTY_CALL, accessor, call);
250
251 return callResolver.resolveCallWithGivenName(context, call, fakeCalleeExpression, functionName);
252 }
253
254 private String renderCall(@NotNull Call call, @NotNull BindingContext context) {
255 JetExpression calleeExpression = call.getCalleeExpression();
256 assert calleeExpression != null : "CalleeExpression should exists for fake call of convention method";
257 StringBuilder builder = new StringBuilder(calleeExpression.getText());
258 builder.append("(");
259 List<JetType> argumentTypes = Lists.newArrayList();
260 for (ValueArgument argument : call.getValueArguments()) {
261 argumentTypes.add(context.get(EXPRESSION_TYPE, argument.getArgumentExpression()));
262
263 }
264 builder.append(Renderers.RENDER_COLLECTION_OF_TYPES.render(argumentTypes));
265 builder.append(")");
266 return builder.toString();
267 }
268
269 @Nullable
270 public JetType resolveDelegateExpression(
271 @NotNull JetExpression delegateExpression,
272 @NotNull JetProperty jetProperty,
273 @NotNull PropertyDescriptor propertyDescriptor,
274 @NotNull JetScope propertyDeclarationInnerScope,
275 @NotNull JetScope accessorScope,
276 @NotNull BindingTrace trace,
277 @NotNull DataFlowInfo dataFlowInfo
278 ) {
279 TemporaryBindingTrace traceToResolveDelegatedProperty = TemporaryBindingTrace.create(trace, "Trace to resolve delegated property");
280 JetExpression calleeExpression = getCalleeExpressionIfAny(delegateExpression);
281 ConstraintSystemCompleter completer = createConstraintSystemCompleter(
282 jetProperty, propertyDescriptor, delegateExpression, accessorScope, trace);
283 if (calleeExpression != null) {
284 traceToResolveDelegatedProperty.record(CONSTRAINT_SYSTEM_COMPLETER, calleeExpression, completer);
285 }
286 JetType delegateType = expressionTypingServices.safeGetType(propertyDeclarationInnerScope, delegateExpression, NO_EXPECTED_TYPE,
287 dataFlowInfo, traceToResolveDelegatedProperty);
288 traceToResolveDelegatedProperty.commit(new TraceEntryFilter() {
289 @Override
290 public boolean accept(@Nullable WritableSlice<?, ?> slice, Object key) {
291 return slice != CONSTRAINT_SYSTEM_COMPLETER;
292 }
293 }, true);
294 return delegateType;
295 }
296
297 @NotNull
298 private ConstraintSystemCompleter createConstraintSystemCompleter(
299 @NotNull JetProperty property,
300 @NotNull final PropertyDescriptor propertyDescriptor,
301 @NotNull final JetExpression delegateExpression,
302 @NotNull final JetScope accessorScope,
303 @NotNull final BindingTrace trace
304 ) {
305 final JetType expectedType = property.getTypeRef() != null ? propertyDescriptor.getType() : NO_EXPECTED_TYPE;
306 return new ConstraintSystemCompleter() {
307 @Override
308 public void completeConstraintSystem(
309 @NotNull ConstraintSystem constraintSystem, @NotNull ResolvedCall<?> resolvedCall
310 ) {
311 JetType returnType = resolvedCall.getCandidateDescriptor().getReturnType();
312 if (returnType == null) return;
313
314 TemporaryBindingTrace traceToResolveConventionMethods =
315 TemporaryBindingTrace.create(trace, "Trace to resolve delegated property convention methods");
316 OverloadResolutionResults<FunctionDescriptor>
317 getMethodResults = getDelegatedPropertyConventionMethod(
318 propertyDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, accessorScope, true);
319
320 if (conventionMethodFound(getMethodResults)) {
321 FunctionDescriptor descriptor = getMethodResults.getResultingDescriptor();
322 JetType returnTypeOfGetMethod = descriptor.getReturnType();
323 if (returnTypeOfGetMethod != null) {
324 constraintSystem.addSupertypeConstraint(expectedType, returnTypeOfGetMethod, ConstraintPosition.FROM_COMPLETER);
325 }
326 addConstraintForThisValue(constraintSystem, descriptor);
327 }
328 if (!propertyDescriptor.isVar()) return;
329
330 // For the case: 'val v by d' (no declared type).
331 // When we add a constraint for 'set' method for delegated expression 'd' we use a type of the declared variable 'v'.
332 // But if the type isn't known yet, the constraint shouldn't be added (we try to infer the type of 'v' here as well).
333 if (propertyDescriptor.getReturnType() instanceof DeferredType) return;
334
335 OverloadResolutionResults<FunctionDescriptor> setMethodResults =
336 getDelegatedPropertyConventionMethod(
337 propertyDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, accessorScope, false);
338
339 if (conventionMethodFound(setMethodResults)) {
340 FunctionDescriptor descriptor = setMethodResults.getResultingDescriptor();
341 List<ValueParameterDescriptor> valueParameters = descriptor.getValueParameters();
342 if (valueParameters.size() == 3) {
343 ValueParameterDescriptor valueParameterForThis = valueParameters.get(2);
344
345 if (!noExpectedType(expectedType)) {
346 constraintSystem.addSubtypeConstraint(
347 expectedType, valueParameterForThis.getType(), ConstraintPosition.FROM_COMPLETER);
348 }
349 addConstraintForThisValue(constraintSystem, descriptor);
350 }
351 }
352 }
353
354 private boolean conventionMethodFound(@NotNull OverloadResolutionResults<FunctionDescriptor> results) {
355 return results.isSuccess() ||
356 (results.isSingleResult() &&
357 results.getResultCode() == OverloadResolutionResults.Code.SINGLE_CANDIDATE_ARGUMENT_MISMATCH);
358 }
359
360 private void addConstraintForThisValue(ConstraintSystem constraintSystem, FunctionDescriptor resultingDescriptor) {
361 ReceiverParameterDescriptor receiverParameter = propertyDescriptor.getReceiverParameter();
362 ReceiverParameterDescriptor thisObject = propertyDescriptor.getExpectedThisObject();
363 JetType typeOfThis =
364 receiverParameter != null ? receiverParameter.getType() :
365 thisObject != null ? thisObject.getType() :
366 KotlinBuiltIns.getInstance().getNullableNothingType();
367
368 List<ValueParameterDescriptor> valueParameters = resultingDescriptor.getValueParameters();
369 if (valueParameters.isEmpty()) return;
370 ValueParameterDescriptor valueParameterForThis = valueParameters.get(0);
371
372 constraintSystem.addSubtypeConstraint(typeOfThis, valueParameterForThis.getType(), ConstraintPosition.FROM_COMPLETER);
373 }
374 };
375 }
376 }