From 86e8142e3c488c5a26180b9abc0de0dccfe3d067 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Fri, 26 Aug 2011 17:51:15 +0400 Subject: [PATCH] Working on type inference for function literals --- idea/src/org/jetbrains/jet/JetNodeTypes.java | 2 +- .../ConstructorDescriptorImpl.java | 2 +- .../descriptors/FunctionDescriptorImpl.java | 17 +- .../descriptors/FunctionDescriptorUtil.java | 55 ++ .../jet/lang/descriptors/FunctionGroup.java | 12 - .../LazySubstitutingClassDescriptor.java | 33 +- .../LazySubstitutingFunctionGroup.java | 36 +- .../descriptors/TypeParameterDescriptor.java | 3 +- .../descriptors/WritableFunctionGroup.java | 53 +- .../psi/JetConstructorCalleeExpression.java | 5 +- .../jet/lang/resolve/ChainedScope.java | 13 +- .../lang/resolve/ClassDescriptorResolver.java | 2 +- .../lang/resolve/DelegatingBindingTrace.java | 79 ++ .../jet/lang/resolve/ScopeWithReceiver.java | 161 ++-- .../jet/lang/resolve/SubstitutingScope.java | 3 +- .../lang/resolve/TemporaryBindingTrace.java | 79 +- .../jet/lang/resolve/TopDownAnalyzer.java | 8 +- .../jet/lang/resolve/WritableScopeImpl.java | 53 +- .../resolve/WritableScopeWithImports.java | 17 +- .../jet/lang/resolve/WriteThroughScope.java | 12 +- .../lang/resolve/java/JavaPackageScope.java | 10 +- .../jetbrains/jet/lang/types/CallMaker.java | 7 +- .../jet/lang/types/CallResolver.java | 688 ++++++++++++++---- .../jet/lang/types/DescriptorSubstitutor.java | 59 ++ .../jetbrains/jet/lang/types/ErrorUtils.java | 8 - .../jet/lang/types/JetTypeInferrer.java | 637 +++------------- .../jet/lang/types/TypeSubstitutor.java | 36 +- .../types/inference/ConstraintSystem.java | 22 +- .../jet/resolve/DescriptorRenderer.java | 24 +- idea/testData/checker/ResolveToJava.jet | 4 +- .../AmbiguityOnLazyTypeComputation.jet | 12 + idea/testData/psi/FunctionLiterals_ERR.txt | 2 +- idea/testData/psi/Properties_ERR.txt | 2 +- idea/testData/psi/examples/With.txt | 4 +- .../jetbrains/jet/resolve/JetResolveTest.java | 8 +- .../jet/types/JetTypeCheckerTest.java | 13 +- 36 files changed, 1151 insertions(+), 1030 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/lang/resolve/DelegatingBindingTrace.java create mode 100644 idea/src/org/jetbrains/jet/lang/types/DescriptorSubstitutor.java create mode 100644 idea/testData/checker/regression/AmbiguityOnLazyTypeComputation.jet diff --git a/idea/src/org/jetbrains/jet/JetNodeTypes.java b/idea/src/org/jetbrains/jet/JetNodeTypes.java index 8df0d323733..5e3993fd528 100644 --- a/idea/src/org/jetbrains/jet/JetNodeTypes.java +++ b/idea/src/org/jetbrains/jet/JetNodeTypes.java @@ -29,7 +29,7 @@ public interface JetNodeTypes { JetNodeType DELEGATOR_BY = new JetNodeType("DELEGATOR_BY", JetDelegatorByExpressionSpecifier.class); JetNodeType DELEGATOR_SUPER_CALL = new JetNodeType("DELEGATOR_SUPER_CALL", JetDelegatorToSuperCall.class); JetNodeType DELEGATOR_SUPER_CLASS = new JetNodeType("DELEGATOR_SUPER_CLASS", JetDelegatorToSuperClass.class); - JetNodeType CONSTRUCTOR_CALLEE = new JetNodeType("CONSTRUCTOR_CALLEE", JetConstructorCalleeExpression.class); + JetNodeType CONSTRUCTOR_CALLEE = new JetNodeType("CONSTRUCTOR_CALLEE", JetConstructorCalleeExpression.class); JetNodeType VALUE_PARAMETER_LIST = new JetNodeType("VALUE_PARAMETER_LIST", JetParameterList.class); JetNodeType VALUE_PARAMETER = new JetNodeType("VALUE_PARAMETER", JetParameter.class); diff --git a/idea/src/org/jetbrains/jet/lang/descriptors/ConstructorDescriptorImpl.java b/idea/src/org/jetbrains/jet/lang/descriptors/ConstructorDescriptorImpl.java index f6642f22907..80c677a89a7 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/ConstructorDescriptorImpl.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/ConstructorDescriptorImpl.java @@ -63,7 +63,7 @@ public class ConstructorDescriptorImpl extends FunctionDescriptorImpl implements @NotNull @Override public Set getOverriddenFunctions() { - return null; + return Collections.emptySet(); } @Override diff --git a/idea/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorImpl.java b/idea/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorImpl.java index 16992f967e8..4dbe72f0f88 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorImpl.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorImpl.java @@ -1,14 +1,15 @@ package org.jetbrains.jet.lang.descriptors; +import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; +import org.jetbrains.jet.lang.types.DescriptorSubstitutor; 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; import java.util.Set; @@ -22,7 +23,7 @@ public class FunctionDescriptorImpl extends DeclarationDescriptorImpl implements private JetType unsubstitutedReturnType; private JetType receiverType; - private final Set overriddenFunctions = Sets.newHashSet(); + private final Set overriddenFunctions = Sets.newLinkedHashSet(); private final FunctionDescriptor original; public FunctionDescriptorImpl( @@ -97,12 +98,14 @@ public class FunctionDescriptorImpl extends DeclarationDescriptorImpl implements } @Override - public final FunctionDescriptor substitute(TypeSubstitutor substitutor) { - if (substitutor.isEmpty()) { + public final FunctionDescriptor substitute(TypeSubstitutor originalSubstitutor) { + if (originalSubstitutor.isEmpty()) { return this; } - FunctionDescriptorImpl substitutedDescriptor; - substitutedDescriptor = createSubstitutedCopy(); + FunctionDescriptorImpl substitutedDescriptor = createSubstitutedCopy(); + + List substitutedTypeParameters = Lists.newArrayList(); + TypeSubstitutor substitutor = DescriptorSubstitutor.substituteTypeParameters(getTypeParameters(), originalSubstitutor, substitutedDescriptor, substitutedTypeParameters); JetType receiverType = getReceiverType(); JetType substitutedReceiverType = null; @@ -125,7 +128,7 @@ public class FunctionDescriptorImpl extends DeclarationDescriptorImpl implements substitutedDescriptor.initialize( substitutedReceiverType, - Collections.emptyList(), // TODO : questionable + substitutedTypeParameters, 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 93cb30a5905..bfb0fc48ece 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorUtil.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorUtil.java @@ -13,6 +13,8 @@ import org.jetbrains.jet.lang.types.*; import java.util.*; +import static org.jetbrains.jet.lang.types.TypeSubstitutor.TypeSubstitution; + /** * @author abreslav */ @@ -110,6 +112,59 @@ public class FunctionDescriptorUtil { return parameterScope; } + @NotNull + public static FunctionDescriptor substituteBounds(@NotNull FunctionDescriptor functionDescriptor) { + final List typeParameters = functionDescriptor.getTypeParameters(); + if (typeParameters.isEmpty()) return functionDescriptor; + final Map typeConstructors = Maps.newHashMap(); + for (TypeParameterDescriptor typeParameter : typeParameters) { + typeConstructors.put(typeParameter.getTypeConstructor(), typeParameter); + } + return functionDescriptor.substitute(new TypeSubstitutor(TypeSubstitution.EMPTY) { + @Override + public boolean inRange(@NotNull TypeConstructor typeConstructor) { + return typeConstructors.containsKey(typeConstructor); + } + + @Override + public boolean isEmpty() { + return typeParameters.isEmpty(); + } + + @NotNull + @Override + public TypeSubstitution getSubstitution() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public JetType safeSubstitute(@NotNull JetType type, @NotNull Variance howThisTypeIsUsed) { + JetType substituted = substitute(type, howThisTypeIsUsed); + if (substituted == null) { + return ErrorUtils.createErrorType("Substitution failed"); + } + return substituted; + } + + @Override + public JetType substitute(@NotNull JetType type, @NotNull Variance howThisTypeIsUsed) { + TypeParameterDescriptor typeParameterDescriptor = typeConstructors.get(type.getConstructor()); + if (typeParameterDescriptor != null) { + switch (howThisTypeIsUsed) { + case INVARIANT: + return type; + case IN_VARIANCE: + throw new UnsupportedOperationException(); // TODO : lower bounds + case OUT_VARIANCE: + return typeParameterDescriptor.getDefaultType(); + } + } + return super.substitute(type, howThisTypeIsUsed); + } + }); + } + public static class OverrideCompatibilityInfo { private static final OverrideCompatibilityInfo SUCCESS = new OverrideCompatibilityInfo(false, "SUCCESS"); diff --git a/idea/src/org/jetbrains/jet/lang/descriptors/FunctionGroup.java b/idea/src/org/jetbrains/jet/lang/descriptors/FunctionGroup.java index 05a5ded0fd9..d767bce0cd9 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/FunctionGroup.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/FunctionGroup.java @@ -1,11 +1,8 @@ package org.jetbrains.jet.lang.descriptors; import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.resolve.OverloadResolutionResult; -import org.jetbrains.jet.lang.types.JetType; import java.util.Collections; -import java.util.List; import java.util.Set; /** @@ -19,12 +16,6 @@ public interface FunctionGroup extends Named { return ""; } - @NotNull - @Override - public OverloadResolutionResult getPossiblyApplicableFunctions(@NotNull List typeArguments, @NotNull List positionedValueArgumentTypes) { - return OverloadResolutionResult.nameNotFound(); - } - @Override public boolean isEmpty() { return true; @@ -42,9 +33,6 @@ public interface FunctionGroup extends Named { @Override String getName(); - @NotNull - OverloadResolutionResult getPossiblyApplicableFunctions(@NotNull List typeArguments, @NotNull List positionedValueArgumentTypes); - boolean isEmpty(); @NotNull diff --git a/idea/src/org/jetbrains/jet/lang/descriptors/LazySubstitutingClassDescriptor.java b/idea/src/org/jetbrains/jet/lang/descriptors/LazySubstitutingClassDescriptor.java index d775e23119d..0acb8cb5f98 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/LazySubstitutingClassDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/LazySubstitutingClassDescriptor.java @@ -16,27 +16,40 @@ import java.util.List; public class LazySubstitutingClassDescriptor implements ClassDescriptor { private final ClassDescriptor original; + private final TypeSubstitutor originalSubstitutor; + private TypeSubstitutor newSubstitutor; + private List typeParameters; private TypeConstructor typeConstructor; - private final TypeSubstitutor substitutor; public LazySubstitutingClassDescriptor(ClassDescriptor descriptor, TypeSubstitutor substitutor) { this.original = descriptor; - this.substitutor = substitutor; + this.originalSubstitutor = substitutor; } + private TypeSubstitutor getSubstitutor() { + if (newSubstitutor == null) { + if (originalSubstitutor.isEmpty()) { + newSubstitutor = originalSubstitutor; + } + else { + typeParameters = Lists.newArrayList(); + newSubstitutor = DescriptorSubstitutor.substituteTypeParameters(original.getTypeConstructor().getParameters(), originalSubstitutor, this, typeParameters); + } + } + return newSubstitutor; + } + @NotNull @Override public TypeConstructor getTypeConstructor() { TypeConstructor originalTypeConstructor = original.getTypeConstructor(); - if (substitutor.isEmpty()) { + if (originalSubstitutor.isEmpty()) { return originalTypeConstructor; } if (typeConstructor == null) { - List parameters = Lists.newArrayList(); - for (TypeParameterDescriptor parameterDescriptor : originalTypeConstructor.getParameters()) { - parameters.add(parameterDescriptor.substitute(substitutor)); - } + TypeSubstitutor substitutor = getSubstitutor(); + Collection supertypes = Lists.newArrayList(); for (JetType supertype : originalTypeConstructor.getSupertypes()) { supertypes.add(substitutor.substitute(supertype, Variance.INVARIANT)); @@ -47,7 +60,7 @@ public class LazySubstitutingClassDescriptor implements ClassDescriptor { originalTypeConstructor.getAnnotations(), originalTypeConstructor.isSealed(), originalTypeConstructor.toString(), - parameters, + typeParameters, supertypes ); } @@ -59,10 +72,10 @@ public class LazySubstitutingClassDescriptor implements ClassDescriptor { @Override public JetScope getMemberScope(List typeArguments) { JetScope memberScope = original.getMemberScope(typeArguments); - if (substitutor.isEmpty()) { + if (originalSubstitutor.isEmpty()) { return memberScope; } - return new SubstitutingScope(memberScope, substitutor); // TODO : compose substitutors + return new SubstitutingScope(memberScope, getSubstitutor()); } @NotNull diff --git a/idea/src/org/jetbrains/jet/lang/descriptors/LazySubstitutingFunctionGroup.java b/idea/src/org/jetbrains/jet/lang/descriptors/LazySubstitutingFunctionGroup.java index 8624cca2bfa..c10e718d60c 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/LazySubstitutingFunctionGroup.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/LazySubstitutingFunctionGroup.java @@ -2,14 +2,9 @@ package org.jetbrains.jet.lang.descriptors; import com.google.common.collect.Sets; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.lang.resolve.OverloadResolutionResult; -import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.TypeSubstitutor; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; +import java.util.Collections; import java.util.Set; /** @@ -31,30 +26,6 @@ public class LazySubstitutingFunctionGroup implements FunctionGroup { return functionGroup.getName(); } - @NotNull - @Override - public OverloadResolutionResult getPossiblyApplicableFunctions(@NotNull List typeArguments, @NotNull List positionedValueArgumentTypes) { - OverloadResolutionResult resolutionResult = functionGroup.getPossiblyApplicableFunctions(typeArguments, positionedValueArgumentTypes); - if (resolutionResult.isNothing()) return resolutionResult; - - Collection result = new ArrayList(); - for (FunctionDescriptor function : resolutionResult.getFunctionDescriptors()) { - FunctionDescriptor functionDescriptor = substitute(function); - if (functionDescriptor != null) { - result.add(functionDescriptor); - } - } - return resolutionResult.newContents(result); - } - - @Nullable - private FunctionDescriptor substitute( - @NotNull FunctionDescriptor functionDescriptor) { - if (substitutor.isEmpty()) return functionDescriptor; - - return functionDescriptor.substitute(substitutor); - } - @Override public boolean isEmpty() { return functionGroup.isEmpty(); @@ -68,13 +39,14 @@ public class LazySubstitutingFunctionGroup implements FunctionGroup { functionDescriptors = functionGroup.getFunctionDescriptors(); } else { - functionDescriptors = Sets.newHashSet(); + Set functionDescriptorSet = Sets.newLinkedHashSet(); for (FunctionDescriptor descriptor : functionGroup.getFunctionDescriptors()) { FunctionDescriptor substitute = descriptor.substitute(substitutor); if (substitute != null) { - functionDescriptors.add(substitute); + functionDescriptorSet.add(substitute); } } + functionDescriptors = Collections.unmodifiableSet(functionDescriptorSet); } } return functionDescriptors; diff --git a/idea/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java b/idea/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java index 92d0f3dc4d9..8c88bad746e 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java @@ -103,8 +103,9 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement @NotNull @Override + @Deprecated // Use the static method TypeParameterDescriptor.substitute() public TypeParameterDescriptor substitute(TypeSubstitutor substitutor) { - throw new UnsupportedOperationException(); // TODO + throw new UnsupportedOperationException(); } @Override diff --git a/idea/src/org/jetbrains/jet/lang/descriptors/WritableFunctionGroup.java b/idea/src/org/jetbrains/jet/lang/descriptors/WritableFunctionGroup.java index f47a5237285..1892b5984a1 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/WritableFunctionGroup.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/WritableFunctionGroup.java @@ -2,11 +2,8 @@ package org.jetbrains.jet.lang.descriptors; import com.google.common.collect.Sets; import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.resolve.OverloadResolutionResult; -import org.jetbrains.jet.lang.types.*; -import java.util.ArrayList; -import java.util.List; +import java.util.Collections; import java.util.Set; /** @@ -15,6 +12,7 @@ import java.util.Set; public class WritableFunctionGroup implements FunctionGroup { private final String name; private Set functionDescriptors; + private Set unmodifiableFunctionDescriptors; public WritableFunctionGroup(String name) { this.name = name; @@ -34,51 +32,32 @@ public class WritableFunctionGroup implements FunctionGroup { } public void addFunction(@NotNull FunctionDescriptor functionDescriptor) { - getFunctionDescriptors().add(functionDescriptor); + getWritableFunctionDescriptors().add(functionDescriptor); } + @Override @NotNull public Set getFunctionDescriptors() { + if (unmodifiableFunctionDescriptors == null) { + unmodifiableFunctionDescriptors = Collections.unmodifiableSet(getWritableFunctionDescriptors()); + } + return unmodifiableFunctionDescriptors; + } + + private Set getWritableFunctionDescriptors() { if (functionDescriptors == null) { functionDescriptors = Sets.newLinkedHashSet(); } return functionDescriptors; } - @NotNull - @Override - public OverloadResolutionResult getPossiblyApplicableFunctions(@NotNull List typeArguments, @NotNull List positionedValueArgumentTypes) { - Set functionDescriptors = getFunctionDescriptors(); - if (functionDescriptors.isEmpty()) return OverloadResolutionResult.nameNotFound(); - - int typeArgCount = typeArguments.size(); - int valueArgCount = positionedValueArgumentTypes.size(); - List result = new ArrayList(); - for (FunctionDescriptor functionDescriptor : functionDescriptors) { - // TODO : type argument inference breaks this logic - if (functionDescriptor.getTypeParameters().size() == typeArgCount) { - if (FunctionDescriptorUtil.getMinimumArity(functionDescriptor) <= valueArgCount && - valueArgCount <= FunctionDescriptorUtil.getMaximumArity(functionDescriptor)) { - FunctionDescriptor substitutedFunctionDescriptor = FunctionDescriptorUtil.substituteFunctionDescriptor(typeArguments, functionDescriptor); - assert substitutedFunctionDescriptor != null; - result.add(substitutedFunctionDescriptor); - } - } - } - if (result.isEmpty()) { - assert !functionDescriptors.isEmpty(); - if (functionDescriptors.size() == 1) { - return OverloadResolutionResult.singleFunctionArgumentMismatch(functionDescriptors.iterator().next()); - } - } - if (result.size() == 1) { - return OverloadResolutionResult.success(result.get(0)); - } - return OverloadResolutionResult.ambiguity(result); - } - @Override public boolean isEmpty() { return functionDescriptors == null || functionDescriptors.isEmpty(); } + + public void addAllFunctions(@NotNull FunctionGroup functionGroup) { + if (functionGroup.isEmpty()) return; + getWritableFunctionDescriptors().addAll(functionGroup.getFunctionDescriptors()); + } } diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetConstructorCalleeExpression.java b/idea/src/org/jetbrains/jet/lang/psi/JetConstructorCalleeExpression.java index fcd388862ea..e765de01f83 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetConstructorCalleeExpression.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetConstructorCalleeExpression.java @@ -24,7 +24,10 @@ public class JetConstructorCalleeExpression extends JetExpression { return null; } JetTypeElement typeElement = typeReference.getTypeElement(); - assert typeElement instanceof JetUserType; + if (typeElement == null) { + return null; + } + assert typeElement instanceof JetUserType : typeElement; return ((JetUserType) typeElement).getReferenceExpression(); } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ChainedScope.java b/idea/src/org/jetbrains/jet/lang/resolve/ChainedScope.java index 11d4755a41e..d1edc538c0d 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/ChainedScope.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/ChainedScope.java @@ -55,11 +55,16 @@ public class ChainedScope implements JetScope { @NotNull @Override public FunctionGroup getFunctionGroup(@NotNull String name) { - for (JetScope jetScope : scopeChain) { - FunctionGroup functionGroup = jetScope.getFunctionGroup(name); - if (!functionGroup.isEmpty()) return functionGroup; + if (scopeChain.length == 0) { + return FunctionGroup.EMPTY; } - return FunctionGroup.EMPTY; + + WritableFunctionGroup result = new WritableFunctionGroup(name); + for (JetScope jetScope : scopeChain) { + result.addAllFunctions(jetScope.getFunctionGroup(name)); + } + + return result; } @NotNull diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java index 7a94482da7f..b0fb1a535d1 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java @@ -67,7 +67,7 @@ public class ClassDescriptorResolver { : resolveDelegationSpecifiers(parameterScope, delegationSpecifiers, typeResolver); boolean open = classElement.hasModifier(JetTokens.OPEN_KEYWORD); - final WritableScope memberDeclarations = new WritableScopeImpl(parameterScope, classDescriptor, trace.getErrorHandler()); + final WritableScope memberDeclarations = new WritableScopeImpl(JetScope.EMPTY, classDescriptor, trace.getErrorHandler()); List declarations = classElement.getDeclarations(); for (JetDeclaration declaration : declarations) { diff --git a/idea/src/org/jetbrains/jet/lang/resolve/DelegatingBindingTrace.java b/idea/src/org/jetbrains/jet/lang/resolve/DelegatingBindingTrace.java new file mode 100644 index 00000000000..81399890394 --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/resolve/DelegatingBindingTrace.java @@ -0,0 +1,79 @@ +package org.jetbrains.jet.lang.resolve; + +import com.google.common.collect.Lists; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.CollectingErrorHandler; +import org.jetbrains.jet.lang.ErrorHandler; +import org.jetbrains.jet.lang.JetDiagnostic; +import org.jetbrains.jet.util.*; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * @author abreslav + */ +public class DelegatingBindingTrace implements BindingTrace { + private final BindingContext parentContext; + private final MutableManyMap map = ManyMapImpl.create(); + private final List diagnostics = Lists.newArrayList(); + private final ErrorHandler errorHandler = new CollectingErrorHandler(diagnostics); + + private final BindingContext bindingContext = new BindingContext() { + @Override + public Collection getDiagnostics() { + throw new UnsupportedOperationException(); // TODO + } + + @Override + public V get(ReadOnlySlice slice, K key) { + return DelegatingBindingTrace.this.get(slice, key); + } + }; + + public DelegatingBindingTrace(BindingContext parentContext) { + this.parentContext = parentContext; + } + + @Override + @NotNull + public ErrorHandler getErrorHandler() { + return errorHandler; + } + + @Override + public BindingContext getBindingContext() { + return bindingContext; + } + + @Override + public void record(WritableSlice slice, K key, V value) { + map.put(slice, key, value); + } + + @Override + public void record(WritableSlice slice, K key) { + record(slice, key, true); + } + + @Override + public V get(ReadOnlySlice slice, K key) { + if (map.containsKey(slice, key)) { + return map.get(slice, key); + } + return parentContext.get(slice, key); + } + + public void addAllMyDataTo(BindingTrace trace) { + for (Map.Entry, ?> entry : map) { + ManyMapKey manyMapKey = entry.getKey(); + Object value = entry.getValue(); + + //noinspection unchecked + trace.record(manyMapKey.getSlice(), manyMapKey.getKey(), value); + } + + AnalyzingUtils.applyHandler(trace.getErrorHandler(), diagnostics); + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ScopeWithReceiver.java b/idea/src/org/jetbrains/jet/lang/resolve/ScopeWithReceiver.java index 32ef6ae7b1b..3648e075c74 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/ScopeWithReceiver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/ScopeWithReceiver.java @@ -1,87 +1,74 @@ -package org.jetbrains.jet.lang.resolve; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.descriptors.*; -import org.jetbrains.jet.lang.types.JetType; -import org.jetbrains.jet.lang.types.JetTypeChecker; - -/** - * @author abreslav - */ -public class ScopeWithReceiver extends JetScopeImpl { - - private final JetType receiverType; - private final JetScope outerScope; - private final JetTypeChecker typeChecker; - - public ScopeWithReceiver(@NotNull JetScope outerScope, @NotNull JetType receiverType, @NotNull JetTypeChecker typeChecker) { - this.outerScope = outerScope; - this.receiverType = receiverType; - this.typeChecker = typeChecker; - } - - @NotNull - @Override - public FunctionGroup getFunctionGroup(@NotNull String name) { - JetScope memberScope = receiverType.getMemberScope(); - FunctionGroup functionGroup = memberScope.getFunctionGroup(name); - if (functionGroup.isEmpty()) { - return outerScope.getFunctionGroup(name); -// 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; -// } -// // TODO : in case of inferred type arguments, substitute the receiver type first -// return typeChecker.startForPairOfTypes(receiverType, functionReceiverType); -// } -// }); - } - return functionGroup; // TODO - } - - @Override - public ClassifierDescriptor getClassifier(@NotNull String name) { - return receiverType.getMemberScope().getClassifier(name); - } - - @Override - public VariableDescriptor getVariable(@NotNull String name) { - VariableDescriptor variable = receiverType.getMemberScope().getVariable(name); - if (variable != null) { - return variable; - } - variable = outerScope.getVariable(name); - if (variable instanceof PropertyDescriptor) { - PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variable; - JetType receiverType = propertyDescriptor.getReceiverType(); - // TODO : in case of type arguments, substitute the receiver type first - if (receiverType != null - && typeChecker.isSubtypeOf(receiverType, receiverType)) { - return variable; - } - } - return null; - } - - @Override - public NamespaceDescriptor getNamespace(@NotNull String name) { - return receiverType.getMemberScope().getNamespace(name); - } - - @NotNull - @Override - public JetType getThisType() { - return receiverType; - } - - @NotNull - @Override - public DeclarationDescriptor getContainingDeclaration() { - return outerScope.getContainingDeclaration(); - } -} +//package org.jetbrains.jet.lang.resolve; +// +//import org.jetbrains.annotations.NotNull; +//import org.jetbrains.jet.lang.descriptors.*; +//import org.jetbrains.jet.lang.types.JetType; +//import org.jetbrains.jet.lang.types.JetTypeChecker; +// +///** +// * @author abreslav +// */ +//public class ScopeWithReceiver extends JetScopeImpl { +// +// private final JetType receiverType; +// private final JetScope outerScope; +// private final JetTypeChecker typeChecker; +// +// public ScopeWithReceiver(@NotNull JetScope outerScope, @NotNull JetType receiverType, @NotNull JetTypeChecker typeChecker) { +// this.outerScope = outerScope; +// this.receiverType = receiverType; +// this.typeChecker = typeChecker; +// } +// +// @NotNull +// @Override +// public FunctionGroup getFunctionGroup(@NotNull String name) { +// WritableFunctionGroup result = new WritableFunctionGroup(name); +// +// result.addAllFunctions(receiverType.getMemberScope().getFunctionGroup(name)); +// result.addAllFunctions(outerScope.getFunctionGroup(name)); +// +// return result; +// } +// +// @Override +// public ClassifierDescriptor getClassifier(@NotNull String name) { +// return receiverType.getMemberScope().getClassifier(name); +// } +// +// @Override +// public VariableDescriptor getVariable(@NotNull String name) { +// VariableDescriptor variable = receiverType.getMemberScope().getVariable(name); +// if (variable != null) { +// return variable; +// } +// variable = outerScope.getVariable(name); +// if (variable instanceof PropertyDescriptor) { +// PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variable; +// JetType receiverType = propertyDescriptor.getReceiverType(); +// // TODO : in case of type arguments, substitute the receiver type first +// if (receiverType != null +// && typeChecker.isSubtypeOf(receiverType, receiverType)) { +// return variable; +// } +// } +// return null; +// } +// +// @Override +// public NamespaceDescriptor getNamespace(@NotNull String name) { +// return receiverType.getMemberScope().getNamespace(name); +// } +// +// @NotNull +// @Override +// public JetType getThisType() { +// return receiverType; +// } +// +// @NotNull +// @Override +// public DeclarationDescriptor getContainingDeclaration() { +// return outerScope.getContainingDeclaration(); +// } +//} diff --git a/idea/src/org/jetbrains/jet/lang/resolve/SubstitutingScope.java b/idea/src/org/jetbrains/jet/lang/resolve/SubstitutingScope.java index 7f1cf889239..bfa03e0f1be 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/SubstitutingScope.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/SubstitutingScope.java @@ -14,7 +14,6 @@ import java.util.Collection; public class SubstitutingScope implements JetScope { private final JetScope workerScope; -// private final Map substitutionContext; private final TypeSubstitutor substitutor; private Collection allDescriptors; @@ -60,7 +59,7 @@ public class SubstitutingScope implements JetScope { @Override public FunctionGroup getFunctionGroup(@NotNull String name) { FunctionGroup functionGroup = workerScope.getFunctionGroup(name); - if (substitutor.isEmpty()) { + if (substitutor.isEmpty() || functionGroup.isEmpty()) { return functionGroup; } return new LazySubstitutingFunctionGroup(substitutor, functionGroup); diff --git a/idea/src/org/jetbrains/jet/lang/resolve/TemporaryBindingTrace.java b/idea/src/org/jetbrains/jet/lang/resolve/TemporaryBindingTrace.java index 505a253ed44..9d7f3fd1a18 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/TemporaryBindingTrace.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/TemporaryBindingTrace.java @@ -1,79 +1,22 @@ package org.jetbrains.jet.lang.resolve; -import com.google.common.collect.Lists; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.CollectingErrorHandler; -import org.jetbrains.jet.lang.ErrorHandler; -import org.jetbrains.jet.lang.JetDiagnostic; -import org.jetbrains.jet.util.*; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - /** * @author abreslav */ -public class TemporaryBindingTrace implements BindingTrace { - private final BindingContext parentContext; - private final MutableManyMap map = ManyMapImpl.create(); - private final List diagnostics = Lists.newArrayList(); - private final ErrorHandler errorHandler = new CollectingErrorHandler(diagnostics); +public class TemporaryBindingTrace extends DelegatingBindingTrace { - private final BindingContext bindingContext = new BindingContext() { - @Override - public Collection getDiagnostics() { - throw new UnsupportedOperationException(); // TODO - } - - @Override - public V get(ReadOnlySlice slice, K key) { - return TemporaryBindingTrace.this.get(slice, key); - } - }; - - public TemporaryBindingTrace(BindingContext parentContext) { - this.parentContext = parentContext; + public static TemporaryBindingTrace create(BindingTrace trace) { + return new TemporaryBindingTrace(trace); } - @Override - @NotNull - public ErrorHandler getErrorHandler() { - return errorHandler; + private final BindingTrace trace; + + private TemporaryBindingTrace(BindingTrace trace) { + super(trace.getBindingContext()); + this.trace = trace; } - @Override - public BindingContext getBindingContext() { - return bindingContext; + public void commit() { + addAllMyDataTo(trace); } - - @Override - public void record(WritableSlice slice, K key, V value) { - map.put(slice, key, value); - } - - @Override - public void record(WritableSlice slice, K key) { - record(slice, key, true); - } - - @Override - public V get(ReadOnlySlice slice, K key) { - if (map.containsKey(slice, key)) { - return map.get(slice, key); - } - return parentContext.get(slice, key); - } - - public void addAllMyDataTo(BindingTrace trace) { - for (Map.Entry, ?> entry : map) { - ManyMapKey manyMapKey = entry.getKey(); - Object value = entry.getValue(); - - //noinspection unchecked - trace.record(manyMapKey.getSlice(), manyMapKey.getKey(), value); - } - - AnalyzingUtils.applyHandler(trace.getErrorHandler(), diagnostics); - } -} \ No newline at end of file +} diff --git a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java index 4af559e9c8e..bd84dca3f44 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java @@ -478,6 +478,8 @@ public class TopDownAnalyzer { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private void resolveBehaviorDeclarationBodies() { + bindOverrides(); + resolveDelegationSpecifierLists(); resolveClassAnnotations(); @@ -488,8 +490,6 @@ public class TopDownAnalyzer { resolveFunctionBodies(); checkIfPrimaryConstructorIsNecessary(); - - bindOverrides(); } private void bindOverrides() { @@ -580,7 +580,7 @@ public class TopDownAnalyzer { JetTypeReference typeReference = call.getTypeReference(); if (typeReference != null) { if (descriptor.getUnsubstitutedPrimaryConstructor() != null) { - typeInferrer.getCallResolver().resolveCall(scopeForConstructor, call, NO_EXPECTED_TYPE); + typeInferrer.getCallResolver().resolveCall(scopeForConstructor, null, call, NO_EXPECTED_TYPE); } else { JetValueArgumentList valueArgumentList = call.getValueArgumentList(); @@ -685,7 +685,7 @@ public class TopDownAnalyzer { public void visitDelegationToSuperCallSpecifier(JetDelegatorToSuperCall call) { JetTypeReference typeReference = call.getTypeReference(); if (typeReference != null) { - typeInferrerForInitializers.getCallResolver().resolveCall(functionInnerScope, call, NO_EXPECTED_TYPE); + typeInferrerForInitializers.getCallResolver().resolveCall(functionInnerScope, null, call, NO_EXPECTED_TYPE); } } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/WritableScopeImpl.java b/idea/src/org/jetbrains/jet/lang/resolve/WritableScopeImpl.java index eb516af6b52..86f1d754b63 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/WritableScopeImpl.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/WritableScopeImpl.java @@ -196,49 +196,28 @@ public class WritableScopeImpl extends WritableScopeWithImports { public FunctionGroup getFunctionGroup(@NotNull String name) { addVariablesAsFunctions(); + WritableFunctionGroup result = new WritableFunctionGroup(name); + FunctionGroup functionGroup = getFunctionGroups().get(name); - FunctionGroup constructors = null; - ClassifierDescriptor classifier = getClassifier(name); - if (classifier instanceof ClassDescriptor) { - ClassDescriptor classDescriptor = (ClassDescriptor) classifier; - constructors = classDescriptor.getConstructors(); - } - if (functionGroup != null && !functionGroup.isEmpty()) { - if (constructors != null) { - WritableFunctionGroup result = new WritableFunctionGroup(name); - for (FunctionDescriptor functionDescriptor : functionGroup.getFunctionDescriptors()) { - result.addFunction(functionDescriptor); - } - for (FunctionDescriptor functionDescriptor : constructors.getFunctionDescriptors()) { - result.addFunction(functionDescriptor); - } - return result; - } - return functionGroup; - } - - if (constructors != null && !constructors.isEmpty()) { - return constructors; + if (functionGroup != null) { + result.addAllFunctions(functionGroup); } + +// ClassifierDescriptor classifier = getClassifier(name); +// if (classifier instanceof ClassDescriptor) { +// ClassDescriptor classDescriptor = (ClassDescriptor) classifier; +// result.addAllFunctions(classDescriptor.getConstructors()); +// } if (thisType != null) { - functionGroup = getThisType().getMemberScope().getFunctionGroup(name); - if (!functionGroup.isEmpty()) { - return functionGroup; - } + result.addAllFunctions(getThisType().getMemberScope().getFunctionGroup(name)); } - // TODO : this logic is questionable - functionGroup = getWorkerScope().getFunctionGroup(name); - if (!functionGroup.isEmpty()) return functionGroup; -// for (JetScope imported : getImports()) { -// FunctionGroup importedDescriptor = imported.getFunctionGroup(name); -// if (!importedDescriptor.isEmpty()) { -// return importedDescriptor; -// } -// } -// return functionGroup; - return super.getFunctionGroup(name); + result.addAllFunctions(getWorkerScope().getFunctionGroup(name)); + + result.addAllFunctions(super.getFunctionGroup(name)); + + return result; } private void addVariablesAsFunctions() { diff --git a/idea/src/org/jetbrains/jet/lang/resolve/WritableScopeWithImports.java b/idea/src/org/jetbrains/jet/lang/resolve/WritableScopeWithImports.java index 8fdb9473559..8b63aca7abc 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/WritableScopeWithImports.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/WritableScopeWithImports.java @@ -3,10 +3,7 @@ package org.jetbrains.jet.lang.resolve; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.ErrorHandler; -import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor; -import org.jetbrains.jet.lang.descriptors.FunctionGroup; -import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; -import org.jetbrains.jet.lang.descriptors.VariableDescriptor; +import org.jetbrains.jet.lang.descriptors.*; import java.util.ArrayList; import java.util.List; @@ -63,13 +60,17 @@ public abstract class WritableScopeWithImports extends JetScopeAdapter implement @NotNull @Override public FunctionGroup getFunctionGroup(@NotNull String name) { + if (getImports().isEmpty()) { + return FunctionGroup.EMPTY; + } + WritableFunctionGroup result = new WritableFunctionGroup(name); for (JetScope imported : getImports()) { - FunctionGroup importedDescriptor = imported.getFunctionGroup(name); - if (!importedDescriptor.isEmpty()) { - return importedDescriptor; + FunctionGroup importedFunctions = imported.getFunctionGroup(name); + if (!importedFunctions.isEmpty()) { + result.addAllFunctions(importedFunctions); } } - return FunctionGroup.EMPTY; + return result; } @Override diff --git a/idea/src/org/jetbrains/jet/lang/resolve/WriteThroughScope.java b/idea/src/org/jetbrains/jet/lang/resolve/WriteThroughScope.java index f154255f6ad..37f9e5410cf 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/WriteThroughScope.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/WriteThroughScope.java @@ -48,13 +48,15 @@ public class WriteThroughScope extends WritableScopeWithImports { @Override @NotNull public FunctionGroup getFunctionGroup(@NotNull String name) { - FunctionGroup functionGroup = writableWorker.getFunctionGroup(name); - if (!functionGroup.isEmpty()) return functionGroup; // TODO : Overloads from different places + WritableFunctionGroup result = new WritableFunctionGroup(name); - functionGroup = getWorkerScope().getFunctionGroup(name); - if (!functionGroup.isEmpty()) return functionGroup; // TODO : Overloads from different places + result.addAllFunctions(writableWorker.getFunctionGroup(name)); - return super.getFunctionGroup(name); // Imports + result.addAllFunctions(getWorkerScope().getFunctionGroup(name)); + + result.addAllFunctions(super.getFunctionGroup(name)); // Imports + + return result; } @Override diff --git a/idea/src/org/jetbrains/jet/lang/resolve/java/JavaPackageScope.java b/idea/src/org/jetbrains/jet/lang/resolve/java/JavaPackageScope.java index d1743442346..ab56eb57e81 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/java/JavaPackageScope.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/java/JavaPackageScope.java @@ -32,11 +32,11 @@ public class JavaPackageScope extends JetScopeImpl { @NotNull @Override public FunctionGroup getFunctionGroup(@NotNull String name) { - ClassifierDescriptor classifier = getClassifier(name); - if (classifier instanceof ClassDescriptor) { - ClassDescriptor classDescriptor = (ClassDescriptor) classifier; - return classDescriptor.getConstructors(); - } +// ClassifierDescriptor classifier = getClassifier(name); +// if (classifier instanceof ClassDescriptor) { +// ClassDescriptor classDescriptor = (ClassDescriptor) classifier; +// return classDescriptor.getConstructors(); +// } return FunctionGroup.EMPTY; } diff --git a/idea/src/org/jetbrains/jet/lang/types/CallMaker.java b/idea/src/org/jetbrains/jet/lang/types/CallMaker.java index 52f6bba710b..43c9ea9e94b 100644 --- a/idea/src/org/jetbrains/jet/lang/types/CallMaker.java +++ b/idea/src/org/jetbrains/jet/lang/types/CallMaker.java @@ -93,9 +93,9 @@ public class CallMaker { } public static Call makeCall(final JetExpression calleeExpression, final List argumentExpressions) { - List arguments = Lists.newArrayList(); + List arguments = Lists.newArrayList(); for (JetExpression argumentExpression : argumentExpressions) { - arguments.add(new ExpressionValueArgument(argumentExpression)); + arguments.add(makeValueArgument(argumentExpression)); } return makeCallWithArguments(calleeExpression, arguments); } @@ -114,4 +114,7 @@ public class CallMaker { return makeCall(arrayAccessExpression, arguments); } + public static ValueArgument makeValueArgument(JetExpression expression) { + return new ExpressionValueArgument(expression); + } } diff --git a/idea/src/org/jetbrains/jet/lang/types/CallResolver.java b/idea/src/org/jetbrains/jet/lang/types/CallResolver.java index 0c6f5464b0c..8d5548feb54 100644 --- a/idea/src/org/jetbrains/jet/lang/types/CallResolver.java +++ b/idea/src/org/jetbrains/jet/lang/types/CallResolver.java @@ -1,5 +1,8 @@ package org.jetbrains.jet.lang.types; +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.intellij.lang.ASTNode; @@ -22,21 +25,31 @@ import static org.jetbrains.jet.lang.types.JetTypeInferrer.NO_EXPECTED_TYPE; */ public class CallResolver { - private static abstract class ResolutionTask { + private static class ResolutionTask { private final Collection candidates; - private final JetExpression receiver; + private final JetType receiverType; private final List typeArguments; private final List valueArguments; + private final List functionLiteralArguments; protected ResolutionTask( @NotNull Collection candidates, - @Nullable JetExpression receiver, + @Nullable JetType receiverType, @NotNull List typeArguments, - @NotNull List valueArguments) { + @NotNull List valueArguments, + @NotNull List functionLiteralArguments) { this.candidates = candidates; - this.receiver = receiver; + this.receiverType = receiverType; this.typeArguments = typeArguments; this.valueArguments = valueArguments; + this.functionLiteralArguments = functionLiteralArguments; + } + + public ResolutionTask( + @NotNull Collection candidates, + @Nullable JetType receiverType, + @NotNull Call call) { + this(candidates, receiverType, call.getTypeArguments(), call.getValueArguments(), call.getFunctionLiteralArguments()); } @NotNull @@ -45,8 +58,8 @@ public class CallResolver { } @Nullable - public JetExpression getReceiver() { - return receiver; + public JetType getReceiverType() { + return receiverType; } @NotNull @@ -59,15 +72,24 @@ public class CallResolver { return valueArguments; } - public abstract void bindFunctionReference(@NotNull BindingTrace trace, @NotNull FunctionDescriptor functionDescriptor); + @NotNull + public List getFunctionLiteralArguments() { + return functionLiteralArguments; + } + } - public abstract void reportOverallResolutionError(@NotNull BindingTrace trace, @NotNull String message); + private interface TracingStrategy { + void bindFunctionReference(@NotNull BindingTrace trace, @NotNull FunctionDescriptor functionDescriptor); - public abstract void reportWrongTypeArguments(@NotNull BindingTrace trace, @NotNull String message); + void reportOverallResolutionError(@NotNull BindingTrace trace, @NotNull String message); - public abstract void reportWrongValueArguments(@NotNull BindingTrace trace, @NotNull String message); + void reportWrongTypeArguments(@NotNull BindingTrace trace, @NotNull String message); - public abstract void reportUnresolvedFunctionReference(@NotNull BindingTrace trace); + void reportWrongValueArguments(@NotNull BindingTrace trace, @NotNull String message); + + void reportUnresolvedFunctionReference(@NotNull BindingTrace trace); + + void reportErrorOnFunctionReference(BindingTrace trace, String message); } private final BindingTrace trace; @@ -87,10 +109,11 @@ public class CallResolver { @Nullable public JetType resolveCall( @NotNull JetScope scope, + @Nullable JetType receiverType, @NotNull JetCallElement call, @NotNull JetType expectedType ) { - FunctionDescriptor functionDescriptor = resolveSimpleCallToFunctionDescriptor(scope, null, call, expectedType); + FunctionDescriptor functionDescriptor = resolveSimpleCallToFunctionDescriptor(scope, receiverType, call, expectedType); return functionDescriptor == null ? null : functionDescriptor.getReturnType(); } @@ -100,47 +123,55 @@ public class CallResolver { @NotNull final Call call, @NotNull final JetReferenceExpression functionReference, @NotNull String name, - @Nullable JetExpression receiver, + @Nullable JetType receiverType, @NotNull JetType expectedType) { - Collection candidates; - if (receiver != null) { - JetType type = typeInferrer.getServices(trace).getType(scope, receiver, false, NO_EXPECTED_TYPE); - if (type == null) { - return null; // TODO : check parameter types anyway? - } - // TODO : autocasts - // TODO : nullability - candidates = type.getMemberScope().getFunctionGroup(name).getFunctionDescriptors(); - } - else { - candidates = scope.getFunctionGroup(name).getFunctionDescriptors(); - } - return resolveCallToFunctionDescriptor(scope, receiver, call, functionReference.getNode(), expectedType, candidates, functionReference); + // TODO : autocasts + // TODO : nullability + List tasks = computePrioritizedTasks(scope, receiverType, call, name); + return resolveCallToFunctionDescriptor(scope, call, functionReference.getNode(), expectedType, tasks, functionReference); } @Nullable public FunctionDescriptor resolveSimpleCallToFunctionDescriptor( @NotNull JetScope scope, - @Nullable JetExpression receiver, + @Nullable JetType receiverType, @NotNull final JetCallElement call, @NotNull JetType expectedType ) { + List prioritizedTasks; + JetExpression calleeExpression = call.getCalleeExpression(); - Collection candidates; final JetReferenceExpression functionReference; if (calleeExpression instanceof JetSimpleNameExpression) { JetSimpleNameExpression expression = (JetSimpleNameExpression) calleeExpression; functionReference = expression; - candidates = scope.getFunctionGroup(expression.getReferencedName()).getFunctionDescriptors(); + + String name = expression.getReferencedName(); + if (name == null) return null; + + prioritizedTasks = computePrioritizedTasks(scope, receiverType, call, name); } else if (calleeExpression instanceof JetConstructorCalleeExpression) { + assert receiverType == null; + + prioritizedTasks = Lists.newArrayList(); + JetConstructorCalleeExpression expression = (JetConstructorCalleeExpression) calleeExpression; functionReference = expression.getConstructorReferenceExpression(); - JetType constructedType = typeResolver.resolveType(scope, expression.getTypeReference()); + if (functionReference == null) { + return null; // No type there + } + JetTypeReference typeReference = expression.getTypeReference(); + JetType constructedType = typeResolver.resolveType(scope, typeReference); DeclarationDescriptor declarationDescriptor = constructedType.getConstructor().getDeclarationDescriptor(); if (declarationDescriptor instanceof ClassDescriptor) { ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor; - candidates = classDescriptor.getConstructors().getFunctionDescriptors(); + Set members = classDescriptor.getConstructors().getFunctionDescriptors(); + if (members.isEmpty()) { + trace.getErrorHandler().genericError(call.getValueArgumentList().getNode(), "This class does not have a constructor"); + return null; + } + addTask(prioritizedTasks, null, call, members); } else { trace.getErrorHandler().genericError(calleeExpression.getNode(), "Not a class"); @@ -148,22 +179,76 @@ public class CallResolver { } } else { - throw new UnsupportedOperationException("Type argument inference not implemented"); + throw new UnsupportedOperationException("Type argument inference not implemented for " + call); } - return resolveCallToFunctionDescriptor(scope, receiver, call, call.getNode(), expectedType, candidates, functionReference); + return resolveCallToFunctionDescriptor(scope, call, call.getNode(), expectedType, prioritizedTasks, functionReference); } + private List computePrioritizedTasks(JetScope scope, JetType receiverType, Call call, String name) { + List result = Lists.newArrayList(); + if (receiverType != null) { + Collection extensionFunctions = Sets.newLinkedHashSet(scope.getFunctionGroup(name).getFunctionDescriptors()); + for (Iterator iterator = extensionFunctions.iterator(); iterator.hasNext(); ) { + FunctionDescriptor functionDescriptor = iterator.next(); + if (functionDescriptor.getReceiverType() == null) { + iterator.remove(); + } + } + List nonlocals = Lists.newArrayList(); + List locals = Lists.newArrayList(); + splitLexicallyLocalDescriptors(extensionFunctions, scope.getContainingDeclaration(), locals, nonlocals); + + Set members = Sets.newHashSet(receiverType.getMemberScope().getFunctionGroup(name).getFunctionDescriptors()); + addConstrtuctors(receiverType.getMemberScope(), name, members); + + addTask(result, receiverType, call, locals); + addTask(result, null, call, members); + addTask(result, receiverType, call, nonlocals); + } + else { + Collection functions = Sets.newLinkedHashSet(scope.getFunctionGroup(name).getFunctionDescriptors()); + for (Iterator iterator = functions.iterator(); iterator.hasNext(); ) { + FunctionDescriptor functionDescriptor = iterator.next(); + if (functionDescriptor.getReceiverType() != null) { + iterator.remove(); + } + } + addConstrtuctors(scope, name, functions); + + List nonlocals = Lists.newArrayList(); + List locals = Lists.newArrayList(); + splitLexicallyLocalDescriptors(functions, scope.getContainingDeclaration(), locals, nonlocals); + + addTask(result, receiverType, call, locals); + + addTask(result, receiverType, call, nonlocals); + } + return result; + } + + private void addTask(List result, JetType receiverType, Call call, Collection candidates) { + if (candidates.isEmpty()) return; + result.add(new ResolutionTask(candidates, receiverType, call)); + } + + private void addConstrtuctors(JetScope scope, String name, Collection functions) { + ClassifierDescriptor classifier = scope.getClassifier(name); + if (classifier instanceof ClassDescriptor) { + ClassDescriptor classDescriptor = (ClassDescriptor) classifier; + functions.addAll(classDescriptor.getConstructors().getFunctionDescriptors()); + } + } private FunctionDescriptor resolveCallToFunctionDescriptor( - JetScope scope, - final JetExpression receiver, - final Call call, - final ASTNode callNode, - JetType expectedType, - final Collection candidates, - final JetReferenceExpression functionReference) { - ResolutionTask task = new ResolutionTask(candidates, receiver, call.getTypeArguments(), call.getValueArguments()) { + @NotNull JetScope scope, + @NotNull final Call call, + @NotNull final ASTNode callNode, + @NotNull JetType expectedType, + @NotNull final List prioritizedTasks, // high to low priority + @NotNull final JetReferenceExpression functionReference) { + TemporaryBindingTrace traceForFirstNonemptyCandidateSet = null; + TracingStrategy tracing = new TracingStrategy() { @Override public void bindFunctionReference(@NotNull BindingTrace trace, @NotNull FunctionDescriptor functionDescriptor) { trace.record(BindingContext.REFERENCE_TARGET, functionReference, functionDescriptor); @@ -207,14 +292,38 @@ public class CallResolver { public void reportUnresolvedFunctionReference(@NotNull BindingTrace trace) { trace.getErrorHandler().unresolvedReference(functionReference); } + + @Override + public void reportErrorOnFunctionReference(BindingTrace trace, String message) { + trace.getErrorHandler().genericError(functionReference.getNode(), message); + } + }; - return performResolution(scope, expectedType, task); + for (ResolutionTask task : prioritizedTasks) { + TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(trace); + FunctionDescriptor functionDescriptor = performResolution(temporaryTrace, scope, expectedType, task, tracing); + if (functionDescriptor != null) { + temporaryTrace.commit(); + return functionDescriptor; + } + if (traceForFirstNonemptyCandidateSet == null && !task.getCandidates().isEmpty()) { + traceForFirstNonemptyCandidateSet = temporaryTrace; + } + } + if (traceForFirstNonemptyCandidateSet != null) { + traceForFirstNonemptyCandidateSet.commit(); + } + else { + trace.getErrorHandler().unresolvedReference(functionReference); + } + return null; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private boolean mapValueArgumentsToParameters( @NotNull ResolutionTask task, + @NotNull TracingStrategy tracing, @NotNull FunctionDescriptor candidate, @NotNull BindingTrace temporaryTrace, @NotNull Map argumentsToParameters @@ -222,6 +331,7 @@ public class CallResolver { Set usedParameters = Sets.newHashSet(); List valueParameters = candidate.getValueParameters(); + Map parameterByName = Maps.newHashMap(); for (ValueParameterDescriptor valueParameter : valueParameters) { parameterByName.put(valueParameter.getName(), valueParameter); @@ -229,22 +339,25 @@ public class CallResolver { List valueArguments = task.getValueArguments(); + boolean error = false; boolean someNamed = false; boolean somePositioned = false; - boolean error = false; for (int i = 0; i < valueArguments.size(); i++) { ValueArgument valueArgument = valueArguments.get(i); if (valueArgument.isNamed()) { someNamed = true; + ASTNode nameNode = valueArgument.getArgumentName().getNode(); if (somePositioned) { - temporaryTrace.getErrorHandler().genericError(valueArgument.getArgumentName().getNode(), "Mixing named and positioned arguments in not allowed"); + temporaryTrace.getErrorHandler().genericError(nameNode, "Mixing named and positioned arguments in not allowed"); error = true; } else { ValueParameterDescriptor valueParameterDescriptor = parameterByName.get(valueArgument.getArgumentName().getName()); - usedParameters.add(valueParameterDescriptor); + if (usedParameters.add(valueParameterDescriptor)) { + temporaryTrace.getErrorHandler().genericError(nameNode, "An argument is already passed for this parameter"); + } if (valueParameterDescriptor == null) { - temporaryTrace.getErrorHandler().genericError(valueArgument.getArgumentName().getNode(), "Cannot find a parameter with this name"); + temporaryTrace.getErrorHandler().genericError(nameNode, "Cannot find a parameter with this name"); error = true; } else { @@ -260,13 +373,13 @@ public class CallResolver { error = true; } else { - if (i < valueParameters.size()) { + int parameterCount = valueParameters.size(); + if (i < parameterCount) { ValueParameterDescriptor valueParameterDescriptor = valueParameters.get(i); usedParameters.add(valueParameterDescriptor); argumentsToParameters.put(valueArgument, valueParameterDescriptor); } else if (!valueParameters.isEmpty()) { - ValueParameterDescriptor valueParameterDescriptor = valueParameters.get(valueParameters.size() - 1); if (valueParameterDescriptor.isVararg()) { argumentsToParameters.put(valueArgument, valueParameterDescriptor); @@ -285,10 +398,51 @@ public class CallResolver { } } + List functionLiteralArguments = task.getFunctionLiteralArguments(); + if (!functionLiteralArguments.isEmpty()) { + JetExpression possiblyLabeledFunctionLiteral = functionLiteralArguments.get(0); + + if (valueParameters.isEmpty()) { + temporaryTrace.getErrorHandler().genericError(possiblyLabeledFunctionLiteral.getNode(), "Too many arguments"); + error = true; + } else { + JetFunctionLiteralExpression functionLiteral; + if (possiblyLabeledFunctionLiteral instanceof JetLabelQualifiedExpression) { + JetLabelQualifiedExpression labeledFunctionLiteral = (JetLabelQualifiedExpression) possiblyLabeledFunctionLiteral; + functionLiteral = (JetFunctionLiteralExpression) labeledFunctionLiteral.getLabeledExpression(); + } + else { + functionLiteral = (JetFunctionLiteralExpression) possiblyLabeledFunctionLiteral; + } + + ValueParameterDescriptor parameterDescriptor = valueParameters.get(valueParameters.size() - 1); + if (parameterDescriptor.isVararg()) { + temporaryTrace.getErrorHandler().genericError(possiblyLabeledFunctionLiteral.getNode(), "Passing value as a vararg is only allowed inside a parenthesized argument list"); + error = true; + } + else { + if (!usedParameters.add(parameterDescriptor)) { + temporaryTrace.getErrorHandler().genericError(possiblyLabeledFunctionLiteral.getNode(), "Too many arguments"); + error = true; + } + else { + argumentsToParameters.put(CallMaker.makeValueArgument(functionLiteral), parameterDescriptor); + } + } + } + + for (int i = 1; i < functionLiteralArguments.size(); i++) { + JetExpression argument = functionLiteralArguments.get(i); + temporaryTrace.getErrorHandler().genericError(argument.getNode(), "Only one function literal is allowed outside a parenthesized argument list"); + error = true; + } + } + + for (ValueParameterDescriptor valueParameter : valueParameters) { if (!usedParameters.contains(valueParameter)) { if (!valueParameter.hasDefaultValue()) { - task.reportWrongValueArguments(temporaryTrace, "No value passed for parameter " + valueParameter.getName()); + tracing.reportWrongValueArguments(temporaryTrace, "No value passed for parameter " + valueParameter.getName()); error = true; } } @@ -297,58 +451,77 @@ public class CallResolver { } @Nullable - private FunctionDescriptor performResolution(@NotNull JetScope scope, @NotNull JetType expectedType, @NotNull ResolutionTask task) { - Map successfulCandidates = Maps.newHashMap(); - Set failedCandidates = Sets.newHashSet(); + private FunctionDescriptor performResolution(@NotNull BindingTrace trace, @NotNull JetScope scope, @NotNull JetType expectedType, @NotNull ResolutionTask task, @NotNull TracingStrategy tracing) { + Map successfulCandidates = Maps.newLinkedHashMap(); + Set failedCandidates = Sets.newLinkedHashSet(); Map solutions = Maps.newHashMap(); Map traces = Maps.newHashMap(); for (FunctionDescriptor candidate : task.getCandidates()) { - TemporaryBindingTrace temporaryTrace = new TemporaryBindingTrace(trace.getBindingContext()); + TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(trace); traces.put(candidate, temporaryTrace); JetTypeInferrer.Services temporaryServices = typeInferrer.getServices(temporaryTrace); - task.bindFunctionReference(temporaryTrace, candidate); + tracing.bindFunctionReference(temporaryTrace, candidate); Map argumentsToParameters = Maps.newHashMap(); - boolean error = mapValueArgumentsToParameters(task, candidate, temporaryTrace, argumentsToParameters); + boolean error = mapValueArgumentsToParameters(task, tracing, candidate, temporaryTrace, argumentsToParameters); if (error) continue; if (task.getTypeArguments().isEmpty()) { - // Type argument inference - - ConstraintSystem constraintSystem = new ConstraintSystem(); - for (TypeParameterDescriptor typeParameterDescriptor : candidate.getTypeParameters()) { - constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); // TODO - } - - for (Map.Entry entry : argumentsToParameters.entrySet()) { - ValueArgument valueArgument = entry.getKey(); - ValueParameterDescriptor valueParameterDescriptor = entry.getValue(); - - JetExpression expression = valueArgument.getArgumentExpression(); - // TODO : more attempts, with different expected types - JetType type = temporaryServices.getType(scope, expression, false, NO_EXPECTED_TYPE); - if (type != null) { - constraintSystem.addSubtypingConstraint(type, valueParameterDescriptor.getOutType()); + if (candidate.getTypeParameters().isEmpty()) { + if (checkValueArgumentTypes(scope, temporaryServices, argumentsToParameters, Functions.identity()) + && checkReceiver(task, tracing, candidate, temporaryTrace)) { + successfulCandidates.put(candidate, candidate); + } + else { + failedCandidates.add(candidate); } } - - if (expectedType != NO_EXPECTED_TYPE) { - constraintSystem.addSubtypingConstraint(candidate.getReturnType(), expectedType); - } - - ConstraintSystem.Solution solution = constraintSystem.solve(); - solutions.put(candidate, solution); - if (solution.isSuccessful()) { - FunctionDescriptor substitute = candidate.substitute(solution.getSubstitutor()); - assert substitute != null; - successfulCandidates.put(candidate, substitute); - } else { - task.reportOverallResolutionError(temporaryTrace, "Type inference failed"); - failedCandidates.add(candidate); + // Type argument inference + + ConstraintSystem constraintSystem = new ConstraintSystem(); + for (TypeParameterDescriptor typeParameterDescriptor : candidate.getTypeParameters()) { + constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); // TODO + } + + for (Map.Entry entry : argumentsToParameters.entrySet()) { + ValueArgument valueArgument = entry.getKey(); + ValueParameterDescriptor valueParameterDescriptor = entry.getValue(); + + JetExpression expression = valueArgument.getArgumentExpression(); + // TODO : more attempts, with different expected types + JetType type = temporaryServices.getType(scope, expression, false, NO_EXPECTED_TYPE); + if (type != null) { + constraintSystem.addSubtypingConstraint(type, valueParameterDescriptor.getOutType()); + } + } + + checkReceiverAbsence(task, tracing, candidate, temporaryTrace); + // Error is already reported if something is missing + JetType receiverType = task.getReceiverType(); + JetType candidateReceiverType = candidate.getReceiverType(); + if (receiverType != null && candidateReceiverType != null) { + constraintSystem.addSubtypingConstraint(receiverType, candidateReceiverType); + } + + if (expectedType != NO_EXPECTED_TYPE) { + constraintSystem.addSubtypingConstraint(candidate.getReturnType(), expectedType); + } + + ConstraintSystem.Solution solution = constraintSystem.solve(); + solutions.put(candidate, solution); + if (solution.isSuccessful()) { + FunctionDescriptor substitute = candidate.substitute(solution.getSubstitutor()); + assert substitute != null; + successfulCandidates.put(candidate, substitute); + } + else { + tracing.reportOverallResolutionError(temporaryTrace, "Type inference failed"); + failedCandidates.add(candidate); + } } } else { @@ -362,57 +535,80 @@ public class CallResolver { } } - List typeArguments = new ArrayList(); - for (JetTypeProjection projection : jetTypeArguments) { - // TODO : check that there's no projection - JetTypeReference typeReference = projection.getTypeReference(); - if (typeReference != null) { - typeArguments.add(new TypeResolver(semanticServices, temporaryTrace, true).resolveType(scope, typeReference)); + if (candidate.getTypeParameters().size() == jetTypeArguments.size()) { + List typeArguments = new ArrayList(); + for (JetTypeProjection projection : jetTypeArguments) { + // TODO : check that there's no projection + JetTypeReference typeReference = projection.getTypeReference(); + if (typeReference != null) { + typeArguments.add(new TypeResolver(semanticServices, temporaryTrace, true).resolveType(scope, typeReference)); + } } - } - checkGenericBoundsInAFunctionCall(jetTypeArguments, typeArguments, candidate); + checkGenericBoundsInAFunctionCall(jetTypeArguments, typeArguments, candidate); - int typeArgCount = typeArguments.size(); - if (candidate.getTypeParameters().size() == typeArgCount) { FunctionDescriptor substitutedFunctionDescriptor = FunctionDescriptorUtil.substituteFunctionDescriptor(typeArguments, candidate); assert substitutedFunctionDescriptor != null; - Map parameterMap = Maps.newHashMap(); + final Map parameterMap = Maps.newHashMap(); for (ValueParameterDescriptor valueParameterDescriptor : substitutedFunctionDescriptor.getValueParameters()) { parameterMap.put(valueParameterDescriptor.getOriginal(), valueParameterDescriptor); } - boolean localError = false; - for (Map.Entry entry : argumentsToParameters.entrySet()) { - ValueArgument valueArgument = entry.getKey(); - ValueParameterDescriptor valueParameterDescriptor = entry.getValue(); - - ValueParameterDescriptor substitutedParameter = parameterMap.get(valueParameterDescriptor.getOriginal()); - - assert substitutedParameter != null; - - JetType parameterType = substitutedParameter.getOutType(); - JetType type = temporaryServices.getType(scope, valueArgument.getArgumentExpression(), false, parameterType); - if (type == null) { - localError = true; + Function mapFunction = new Function() { + @Override + public ValueParameterDescriptor apply(ValueParameterDescriptor input) { + return parameterMap.get(input.getOriginal()); } - } - if (localError) { - failedCandidates.add(candidate); - } - else { + }; + if (checkValueArgumentTypes(scope, temporaryServices, argumentsToParameters, mapFunction) + && checkReceiver(task, tracing, substitutedFunctionDescriptor, temporaryTrace)) { successfulCandidates.put(candidate, substitutedFunctionDescriptor); } - + else { + failedCandidates.add(candidate); + } } else { failedCandidates.add(candidate); - task.reportWrongTypeArguments(temporaryTrace, "Number of type arguments does not match " + DescriptorRenderer.TEXT.render(candidate)); + tracing.reportWrongTypeArguments(temporaryTrace, "Number of type arguments does not match " + DescriptorRenderer.TEXT.render(candidate)); } } } + return computeResultAndReportErrors(trace, tracing, successfulCandidates, failedCandidates, traces); + } + + private boolean checkReceiver(ResolutionTask task, TracingStrategy tracing, FunctionDescriptor candidate, TemporaryBindingTrace temporaryTrace) { + if (!checkReceiverAbsence(task, tracing, candidate, temporaryTrace)) return false; + JetType receiverType = task.getReceiverType(); + JetType candidateReceiverType = candidate.getReceiverType(); + if (receiverType != null + && candidateReceiverType != null + && !semanticServices.getTypeChecker().isSubtypeOf(receiverType, candidateReceiverType)) { + tracing.reportErrorOnFunctionReference(temporaryTrace, "This function requires a receiver of type " + candidateReceiverType); + return false; + } + return true; + } + + private boolean checkReceiverAbsence(ResolutionTask task, TracingStrategy tracing, FunctionDescriptor candidate, TemporaryBindingTrace temporaryTrace) { + JetType receiverType = task.getReceiverType(); + JetType candidateReceiverType = candidate.getReceiverType(); + if (receiverType != null) { + if (candidateReceiverType == null) { + tracing.reportErrorOnFunctionReference(temporaryTrace, "This function does not admit a receiver"); + return false; + } + } + else if (candidateReceiverType != null) { + tracing.reportErrorOnFunctionReference(temporaryTrace, "Receiver is missing" + candidateReceiverType); + return false; + } + return true; + } + + private FunctionDescriptor computeResultAndReportErrors(BindingTrace trace, TracingStrategy tracing, Map successfulCandidates, Set failedCandidates, Map traces) { if (successfulCandidates.size() > 0) { if (successfulCandidates.size() == 1) { Map.Entry entry = successfulCandidates.entrySet().iterator().next(); @@ -420,25 +616,18 @@ public class CallResolver { FunctionDescriptor result = entry.getValue(); TemporaryBindingTrace temporaryTrace = traces.get(functionDescriptor); - temporaryTrace.addAllMyDataTo(trace); + temporaryTrace.commit(); return result; } else { - Map maximallySpecific = Maps.newHashMap(); - meLoop: - for (Map.Entry myEntry : successfulCandidates.entrySet()) { - FunctionDescriptor me = myEntry.getValue(); - TemporaryBindingTrace myTrace = traces.get(myEntry.getKey()); - for (FunctionDescriptor other : successfulCandidates.values()) { - if (other == me) continue; - if (!moreSpecific(me, other) || moreSpecific(other, me)) continue meLoop; - } - maximallySpecific.put(me, myTrace); + FunctionDescriptor maximallySpecific = findMaximallySpecific(successfulCandidates, traces, false); + if (maximallySpecific != null) { + return maximallySpecific; } - if (maximallySpecific.size() == 1) { - Map.Entry result = maximallySpecific.entrySet().iterator().next(); - result.getValue().addAllMyDataTo(trace); - return result.getKey(); + + FunctionDescriptor maximallySpecificGenericsDiscriminated = findMaximallySpecific(successfulCandidates, traces, true); + if (maximallySpecificGenericsDiscriminated != null) { + return maximallySpecificGenericsDiscriminated; } StringBuilder stringBuilder = new StringBuilder(); @@ -446,14 +635,14 @@ public class CallResolver { stringBuilder.append(DescriptorRenderer.TEXT.render(functionDescriptor)).append(" "); } - task.reportOverallResolutionError(trace, "Overload resolution ambiguity: " + stringBuilder); + tracing.reportOverallResolutionError(trace, "Overload resolution ambiguity: " + stringBuilder); } } else if (!failedCandidates.isEmpty()) { if (failedCandidates.size() == 1) { FunctionDescriptor functionDescriptor = failedCandidates.iterator().next(); TemporaryBindingTrace temporaryTrace = traces.get(functionDescriptor); - temporaryTrace.addAllMyDataTo(trace); + temporaryTrace.commit(); } else { StringBuilder stringBuilder = new StringBuilder(); @@ -461,16 +650,65 @@ public class CallResolver { stringBuilder.append(DescriptorRenderer.TEXT.render(functionDescriptor)).append(" "); } - task.reportOverallResolutionError(trace, "None of the following functions can be called with the arguments supplied: " + stringBuilder); + tracing.reportOverallResolutionError(trace, "None of the following functions can be called with the arguments supplied: " + stringBuilder); } } else { - task.reportUnresolvedFunctionReference(trace); + tracing.reportUnresolvedFunctionReference(trace); } return null; } - private boolean moreSpecific(FunctionDescriptor f, FunctionDescriptor g) { + private boolean checkValueArgumentTypes(JetScope scope, JetTypeInferrer.Services temporaryServices, Map argumentsToParameters, Function parameterMap) { + for (Map.Entry entry : argumentsToParameters.entrySet()) { + ValueArgument valueArgument = entry.getKey(); + ValueParameterDescriptor valueParameterDescriptor = entry.getValue(); + + ValueParameterDescriptor substitutedParameter = parameterMap.apply(valueParameterDescriptor); + + assert substitutedParameter != null; + + JetType parameterType = substitutedParameter.getOutType(); + JetType type = temporaryServices.getType(scope, valueArgument.getArgumentExpression(), false, parameterType); + if (type == null || !semanticServices.getTypeChecker().isSubtypeOf(type, parameterType)) { + return false; + } + } + return true; + } + + @Nullable + private FunctionDescriptor findMaximallySpecific(Map candidates, Map traces, boolean discriminateGenericFunctions) { + Map maximallySpecific = Maps.newHashMap(); + meLoop: + for (Map.Entry myEntry : candidates.entrySet()) { + FunctionDescriptor me = myEntry.getValue(); + TemporaryBindingTrace myTrace = traces.get(myEntry.getKey()); + for (FunctionDescriptor other : candidates.values()) { + if (other == me) continue; + if (!moreSpecific(me, other, discriminateGenericFunctions) || moreSpecific(other, me, discriminateGenericFunctions)) continue meLoop; + } + maximallySpecific.put(me, myTrace); + } + if (maximallySpecific.size() == 1) { + Map.Entry result = maximallySpecific.entrySet().iterator().next(); + result.getValue().commit(); + return result.getKey(); + } + return null; + } + + /** + * Let < mean "more specific" + * Subtype < supertype + * Double < Float + * Int < Long + * Int < Short < Byte + */ + private boolean moreSpecific(FunctionDescriptor f, FunctionDescriptor g, boolean discriminateGenericFunctions) { + if (overrides(f, g)) return true; + if (overrides(g, f)) return false; + List fParams = f.getValueParameters(); List gParams = g.getValueParameters(); @@ -480,13 +718,62 @@ public class CallResolver { JetType fParamType = fParams.get(i).getOutType(); JetType gParamType = gParams.get(i).getOutType(); - if (!JetTypeChecker.INSTANCE.isSubtypeOf(fParamType, gParamType)) { + if (!semanticServices.getTypeChecker().isSubtypeOf(fParamType, gParamType) + && !numericTypeMoreSpecific(fParamType, gParamType) + ) { return false; } } + + if (discriminateGenericFunctions && isGenericFunction(f)) { + if (!isGenericFunction(g)) { + return false; + } + + // g is generic, too + + return moreSpecific(FunctionDescriptorUtil.substituteBounds(f), FunctionDescriptorUtil.substituteBounds(g), false); + } + return true; } + private boolean isGenericFunction(FunctionDescriptor f) { + return !f.getOriginal().getTypeParameters().isEmpty(); + } + + private boolean numericTypeMoreSpecific(@NotNull JetType specific, @NotNull JetType general) { + JetStandardLibrary standardLibrary = semanticServices.getStandardLibrary(); + JetType _double = standardLibrary.getDoubleType(); + JetType _float = standardLibrary.getFloatType(); + JetType _long = standardLibrary.getLongType(); + JetType _int = standardLibrary.getIntType(); + JetType _byte = standardLibrary.getByteType(); + JetType _short = standardLibrary.getShortType(); + + if (eq(specific, _double) && eq(general, _float)) return true; + if (eq(specific, _int)) { + if (eq(general, _long)) return true; + if (eq(general, _byte)) return true; + if (eq(general, _short)) return true; + } + if (eq(specific, _short) && eq(general, _byte)) return true; + return false; + } + + private boolean eq(@NotNull JetType a, @NotNull JetType b) { + return semanticServices.getTypeChecker().isSubtypeOf(a, b) && semanticServices.getTypeChecker().isSubtypeOf(b, a); + } + + private boolean overrides(@NotNull FunctionDescriptor f, @NotNull FunctionDescriptor g) { + Set overriddenFunctions = f.getOverriddenFunctions(); + FunctionDescriptor originalG = g.getOriginal(); + for (FunctionDescriptor overriddenFunction : overriddenFunctions) { + if (originalG.equals(overriddenFunction.getOriginal())) return true; + } + return false; + } + public void checkGenericBoundsInAFunctionCall(List jetTypeArguments, List typeArguments, FunctionDescriptor functionDescriptor) { Map context = Maps.newHashMap(); @@ -507,9 +794,126 @@ public class CallResolver { } @NotNull - public OverloadResolutionResult resolveExactSignature(@NotNull JetScope scope, @NotNull JetType receiver, @NotNull String name, @NotNull List jetTypes) { - // TODO - return OverloadResolutionResult.nameNotFound(); + public OverloadResolutionResult resolveExactSignature(@NotNull JetScope scope, @Nullable JetType receiverType, @NotNull String name, @NotNull List parameterTypes) { + List result = findCandidatesByExactSignature(scope, receiverType, name, parameterTypes); + return listToOverloadResolutionResult(result); + } + + private List findCandidatesByExactSignature(JetScope scope, JetType receiverType, String name, List parameterTypes) { + List result = Lists.newArrayList(); + if (receiverType != null) { + Set extensionFunctionDescriptors = scope.getFunctionGroup(name).getFunctionDescriptors(); + List nonlocal = Lists.newArrayList(); + List local = Lists.newArrayList(); + splitLexicallyLocalDescriptors(extensionFunctionDescriptors, scope.getContainingDeclaration(), local, nonlocal); + + + if (findExtensionFunctions(local, receiverType, parameterTypes, result)) { + return result; + } + + Set functionDescriptors = receiverType.getMemberScope().getFunctionGroup(name).getFunctionDescriptors(); + if (lookupExactSignature(functionDescriptors, parameterTypes, result)) { + return result; + + } + findExtensionFunctions(nonlocal, receiverType, parameterTypes, result); + return result; + } + else { + lookupExactSignature(scope.getFunctionGroup(name).getFunctionDescriptors(), parameterTypes, result); + return result; + } + } + + private boolean lookupExactSignature(Set candidates, List parameterTypes, List result) { + boolean found = false; + for (FunctionDescriptor functionDescriptor : candidates) { + if (functionDescriptor.getReceiverType() != null) continue; + if (!functionDescriptor.getTypeParameters().isEmpty()) continue; + if (!checkValueParameters(functionDescriptor, parameterTypes)) continue; + result.add(functionDescriptor); + found = true; + } + return found; + } + + private OverloadResolutionResult listToOverloadResolutionResult(List result) { + if (result.isEmpty()) { + return OverloadResolutionResult.nameNotFound(); + } + else if (result.size() == 1) { + return OverloadResolutionResult.success(result.get(0)); + } + else { + return OverloadResolutionResult.ambiguity(result); + } + } + + private boolean findExtensionFunctions(Collection candidates, JetType receiverType, List parameterTypes, List result) { + boolean found = false; + for (FunctionDescriptor functionDescriptor : candidates) { + JetType functionReceiverType = functionDescriptor.getReceiverType(); + if (functionReceiverType == null) continue; + if (!functionDescriptor.getTypeParameters().isEmpty()) continue; + if (!semanticServices.getTypeChecker().isSubtypeOf(receiverType, functionReceiverType)) continue; + if (!checkValueParameters(functionDescriptor, parameterTypes))continue; + result.add(functionDescriptor); + found = true; + } + return found; + } + + private void splitLexicallyLocalDescriptors( + Collection allDescriptors, DeclarationDescriptor containerOfTheCurrentLocality, List local, List nonlocal) { + + for (T descriptor : allDescriptors) { + if (isLocal(containerOfTheCurrentLocality, descriptor)) { + local.add(descriptor); + } + else { + nonlocal.add(descriptor); + } + } + } + + /** + * The primary case for local extensions is the following: + * + * I had a locally declared extension function or a local variable of function type called foo + * And I called it on my x + * Now, someone added function foo() to the class of x + * My code should not change + * + * thus + * + * local extension prevail over members (and members prevail over all non-local extensions) + */ + private boolean isLocal(DeclarationDescriptor containerOfTheCurrentLocality, DeclarationDescriptor candidate) { + DeclarationDescriptor parent = candidate.getContainingDeclaration(); + if (!(parent instanceof FunctionDescriptor)) { + return false; + } + FunctionDescriptor functionDescriptor = (FunctionDescriptor) parent; + DeclarationDescriptor current = containerOfTheCurrentLocality; + while (current != null) { + if (current == functionDescriptor) { + return true; + } + current = current.getContainingDeclaration(); + } + return false; + } + + private boolean checkValueParameters(@NotNull FunctionDescriptor functionDescriptor, @NotNull List parameterTypes) { + List valueParameters = functionDescriptor.getValueParameters(); + if (valueParameters.size() != parameterTypes.size()) return false; + for (int i = 0; i < valueParameters.size(); i++) { + ValueParameterDescriptor valueParameter = valueParameters.get(i); + JetType expectedType = parameterTypes.get(i); + if (!eq(expectedType, valueParameter.getOutType())) return false; + } + return true; } } diff --git a/idea/src/org/jetbrains/jet/lang/types/DescriptorSubstitutor.java b/idea/src/org/jetbrains/jet/lang/types/DescriptorSubstitutor.java new file mode 100644 index 00000000000..3d29ab80a1d --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/types/DescriptorSubstitutor.java @@ -0,0 +1,59 @@ +package org.jetbrains.jet.lang.types; + +import com.google.common.collect.Maps; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; + +import java.util.List; +import java.util.Map; + +/** + * @author abreslav + */ +public class DescriptorSubstitutor { + + @NotNull + public static TypeSubstitutor substituteTypeParameters( + @NotNull List typeParameters, + @NotNull final TypeSubstitutor originalSubstitutor, + @NotNull DeclarationDescriptor newContainingDeclaration, + @NotNull List result) { + final Map mutableSubstitution = Maps.newHashMap(); + TypeSubstitutor substitutor = TypeSubstitutor.create(new TypeSubstitutor.TypeSubstitution() { + + @Override + public TypeProjection get(TypeConstructor key) { + if (originalSubstitutor.inRange(key)) { + return originalSubstitutor.getSubstitution().get(key); + } + return mutableSubstitution.get(key); + } + + @Override + public boolean isEmpty() { + return originalSubstitutor.isEmpty() && mutableSubstitution.isEmpty(); + } + }); + + for (TypeParameterDescriptor descriptor : typeParameters) { + TypeParameterDescriptor substituted = TypeParameterDescriptor.createForFurtherModification( + newContainingDeclaration, + descriptor.getAnnotations(), + descriptor.getVariance(), + descriptor.getName(), + descriptor.getIndex()); + + mutableSubstitution.put(descriptor.getTypeConstructor(), new TypeProjection(substituted.getDefaultType())); + + for (JetType upperBound : descriptor.getUpperBounds()) { + substituted.getUpperBounds().add(substitutor.substitute(upperBound, Variance.INVARIANT)); + } + + result.add(substituted); + } + + return substitutor; + } + +} diff --git a/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java b/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java index da723b2ce6d..aa127fd1875 100644 --- a/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java +++ b/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java @@ -4,7 +4,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.jet.lang.resolve.JetScope; -import org.jetbrains.jet.lang.resolve.OverloadResolutionResult; import java.util.*; @@ -79,13 +78,6 @@ public class ErrorUtils { return ""; } - @NotNull - @Override - public OverloadResolutionResult getPossiblyApplicableFunctions(@NotNull List typeArguments, @NotNull List positionedValueArgumentTypes) { - List typeParameters = Collections.emptyList(); - return OverloadResolutionResult.success(createErrorFunction(typeParameters, positionedValueArgumentTypes)); - } - @Override public boolean isEmpty() { return false; diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index 02435b0a01f..ab69e006e06 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -21,10 +21,12 @@ import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstantResolver; import org.jetbrains.jet.lang.resolve.constants.ErrorValue; import org.jetbrains.jet.lang.resolve.constants.StringValue; import org.jetbrains.jet.lexer.JetTokens; +import org.jetbrains.jet.resolve.DescriptorRenderer; import org.jetbrains.jet.util.WritableSlice; import java.util.*; +import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET; import static org.jetbrains.jet.lang.resolve.BindingContext.STATEMENT; /** @@ -60,6 +62,11 @@ public class JetTypeInferrer { public List getAnnotations() { throw new UnsupportedOperationException(); // TODO } + + @Override + public String toString() { + return "FORBIDDEN"; + } }; public static final JetType NO_EXPECTED_TYPE = new JetType() { @NotNull @@ -89,6 +96,11 @@ public class JetTypeInferrer { public List getAnnotations() { throw new UnsupportedOperationException(); // TODO } + + @Override + public String toString() { + return "NO_EXPECTED_TYPE"; + } }; private static final ImmutableMap unaryOperationNames = ImmutableMap.builder() @@ -180,84 +192,6 @@ public class JetTypeInferrer { return typeInferrerVisitorWithNamespaces.getType(expression, new TypeInferenceContext(trace, scope, preferBlock, DataFlowInfo.getEmpty(), NO_EXPECTED_TYPE, NO_EXPECTED_TYPE)); } - @NotNull - private OverloadResolutionResult resolveNoParametersFunction(@NotNull JetType receiverType, @NotNull JetScope scope, @NotNull String name) { -// OverloadDomain overloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, name); - // No generics. Guaranteed -// return overloadDomain.getFunctionDescriptorForPositionedArguments(Collections.emptyList(), Collections.emptyList()); - return callResolver.resolveExactSignature(scope, receiverType, name, Collections.emptyList()); - } - -// private OverloadDomain getOverloadDomain( -// @Nullable final JetType receiverType, -// @NotNull final JetScope scope, -// @NotNull JetExpression calleeExpression, -// @Nullable PsiElement argumentList -// ) { -// final OverloadDomain[] result = new OverloadDomain[1]; -// final JetSimpleNameExpression[] reference = new JetSimpleNameExpression[1]; -// calleeExpression.accept(new JetVisitorVoid() { -// -// @Override -// public void visitHashQualifiedExpression(JetHashQualifiedExpression expression) { -// // a#b -- create a domain for all overloads of b in a -// throw new UnsupportedOperationException(); // TODO -// } -// -// @Override -// public void visitPredicateExpression(JetPredicateExpression expression) { -// // overload lookup for checking, but the type is receiver's type + nullable -// throw new UnsupportedOperationException(); // TODO -// } -// -// @Override -// public void visitQualifiedExpression(JetQualifiedExpression expression) { -// trace.getErrorHandler().genericError(expression.getNode(), "Unsupported [JetTypeInferrer]"); -// -// // . or ?. -// // JetType receiverType = getType(scope, expression.getReceiverExpression(), false); -// // checkNullSafety(receiverType, expression.getOperationTokenNode()); -// // -// // JetExpression selectorExpression = expression.getSelectorExpression(); -// // if (selectorExpression instanceof JetSimpleNameExpression) { -// // JetSimpleNameExpression referenceExpression = (JetSimpleNameExpression) selectorExpression; -// // String referencedName = referenceExpression.getReferencedName(); -// // -// // if (receiverType != null && referencedName != null) { -// // // No generics. Guaranteed -// // result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName); -// // reference[0] = referenceExpression; -// // } -// // } else { -// // throw new UnsupportedOperationException(); // TODO -// // } -// } -// -// @Override -// public void visitSimpleNameExpression(JetSimpleNameExpression expression) { -// // a -- create a hierarchical lookup domain for this.a -// String referencedName = expression.getReferencedName(); -// if (referencedName != null) { -// // No generics. Guaranteed -// result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName); -// reference[0] = expression; -// } -// } -// -// @Override -// public void visitExpression(JetExpression expression) { -// // create a dummy domain for the type of e -// throw new UnsupportedOperationException(expression.getText()); // TODO -// } -// -// @Override -// public void visitJetElement(JetElement element) { -// trace.getErrorHandler().genericError(element.getNode(), "Unsupported in call element"); // TODO : Message -// } -// }); -// return wrapForTracing(result[0], reference[0], argumentList, true); -// } - public CallResolver getCallResolver() { return callResolver; } @@ -284,68 +218,6 @@ public class JetTypeInferrer { } } -// private OverloadDomain wrapForTracing( -// @NotNull final OverloadDomain overloadDomain, -// @NotNull final JetReferenceExpression referenceExpression, -// @Nullable final PsiElement argumentList, -// final boolean reportErrors) { -// return new OverloadDomain() { -// @NotNull -// @Override -// public OverloadResolutionResult getFunctionDescriptorForNamedArguments(@NotNull List typeArguments, @NotNull Map valueArgumentTypes, @Nullable JetType functionLiteralArgumentType) { -// OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForNamedArguments(typeArguments, valueArgumentTypes, functionLiteralArgumentType); -// report(resolutionResult); -// return resolutionResult; -// } -// -// @NotNull -// @Override -// public OverloadResolutionResult getFunctionDescriptorForPositionedArguments(@NotNull List typeArguments, @NotNull List positionedValueArgumentTypes) { -// OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(typeArguments, positionedValueArgumentTypes); -// report(resolutionResult); -// return resolutionResult; -// } -// -// private void report(OverloadResolutionResult resolutionResult) { -// if (resolutionResult.isSuccess() || resolutionResult.singleFunction()) { -// trace.record(BindingContext.REFERENCE_TARGET, referenceExpression, resolutionResult.getFunctionDescriptor()); -// } -// if (reportErrors) { -// switch (resolutionResult.getResultCode()) { -// case NAME_NOT_FOUND: -// trace.getErrorHandler().unresolvedReference(referenceExpression); -// break; -// case SINGLE_FUNCTION_ARGUMENT_MISMATCH: -// if (argumentList != null) { -// // TODO : More helpful message. NOTE: there's a separate handling for this for constructors -// trace.getErrorHandler().genericError(argumentList.getNode(), "Arguments do not match " + DescriptorRenderer.TEXT.render(resolutionResult.getFunctionDescriptor())); -// } -// else { -// trace.getErrorHandler().unresolvedReference(referenceExpression); -// } -// break; -// case AMBIGUITY: -// if (argumentList != null) { -// // TODO : More helpful message. NOTE: there's a separate handling for this for constructors -// trace.getErrorHandler().genericError(argumentList.getNode(), "Overload ambiguity [TODO : more helpful message]"); -// } -// else { -// trace.getErrorHandler().unresolvedReference(referenceExpression); -// } -// break; -// default: -// // Not a success -// } -// } -// } -// -// @Override -// public boolean isEmpty() { -// return overloadDomain.isEmpty(); -// } -// }; -// } - public void checkFunctionReturnType(@NotNull JetScope outerScope, @NotNull JetDeclarationWithBody function, @NotNull FunctionDescriptor functionDescriptor) { checkFunctionReturnType(outerScope, function, functionDescriptor, DataFlowInfo.getEmpty()); } @@ -520,195 +392,6 @@ public class JetTypeInferrer { return result; } - - - -// @Nullable -// private JetType resolveCall1( -// @NotNull JetScope scope, -// @NotNull OverloadDomain overloadDomain, -// @NotNull JetCallElement call) { -// // 1) ends with a name -> (scope, name) to look up -// // 2) ends with something else -> just check types -// -// final List jetTypeArguments = call.getTypeArguments(); -// -// for (JetTypeProjection typeArgument : jetTypeArguments) { -// if (typeArgument.getProjectionKind() != JetProjectionKind.NONE) { -// trace.getErrorHandler().genericError(typeArgument.getNode(), "Projections are not allowed on type parameters for methods"); // TODO : better positioning -// } -// } -// -// List typeArguments = new ArrayList(); -// for (JetTypeProjection projection : jetTypeArguments) { -// // TODO : check that there's no projection -// JetTypeReference typeReference = projection.getTypeReference(); -// if (typeReference != null) { -// typeArguments.add(new TypeResolver(semanticServices, trace, true).resolveType(scope, typeReference)); -// } -// } -// -// return resolveCallWithTypeArguments(scope, overloadDomain, call, typeArguments); -// } - -// private JetType resolveCallWithTypeArguments(JetScope scope, OverloadDomain overloadDomain, JetCallElement call, List typeArguments) { -// final List valueArguments = call.getValueArguments(); -// -// boolean someNamed = false; -// for (JetValueArgument argument : valueArguments) { -// if (argument.isNamed()) { -// someNamed = true; -// break; -// } -// } -// -// final List functionLiteralArguments = call.getFunctionLiteralArguments(); -// -// // TODO : must be a check -// assert functionLiteralArguments.size() <= 1; -// -// if (someNamed) { -// // TODO : check that all are named -// trace.getErrorHandler().genericError(call.getNode(), "Named arguments are not supported"); // TODO -// -// } else { -// List positionedValueArguments = new ArrayList(); -// for (JetValueArgument argument : valueArguments) { -// JetExpression argumentExpression = argument.getArgumentExpression(); -// if (argumentExpression != null) { -// positionedValueArguments.add(argumentExpression); -// } -// } -// -// positionedValueArguments.addAll(functionLiteralArguments); -// -// List valueArgumentTypes = new ArrayList(); -// for (JetExpression valueArgument : positionedValueArguments) { -// valueArgumentTypes.add(safeGetType(scope, valueArgument, false, NO_EXPECTED_TYPE)); // TODO -// } -// -// OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(typeArguments, valueArgumentTypes); -// if (resolutionResult.isSuccess()) { -// final FunctionDescriptor functionDescriptor = resolutionResult.getFunctionDescriptor(); -// -// callResolver.checkGenericBoundsInAFunctionCall(call.getTypeArguments(), typeArguments, functionDescriptor); -// return functionDescriptor.getReturnType(); -// } -// } -// return null; -// } - - -// @Nullable -// public JetType checkTypeInitializerCall(JetScope scope, @NotNull JetTypeReference typeReference, @NotNull JetCallElement call) { -// JetTypeElement typeElement = typeReference.getTypeElement(); -// if (typeElement instanceof JetUserType) { -// JetUserType userType = (JetUserType) typeElement; -// // TODO : to infer constructor parameters, one will need to -// // 1) resolve a _class_ from the typeReference -// // 2) rely on the overload domain of constructors of this class to infer type arguments -// // For now we assume that the type arguments are provided, and thus the typeReference can be -// // resolved into a valid type -// JetType receiverType = typeResolver.resolveType(scope, typeReference); -// DeclarationDescriptor declarationDescriptor = receiverType.getConstructor().getDeclarationDescriptor(); -// if (declarationDescriptor instanceof ClassDescriptor) { -// ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor; -// -// for (JetTypeProjection typeProjection : userType.getTypeArguments()) { -// switch (typeProjection.getProjectionKind()) { -// case IN: -// case OUT: -// case STAR: -// // TODO : Bug in the editor -// trace.getErrorHandler().genericError(typeProjection.getProjectionNode(), "Projections are not allowed in constructor type arguments"); -// break; -// case NONE: -// break; -// } -// } -// -// JetSimpleNameExpression referenceExpression = userType.getReferenceExpression(); -// if (referenceExpression != null) { -// return checkClassConstructorCall(scope, referenceExpression, classDescriptor, receiverType, call); -// } -// } -// else { -// trace.getErrorHandler().genericError(((JetElement) call).getNode(), "Calling a constructor is only supported for ordinary classes"); // TODO : review the message -// } -// return null; -// } -// else { -// if (typeElement != null) { -// trace.getErrorHandler().genericError(typeElement.getNode(), "Calling a constructor is only supported for ordinary classes"); // TODO : Better message -// } -// } -// return null; -// } - -// @Nullable -// public JetType checkClassConstructorCall( -// @NotNull JetScope scope, -// @NotNull JetReferenceExpression referenceExpression, -// @NotNull ClassDescriptor classDescriptor, -// @NotNull JetType receiverType, -// @NotNull JetCallElement call) { -// // When one writes 'new Array(...)' this does not make much sense, and an instance -// // of 'Array' must be created anyway. -// // Thus, we should either prohibit projections in type arguments in such contexts, -// // or treat them as an automatic upcast to the desired type, i.e. for the user not -// // to be forced to write -// // val a : Array = new Array(...) -// // NOTE: Array may be a bad example here, some classes may have substantial functionality -// // not involving their type parameters -// // -// // The code below upcasts the type automatically -// -// -// FunctionGroup constructors = classDescriptor.getConstructors(); -//// OverloadDomain constructorsOverloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(null, constructors); -// -// JetType constructorReturnedType; -// if (call instanceof JetDelegatorToThisCall) { -// List typeArguments = receiverType.getArguments(); -// -// List projectionsStripped = Lists.newArrayList(); -// for (TypeProjection typeArgument : typeArguments) { -// projectionsStripped.add(typeArgument.getType()); -// } -// -// constructorReturnedType = resolveCallWithTypeArguments( -// scope, -// wrapForTracing(constructorsOverloadDomain, referenceExpression, call.getValueArgumentList(), false), -// call, projectionsStripped); -// } -// else { -// constructorReturnedType = callResolver.resolveCall( -// scope, call, NO_EXPECTED_TYPE); -//// wrapForTracing(constructorsOverloadDomain, referenceExpression, call.getValueArgumentList(), false), -//// call); -// } -// if (constructorReturnedType == null && !ErrorUtils.isErrorType(receiverType)) { -// DeclarationDescriptor declarationDescriptor = receiverType.getConstructor().getDeclarationDescriptor(); -// assert declarationDescriptor != null; -// trace.record(BindingContext.REFERENCE_TARGET, referenceExpression, declarationDescriptor); -// // TODO : more helpful message -// JetValueArgumentList argumentList = call.getValueArgumentList(); -// final String errorMessage = "Cannot find a constructor overload for class " + classDescriptor.getName() + " with these arguments"; -// if (argumentList != null) { -// trace.getErrorHandler().genericError(argumentList.getNode(), errorMessage); -// } -// else { -// trace.getErrorHandler().genericError(call.getNode(), errorMessage); -// } -// constructorReturnedType = receiverType; -// } -// // If no upcast needed: -// return constructorReturnedType; -// -// // Automatic upcast: -// // result = receiverType; -// } - //TODO private JetType enrichOutType(JetExpression expression, JetType initialType, @NotNull TypeInferenceContext context) { if (expression == null) return initialType; @@ -736,32 +419,31 @@ public class JetTypeInferrer { return expressionType; } VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(expression, context); - if (variableDescriptor == null) return expressionType; - - JetType enrichedType = null; - List possibleTypes = Lists.newArrayList(context.dataFlowInfo.getPossibleTypes(variableDescriptor)); - Collections.reverse(possibleTypes); - for (JetType possibleType: possibleTypes) { - if (semanticServices.getTypeChecker().isSubtypeOf(possibleType, context.expectedType)) { - enrichedType = possibleType; - break; + boolean appropriateTypeFound = false; + if (variableDescriptor != null) { + List possibleTypes = Lists.newArrayList(context.dataFlowInfo.getPossibleTypes(variableDescriptor)); + Collections.reverse(possibleTypes); + for (JetType possibleType : possibleTypes) { + if (semanticServices.getTypeChecker().isSubtypeOf(possibleType, context.expectedType)) { + appropriateTypeFound = true; + break; + } + } + if (!appropriateTypeFound) { + JetType notnullType = context.dataFlowInfo.getOutType(variableDescriptor); + if (notnullType != null && semanticServices.getTypeChecker().isSubtypeOf(notnullType, context.expectedType)) { + appropriateTypeFound = true; + } } } - if (enrichedType == null) { - enrichedType = context.dataFlowInfo.getOutType(variableDescriptor); - } - if (enrichedType == null) { - enrichedType = expressionType; - } - - if (!semanticServices.getTypeChecker().isSubtypeOf(enrichedType, context.expectedType)) { + if (!appropriateTypeFound) { context.trace.getErrorHandler().typeMismatch(expression, context.expectedType, expressionType); - } else { - checkAutoCast(expression, context.expectedType, variableDescriptor, context.trace); + return expressionType; } - return enrichedType; + checkAutoCast(expression, context.expectedType, variableDescriptor, context.trace); + return context.expectedType; } - + private void checkAutoCast(JetExpression expression, JetType type, VariableDescriptor variableDescriptor, BindingTrace trace) { if (variableDescriptor.isVar()) { trace.getErrorHandler().genericError(expression.getNode(), "Automatic cast to " + type + " is impossible, because variable " + variableDescriptor.getName() + " is mutable"); @@ -793,7 +475,7 @@ public class JetTypeInferrer { VariableDescriptor variableDescriptor = null; if (receiverExpression instanceof JetSimpleNameExpression) { JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) receiverExpression; - DeclarationDescriptor declarationDescriptor = context.trace.getBindingContext().get(BindingContext.REFERENCE_TARGET, nameExpression); + DeclarationDescriptor declarationDescriptor = context.trace.getBindingContext().get(REFERENCE_TARGET, nameExpression); if (declarationDescriptor instanceof VariableDescriptor) { variableDescriptor = (VariableDescriptor) declarationDescriptor; } @@ -956,7 +638,7 @@ public class JetTypeInferrer { context.trace.getErrorHandler().unresolvedReference(expression); } else { - context.trace.record(BindingContext.REFERENCE_TARGET, expression, property); + context.trace.record(REFERENCE_TARGET, expression, property); return context.services.checkEnrichedType(property.getOutType(), expression, context); } } @@ -965,7 +647,7 @@ public class JetTypeInferrer { if (referencedName != null) { VariableDescriptor variable = context.scope.getVariable(referencedName); if (variable != null) { - context.trace.record(BindingContext.REFERENCE_TARGET, expression, variable); + context.trace.record(REFERENCE_TARGET, expression, variable); JetType result = variable.getOutType(); if (result == null) { context.trace.getErrorHandler().genericError(expression.getNode(), "This variable is not readable in this context"); @@ -983,7 +665,7 @@ public class JetTypeInferrer { else { context.trace.getErrorHandler().genericError(expression.getNode(), "Classifier " + classifier.getName() + " does not have a class object"); } - context.trace.record(BindingContext.REFERENCE_TARGET, expression, classifier); + context.trace.record(REFERENCE_TARGET, expression, classifier); return context.services.checkEnrichedType(result, expression, context); } else { @@ -1019,7 +701,7 @@ public class JetTypeInferrer { if (namespace == null) { return null; } - context.trace.record(BindingContext.REFERENCE_TARGET, expression, namespace); + context.trace.record(REFERENCE_TARGET, expression, namespace); return namespace.getNamespaceType(); } @@ -1077,6 +759,14 @@ public class JetTypeInferrer { List parameterTypes = new ArrayList(); List valueParameterDescriptors = Lists.newArrayList(); List parameters = functionLiteral.getValueParameters(); + JetType expectedType = context.expectedType; + + List valueParameters = null; + boolean functionTypeExpected = expectedType != NO_EXPECTED_TYPE && JetStandardClasses.isFunctionType(expectedType); + if (functionTypeExpected) { + valueParameters = JetStandardClasses.getValueParameters(functionDescriptor, expectedType); + } + for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) { JetParameter parameter = parameters.get(i); JetTypeReference typeReference = parameter.getTypeReference(); @@ -1086,15 +776,31 @@ public class JetTypeInferrer { type = context.typeResolver.resolveType(context.scope, typeReference); } else { - context.trace.getErrorHandler().genericError(parameter.getNode(), "Type inference for parameters is not implemented yet"); - type = ErrorUtils.createErrorType("Not inferred"); + if (valueParameters != null) { + type = valueParameters.get(i).getOutType(); + } + else { + context.trace.getErrorHandler().genericError(parameter.getNode(), "Cannot infer a type for this parameter. To specify it explicitly use the {(p : Type) => ...} notation"); + type = ErrorUtils.createErrorType("Cannot be inferred"); + } } ValueParameterDescriptor valueParameterDescriptor = context.classDescriptorResolver.resolveValueParameterDescriptor(functionDescriptor, parameter, i, type); parameterTypes.add(valueParameterDescriptor.getOutType()); valueParameterDescriptors.add(valueParameterDescriptor); } - JetType effectiveReceiverType = receiverTypeRef == null ? null : receiverType; + JetType effectiveReceiverType; + if (receiverTypeRef == null) { + if (functionTypeExpected) { + effectiveReceiverType = JetStandardClasses.getReceiverType(expectedType); + } + else { + effectiveReceiverType = null; + } + } + else { + effectiveReceiverType = receiverType; + } functionDescriptor.initialize(effectiveReceiverType, Collections.emptyList(), valueParameterDescriptors, null); context.trace.record(BindingContext.FUNCTION, expression, functionDescriptor); @@ -1106,15 +812,17 @@ public class JetTypeInferrer { context.services.checkFunctionReturnType(functionInnerScope, expression, returnType, context.dataFlowInfo); } else { - if (context.expectedType != NO_EXPECTED_TYPE && JetStandardClasses.isFunctionType(context.expectedType)) { - returnType = JetStandardClasses.getReturnType(context.expectedType); + if (functionTypeExpected) { + returnType = JetStandardClasses.getReturnType(expectedType); } returnType = getBlockReturnedType(functionInnerScope, functionLiteral.getBodyExpression(), context.replaceExpectedType(returnType)); } JetType safeReturnType = returnType == null ? ErrorUtils.createErrorType("") : returnType; functionDescriptor.setReturnType(safeReturnType); + if (functionTypeExpected) { + } return context.services.checkType(JetStandardClasses.getFunctionType(Collections.emptyList(), effectiveReceiverType, parameterTypes, safeReturnType), expression, context); } @@ -1293,8 +1001,8 @@ public class JetTypeInferrer { else { throw new UnsupportedOperationException(); // TODO } - context.trace.record(BindingContext.REFERENCE_TARGET, targetLabel, declarationDescriptor); - context.trace.record(BindingContext.REFERENCE_TARGET, expression.getThisReference(), declarationDescriptor); + context.trace.record(REFERENCE_TARGET, targetLabel, declarationDescriptor); + context.trace.record(REFERENCE_TARGET, expression.getThisReference(), declarationDescriptor); } else if (size == 0) { // This uses the info written by the control flow processor @@ -1307,8 +1015,8 @@ public class JetTypeInferrer { thisType = JetStandardClasses.getNothingType(); } else { - context.trace.record(BindingContext.REFERENCE_TARGET, targetLabel, declarationDescriptor); - context.trace.record(BindingContext.REFERENCE_TARGET, expression.getThisReference(), declarationDescriptor); + context.trace.record(REFERENCE_TARGET, targetLabel, declarationDescriptor); + context.trace.record(REFERENCE_TARGET, expression.getThisReference(), declarationDescriptor); } } else { @@ -1328,7 +1036,7 @@ public class JetTypeInferrer { DeclarationDescriptor declarationDescriptorForUnqualifiedThis = context.scope.getDeclarationDescriptorForUnqualifiedThis(); if (declarationDescriptorForUnqualifiedThis != null) { - context.trace.record(BindingContext.REFERENCE_TARGET, expression.getThisReference(), declarationDescriptorForUnqualifiedThis); + context.trace.record(REFERENCE_TARGET, expression.getThisReference(), declarationDescriptorForUnqualifiedThis); } } @@ -1445,9 +1153,10 @@ public class JetTypeInferrer { @Override public void visitWhenConditionCall(JetWhenConditionCall condition) { JetExpression callSuffixExpression = condition.getCallSuffixExpression(); - JetScope compositeScope = new ScopeWithReceiver(context.scope, subjectType, semanticServices.getTypeChecker()); +// JetScope compositeScope = new ScopeWithReceiver(context.scope, subjectType, semanticServices.getTypeChecker()); if (callSuffixExpression != null) { - JetType selectorReturnType = getType(compositeScope, callSuffixExpression, false, context); +// JetType selectorReturnType = getType(compositeScope, callSuffixExpression, false, context); + JetType selectorReturnType = getSelectorReturnType(subjectType, callSuffixExpression, context);//getType(compositeScope, callSuffixExpression, false, context); ensureBooleanResultWithCustomSubject(callSuffixExpression, selectorReturnType, "This expression", context); context.services.checkNullSafety(subjectType, condition.getOperationTokenNode(), getCalleeFunctionDescriptor(callSuffixExpression, context)); } @@ -1923,7 +1632,7 @@ public class JetTypeInferrer { @Nullable private JetType checkIterableConvention(@NotNull JetType type, @NotNull ASTNode reportErrorsOn, TypeInferenceContext context) { - OverloadResolutionResult iteratorResolutionResult = context.services.resolveNoParametersFunction(type, context.scope, "iterator"); + OverloadResolutionResult iteratorResolutionResult = context.services.callResolver.resolveExactSignature(context.scope, type, "iterator", Collections.emptyList()); if (iteratorResolutionResult.isSuccess()) { JetType iteratorType = iteratorResolutionResult.getFunctionDescriptor().getReturnType(); boolean hasNextFunctionSupported = checkHasNextFunctionSupport(reportErrorsOn, iteratorType, context); @@ -1936,7 +1645,7 @@ public class JetTypeInferrer { context.trace.getErrorHandler().genericError(reportErrorsOn, "Loop range must have an 'iterator().hasNext()' function or an 'iterator().hasNext' property"); } - OverloadResolutionResult nextResolutionResult = context.services.resolveNoParametersFunction(iteratorType, context.scope, "next"); + OverloadResolutionResult nextResolutionResult = context.services.callResolver.resolveExactSignature(context.scope, iteratorType, "next", Collections.emptyList()); if (nextResolutionResult.isAmbiguity()) { context.trace.getErrorHandler().genericError(reportErrorsOn, "Method 'iterator().next()' is ambiguous for this expression"); } else if (nextResolutionResult.isNothing()) { @@ -1956,7 +1665,7 @@ public class JetTypeInferrer { } private boolean checkHasNextFunctionSupport(@NotNull ASTNode reportErrorsOn, @NotNull JetType iteratorType, TypeInferenceContext context) { - OverloadResolutionResult hasNextResolutionResult = context.services.resolveNoParametersFunction(iteratorType, context.scope, "hasNext"); + OverloadResolutionResult hasNextResolutionResult = context.services.callResolver.resolveExactSignature(context.scope, iteratorType, "hasNext", Collections.emptyList()); if (hasNextResolutionResult.isAmbiguity()) { context.trace.getErrorHandler().genericError(reportErrorsOn, "Method 'iterator().hasNext()' is ambiguous for this expression"); } else if (hasNextResolutionResult.isNothing()) { @@ -1988,89 +1697,6 @@ public class JetTypeInferrer { return true; } -// @Override -// public void visitNewExpression(JetNewExpression expression) { -// // TODO : type argument inference -// JetTypeReference typeReference = expression.getTypeReference(); -// if (typeReference != null) { -// result = checkTypeInitializerCall(scope, typeReference, expression); -// } -// } - -// private void resolveCallWithExplicitName(@NotNull JetScope scope, JetExpression receiver, String functionName, List resolvedTypeArguments, List valueArguments) { -// -// boolean someNamed = false; -// boolean allNamed = true; -// for (ValueArgumentPsi valueArgument : valueArguments) { -// if (valueArgument.isNamed()) { -// context.trace.getErrorHandler().genericError(valueArgument.asElement().getNode(), "Named arguments are not supported"); -// someNamed = true; -// } -// else { -// allNamed = false; -// } -// } -// if (someNamed) { -// return; // TODO -// } -// if (someNamed && !allNamed) { -// // TODO function literals outside parentheses -// } -// -// ErrorHandlerWithRegions errorHandler = context.trace.getErrorHandler(); -// -// // 1. resolve 'receiver' in 'scope' with expected type 'NO_EXPECTED_TYPE' -// errorHandler.openRegion(); -// JetType receiverType = JetTypeInferrer.this.getType(scope, receiver, false, NO_EXPECTED_TYPE); -// // for each applicable function in 'receiverType' -// Set allFunctions = receiverType.getMemberScope().getFunctionGroup(functionName).getFunctionDescriptors(); -// Map> applicableFunctions = Maps.newHashMap(); -// int typeArgCount = resolvedTypeArguments.size(); -// int valueArgCount = valueArguments.size(); -// for (FunctionDescriptor functionDescriptor : allFunctions) { -// if (typeArgCount == 0 || functionDescriptor.getTypeParameters().size() == typeArgCount) { -// if (FunctionDescriptorUtil.getMinimumArity(functionDescriptor) <= valueArgCount && -// valueArgCount <= FunctionDescriptorUtil.getMaximumArity(functionDescriptor)) { -// // get expected types for value parameters -// if (typeArgCount > 0) { -// FunctionDescriptor substitutedFunctionDescriptor = FunctionDescriptorUtil.substituteFunctionDescriptor(resolvedTypeArguments, functionDescriptor); -// } -// else { -// FunctionDescriptor substitutedFunctionDescriptor = FunctionDescriptorUtil.substituteFunctionDescriptor(TypeUtils.getDefaultTypes(functionDescriptor.getTypeParameters()), functionDescriptor); -// List valueArgumentTypes = getArgumentTypes(substitutedFunctionDescriptor, valueArguments); -// if (valueArgumentTypes == null) { -// Map noExpectedTypes = Maps.newHashMap(); -// for (TypeParameterDescriptor typeParameterDescriptor : functionDescriptor.getTypeParameters()) { -// noExpectedTypes.put(typeParameterDescriptor.getTypeConstructor(), new TypeProjection(NO_EXPECTED_TYPE)); -// } -// substitutedFunctionDescriptor = functionDescriptor.substitute(TypeSubstitutor.create(noExpectedTypes)); -// valueArgumentTypes = getArgumentTypes(substitutedFunctionDescriptor, valueArguments); -// } -// if (valueArgumentTypes != null) { -// List typeArguments = solveConstraintSystem(functionDescriptor, valueArgumentTypes, expectedReturnType); -// if (typeArguments != null) { -// applicableFunctions.put(functionDescriptor, typeArguments); -// } -// } -// } -// // type-check the parameters -// // if something was found (one or many options -// errorHandler.closeAndCommitCurrentRegion(); -// // otherwise -// errorHandler.closeAndReturnCurrentRegion(); -// -// } -// } -// } -// // get expected types for value parameters -// // type-check the parameters -// // if something was found (one or many options -// errorHandler.closeAndCommitCurrentRegion(); -// // otherwise -// errorHandler.closeAndReturnCurrentRegion(); -// -// } - @Override public JetType visitHashQualifiedExpression(JetHashQualifiedExpression expression, TypeInferenceContext context) { context.trace.getErrorHandler().genericError(expression.getOperationTokenNode(), "Unsupported"); @@ -2087,10 +1713,8 @@ public class JetTypeInferrer { if (receiverType == null) return null; // Clean resolution: no autocasts - TemporaryBindingTrace cleanResolutionTrace = new TemporaryBindingTrace(context.trace.getBindingContext()); + TemporaryBindingTrace cleanResolutionTrace = TemporaryBindingTrace.create(context.trace); TypeInferenceContext cleanResolutionContext = context.replaceBindingTrace(cleanResolutionTrace); -// ErrorHandler errorHandler = context.trace.getErrorHandler(); -// errorHandler.openRegion(); JetType selectorReturnType = getSelectorReturnType(receiverType, selectorExpression, cleanResolutionContext); //TODO move further @@ -2109,24 +1733,24 @@ public class JetTypeInferrer { List possibleTypes = Lists.newArrayList(context.dataFlowInfo.getPossibleTypes(variableDescriptor)); Collections.reverse(possibleTypes); - TemporaryBindingTrace autocastResolutionTrace = new TemporaryBindingTrace(context.trace.getBindingContext()); + TemporaryBindingTrace autocastResolutionTrace = TemporaryBindingTrace.create(context.trace); TypeInferenceContext autocastResolutionContext = context.replaceBindingTrace(autocastResolutionTrace); for (JetType possibleType : possibleTypes) { selectorReturnType = getSelectorReturnType(possibleType, selectorExpression, autocastResolutionContext); if (selectorReturnType != null) { context.services.checkAutoCast(receiverExpression, possibleType, variableDescriptor, autocastResolutionTrace); - autocastResolutionTrace.addAllMyDataTo(context.trace); + autocastResolutionTrace.commit(); somethingFound = true; break; } else { - autocastResolutionTrace = new TemporaryBindingTrace(context.trace.getBindingContext()); + autocastResolutionTrace = TemporaryBindingTrace.create(context.trace); autocastResolutionContext = context.replaceBindingTrace(autocastResolutionTrace); } } } if (!somethingFound) { - cleanResolutionTrace.addAllMyDataTo(context.trace); + cleanResolutionTrace.commit(); } } @@ -2170,7 +1794,7 @@ public class JetTypeInferrer { @Override public void visitReferenceExpression(JetReferenceExpression referenceExpression) { - DeclarationDescriptor declarationDescriptor = context.trace.getBindingContext().get(BindingContext.REFERENCE_TARGET, referenceExpression); + DeclarationDescriptor declarationDescriptor = context.trace.getBindingContext().get(REFERENCE_TARGET, referenceExpression); if (declarationDescriptor instanceof FunctionDescriptor) { result[0] = (FunctionDescriptor) declarationDescriptor; } @@ -2202,24 +1826,15 @@ public class JetTypeInferrer { return result[0]; } - private JetType getCallExpressionType(@Nullable JetType receiverType, @NotNull JetCallExpression callExpression, TypeInferenceContext context) { -// JetExpression calleeExpression = callExpression.getCalleeExpression(); -// if (calleeExpression == null) { -// return null; -// } -// OverloadDomain overloadDomain = context.services.getOverloadDomain(receiverType, context.scope, calleeExpression, callExpression.getValueArgumentList()); - JetScope scope = receiverType == null ? context.scope : new ScopeWithReceiver(context.scope, receiverType, JetTypeChecker.INSTANCE); - return context.services.callResolver.resolveCall(scope, callExpression, context.expectedType); -// return context.services.resolveCall(context.scope, overloadDomain, callExpression); - } - - private JetType getSelectorReturnType(JetType receiverType, JetExpression selectorExpression, TypeInferenceContext context) { + @Nullable + private JetType getSelectorReturnType(@Nullable JetType receiverType, @NotNull JetExpression selectorExpression, @NotNull TypeInferenceContext context) { if (selectorExpression instanceof JetCallExpression) { - return getCallExpressionType(receiverType, (JetCallExpression) selectorExpression, context); + return context.services.callResolver.resolveCall(context.scope, receiverType, (JetCallExpression) selectorExpression, context.expectedType); } else if (selectorExpression instanceof JetSimpleNameExpression) { - JetScope compositeScope = new ScopeWithReceiver(context.scope, receiverType, semanticServices.getTypeChecker()); - return getType(compositeScope, selectorExpression, false, context); +// JetScope compositeScope = new ScopeWithReceiver(context.scope, receiverType, semanticServices.getTypeChecker()); + JetScope scope = receiverType != null ? receiverType.getMemberScope() : context.scope; + return getType(scope, selectorExpression, false, context); } else if (selectorExpression != null) { // TODO : not a simple name -> resolve in scope, expect property type or a function type @@ -2230,8 +1845,8 @@ public class JetTypeInferrer { @Override public JetType visitCallExpression(JetCallExpression expression, TypeInferenceContext context) { -// return context.services.checkType(context.services.resolveCall(context.scope, expression, context.expectedType), expression, context); - return context.services.checkType(getCallExpressionType(null, expression, context), expression, context); + JetType expressionType = context.services.callResolver.resolveCall(context.scope, null, expression, context.expectedType); + return context.services.checkType(expressionType, expression, context); } @Override @@ -2271,7 +1886,7 @@ public class JetTypeInferrer { CallMaker.makeCall(expression), expression.getOperationSign(), name, - baseExpression, + receiverType, context.expectedType); if (functionDescriptor == null) return null; @@ -2346,21 +1961,24 @@ public class JetTypeInferrer { OverloadResolutionResult resolutionResult = context.services.callResolver.resolveExactSignature( context.scope, leftType, "equals", Collections.singletonList(JetStandardClasses.getNullableAnyType())); - FunctionDescriptor equals; if (resolutionResult.isSuccess()) { - equals = resolutionResult.getFunctionDescriptor(); - } - else { - // TODO : Report ambiguous equals - equals = null; - } - if (equals != null) { + FunctionDescriptor equals = resolutionResult.getFunctionDescriptor(); + context.trace.record(REFERENCE_TARGET, operationSign, equals); if (ensureBooleanResult(operationSign, name, equals.getReturnType(), context)) { ensureNonemptyIntersectionOfOperandTypes(expression, context); } } else { - context.trace.getErrorHandler().genericError(operationSign.getNode(), "No method 'equals(Any?) : Boolean' available"); + if (resolutionResult.isAmbiguity()) { + StringBuilder stringBuilder = new StringBuilder(); + for (FunctionDescriptor functionDescriptor : resolutionResult.getFunctionDescriptors()) { + stringBuilder.append(DescriptorRenderer.TEXT.render(functionDescriptor)).append(" "); + } + context.trace.getErrorHandler().genericError(operationSign.getNode(), "Ambiguous function: " + stringBuilder); + } + else { + context.trace.getErrorHandler().genericError(operationSign.getNode(), "No method 'equals(Any?) : Boolean' available"); + } } } } @@ -2416,11 +2034,12 @@ public class JetTypeInferrer { private void checkInExpression(JetSimpleNameExpression operationSign, JetExpression left, JetExpression right, TypeInferenceContext context) { String name = "contains"; + JetType receiverType = context.services.safeGetType(context.scope, right, false, NO_EXPECTED_TYPE); FunctionDescriptor functionDescriptor = context.services.callResolver.resolveCallWithGivenName( context.scope, CallMaker.makeCall(operationSign, Collections.singletonList(left)), operationSign, - name, right, context.expectedType); + name, receiverType, context.expectedType); JetType containsType = functionDescriptor != null ? functionDescriptor.getReturnType() : null; ensureBooleanResult(operationSign, name, containsType, context); } @@ -2496,9 +2115,6 @@ public class JetTypeInferrer { TypeInferenceContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE); JetExpression arrayExpression = expression.getArrayExpression(); JetType receiverType = getType(context.scope, arrayExpression, false, context); -// List indexArguments = expression.getIndexArguments(); -// List argumentTypes = getTypes(context.scope, indexArguments, context); -// if (argumentTypes == null) return null; if (receiverType != null) { FunctionDescriptor functionDescriptor = context.services.callResolver.resolveCallWithGivenName( @@ -2506,10 +2122,9 @@ public class JetTypeInferrer { CallMaker.makeCall(expression, expression.getIndexExpressions()), expression, "get", - arrayExpression, + receiverType, context.expectedType); if (functionDescriptor != null) { -// checkNullSafety(receiverType, expression.getIndexArguments().get(0).getNode(), functionDescriptor); return context.services.checkType(functionDescriptor.getReturnType(), expression, contextWithExpectedType); } } @@ -2518,17 +2133,13 @@ public class JetTypeInferrer { @Nullable protected JetType getTypeForBinaryCall(JetScope scope, String name, TypeInferenceContext context, JetBinaryExpression binaryExpression) { - // JetType leftType = getType(scope, left, false, context); -// JetType rightType = getType(scope, right, false, context); -// if (leftType == null || rightType == null) { -// return null; -// } + JetType leftType = getType(scope, binaryExpression.getLeft(), false, context); FunctionDescriptor functionDescriptor = context.services.callResolver.resolveCallWithGivenName( scope, CallMaker.makeCall(binaryExpression), binaryExpression.getOperationReference(), name, - binaryExpression.getLeft(), + leftType, context.expectedType); if (functionDescriptor != null) { // if (leftType.isNullable()) { @@ -2636,12 +2247,6 @@ public class JetTypeInferrer { public TypeInferrerVisitorWithWritableScope(@NotNull WritableScope scope) { this.scope = scope; } - -// public TypeInferrerVisitorWithWritableScope(@NotNull BindingTrace trace, @NotNull JetScope scope) { -// super(trace); -// this.scope = newWritableScopeImpl(scope, trace).setDebugName("Block scope"); -// } - @Override public JetType visitObjectDeclaration(JetObjectDeclaration declaration, TypeInferenceContext context) { @@ -2715,7 +2320,9 @@ public class JetTypeInferrer { protected JetType visitAssignmentOperation(JetBinaryExpression expression, TypeInferenceContext context) { IElementType operationType = expression.getOperationReference().getReferencedNameElementType(); String name = assignmentOperationNames.get(operationType); - JetType assignmentOperationType = getTypeForBinaryCall(scope, name, context, expression); + + TemporaryBindingTrace temporaryBindingTrace = TemporaryBindingTrace.create(context.trace); + JetType assignmentOperationType = getTypeForBinaryCall(scope, name, context.replaceBindingTrace(temporaryBindingTrace), expression); if (assignmentOperationType == null) { String counterpartName = binaryOperationNames.get(assignmentOperationCounterparts.get(operationType)); @@ -2725,6 +2332,9 @@ public class JetTypeInferrer { context.trace.record(BindingContext.VARIABLE_REASSIGNMENT, expression); } } + else { + temporaryBindingTrace.commit(); + } return null; } @@ -2750,15 +2360,8 @@ public class JetTypeInferrer { } private JetType resolveArrayAccessToLValue(JetArrayAccessExpression arrayAccessExpression, JetExpression rightHandSide, JetSimpleNameExpression operationSign, TypeInferenceContext context) { -// TypeInferenceContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE); -// List argumentTypes = getTypes(scope, arrayAccessExpression.getIndexExpressions(), context); -// if (argumentTypes == null) return null; -// JetType rhsType = getType(scope, rightHandSide, false, context); -// if (rhsType == null) return null; -// argumentTypes.add(rhsType); -// -// JetType receiverType = getType(scope, arrayAccessExpression.getArrayExpression(), false, context); -// if (receiverType == null) return null; + JetType receiverType = getType(scope, arrayAccessExpression.getArrayExpression(), false, context); + if (receiverType == null) return null; // Call call = CallMaker.makeCall(arrayAccessExpression, rightHandSide); // // TODO : nasty hack: effort is duplicated @@ -2771,7 +2374,7 @@ public class JetTypeInferrer { scope, call, operationSign, - "set", arrayAccessExpression.getArrayExpression(), NO_EXPECTED_TYPE); + "set", receiverType, NO_EXPECTED_TYPE); if (functionDescriptor == null) return null; return context.services.checkType(functionDescriptor.getReturnType(), arrayAccessExpression, context); } diff --git a/idea/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java b/idea/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java index 441054b7b3f..7f548f50877 100644 --- a/idea/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java +++ b/idea/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java @@ -6,7 +6,6 @@ import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; import org.jetbrains.jet.lang.resolve.SubstitutingScope; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -16,6 +15,18 @@ import java.util.Map; public class TypeSubstitutor { public interface TypeSubstitution { + TypeSubstitution EMPTY = new TypeSubstitution() { + @Override + public TypeProjection get(TypeConstructor key) { + return null; + } + + @Override + public boolean isEmpty() { + return true; + } + }; + @Nullable TypeProjection get(TypeConstructor key); boolean isEmpty(); @@ -39,7 +50,7 @@ public class TypeSubstitutor { } } - public static final TypeSubstitutor EMPTY = create(Collections.emptyMap()); + public static final TypeSubstitutor EMPTY = create(TypeSubstitution.EMPTY); public static final class SubstitutionException extends Exception { public SubstitutionException(String message) { @@ -61,14 +72,23 @@ public class TypeSubstitutor { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - private final @NotNull TypeSubstitution substitutionContext; + private final @NotNull TypeSubstitution substitution; - private TypeSubstitutor(@NotNull TypeSubstitution substitutionContext) { - this.substitutionContext = substitutionContext; + protected TypeSubstitutor(@NotNull TypeSubstitution substitution) { + this.substitution = substitution; + } + + public boolean inRange(@NotNull TypeConstructor typeConstructor) { + return substitution.get(typeConstructor) != null; } public boolean isEmpty() { - return substitutionContext.isEmpty(); + return substitution.isEmpty(); + } + + @NotNull + public TypeSubstitution getSubstitution() { + return substitution; } @NotNull @@ -100,7 +120,7 @@ public class TypeSubstitutor { @NotNull private JetType unsafeSubstitute(@NotNull JetType type, @NotNull Variance howThisTypeIsUsed) throws SubstitutionException { TypeConstructor constructor = type.getConstructor(); - TypeProjection value = substitutionContext.get(constructor); + TypeProjection value = substitution.get(constructor); if (value != null) { assert constructor.getDeclarationDescriptor() instanceof TypeParameterDescriptor; @@ -122,7 +142,7 @@ public class TypeSubstitutor { TypeProjection argument = arguments.get(i); TypeParameterDescriptor parameterDescriptor = subjectType.getConstructor().getParameters().get(i); newArguments.add(substituteInProjection( - substitutionContext, + substitution, argument, parameterDescriptor, callSiteVariance)); diff --git a/idea/src/org/jetbrains/jet/lang/types/inference/ConstraintSystem.java b/idea/src/org/jetbrains/jet/lang/types/inference/ConstraintSystem.java index d6cdb5ec09b..175f6647891 100644 --- a/idea/src/org/jetbrains/jet/lang/types/inference/ConstraintSystem.java +++ b/idea/src/org/jetbrains/jet/lang/types/inference/ConstraintSystem.java @@ -102,7 +102,7 @@ public class ConstraintSystem { } } - System.out.println("minimal solution from lowerbounds for " + this + " is " + commonSupertype); + println("minimal solution from lowerbounds for " + this + " is " + commonSupertype); value = new KnownType(commonSupertype); } else { @@ -121,7 +121,7 @@ public class ConstraintSystem { return value; } - + private Set getTypes(Set lowerBounds) { Set types = Sets.newHashSet(); for (TypeValue lowerBound : lowerBounds) { @@ -206,7 +206,7 @@ public class ConstraintSystem { } private void addSubtypingConstraintOnTypeValues(TypeValue typeValueForLower, TypeValue typeValueForUpper) { - System.out.println(typeValueForLower + " :< " + typeValueForUpper); + println(typeValueForLower + " :< " + typeValueForUpper); typeValueForLower.getUpperBounds().add(typeValueForUpper); typeValueForUpper.getLowerBounds().add(typeValueForLower); } @@ -271,14 +271,14 @@ public class ConstraintSystem { JetType boundingType = solution.getSubstitutor().substitute(upperBound.getValue().getType(), Variance.INVARIANT); if (!typeChecker.isSubtypeOf(type, boundingType)) { // TODO solution.registerError(); - System.out.println("Constraint violation: " + type + " :< " + boundingType); + println("Constraint violation: " + type + " :< " + boundingType); } } for (TypeValue lowerBound : typeValue.getLowerBounds()) { JetType boundingType = solution.getSubstitutor().substitute(lowerBound.getValue().getType(), Variance.INVARIANT); if (!typeChecker.isSubtypeOf(boundingType, type)) { solution.registerError(); - System.out.println("Constraint violation: " + boundingType + " :< " + type); + println("Constraint violation: " + boundingType + " :< " + type); } } } @@ -309,7 +309,7 @@ public class ConstraintSystem { DeclarationDescriptor declarationDescriptor = key.getDeclarationDescriptor(); if (declarationDescriptor instanceof TypeParameterDescriptor) { TypeParameterDescriptor descriptor = (TypeParameterDescriptor) declarationDescriptor; - System.out.println(descriptor + " |-> " + getValue(descriptor)); + println(descriptor + " |-> " + getValue(descriptor)); return new TypeProjection(getValue(descriptor)); } return null; @@ -368,12 +368,6 @@ public class ConstraintSystem { addSubtypingConstraintOnTypeValues(subtypeValue, supertypeValue); } return StatusAction.PROCEED; - -// // both types are known -// if (typeChecker.isSubtypeOf(subtype, supertype)) { -// return StatusAction.PROCEED; -// } -// return fail(); } private boolean someUnknown(TypeValue subtypeValue, TypeValue supertypeValue) { @@ -412,4 +406,8 @@ public class ConstraintSystem { } } + private static void println(String message) { +// System.out.println(message); + } + } \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/resolve/DescriptorRenderer.java b/idea/src/org/jetbrains/jet/resolve/DescriptorRenderer.java index 98e7a0bd41f..7481e1aa8d2 100644 --- a/idea/src/org/jetbrains/jet/resolve/DescriptorRenderer.java +++ b/idea/src/org/jetbrains/jet/resolve/DescriptorRenderer.java @@ -183,8 +183,7 @@ public class DescriptorRenderer { @Override public Void visitFunctionDescriptor(FunctionDescriptor descriptor, StringBuilder builder) { builder.append(renderKeyword("fun")).append(" "); - List typeParameters = descriptor.getTypeParameters(); - renderTypeParameters(typeParameters, builder); + renderTypeParameters(descriptor.getTypeParameters(), builder); JetType receiverType = descriptor.getReceiverType(); if (receiverType != null) { @@ -192,6 +191,12 @@ public class DescriptorRenderer { } renderName(descriptor, builder); + renderValueParameters(descriptor, builder); + builder.append(" : ").append(escape(renderType(descriptor.getReturnType()))); + return super.visitFunctionDescriptor(descriptor, builder); + } + + private void renderValueParameters(FunctionDescriptor descriptor, StringBuilder builder) { builder.append("("); for (Iterator iterator = descriptor.getValueParameters().iterator(); iterator.hasNext(); ) { ValueParameterDescriptor parameterDescriptor = iterator.next(); @@ -200,8 +205,19 @@ public class DescriptorRenderer { builder.append(", "); } } - builder.append(") : ").append(escape(renderType(descriptor.getReturnType()))); - return super.visitFunctionDescriptor(descriptor, builder); + builder.append(")"); + } + + @Override + public Void visitConstructorDescriptor(ConstructorDescriptor constructorDescriptor, StringBuilder builder) { + builder.append(renderKeyword("ctor")).append(" "); + + ClassDescriptor classDescriptor = constructorDescriptor.getContainingDeclaration(); + builder.append(classDescriptor.getName()); + + renderTypeParameters(classDescriptor.getTypeConstructor().getParameters(), builder); + renderValueParameters(constructorDescriptor, builder); + return null; } private void renderTypeParameters(List typeParameters, StringBuilder builder) { diff --git a/idea/testData/checker/ResolveToJava.jet b/idea/testData/checker/ResolveToJava.jet index c308dd86974..cc954743a82 100644 --- a/idea/testData/checker/ResolveToJava.jet +++ b/idea/testData/checker/ResolveToJava.jet @@ -19,7 +19,7 @@ fun test(l : java.util.List) { Collections.emptyList Collections.emptyList Collections.emptyList() - Collections.emptyList() + Collections.emptyList() Collections.singleton(1) : Set? Collections.singleton(1.0) @@ -43,7 +43,7 @@ fun test(l : java.util.List) { c : java.lang.Comparable? // Collections.sort(ArrayList()) - xxx.Class() + xxx.Class() } diff --git a/idea/testData/checker/regression/AmbiguityOnLazyTypeComputation.jet b/idea/testData/checker/regression/AmbiguityOnLazyTypeComputation.jet new file mode 100644 index 00000000000..908cac3adad --- /dev/null +++ b/idea/testData/checker/regression/AmbiguityOnLazyTypeComputation.jet @@ -0,0 +1,12 @@ +// One of the two passes is making a scope and turning vals into functions +// See KT-76 + +namespace x + +val b : Foo +val a1 = b.compareTo(2) + +class Foo() { + fun compareTo(other : Byte) : Int + fun compareTo(other : Char) : Int +} diff --git a/idea/testData/psi/FunctionLiterals_ERR.txt b/idea/testData/psi/FunctionLiterals_ERR.txt index 846d93a7a81..b6544f93aae 100644 --- a/idea/testData/psi/FunctionLiterals_ERR.txt +++ b/idea/testData/psi/FunctionLiterals_ERR.txt @@ -298,7 +298,7 @@ JetFile: FunctionLiterals_ERR.jet REFERENCE_EXPRESSION PsiElement(IDENTIFIER)('T') PsiElement(DOT)('.') - PsiErrorElement:To specify a receiver type, use the full notation: {ReceiverType.(parameters) [: ReturnType] => ...} + PsiErrorElement:To specify a receiverType type, use the full notation: {ReceiverType.(parameters) [: ReturnType] => ...} PsiElement(IDENTIFIER)('a') PsiWhiteSpace(' ') PsiElement(COLON)(':') diff --git a/idea/testData/psi/Properties_ERR.txt b/idea/testData/psi/Properties_ERR.txt index b4a8fc99fb2..c5dd405d09c 100644 --- a/idea/testData/psi/Properties_ERR.txt +++ b/idea/testData/psi/Properties_ERR.txt @@ -3,7 +3,7 @@ JetFile: Properties_ERR.jet PROPERTY PsiElement(var)('var') PsiWhiteSpace(' ') - PsiErrorElement:Expecting property name or receiver type + PsiErrorElement:Expecting property name or receiverType type PsiElement(MINUS)('-') PsiWhiteSpace(' ') PsiErrorElement:Property getter or setter expected diff --git a/idea/testData/psi/examples/With.txt b/idea/testData/psi/examples/With.txt index 25fdde079e5..5a23d21e7a6 100644 --- a/idea/testData/psi/examples/With.txt +++ b/idea/testData/psi/examples/With.txt @@ -23,7 +23,7 @@ JetFile: With.jet VALUE_PARAMETER_LIST PsiElement(LPAR)('(') VALUE_PARAMETER - PsiElement(IDENTIFIER)('receiver') + PsiElement(IDENTIFIER)('receiverType') PsiWhiteSpace(' ') PsiElement(COLON)(':') PsiWhiteSpace(' ') @@ -63,7 +63,7 @@ JetFile: With.jet PsiWhiteSpace(' ') DOT_QUALIFIED_EXPRESSION REFERENCE_EXPRESSION - PsiElement(IDENTIFIER)('receiver') + PsiElement(IDENTIFIER)('receiverType') PsiElement(DOT)('.') CALL_EXPRESSION REFERENCE_EXPRESSION diff --git a/idea/tests/org/jetbrains/jet/resolve/JetResolveTest.java b/idea/tests/org/jetbrains/jet/resolve/JetResolveTest.java index 32b084a67a6..1e88c2ea377 100644 --- a/idea/tests/org/jetbrains/jet/resolve/JetResolveTest.java +++ b/idea/tests/org/jetbrains/jet/resolve/JetResolveTest.java @@ -11,7 +11,10 @@ import com.intellij.psi.search.GlobalSearchScope; import junit.framework.Test; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.JetTestCaseBase; +import org.jetbrains.jet.lang.JetSemanticServices; +import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider; import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.resolve.BindingTraceContext; import org.jetbrains.jet.lang.resolve.OverloadResolutionResult; import org.jetbrains.jet.lang.types.*; import org.jetbrains.jet.parsing.JetParsingTest; @@ -100,9 +103,10 @@ public class JetResolveTest extends ExtensibleResolveTestCase { @NotNull private FunctionDescriptor standardFunction(ClassDescriptor classDescriptor, List typeArguments, String name, JetType... parameterType) { - FunctionGroup functionGroup = classDescriptor.getMemberScope(typeArguments).getFunctionGroup(name); List parameterTypeList = Arrays.asList(parameterType); - OverloadResolutionResult functions = functionGroup.getPossiblyApplicableFunctions(Collections.emptyList(), parameterTypeList); + JetTypeInferrer.Services typeInferrerServices = JetSemanticServices.createSemanticServices(getProject()).getTypeInferrerServices(new BindingTraceContext(), JetFlowInformationProvider.NONE); + + OverloadResolutionResult functions = typeInferrerServices.getCallResolver().resolveExactSignature(classDescriptor.getMemberScope(typeArguments), null, name, parameterTypeList); for (FunctionDescriptor function : functions.getFunctionDescriptors()) { List unsubstitutedValueParameters = function.getValueParameters(); for (int i = 0, unsubstitutedValueParametersSize = unsubstitutedValueParameters.size(); i < unsubstitutedValueParametersSize; i++) { diff --git a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index 387b536df01..9240616035f 100644 --- a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java @@ -606,14 +606,15 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { @NotNull @Override public FunctionGroup getFunctionGroup(@NotNull String name) { - ClassifierDescriptor classifier = getClassifier(name); - if (classifier instanceof ClassDescriptor) { - ClassDescriptor classDescriptor = (ClassDescriptor) classifier; - return classDescriptor.getConstructors(); - } WritableFunctionGroup writableFunctionGroup = new WritableFunctionGroup(name); +// ClassifierDescriptor classifier = getClassifier(name); +// if (classifier instanceof ClassDescriptor) { +// ClassDescriptor classDescriptor = (ClassDescriptor) classifier; +// writableFunctionGroup.addAllFunctions(classDescriptor.getConstructors()); +// } + ModuleDescriptor module = new ModuleDescriptor("TypeCheckerTest"); for (String funDecl : FUNCTION_DECLARATIONS) { - FunctionDescriptor functionDescriptor = classDescriptorResolver.resolveFunctionDescriptor(JetStandardClasses.getAny(), this, JetChangeUtil.createFunction(getProject(), funDecl)); + FunctionDescriptor functionDescriptor = classDescriptorResolver.resolveFunctionDescriptor(module, this, JetChangeUtil.createFunction(getProject(), funDecl)); if (name.equals(functionDescriptor.getName())) { writableFunctionGroup.addFunction(functionDescriptor); }