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 @Inject
065 public void setExpressionTypingServices(@NotNull ExpressionTypingServices expressionTypingServices) {
066 this.expressionTypingServices = expressionTypingServices;
067 }
068
069 @Inject
070 public void setCallResolver(@NotNull CallResolver callResolver) {
071 this.callResolver = callResolver;
072 }
073
074 @Nullable
075 public JetType getDelegatedPropertyGetMethodReturnType(
076 @NotNull PropertyDescriptor propertyDescriptor,
077 @NotNull JetExpression delegateExpression,
078 @NotNull JetType delegateType,
079 @NotNull BindingTrace trace,
080 @NotNull JetScope scope
081 ) {
082 resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, scope, true);
083 ResolvedCall<FunctionDescriptor> resolvedCall =
084 trace.getBindingContext().get(DELEGATED_PROPERTY_RESOLVED_CALL, propertyDescriptor.getGetter());
085 return resolvedCall != null ? resolvedCall.getResultingDescriptor().getReturnType() : null;
086 }
087
088 public void resolveDelegatedPropertyGetMethod(
089 @NotNull PropertyDescriptor propertyDescriptor,
090 @NotNull JetExpression delegateExpression,
091 @NotNull JetType delegateType,
092 @NotNull BindingTrace trace,
093 @NotNull JetScope scope
094 ) {
095 JetType returnType = getDelegatedPropertyGetMethodReturnType(
096 propertyDescriptor, delegateExpression, delegateType, trace, scope);
097 JetType propertyType = propertyDescriptor.getType();
098
099 /* Do not check return type of get() method of delegate for properties with DeferredType because property type is taken from it */
100 if (!(propertyType instanceof DeferredType) && returnType != null && !JetTypeChecker.DEFAULT.isSubtypeOf(returnType, propertyType)) {
101 Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, propertyDescriptor.getGetter());
102 assert call != null : "Call should exists for " + propertyDescriptor.getGetter();
103 trace.report(DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH
104 .on(delegateExpression, renderCall(call, trace.getBindingContext()), propertyDescriptor.getType(), returnType));
105 }
106 }
107
108 public void resolveDelegatedPropertySetMethod(
109 @NotNull PropertyDescriptor propertyDescriptor,
110 @NotNull JetExpression delegateExpression,
111 @NotNull JetType delegateType,
112 @NotNull BindingTrace trace,
113 @NotNull JetScope scope
114 ) {
115 resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, scope, false);
116 }
117
118 /* Resolve get() or set() methods from delegate */
119 private void resolveDelegatedPropertyConventionMethod(
120 @NotNull PropertyDescriptor propertyDescriptor,
121 @NotNull JetExpression delegateExpression,
122 @NotNull JetType delegateType,
123 @NotNull BindingTrace trace,
124 @NotNull JetScope scope,
125 boolean isGet
126 ) {
127 PropertyAccessorDescriptor accessor = isGet ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
128 assert accessor != null : "Delegated property should have getter/setter " + propertyDescriptor + " " + delegateExpression.getText();
129
130 if (trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, accessor) != null) return;
131
132 OverloadResolutionResults<FunctionDescriptor> functionResults = getDelegatedPropertyConventionMethod(
133 propertyDescriptor, delegateExpression, delegateType, trace, scope, isGet);
134 Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, accessor);
135 assert call != null : "'getDelegatedPropertyConventionMethod' didn't record a call";
136
137 if (!functionResults.isSuccess()) {
138 String expectedFunction = renderCall(call, trace.getBindingContext());
139 if (functionResults.isIncomplete()) {
140 trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType));
141 }
142 else if (functionResults.isSingleResult() ||
143 functionResults.getResultCode() == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES) {
144 trace.report(DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE
145 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
146 }
147 else if (functionResults.isAmbiguity()) {
148 trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY
149 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
150 }
151 else {
152 trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType));
153 }
154 return;
155 }
156
157 trace.record(DELEGATED_PROPERTY_RESOLVED_CALL, accessor, functionResults.getResultingCall());
158 }
159
160 /* Resolve get() or set() methods from delegate */
161 public OverloadResolutionResults<FunctionDescriptor> getDelegatedPropertyConventionMethod(
162 @NotNull PropertyDescriptor propertyDescriptor,
163 @NotNull JetExpression delegateExpression,
164 @NotNull JetType delegateType,
165 @NotNull BindingTrace trace,
166 @NotNull JetScope scope,
167 boolean isGet
168 ) {
169 PropertyAccessorDescriptor accessor = isGet ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
170 assert accessor != null : "Delegated property should have getter/setter " + propertyDescriptor + " " + delegateExpression.getText();
171
172 ExpressionTypingContext context = ExpressionTypingContext.newContext(
173 expressionTypingServices, trace, scope,
174 DataFlowInfo.EMPTY, TypeUtils.NO_EXPECTED_TYPE);
175
176 boolean hasThis = propertyDescriptor.getReceiverParameter() != null || propertyDescriptor.getExpectedThisObject() != null;
177
178 List<JetExpression> arguments = Lists.newArrayList();
179 JetPsiFactory psiFactory = JetPsiFactory(delegateExpression);
180 arguments.add(psiFactory.createExpression(hasThis ? "this" : "null"));
181
182 arguments.add(psiFactory.createExpression(KotlinBuiltIns.getInstance().getPropertyMetadataImpl().getName().asString() +
183 "(\"" +
184 propertyDescriptor.getName().asString() +
185 "\")"));
186
187 if (!isGet) {
188 JetReferenceExpression fakeArgument = (JetReferenceExpression) createFakeExpressionOfType(expressionTypingServices.getProject(), trace,
189 "fakeArgument" + arguments.size(),
190 propertyDescriptor.getType());
191 arguments.add(fakeArgument);
192 List<ValueParameterDescriptor> valueParameters = accessor.getValueParameters();
193 trace.record(REFERENCE_TARGET, fakeArgument, valueParameters.get(0));
194 }
195
196 Name functionName = Name.identifier(isGet ? "get" : "set");
197 JetReferenceExpression fakeCalleeExpression = psiFactory.createSimpleName(functionName.asString());
198
199 ExpressionReceiver receiver = new ExpressionReceiver(delegateExpression, delegateType);
200 Call call = CallMaker.makeCallWithExpressions(fakeCalleeExpression, receiver, null, fakeCalleeExpression, arguments, Call.CallType.DEFAULT);
201 trace.record(BindingContext.DELEGATED_PROPERTY_CALL, accessor, call);
202
203 return callResolver.resolveCallWithGivenName(context, call, fakeCalleeExpression, functionName);
204 }
205
206 private String renderCall(@NotNull Call call, @NotNull BindingContext context) {
207 JetExpression calleeExpression = call.getCalleeExpression();
208 assert calleeExpression != null : "CalleeExpression should exists for fake call of convention method";
209 StringBuilder builder = new StringBuilder(calleeExpression.getText());
210 builder.append("(");
211 List<JetType> argumentTypes = Lists.newArrayList();
212 for (ValueArgument argument : call.getValueArguments()) {
213 argumentTypes.add(context.get(EXPRESSION_TYPE, argument.getArgumentExpression()));
214
215 }
216 builder.append(Renderers.RENDER_COLLECTION_OF_TYPES.render(argumentTypes));
217 builder.append(")");
218 return builder.toString();
219 }
220
221 @Nullable
222 public JetType resolveDelegateExpression(
223 @NotNull JetExpression delegateExpression,
224 @NotNull JetProperty jetProperty,
225 @NotNull PropertyDescriptor propertyDescriptor,
226 @NotNull JetScope propertyDeclarationInnerScope,
227 @NotNull JetScope accessorScope,
228 @NotNull BindingTrace trace,
229 @NotNull DataFlowInfo dataFlowInfo
230 ) {
231 TemporaryBindingTrace traceToResolveDelegatedProperty = TemporaryBindingTrace.create(trace, "Trace to resolve delegated property");
232 JetExpression calleeExpression = getCalleeExpressionIfAny(delegateExpression);
233 ConstraintSystemCompleter completer = createConstraintSystemCompleter(
234 jetProperty, propertyDescriptor, delegateExpression, accessorScope, trace);
235 if (calleeExpression != null) {
236 traceToResolveDelegatedProperty.record(CONSTRAINT_SYSTEM_COMPLETER, calleeExpression, completer);
237 }
238 JetType delegateType = expressionTypingServices.safeGetType(propertyDeclarationInnerScope, delegateExpression, NO_EXPECTED_TYPE,
239 dataFlowInfo, traceToResolveDelegatedProperty);
240 traceToResolveDelegatedProperty.commit(new TraceEntryFilter() {
241 @Override
242 public boolean accept(@Nullable WritableSlice<?, ?> slice, Object key) {
243 return slice != CONSTRAINT_SYSTEM_COMPLETER;
244 }
245 }, true);
246 return delegateType;
247 }
248
249 @NotNull
250 private ConstraintSystemCompleter createConstraintSystemCompleter(
251 @NotNull JetProperty property,
252 @NotNull final PropertyDescriptor propertyDescriptor,
253 @NotNull final JetExpression delegateExpression,
254 @NotNull final JetScope accessorScope,
255 @NotNull final BindingTrace trace
256 ) {
257 final JetType expectedType = property.getTypeRef() != null ? propertyDescriptor.getType() : NO_EXPECTED_TYPE;
258 return new ConstraintSystemCompleter() {
259 @Override
260 public void completeConstraintSystem(
261 @NotNull ConstraintSystem constraintSystem, @NotNull ResolvedCall<?> resolvedCall
262 ) {
263 JetType returnType = resolvedCall.getCandidateDescriptor().getReturnType();
264 if (returnType == null) return;
265
266 TemporaryBindingTrace traceToResolveConventionMethods =
267 TemporaryBindingTrace.create(trace, "Trace to resolve delegated property convention methods");
268 OverloadResolutionResults<FunctionDescriptor>
269 getMethodResults = getDelegatedPropertyConventionMethod(
270 propertyDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, accessorScope, true);
271
272 if (conventionMethodFound(getMethodResults)) {
273 FunctionDescriptor descriptor = getMethodResults.getResultingDescriptor();
274 JetType returnTypeOfGetMethod = descriptor.getReturnType();
275 if (returnTypeOfGetMethod != null) {
276 constraintSystem.addSupertypeConstraint(expectedType, returnTypeOfGetMethod, ConstraintPosition.FROM_COMPLETER);
277 }
278 addConstraintForThisValue(constraintSystem, descriptor);
279 }
280 if (!propertyDescriptor.isVar()) return;
281
282 // For the case: 'val v by d' (no declared type).
283 // When we add a constraint for 'set' method for delegated expression 'd' we use a type of the declared variable 'v'.
284 // 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).
285 if (propertyDescriptor.getReturnType() instanceof DeferredType) return;
286
287 OverloadResolutionResults<FunctionDescriptor> setMethodResults =
288 getDelegatedPropertyConventionMethod(
289 propertyDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, accessorScope, false);
290
291 if (conventionMethodFound(setMethodResults)) {
292 FunctionDescriptor descriptor = setMethodResults.getResultingDescriptor();
293 List<ValueParameterDescriptor> valueParameters = descriptor.getValueParameters();
294 if (valueParameters.size() == 3) {
295 ValueParameterDescriptor valueParameterForThis = valueParameters.get(2);
296
297 if (!noExpectedType(expectedType)) {
298 constraintSystem.addSubtypeConstraint(
299 expectedType, valueParameterForThis.getType(), ConstraintPosition.FROM_COMPLETER);
300 }
301 addConstraintForThisValue(constraintSystem, descriptor);
302 }
303 }
304 }
305
306 private boolean conventionMethodFound(@NotNull OverloadResolutionResults<FunctionDescriptor> results) {
307 return results.isSuccess() ||
308 (results.isSingleResult() &&
309 results.getResultCode() == OverloadResolutionResults.Code.SINGLE_CANDIDATE_ARGUMENT_MISMATCH);
310 }
311
312 private void addConstraintForThisValue(ConstraintSystem constraintSystem, FunctionDescriptor resultingDescriptor) {
313 ReceiverParameterDescriptor receiverParameter = propertyDescriptor.getReceiverParameter();
314 ReceiverParameterDescriptor thisObject = propertyDescriptor.getExpectedThisObject();
315 JetType typeOfThis =
316 receiverParameter != null ? receiverParameter.getType() :
317 thisObject != null ? thisObject.getType() :
318 KotlinBuiltIns.getInstance().getNullableNothingType();
319
320 List<ValueParameterDescriptor> valueParameters = resultingDescriptor.getValueParameters();
321 if (valueParameters.isEmpty()) return;
322 ValueParameterDescriptor valueParameterForThis = valueParameters.get(0);
323
324 constraintSystem.addSubtypeConstraint(typeOfThis, valueParameterForThis.getType(), ConstraintPosition.FROM_COMPLETER);
325 }
326 };
327 }
328 }