diff --git a/idea/src/org/jetbrains/jet/lang/descriptors/ConstructorDescriptorImpl.java b/idea/src/org/jetbrains/jet/lang/descriptors/ConstructorDescriptorImpl.java index b786a45904a..fe669700b35 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/ConstructorDescriptorImpl.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/ConstructorDescriptorImpl.java @@ -1,6 +1,7 @@ package org.jetbrains.jet.lang.descriptors; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.types.JetType; import java.util.Collections; @@ -26,12 +27,13 @@ public class ConstructorDescriptorImpl extends FunctionDescriptorImpl implements @Override @Deprecated - public FunctionDescriptor initialize(@NotNull List typeParameters, @NotNull List unsubstitutedValueParameters, @NotNull JetType unsubstitutedReturnType) { - return super.initialize(typeParameters, unsubstitutedValueParameters, unsubstitutedReturnType); + public FunctionDescriptor initialize(@Nullable JetType receiverType, @NotNull List typeParameters, @NotNull List unsubstitutedValueParameters, @NotNull JetType unsubstitutedReturnType) { + assert receiverType == null; + return super.initialize(null, typeParameters, unsubstitutedValueParameters, unsubstitutedReturnType); } public ConstructorDescriptor initialize(@NotNull List unsubstitutedValueParameters) { - super.initialize(Collections.emptyList(), unsubstitutedValueParameters, getContainingDeclaration().getDefaultType()); + super.initialize(null, Collections.emptyList(), unsubstitutedValueParameters, getContainingDeclaration().getDefaultType()); return this; } diff --git a/idea/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptor.java b/idea/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptor.java index fec4fb5048c..60848f035e6 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptor.java @@ -16,8 +16,8 @@ public interface FunctionDescriptor extends DeclarationDescriptor { @NotNull DeclarationDescriptor getContainingDeclaration(); -// @Nullable -// JetType getReceiverType(); + @Nullable + JetType getReceiverType(); @NotNull List getTypeParameters(); diff --git a/idea/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorImpl.java b/idea/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorImpl.java index 5a0d13ddbe7..7f92fb56e88 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorImpl.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorImpl.java @@ -5,6 +5,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.TypeSubstitutor; +import org.jetbrains.jet.lang.types.Variance; import java.util.Collections; import java.util.List; @@ -18,9 +19,9 @@ public class FunctionDescriptorImpl extends DeclarationDescriptorImpl implements private List typeParameters; private List unsubstitutedValueParameters; private JetType unsubstitutedReturnType; + private JetType receiverType; private final Set overriddenFunctions = Sets.newHashSet(); - private final FunctionDescriptor original; public FunctionDescriptorImpl( @@ -40,15 +41,22 @@ public class FunctionDescriptorImpl extends DeclarationDescriptorImpl implements } public FunctionDescriptor initialize( + @Nullable JetType receiverType, @NotNull List typeParameters, @NotNull List unsubstitutedValueParameters, @Nullable JetType unsubstitutedReturnType) { + this.receiverType = receiverType; this.typeParameters = typeParameters; this.unsubstitutedValueParameters = unsubstitutedValueParameters; this.unsubstitutedReturnType = unsubstitutedReturnType; return this; } + @Override + public JetType getReceiverType() { + return receiverType; + } + @NotNull @Override public Set getOverriddenFunctions() { @@ -91,6 +99,15 @@ public class FunctionDescriptorImpl extends DeclarationDescriptorImpl implements FunctionDescriptorImpl substitutedDescriptor; substitutedDescriptor = createSubstitutedCopy(); + JetType receiverType = getReceiverType(); + JetType substitutedReceiverType = null; + if (receiverType != null) { + substitutedReceiverType = substitutor.substitute(receiverType, Variance.IN_VARIANCE); + if (substitutedReceiverType == null) { + return null; + } + } + List substitutedValueParameters = FunctionDescriptorUtil.getSubstitutedValueParameters(substitutedDescriptor, this, substitutor); if (substitutedValueParameters == null) { return null; @@ -102,6 +119,7 @@ public class FunctionDescriptorImpl extends DeclarationDescriptorImpl implements } substitutedDescriptor.initialize( + substitutedReceiverType, Collections.emptyList(), // TODO : questionable substitutedValueParameters, substitutedReturnType diff --git a/idea/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorUtil.java b/idea/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorUtil.java index ae0ee0e2bbb..17345fb365e 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorUtil.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorUtil.java @@ -1,14 +1,13 @@ package org.jetbrains.jet.lang.descriptors; +import com.google.common.base.Function; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.lang.resolve.BindingTrace; -import org.jetbrains.jet.lang.resolve.JetScope; -import org.jetbrains.jet.lang.resolve.WritableScope; -import org.jetbrains.jet.lang.resolve.WritableScopeImpl; +import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.types.*; import java.util.*; @@ -99,6 +98,10 @@ public class FunctionDescriptorUtil { @NotNull public static JetScope getFunctionInnerScope(@NotNull JetScope outerScope, @NotNull FunctionDescriptor descriptor, @NotNull BindingTrace trace) { WritableScope parameterScope = new WritableScopeImpl(outerScope, descriptor, trace.getErrorHandler()); + JetType receiverType = descriptor.getReceiverType(); + if (receiverType != null) { + parameterScope.setThisType(receiverType); + } for (TypeParameterDescriptor typeParameter : descriptor.getTypeParameters()) { parameterScope.addTypeParameterDescriptor(typeParameter); } @@ -229,4 +232,54 @@ public class FunctionDescriptorUtil { return OverrideCompatibilityInfo.success(); } + + @NotNull + public static FunctionGroup filteredFunctionGroup(@NotNull final FunctionGroup functionGroup, @NotNull final Function criterion) { + return new FunctionGroup() { + @NotNull + @Override + public String getName() { + return functionGroup.getName(); + } + + @NotNull + @Override + public OverloadResolutionResult getPossiblyApplicableFunctions(@NotNull List typeArguments, @NotNull List positionedValueArgumentTypes) { + OverloadResolutionResult possiblyApplicableFunctions = functionGroup.getPossiblyApplicableFunctions(typeArguments, positionedValueArgumentTypes); + List functionDescriptors = Lists.newArrayList(possiblyApplicableFunctions.getFunctionDescriptors()); + for (Iterator iterator = functionDescriptors.iterator(); iterator.hasNext(); ) { + FunctionDescriptor functionDescriptor = iterator.next(); + if (!criterion.apply(functionDescriptor)) { + iterator.remove(); + } + } + if (functionDescriptors.isEmpty()) { + return OverloadResolutionResult.nameNotFound(); + } + if (possiblyApplicableFunctions.isSuccess() || possiblyApplicableFunctions.isAmbiguity()) { + if (functionDescriptors.size() == 1) { + return OverloadResolutionResult.success(functionDescriptors.get(0)); + } + return OverloadResolutionResult.ambiguity(functionDescriptors); + } + if (functionDescriptors.size() == 1) { + return OverloadResolutionResult.singleFunctionArgumentMismatch(functionDescriptors.get(0)); + } + return OverloadResolutionResult.ambiguity(functionDescriptors); + } + + @Override + public boolean isEmpty() { + // TODO: not implemented + return false; + } + + @NotNull + @Override + public Set getFunctionDescriptors() { + // TODO: not implemented + return null; + } + }; + } } diff --git a/idea/src/org/jetbrains/jet/lang/descriptors/PropertyGetterDescriptor.java b/idea/src/org/jetbrains/jet/lang/descriptors/PropertyGetterDescriptor.java index faf9cf56240..d038394fd89 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/PropertyGetterDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/PropertyGetterDescriptor.java @@ -31,6 +31,11 @@ public class PropertyGetterDescriptor extends PropertyAccessorDescriptor { overriddenGetters.add(overriddenGetter); } + @Override + public JetType getReceiverType() { + return null; // TODO + } + @NotNull @Override public List getUnsubstitutedValueParameters() { diff --git a/idea/src/org/jetbrains/jet/lang/descriptors/PropertySetterDescriptor.java b/idea/src/org/jetbrains/jet/lang/descriptors/PropertySetterDescriptor.java index 19c4ca2f39d..46813568689 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/PropertySetterDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/PropertySetterDescriptor.java @@ -40,6 +40,11 @@ public class PropertySetterDescriptor extends PropertyAccessorDescriptor { overriddenSetters.add(overriddenSetter); } + @Override + public JetType getReceiverType() { + return null; // TODO + } + @NotNull @Override public List getUnsubstitutedValueParameters() { diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetFunction.java b/idea/src/org/jetbrains/jet/lang/psi/JetFunction.java index ad7dce04d5e..7e2c107a959 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetFunction.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetFunction.java @@ -24,6 +24,18 @@ public class JetFunction extends JetTypeParameterListOwner implements JetDeclara visitor.visitFunction(this); } + public boolean hasTypeParameterListBeforeFunctionName() { + JetTypeParameterList typeParameterList = getTypeParameterList(); + if (typeParameterList == null) { + return false; + } + PsiElement nameIdentifier = getNameIdentifier(); + if (nameIdentifier == null) { + return false; + } + return nameIdentifier.getTextOffset() > typeParameterList.getTextOffset(); + } + @Nullable @IfNotParsed public JetParameterList getValueParameterList() { return (JetParameterList) findChildByType(JetNodeTypes.VALUE_PARAMETER_LIST); diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java index db22ba28712..2aa84367b4a 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java @@ -174,8 +174,19 @@ public class ClassDescriptorResolver { WritableScope innerScope = new WritableScopeImpl(scope, functionDescriptor, trace.getErrorHandler()); innerScope.addLabeledDeclaration(functionDescriptor); - // The two calls below have side-effects on parameterScope + List typeParameterDescriptors = resolveTypeParameters(functionDescriptor, innerScope, function.getTypeParameters()); + + JetType receiverType = null; + JetTypeReference receiverTypeRef = function.getReceiverTypeRef(); + if (receiverTypeRef != null) { + JetScope scopeForReceiver = + function.hasTypeParameterListBeforeFunctionName() + ? innerScope + : scope; + receiverType = typeResolver.resolveType(scopeForReceiver, receiverTypeRef); + } + List valueParameterDescriptors = resolveValueParameters(functionDescriptor, innerScope, function.getValueParameters()); JetTypeReference returnTypeRef = function.getReturnTypeRef(); @@ -201,6 +212,7 @@ public class ClassDescriptorResolver { } functionDescriptor.initialize( + receiverType, typeParameterDescriptors, valueParameterDescriptors, returnType); diff --git a/idea/src/org/jetbrains/jet/lang/resolve/OverloadResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/OverloadResolver.java index 6ab62b07b86..a5eecae34a7 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/OverloadResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/OverloadResolver.java @@ -23,7 +23,7 @@ public class OverloadResolver { @NotNull public OverloadDomain getOverloadDomain(JetType receiverType, @NotNull JetScope outerScope, @NotNull String name) { // TODO : extension lookup - JetScope scope = receiverType == null ? outerScope : receiverType.getMemberScope(); + JetScope scope = receiverType == null ? outerScope : new ScopeWithReceiver(outerScope, receiverType, typeChecker); final FunctionGroup functionGroup = scope.getFunctionGroup(name); diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ScopeWithReceiver.java b/idea/src/org/jetbrains/jet/lang/resolve/ScopeWithReceiver.java index d309e442247..e3edd82e091 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/ScopeWithReceiver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/ScopeWithReceiver.java @@ -1,8 +1,11 @@ package org.jetbrains.jet.lang.resolve; +import com.google.common.base.Function; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; -import org.jetbrains.jet.lang.types.*; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.JetTypeChecker; /** * @author abreslav @@ -11,16 +14,33 @@ public class ScopeWithReceiver extends JetScopeImpl { private final JetScope receiverTypeScope; private final JetScope outerScope; + private final JetTypeChecker typeChecker; - public ScopeWithReceiver(JetScope outerScope, JetType receiverType) { + public ScopeWithReceiver(JetScope outerScope, JetType receiverType, JetTypeChecker typeChecker) { this.outerScope = outerScope; this.receiverTypeScope = receiverType.getMemberScope(); + this.typeChecker = typeChecker; } @NotNull @Override public FunctionGroup getFunctionGroup(@NotNull String name) { - return receiverTypeScope.getFunctionGroup(name); // TODO + FunctionGroup functionGroup = receiverTypeScope.getFunctionGroup(name); + if (functionGroup.isEmpty()) { + return FunctionDescriptorUtil.filteredFunctionGroup(outerScope.getFunctionGroup(name), + new Function() { + @Override + public Boolean apply(@Nullable FunctionDescriptor functionDescriptor) { + if (functionDescriptor == null) return false; + JetType functionReceiverType = functionDescriptor.getReceiverType(); + if (functionReceiverType == null) { + return false; + } + return typeChecker.isSubtypeOf(receiverTypeScope.getThisType(), functionReceiverType); + } + }); + } + return functionGroup; // TODO } @Override diff --git a/idea/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java index 8b305c0797e..80507102658 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java @@ -249,6 +249,7 @@ public class JavaDescriptorResolver { methodName ); functionDescriptor.initialize( + null, resolveTypeParameters(method.getTypeParameters()), semanticServices.getDescriptorResolver().resolveParameterDescriptors(functionDescriptor, parameters), semanticServices.getTypeTransformer().transformToType(method.getReturnType()) diff --git a/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java b/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java index 6a2c8f52b28..ce44f92e1fe 100644 --- a/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java +++ b/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java @@ -105,6 +105,7 @@ public class ErrorUtils { private static FunctionDescriptor createErrorFunction(List typeParameters, List positionedValueArgumentTypes) { FunctionDescriptorImpl functionDescriptor = new FunctionDescriptorImpl(ERROR_CLASS, Collections.emptyList(), ""); return functionDescriptor.initialize( + null, typeParameters, getValueParameters(functionDescriptor, positionedValueArgumentTypes), createErrorType("") @@ -117,6 +118,7 @@ public class ErrorUtils { private static FunctionDescriptor createErrorFunction(int typeParameterCount, List positionedValueParameterTypes) { return new FunctionDescriptorImpl(ERROR_CLASS, Collections.emptyList(), "").initialize( + null, Collections.emptyList(), // TODO Collections.emptyList(), // TODO createErrorType("") diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index 4f001065bc2..1e48186c714 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -1002,7 +1002,7 @@ public class JetTypeInferrer { public void visitWhenConditionCall(JetWhenConditionCall condition) { checkNullSafety(finalSubjectType, condition.getOperationTokenNode()); JetExpression callSuffixExpression = condition.getCallSuffixExpression(); - JetScope compositeScope = new ScopeWithReceiver(scope, finalSubjectType); + JetScope compositeScope = new ScopeWithReceiver(scope, finalSubjectType, semanticServices.getTypeChecker()); if (callSuffixExpression != null) { JetType selectorReturnType = getType(compositeScope, callSuffixExpression, false); ensureBooleanResultWithCustomSubject(callSuffixExpression, selectorReturnType, "This expression"); @@ -1290,11 +1290,12 @@ public class JetTypeInferrer { @Override public void visitQualifiedExpression(JetQualifiedExpression expression) { - // TODO : functions + // TODO : functions as values JetExpression selectorExpression = expression.getSelectorExpression(); JetExpression receiverExpression = expression.getReceiverExpression(); JetType receiverType = new TypeInferrerVisitorWithNamespaces(scope, false).getType(receiverExpression); if (receiverType != null) { + // TODO : extensions to 'Any?' checkNullSafety(receiverType, expression.getOperationTokenNode()); JetType selectorReturnType = getSelectorReturnType(receiverType, selectorExpression); if (expression.getOperationSign() == JetTokens.QUEST) { @@ -1314,7 +1315,7 @@ public class JetTypeInferrer { } private JetType getSelectorReturnType(JetType receiverType, JetExpression selectorExpression) { - JetScope compositeScope = new ScopeWithReceiver(scope, receiverType); + JetScope compositeScope = new ScopeWithReceiver(scope, receiverType, semanticServices.getTypeChecker()); if (selectorExpression instanceof JetCallExpression) { JetCallExpression callExpression = (JetCallExpression) selectorExpression; OverloadDomain overloadDomain = getOverloadDomain(compositeScope, callExpression.getCalleeExpression(), callExpression.getValueArgumentList()); diff --git a/idea/src/org/jetbrains/jet/resolve/DescriptorRenderer.java b/idea/src/org/jetbrains/jet/resolve/DescriptorRenderer.java index 32640a255d4..f0db290804a 100644 --- a/idea/src/org/jetbrains/jet/resolve/DescriptorRenderer.java +++ b/idea/src/org/jetbrains/jet/resolve/DescriptorRenderer.java @@ -46,6 +46,17 @@ public class DescriptorRenderer { protected void renderName(DeclarationDescriptor descriptor, StringBuilder stringBuilder) { stringBuilder.append(descriptor.getName()); } + + @Override + public Void visitTypeParameterDescriptor(TypeParameterDescriptor descriptor, StringBuilder builder) { + renderTypeParameter(descriptor, builder); + return null; + } + + @Override + public Void visitValueParameterDescriptor(ValueParameterDescriptor descriptor, StringBuilder builder) { + return super.visitVariableDescriptor(descriptor, builder); + } }; private DescriptorRenderer() {} @@ -106,9 +117,18 @@ public class DescriptorRenderer { @Override public Void visitFunctionDescriptor(FunctionDescriptor descriptor, StringBuilder builder) { builder.append(renderKeyword("fun")).append(" "); - renderName(descriptor, builder); List typeParameters = descriptor.getTypeParameters(); renderTypeParameters(typeParameters, builder); + if (!typeParameters.isEmpty()) { + builder.append(" "); + } + + JetType receiverType = descriptor.getReceiverType(); + if (receiverType != null) { + builder.append(escape(receiverType.toString())).append("."); + } + + renderName(descriptor, builder); builder.append("("); for (Iterator iterator = descriptor.getUnsubstitutedValueParameters().iterator(); iterator.hasNext(); ) { ValueParameterDescriptor parameterDescriptor = iterator.next(); @@ -178,7 +198,7 @@ public class DescriptorRenderer { stringBuilder.append(escape(descriptor.getName())); } - private void renderTypeParameter(TypeParameterDescriptor descriptor, StringBuilder builder) { + protected void renderTypeParameter(TypeParameterDescriptor descriptor, StringBuilder builder) { renderName(descriptor, builder); if (!descriptor.getUpperBounds().isEmpty()) { JetType bound = descriptor.getUpperBounds().iterator().next(); diff --git a/idea/testData/resolve/ExtensionFunctions.jet b/idea/testData/resolve/ExtensionFunctions.jet new file mode 100644 index 00000000000..e6e86872ad5 --- /dev/null +++ b/idea/testData/resolve/ExtensionFunctions.jet @@ -0,0 +1,21 @@ +fun <~T~T, ~E~E> `T`T.foo(x : `E`E, y : `A`A) : `T`T { + y.`+`plus(1) + y `+`plus 1 + y `+1`+ 1.0 + + this?.`-`minus(this) + + this +} + +~A~class A + +~+1~fun `A`A.plus(a : Any) { + 1 +} + +~+~fun `A`A.plus(a : Int) { + 1 +} + +~-~fun T.minus(t : T) : Int = 1 \ No newline at end of file