KT-743 Wrong type inference

This commit is contained in:
Andrey Breslav
2011-12-08 19:17:31 +04:00
parent a0cd4af3bd
commit 22e1412c38
6 changed files with 89 additions and 53 deletions
@@ -226,11 +226,10 @@ public abstract class CodegenContext {
pd.getModality(), pd.getModality(),
pd.getVisibility(), pd.getVisibility(),
pd.isVar(), pd.isVar(),
pd.getExpectedThisObject(),
pd.getName() + "$bridge$" + accessors.size() pd.getName() + "$bridge$" + accessors.size()
); );
JetType receiverType = pd.getReceiverParameter().exists() ? pd.getReceiverParameter().getType() : null; JetType receiverType = pd.getReceiverParameter().exists() ? pd.getReceiverParameter().getType() : null;
myAccessor.setType(pd.getInType(), pd.getOutType(), Collections.<TypeParameterDescriptor>emptyList(), receiverType); myAccessor.setType(pd.getInType(), pd.getOutType(), Collections.<TypeParameterDescriptor>emptyList(), pd.getExpectedThisObject(), receiverType);
PropertyGetterDescriptor pgd = new PropertyGetterDescriptor( PropertyGetterDescriptor pgd = new PropertyGetterDescriptor(
myAccessor, Collections.<AnnotationDescriptor>emptyList(), myAccessor.getModality(), myAccessor, Collections.<AnnotationDescriptor>emptyList(), myAccessor.getModality(),
@@ -16,6 +16,19 @@ import java.util.*;
* @author abreslav * @author abreslav
*/ */
public class FunctionDescriptorUtil { public class FunctionDescriptorUtil {
private static final TypeSubstitutor MAKE_TYPE_PARAMETERS_FRESH = TypeSubstitutor.create(new TypeSubstitutor.TypeSubstitution() {
@Override
public TypeProjection get(TypeConstructor key) {
return null;
}
@Override
public boolean isEmpty() {
return false;
}
});
public static Map<TypeConstructor, TypeProjection> createSubstitutionContext(@NotNull FunctionDescriptor functionDescriptor, List<JetType> typeArguments) { public static Map<TypeConstructor, TypeProjection> createSubstitutionContext(@NotNull FunctionDescriptor functionDescriptor, List<JetType> typeArguments) {
if (functionDescriptor.getTypeParameters().isEmpty()) return Collections.emptyMap(); if (functionDescriptor.getTypeParameters().isEmpty()) return Collections.emptyMap();
@@ -93,4 +106,8 @@ public class FunctionDescriptorUtil {
JetStandardClasses.getReturnTypeFromFunctionType(functionType), JetStandardClasses.getReturnTypeFromFunctionType(functionType),
Modality.FINAL, Visibility.LOCAL); Modality.FINAL, Visibility.LOCAL);
} }
public static <D extends CallableDescriptor> D alphaConvertTypeParameters(D candidate) {
return (D) candidate.substitute(MAKE_TYPE_PARAMETERS_FRESH);
}
} }
@@ -9,6 +9,7 @@ import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExtensionReceiver; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExtensionReceiver;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver; import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver;
import org.jetbrains.jet.lang.types.DescriptorSubstitutor;
import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeSubstitutor; import org.jetbrains.jet.lang.types.TypeSubstitutor;
import org.jetbrains.jet.lang.types.Variance; import org.jetbrains.jet.lang.types.Variance;
@@ -17,6 +18,8 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor.NO_RECEIVER;
/** /**
* @author abreslav * @author abreslav
*/ */
@@ -25,10 +28,10 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
private final Modality modality; private final Modality modality;
private final Visibility visibility; private final Visibility visibility;
private final boolean isVar; private final boolean isVar;
private final ReceiverDescriptor expectedThisObject;
private final Set<PropertyDescriptor> overriddenProperties = Sets.newLinkedHashSet(); private final Set<PropertyDescriptor> overriddenProperties = Sets.newLinkedHashSet();
private final PropertyDescriptor original; private final PropertyDescriptor original;
private ReceiverDescriptor expectedThisObject;
private ReceiverDescriptor receiver; private ReceiverDescriptor receiver;
private List<TypeParameterDescriptor> typeParemeters; private List<TypeParameterDescriptor> typeParemeters;
private PropertyGetterDescriptor getter; private PropertyGetterDescriptor getter;
@@ -41,14 +44,11 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
@NotNull Modality modality, @NotNull Modality modality,
@NotNull Visibility visibility, @NotNull Visibility visibility,
boolean isVar, boolean isVar,
@NotNull ReceiverDescriptor expectedThisObject,
@NotNull String name) { @NotNull String name) {
super(containingDeclaration, annotations, name); super(containingDeclaration, annotations, name);
// assert outType != null;
this.isVar = isVar; this.isVar = isVar;
this.modality = modality; this.modality = modality;
this.visibility = visibility; this.visibility = visibility;
this.expectedThisObject = expectedThisObject;
this.original = original == null ? this : original.getOriginal(); this.original = original == null ? this : original.getOriginal();
} }
@@ -58,9 +58,8 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
@NotNull Modality modality, @NotNull Modality modality,
@NotNull Visibility visibility, @NotNull Visibility visibility,
boolean isVar, boolean isVar,
@NotNull ReceiverDescriptor expectedThisObject,
@NotNull String name) { @NotNull String name) {
this(null, containingDeclaration, annotations, modality, visibility, isVar, expectedThisObject, name); this(null, containingDeclaration, annotations, modality, visibility, isVar, name);
} }
public PropertyDescriptor( public PropertyDescriptor(
@@ -74,10 +73,9 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
@NotNull String name, @NotNull String name,
@Nullable JetType inType, @Nullable JetType inType,
@NotNull JetType outType @NotNull JetType outType
) ) {
{ this(containingDeclaration, annotations, modality, visibility, isVar, name);
this(containingDeclaration, annotations, modality, visibility, isVar, expectedThisObject, name); setType(inType, outType, Collections.<TypeParameterDescriptor>emptyList(), expectedThisObject, receiverType);
setType(inType, outType, Collections.<TypeParameterDescriptor>emptyList(), receiverType);
} }
private PropertyDescriptor( private PropertyDescriptor(
@@ -86,8 +84,7 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
@NotNull ReceiverDescriptor expectedThisObject, @NotNull ReceiverDescriptor expectedThisObject,
@Nullable JetType inType, @Nullable JetType inType,
@NotNull JetType outType @NotNull JetType outType
) ) {
{
this( this(
original, original,
original.getContainingDeclaration(), original.getContainingDeclaration(),
@@ -95,26 +92,19 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
original.getModality(), original.getModality(),
original.getVisibility(), original.getVisibility(),
original.isVar, original.isVar,
expectedThisObject,
original.getName() original.getName()
); );
setType(inType, outType, Collections.<TypeParameterDescriptor>emptyList(), receiverType); setType(inType, outType, Collections.<TypeParameterDescriptor>emptyList(), expectedThisObject, receiverType);
} }
public void setType(@Nullable JetType inType, @NotNull JetType outType, public void setType(@Nullable JetType inType, @NotNull JetType outType, @NotNull List<TypeParameterDescriptor> typeParameters, @NotNull ReceiverDescriptor expectedThisObject, @Nullable JetType receiverType) {
@NotNull List<TypeParameterDescriptor> typeParameters,
@Nullable JetType receiverType
)
{
ReceiverDescriptor receiver = receiverType == null ReceiverDescriptor receiver = receiverType == null
? ReceiverDescriptor.NO_RECEIVER ? NO_RECEIVER
: new ExtensionReceiver(this, receiverType); : new ExtensionReceiver(this, receiverType);
setType(inType, outType, typeParameters, receiver); setType(inType, outType, typeParameters, expectedThisObject, receiver);
} }
public void setType(@Nullable JetType inType, @NotNull JetType outType, public void setType(@Nullable JetType inType, @NotNull JetType outType, @NotNull List<TypeParameterDescriptor> typeParameters, @NotNull ReceiverDescriptor expectedThisObject, @NotNull ReceiverDescriptor receiver) {
@NotNull List<TypeParameterDescriptor> typeParameters, @NotNull ReceiverDescriptor receiver)
{
assert !isVar || inType != null; assert !isVar || inType != null;
setInType(inType); setInType(inType);
setOutType(outType); setOutType(outType);
@@ -122,11 +112,10 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
this.typeParemeters = typeParameters; this.typeParemeters = typeParameters;
this.receiver = receiver; this.receiver = receiver;
this.expectedThisObject = expectedThisObject;
} }
public void initialize( public void initialize(@Nullable PropertyGetterDescriptor getter, @Nullable PropertySetterDescriptor setter) {
@Nullable PropertyGetterDescriptor getter, @Nullable PropertySetterDescriptor setter)
{
this.getter = getter; this.getter = getter;
this.setter = setter; this.setter = setter;
} }
@@ -191,7 +180,15 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
} }
@Override @Override
public PropertyDescriptor substitute(TypeSubstitutor substitutor) { public PropertyDescriptor substitute(TypeSubstitutor originalSubstitutor) {
if (originalSubstitutor.isEmpty()) {
return this;
}
PropertyDescriptor substitutedDescriptor = new PropertyDescriptor(this, getContainingDeclaration(), getAnnotations(), getModality(), getVisibility(), isVar(), getName());
List<TypeParameterDescriptor> substitutedTypeParameters = Lists.newArrayList();
TypeSubstitutor substitutor = DescriptorSubstitutor.substituteTypeParameters(getTypeParameters(), originalSubstitutor, substitutedDescriptor, substitutedTypeParameters);
JetType originalInType = getInType(); JetType originalInType = getInType();
JetType inType = originalInType == null ? null : substitutor.substitute(originalInType, Variance.IN_VARIANCE); JetType inType = originalInType == null ? null : substitutor.substitute(originalInType, Variance.IN_VARIANCE);
JetType originalOutType = getOutType(); JetType originalOutType = getOutType();
@@ -199,6 +196,16 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
if (inType == null && outType == null) { if (inType == null && outType == null) {
return null; // TODO : tell the user that the property was projected out return null; // TODO : tell the user that the property was projected out
} }
ReceiverDescriptor substitutedExpectedThisObject;
if (expectedThisObject.exists()) {
JetType substitutedExpectedThisObjectType = substitutor.substitute(getExpectedThisObject().getType(), Variance.INVARIANT);
substitutedExpectedThisObject = new TransientReceiver(substitutedExpectedThisObjectType);
}
else {
substitutedExpectedThisObject = NO_RECEIVER;
}
JetType substitutedReceiverType; JetType substitutedReceiverType;
if (receiver.exists()) { if (receiver.exists()) {
substitutedReceiverType = substitutor.substitute(receiver.getType(), Variance.IN_VARIANCE); substitutedReceiverType = substitutor.substitute(receiver.getType(), Variance.IN_VARIANCE);
@@ -207,13 +214,10 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
else { else {
substitutedReceiverType = null; substitutedReceiverType = null;
} }
return new PropertyDescriptor(
this, substitutedDescriptor.setType(inType, outType, substitutedTypeParameters, substitutedExpectedThisObject, substitutedReceiverType);
substitutedReceiverType,
expectedThisObject.exists() ? new TransientReceiver(substitutor.substitute(expectedThisObject.getType(), Variance.IN_VARIANCE)) : expectedThisObject, return substitutedDescriptor;
inType,
outType
);
} }
@Override @Override
@@ -244,10 +248,9 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
newOwner, newOwner,
Lists.newArrayList(getAnnotations()), Lists.newArrayList(getAnnotations()),
DescriptorUtils.convertModality(modality, makeNonAbstract), visibility, isVar, DescriptorUtils.convertModality(modality, makeNonAbstract), visibility, isVar,
expectedThisObject,
getName()); getName());
propertyDescriptor.setType(getInType(), getOutType(), DescriptorUtils.copyTypeParameters(propertyDescriptor, getTypeParameters()), receiver.exists() ? receiver.getType() : null); propertyDescriptor.setType(getInType(), getOutType(), DescriptorUtils.copyTypeParameters(propertyDescriptor, getTypeParameters()), expectedThisObject, receiver.exists() ? receiver.getType() : null);
PropertyGetterDescriptor newGetter = getter == null ? null : new PropertyGetterDescriptor( PropertyGetterDescriptor newGetter = getter == null ? null : new PropertyGetterDescriptor(
propertyDescriptor, Lists.newArrayList(getter.getAnnotations()), propertyDescriptor, Lists.newArrayList(getter.getAnnotations()),
@@ -475,11 +475,10 @@ public class DescriptorResolver {
Modality.FINAL, Modality.FINAL,
resolveVisibilityFromModifiers(objectDeclaration.getModifierList()), resolveVisibilityFromModifiers(objectDeclaration.getModifierList()),
false, false,
DescriptorUtils.getExpectedThisObjectIfNeeded(containingDeclaration),
JetPsiUtil.safeName(objectDeclaration.getName()) JetPsiUtil.safeName(objectDeclaration.getName())
); );
propertyDescriptor.setType(null, classDescriptor.getDefaultType(), Collections.<TypeParameterDescriptor>emptyList(), ReceiverDescriptor.NO_RECEIVER); propertyDescriptor.setType(null, classDescriptor.getDefaultType(), Collections.<TypeParameterDescriptor>emptyList(), DescriptorUtils.getExpectedThisObjectIfNeeded(containingDeclaration), ReceiverDescriptor.NO_RECEIVER);
propertyDescriptor.initialize(null, null); propertyDescriptor.initialize(null, null);
JetObjectDeclarationName nameAsDeclaration = objectDeclaration.getNameAsDeclaration(); JetObjectDeclarationName nameAsDeclaration = objectDeclaration.getNameAsDeclaration();
@@ -518,7 +517,6 @@ public class DescriptorResolver {
resolveModalityFromModifiers(property.getModifierList(), defaultModality), resolveModalityFromModifiers(property.getModifierList(), defaultModality),
resolveVisibilityFromModifiers(property.getModifierList()), resolveVisibilityFromModifiers(property.getModifierList()),
isVar, isVar,
DescriptorUtils.getExpectedThisObjectIfNeeded(containingDeclaration),
JetPsiUtil.safeName(property.getName()) JetPsiUtil.safeName(property.getName())
); );
@@ -555,7 +553,7 @@ public class DescriptorResolver {
JetType type = getVariableType(scope2, property, true); JetType type = getVariableType(scope2, property, true);
JetType inType = isVar ? type : null; JetType inType = isVar ? type : null;
propertyDescriptor.setType(inType, type, typeParameterDescriptors, receiverDescriptor); propertyDescriptor.setType(inType, type, typeParameterDescriptors, DescriptorUtils.getExpectedThisObjectIfNeeded(containingDeclaration), receiverDescriptor);
PropertyGetterDescriptor getter = resolvePropertyGetterDescriptor(scopeWithTypeParameters, property, propertyDescriptor); PropertyGetterDescriptor getter = resolvePropertyGetterDescriptor(scopeWithTypeParameters, property, propertyDescriptor);
PropertySetterDescriptor setter = resolvePropertySetterDescriptor(scopeWithTypeParameters, property, propertyDescriptor); PropertySetterDescriptor setter = resolvePropertySetterDescriptor(scopeWithTypeParameters, property, propertyDescriptor);
@@ -838,14 +836,13 @@ public class DescriptorResolver {
resolveModalityFromModifiers(parameter.getModifierList(), Modality.FINAL), resolveModalityFromModifiers(parameter.getModifierList(), Modality.FINAL),
resolveVisibilityFromModifiers(parameter.getModifierList()), resolveVisibilityFromModifiers(parameter.getModifierList()),
isMutable, isMutable,
DescriptorUtils.getExpectedThisObjectIfNeeded(classDescriptor),
name == null ? "<no name>" : name name == null ? "<no name>" : name
); );
PropertyGetterDescriptor getter = createDefaultGetter(propertyDescriptor); PropertyGetterDescriptor getter = createDefaultGetter(propertyDescriptor);
PropertySetterDescriptor setter = createDefaultSetter(propertyDescriptor); PropertySetterDescriptor setter = createDefaultSetter(propertyDescriptor);
JetType inType = isMutable ? type : null; JetType inType = isMutable ? type : null;
propertyDescriptor.setType(inType, type, Collections.<TypeParameterDescriptor>emptyList(), ReceiverDescriptor.NO_RECEIVER); propertyDescriptor.setType(inType, type, Collections.<TypeParameterDescriptor>emptyList(), DescriptorUtils.getExpectedThisObjectIfNeeded(classDescriptor), ReceiverDescriptor.NO_RECEIVER);
propertyDescriptor.initialize(getter, setter); propertyDescriptor.initialize(getter, setter);
getter.initialize(propertyDescriptor.getOutType()); getter.initialize(propertyDescriptor.getOutType());
@@ -433,15 +433,25 @@ public class CallResolver {
ConstraintSystem constraintSystem = new ConstraintSystemImpl(new DebugConstraintResolutionListener(debugInfo)); ConstraintSystem constraintSystem = new ConstraintSystemImpl(new DebugConstraintResolutionListener(debugInfo));
for (TypeParameterDescriptor typeParameterDescriptor : candidate.getTypeParameters()) { // If the call is recursive, e.g.
constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); // TODO // fun foo<T>(t : T) : T = foo(t)
// we can't use same descriptor objects for T's as actual type values and same T's as unknowns,
// because constraints become trivial (T :< T), and inference fails
//
// Thus, we replace the parameters of our descriptor with fresh objects (perform alpha-conversion)
CallableDescriptor candidateWithFreshVariables = FunctionDescriptorUtil.alphaConvertTypeParameters(candidate);
for (TypeParameterDescriptor typeParameterDescriptor : candidateWithFreshVariables.getTypeParameters()) {
constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); // TODO: variance of the occurrences
} }
TypeSubstitutor substituteDontCare = ConstraintSystemImpl.makeConstantSubstitutor(candidate.getTypeParameters(), DONT_CARE); TypeSubstitutor substituteDontCare = ConstraintSystemImpl.makeConstantSubstitutor(candidateWithFreshVariables.getTypeParameters(), DONT_CARE);
// Value parameters
for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : candidateCall.getValueArguments().entrySet()) { for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : candidateCall.getValueArguments().entrySet()) {
ResolvedValueArgument valueArgument = entry.getValue(); ResolvedValueArgument valueArgument = entry.getValue();
ValueParameterDescriptor valueParameterDescriptor = entry.getKey(); ValueParameterDescriptor valueParameterDescriptor = candidateWithFreshVariables.getValueParameters().get(entry.getKey().getIndex());
JetType effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor); JetType effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor);
@@ -463,25 +473,29 @@ public class CallResolver {
} }
} }
// Receiver
// Error is already reported if something is missing // Error is already reported if something is missing
ReceiverDescriptor receiverArgument = candidateCall.getReceiverArgument(); ReceiverDescriptor receiverArgument = candidateCall.getReceiverArgument();
ReceiverDescriptor receiverParameter = candidate.getReceiverParameter(); ReceiverDescriptor receiverParameter = candidateWithFreshVariables.getReceiverParameter();
if (receiverArgument.exists() && receiverParameter.exists()) { if (receiverArgument.exists() && receiverParameter.exists()) {
constraintSystem.addSubtypingConstraint(receiverArgument.getType(), receiverParameter.getType()); constraintSystem.addSubtypingConstraint(receiverArgument.getType(), receiverParameter.getType());
} }
// Return type
if (expectedType != NO_EXPECTED_TYPE) { if (expectedType != NO_EXPECTED_TYPE) {
constraintSystem.addSubtypingConstraint(candidate.getReturnType(), expectedType); constraintSystem.addSubtypingConstraint(candidateWithFreshVariables.getReturnType(), expectedType);
} }
// Solution
ConstraintSystemSolution solution = constraintSystem.solve(); ConstraintSystemSolution solution = constraintSystem.solve();
if (solution.getStatus().isSuccessful()) { if (solution.getStatus().isSuccessful()) {
D substitute = (D) candidate.substitute(solution.getSubstitutor()); D substitute = (D) candidateWithFreshVariables.substitute(solution.getSubstitutor());
assert substitute != null; assert substitute != null;
replaceValueParametersWithSubstitutedOnes(candidateCall, substitute); replaceValueParametersWithSubstitutedOnes(candidateCall, substitute);
candidateCall.setResultingDescriptor(substitute); candidateCall.setResultingDescriptor(substitute);
for (TypeParameterDescriptor typeParameterDescriptor : candidateCall.getCandidateDescriptor().getTypeParameters()) { for (TypeParameterDescriptor typeParameterDescriptor : candidateCall.getCandidateDescriptor().getTypeParameters()) {
candidateCall.recordTypeArgument(typeParameterDescriptor, solution.getValue(typeParameterDescriptor)); candidateCall.recordTypeArgument(typeParameterDescriptor, solution.getValue(candidateWithFreshVariables.getTypeParameters().get(typeParameterDescriptor.getIndex())));
} }
// Here we type check the arguments with inferred types expected // Here we type check the arguments with inferred types expected
@@ -0,0 +1,6 @@
//KT-743 Wrong type inference
// +JDK
class List<T>(val head: T, val tail: List<T>? = null)
fun <T, Q> List<T>.map(f: fun(T): Q): List<T>? = tail.sure<List<T>>().map(f)
fun foo<T>(t : T) : T = foo(t)