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
017package org.jetbrains.jet.lang.resolve;
018
019import com.intellij.psi.util.PsiTreeUtil;
020import org.jetbrains.annotations.NotNull;
021import org.jetbrains.annotations.Nullable;
022import org.jetbrains.jet.lang.descriptors.*;
023import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
024import org.jetbrains.jet.lang.psi.*;
025import org.jetbrains.jet.lang.resolve.scopes.JetScope;
026import org.jetbrains.jet.lang.resolve.scopes.LazyScopeAdapter;
027import org.jetbrains.jet.lang.types.*;
028import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
029import org.jetbrains.jet.util.lazy.RecursionIntolerantLazyValue;
030
031import javax.inject.Inject;
032import java.util.ArrayList;
033import java.util.Collection;
034import java.util.Collections;
035import java.util.List;
036
037import static org.jetbrains.jet.lang.diagnostics.Errors.*;
038import static org.jetbrains.jet.lang.types.Variance.*;
039
040public class TypeResolver {
041
042    private AnnotationResolver annotationResolver;
043    private DescriptorResolver descriptorResolver;
044    private QualifiedExpressionResolver qualifiedExpressionResolver;
045    private ModuleDescriptor moduleDescriptor;
046
047    @Inject
048    public void setDescriptorResolver(DescriptorResolver descriptorResolver) {
049        this.descriptorResolver = descriptorResolver;
050    }
051
052    @Inject
053    public void setAnnotationResolver(AnnotationResolver annotationResolver) {
054        this.annotationResolver = annotationResolver;
055    }
056
057    @Inject
058    public void setQualifiedExpressionResolver(QualifiedExpressionResolver qualifiedExpressionResolver) {
059        this.qualifiedExpressionResolver = qualifiedExpressionResolver;
060    }
061
062    @Inject
063    public void setModuleDescriptor(@NotNull ModuleDescriptor moduleDescriptor) {
064        this.moduleDescriptor = moduleDescriptor;
065    }
066
067    @NotNull
068    public JetType resolveType(@NotNull JetScope scope, @NotNull JetTypeReference typeReference, BindingTrace trace, boolean checkBounds) {
069        JetType cachedType = trace.getBindingContext().get(BindingContext.TYPE, typeReference);
070        if (cachedType != null) return cachedType;
071
072        List<AnnotationDescriptor> annotations = annotationResolver.getResolvedAnnotations(typeReference.getAnnotations(), trace);
073
074        JetTypeElement typeElement = typeReference.getTypeElement();
075        JetType type = resolveTypeElement(scope, annotations, typeElement, trace, checkBounds);
076        trace.record(BindingContext.TYPE, typeReference, type);
077        trace.record(BindingContext.TYPE_RESOLUTION_SCOPE, typeReference, scope);
078
079        return type;
080    }
081
082    @NotNull
083    private JetType resolveTypeElement(final JetScope scope, final List<AnnotationDescriptor> annotations,
084            JetTypeElement typeElement, final BindingTrace trace, final boolean checkBounds) {
085
086        final JetType[] result = new JetType[1];
087        if (typeElement != null) {
088            typeElement.accept(new JetVisitorVoid() {
089                @Override
090                public void visitUserType(JetUserType type) {
091                    JetSimpleNameExpression referenceExpression = type.getReferenceExpression();
092                    String referencedName = type.getReferencedName();
093                    if (referenceExpression == null || referencedName == null) {
094                        return;
095                    }
096
097                    ClassifierDescriptor classifierDescriptor = resolveClass(scope, type, trace);
098                    if (classifierDescriptor == null) {
099                        resolveTypeProjections(scope, ErrorUtils.createErrorType("No type").getConstructor(), type.getTypeArguments(), trace, checkBounds);
100                        return;
101                    }
102
103                    trace.record(BindingContext.REFERENCE_TARGET, referenceExpression, classifierDescriptor);
104
105                    if (classifierDescriptor instanceof TypeParameterDescriptor) {
106                        TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) classifierDescriptor;
107
108                        JetScope scopeForTypeParameter = getScopeForTypeParameter(typeParameterDescriptor, checkBounds);
109                        if (scopeForTypeParameter instanceof ErrorUtils.ErrorScope) {
110                            result[0] = ErrorUtils.createErrorType("?");
111                        }
112                        else {
113                            result[0] = new JetTypeImpl(
114                                    annotations,
115                                    typeParameterDescriptor.getTypeConstructor(),
116                                    TypeUtils.hasNullableLowerBound(typeParameterDescriptor),
117                                    Collections.<TypeProjection>emptyList(),
118                                    scopeForTypeParameter
119                            );
120                        }
121
122                        resolveTypeProjections(scope, ErrorUtils.createErrorType("No type").getConstructor(), type.getTypeArguments(), trace, checkBounds);
123
124                        DeclarationDescriptor containing = typeParameterDescriptor.getContainingDeclaration();
125                        if (containing instanceof ClassDescriptor) {
126                            // Type parameter can't be inherited from member of parent class, so we can skip subclass check
127                            DescriptorResolver.checkHasOuterClassInstance(scope, trace, referenceExpression, (ClassDescriptor) containing, false);
128                        }
129                    }
130                    else if (classifierDescriptor instanceof ClassDescriptor) {
131                        ClassDescriptor classDescriptor = (ClassDescriptor) classifierDescriptor;
132
133                        TypeConstructor typeConstructor = classifierDescriptor.getTypeConstructor();
134                        List<TypeProjection> arguments = resolveTypeProjections(scope, typeConstructor, type.getTypeArguments(), trace, checkBounds);
135                        List<TypeParameterDescriptor> parameters = typeConstructor.getParameters();
136                        int expectedArgumentCount = parameters.size();
137                        int actualArgumentCount = arguments.size();
138                        if (ErrorUtils.isError(typeConstructor)) {
139                            result[0] = ErrorUtils.createErrorType("[Error type: " + typeConstructor + "]");
140                        }
141                        else {
142                            if (actualArgumentCount != expectedArgumentCount) {
143                                if (actualArgumentCount == 0) {
144                                    if (rhsOfIsExpression(type) || rhsOfIsPattern(type)) {
145                                        trace.report(NO_TYPE_ARGUMENTS_ON_RHS_OF_IS_EXPRESSION.on(type, expectedArgumentCount, allStarProjectionsString(typeConstructor)));
146                                    }
147                                    else {
148                                        trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(type, expectedArgumentCount));
149                                    }
150                                }
151                                else {
152                                    trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(type.getTypeArgumentList(), expectedArgumentCount));
153                                }
154                            }
155                            else {
156                                result[0] = new JetTypeImpl(
157                                        annotations,
158                                        typeConstructor,
159                                        false,
160                                        arguments,
161                                        classDescriptor.getMemberScope(arguments)
162                                );
163                                if (checkBounds) {
164                                    TypeSubstitutor substitutor = TypeSubstitutor.create(result[0]);
165                                    for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
166                                        TypeParameterDescriptor parameter = parameters.get(i);
167                                        JetType argument = arguments.get(i).getType();
168                                        JetTypeReference typeReference = type.getTypeArguments().get(i).getTypeReference();
169
170                                        if (typeReference != null) {
171                                            descriptorResolver.checkBounds(typeReference, argument, parameter, substitutor, trace);
172                                        }
173                                    }
174                                }
175                            }
176                        }
177                    }
178                }
179
180                @Override
181                public void visitNullableType(JetNullableType nullableType) {
182                    JetType baseType = resolveTypeElement(scope, annotations, nullableType.getInnerType(), trace, checkBounds);
183                    if (baseType.isNullable()) {
184                        trace.report(REDUNDANT_NULLABLE.on(nullableType));
185                    }
186                    else if (TypeUtils.hasNullableSuperType(baseType)) {
187                        trace.report(BASE_WITH_NULLABLE_UPPER_BOUND.on(nullableType, baseType));
188                    }
189                    result[0] = TypeUtils.makeNullable(baseType);
190                }
191
192                @Override
193                public void visitFunctionType(JetFunctionType type) {
194                    JetTypeReference receiverTypeRef = type.getReceiverTypeRef();
195                    JetType receiverType = receiverTypeRef == null ? null : resolveType(scope, receiverTypeRef, trace, checkBounds);
196
197                    List<JetType> parameterTypes = new ArrayList<JetType>();
198                    for (JetParameter parameter : type.getParameters()) {
199                        parameterTypes.add(resolveType(scope, parameter.getTypeReference(), trace, checkBounds));
200                    }
201
202                    JetTypeReference returnTypeRef = type.getReturnTypeRef();
203                    JetType returnType;
204                    if (returnTypeRef != null) {
205                        returnType = resolveType(scope, returnTypeRef, trace, checkBounds);
206                    }
207                    else {
208                        returnType = KotlinBuiltIns.getInstance().getUnitType();
209                    }
210                    result[0] = KotlinBuiltIns.getInstance().getFunctionType(annotations, receiverType, parameterTypes, returnType);
211                }
212
213                @Override
214                public void visitJetElement(JetElement element) {
215                    trace.report(UNSUPPORTED.on(element, "Self-types are not supported yet"));
216//                    throw new IllegalArgumentException("Unsupported type: " + element);
217                }
218            });
219        }
220        if (result[0] == null) {
221            return ErrorUtils.createErrorType(typeElement == null ? "No type element" : typeElement.getText());
222        }
223        return result[0];
224    }
225
226    private static boolean rhsOfIsExpression(@NotNull JetUserType type) {
227        // Look for the FIRST expression containing this type
228        JetExpression outerExpression = PsiTreeUtil.getParentOfType(type, JetExpression.class);
229        if (outerExpression instanceof JetIsExpression) {
230            JetIsExpression isExpression = (JetIsExpression) outerExpression;
231            // If this expression is JetIsExpression, and the type is the outermost on the RHS
232            if (type.getParent() == isExpression.getTypeRef()) {
233                return true;
234            }
235        }
236        return false;
237    }
238
239    private static boolean rhsOfIsPattern(@NotNull JetUserType type) {
240        // Look for the is-pattern containing this type
241        JetWhenConditionIsPattern outerPattern = PsiTreeUtil.getParentOfType(type, JetWhenConditionIsPattern.class, false, JetExpression.class);
242        if (outerPattern == null) return false;
243        // We are interested only in the outermost type on the RHS
244        return type.getParent() == outerPattern.getTypeRef();
245    }
246
247    private JetScope getScopeForTypeParameter(final TypeParameterDescriptor typeParameterDescriptor, boolean checkBounds) {
248        if (checkBounds) {
249            return typeParameterDescriptor.getUpperBoundsAsType().getMemberScope();
250        }
251        else {
252            return new LazyScopeAdapter(new RecursionIntolerantLazyValue<JetScope>() {
253                @Override
254                protected JetScope compute() {
255                    return typeParameterDescriptor.getUpperBoundsAsType().getMemberScope();
256                }
257            });
258        }
259    }
260
261    private List<JetType> resolveTypes(JetScope scope, List<JetTypeReference> argumentElements, BindingTrace trace, boolean checkBounds) {
262        List<JetType> arguments = new ArrayList<JetType>();
263        for (JetTypeReference argumentElement : argumentElements) {
264            arguments.add(resolveType(scope, argumentElement, trace, checkBounds));
265        }
266        return arguments;
267    }
268
269    @NotNull
270    private List<TypeProjection> resolveTypeProjections(JetScope scope, TypeConstructor constructor, List<JetTypeProjection> argumentElements, BindingTrace trace, boolean checkBounds) {
271        List<TypeProjection> arguments = new ArrayList<TypeProjection>();
272        for (int i = 0, argumentElementsSize = argumentElements.size(); i < argumentElementsSize; i++) {
273            JetTypeProjection argumentElement = argumentElements.get(i);
274
275            JetProjectionKind projectionKind = argumentElement.getProjectionKind();
276            JetType type;
277            if (projectionKind == JetProjectionKind.STAR) {
278                List<TypeParameterDescriptor> parameters = constructor.getParameters();
279                if (parameters.size() > i) {
280                    TypeParameterDescriptor parameterDescriptor = parameters.get(i);
281                    arguments.add(SubstitutionUtils.makeStarProjection(parameterDescriptor));
282                }
283                else {
284                    arguments.add(new TypeProjection(OUT_VARIANCE, ErrorUtils.createErrorType("*")));
285                }
286            }
287            else {
288                // TODO : handle the Foo<in *> case
289                type = resolveType(scope, argumentElement.getTypeReference(), trace, checkBounds);
290                Variance kind = resolveProjectionKind(projectionKind);
291                if (constructor.getParameters().size() > i) {
292                    TypeParameterDescriptor parameterDescriptor = constructor.getParameters().get(i);
293                    if (kind != INVARIANT && parameterDescriptor.getVariance() != INVARIANT) {
294                        if (kind == parameterDescriptor.getVariance()) {
295                            trace.report(REDUNDANT_PROJECTION.on(argumentElement, constructor.getDeclarationDescriptor()));
296                        }
297                        else {
298                            trace.report(CONFLICTING_PROJECTION.on(argumentElement, constructor.getDeclarationDescriptor()));
299                        }
300                    }
301                }
302                arguments.add(new TypeProjection(kind, type));
303            }
304        }
305        return arguments;
306    }
307
308    @NotNull
309    public static Variance resolveProjectionKind(@NotNull JetProjectionKind projectionKind) {
310        Variance kind = null;
311        switch (projectionKind) {
312            case IN:
313                kind = IN_VARIANCE;
314                break;
315            case OUT:
316                kind = OUT_VARIANCE;
317                break;
318            case NONE:
319                kind = INVARIANT;
320                break;
321            default:
322                // NOTE: Star projections must be handled before this method is called
323                throw new IllegalStateException("Illegal projection kind:" + projectionKind);
324        }
325        return kind;
326    }
327
328    @Nullable
329    public ClassifierDescriptor resolveClass(JetScope scope, JetUserType userType, BindingTrace trace) {
330        Collection<? extends DeclarationDescriptor> descriptors = qualifiedExpressionResolver.lookupDescriptorsForUserType(userType, scope, trace);
331        for (DeclarationDescriptor descriptor : descriptors) {
332            if (descriptor instanceof ClassifierDescriptor) {
333                ImportsResolver.reportPlatformClassMappedToKotlin(moduleDescriptor, trace, userType, descriptor);
334                return (ClassifierDescriptor) descriptor;
335            }
336        }
337        return null;
338    }
339
340    @NotNull
341    private static String allStarProjectionsString(@NotNull TypeConstructor constructor) {
342        int size = constructor.getParameters().size();
343        assert size != 0 : "No projections possible for a nilary type constructor" + constructor;
344        ClassifierDescriptor declarationDescriptor = constructor.getDeclarationDescriptor();
345        assert declarationDescriptor != null : "No declaration descriptor for type constructor " + constructor;
346        String name = declarationDescriptor.getName().asString();
347
348        return TypeUtils.getTypeNameAndStarProjectionsString(name, size);
349    }
350}