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.getVisibility(),
pd.isVar(),
pd.getExpectedThisObject(),
pd.getName() + "$bridge$" + accessors.size()
);
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(
myAccessor, Collections.<AnnotationDescriptor>emptyList(), myAccessor.getModality(),
@@ -16,6 +16,19 @@ import java.util.*;
* @author abreslav
*/
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) {
if (functionDescriptor.getTypeParameters().isEmpty()) return Collections.emptyMap();
@@ -93,4 +106,8 @@ public class FunctionDescriptorUtil {
JetStandardClasses.getReturnTypeFromFunctionType(functionType),
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.ReceiverDescriptor;
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.TypeSubstitutor;
import org.jetbrains.jet.lang.types.Variance;
@@ -17,6 +18,8 @@ import java.util.Collections;
import java.util.List;
import java.util.Set;
import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor.NO_RECEIVER;
/**
* @author abreslav
*/
@@ -25,10 +28,10 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
private final Modality modality;
private final Visibility visibility;
private final boolean isVar;
private final ReceiverDescriptor expectedThisObject;
private final Set<PropertyDescriptor> overriddenProperties = Sets.newLinkedHashSet();
private final PropertyDescriptor original;
private ReceiverDescriptor expectedThisObject;
private ReceiverDescriptor receiver;
private List<TypeParameterDescriptor> typeParemeters;
private PropertyGetterDescriptor getter;
@@ -41,14 +44,11 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
@NotNull Modality modality,
@NotNull Visibility visibility,
boolean isVar,
@NotNull ReceiverDescriptor expectedThisObject,
@NotNull String name) {
super(containingDeclaration, annotations, name);
// assert outType != null;
this.isVar = isVar;
this.modality = modality;
this.visibility = visibility;
this.expectedThisObject = expectedThisObject;
this.original = original == null ? this : original.getOriginal();
}
@@ -58,9 +58,8 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
@NotNull Modality modality,
@NotNull Visibility visibility,
boolean isVar,
@NotNull ReceiverDescriptor expectedThisObject,
@NotNull String name) {
this(null, containingDeclaration, annotations, modality, visibility, isVar, expectedThisObject, name);
this(null, containingDeclaration, annotations, modality, visibility, isVar, name);
}
public PropertyDescriptor(
@@ -74,10 +73,9 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
@NotNull String name,
@Nullable JetType inType,
@NotNull JetType outType
)
{
this(containingDeclaration, annotations, modality, visibility, isVar, expectedThisObject, name);
setType(inType, outType, Collections.<TypeParameterDescriptor>emptyList(), receiverType);
) {
this(containingDeclaration, annotations, modality, visibility, isVar, name);
setType(inType, outType, Collections.<TypeParameterDescriptor>emptyList(), expectedThisObject, receiverType);
}
private PropertyDescriptor(
@@ -86,8 +84,7 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
@NotNull ReceiverDescriptor expectedThisObject,
@Nullable JetType inType,
@NotNull JetType outType
)
{
) {
this(
original,
original.getContainingDeclaration(),
@@ -95,26 +92,19 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
original.getModality(),
original.getVisibility(),
original.isVar,
expectedThisObject,
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,
@NotNull List<TypeParameterDescriptor> typeParameters,
@Nullable JetType receiverType
)
{
public void setType(@Nullable JetType inType, @NotNull JetType outType, @NotNull List<TypeParameterDescriptor> typeParameters, @NotNull ReceiverDescriptor expectedThisObject, @Nullable JetType receiverType) {
ReceiverDescriptor receiver = receiverType == null
? ReceiverDescriptor.NO_RECEIVER
? NO_RECEIVER
: new ExtensionReceiver(this, receiverType);
setType(inType, outType, typeParameters, receiver);
setType(inType, outType, typeParameters, expectedThisObject, receiver);
}
public void setType(@Nullable JetType inType, @NotNull JetType outType,
@NotNull List<TypeParameterDescriptor> typeParameters, @NotNull ReceiverDescriptor receiver)
{
public void setType(@Nullable JetType inType, @NotNull JetType outType, @NotNull List<TypeParameterDescriptor> typeParameters, @NotNull ReceiverDescriptor expectedThisObject, @NotNull ReceiverDescriptor receiver) {
assert !isVar || inType != null;
setInType(inType);
setOutType(outType);
@@ -122,11 +112,10 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
this.typeParemeters = typeParameters;
this.receiver = receiver;
this.expectedThisObject = expectedThisObject;
}
public void initialize(
@Nullable PropertyGetterDescriptor getter, @Nullable PropertySetterDescriptor setter)
{
public void initialize(@Nullable PropertyGetterDescriptor getter, @Nullable PropertySetterDescriptor setter) {
this.getter = getter;
this.setter = setter;
}
@@ -191,7 +180,15 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
}
@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 inType = originalInType == null ? null : substitutor.substitute(originalInType, Variance.IN_VARIANCE);
JetType originalOutType = getOutType();
@@ -199,6 +196,16 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
if (inType == null && outType == null) {
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;
if (receiver.exists()) {
substitutedReceiverType = substitutor.substitute(receiver.getType(), Variance.IN_VARIANCE);
@@ -207,13 +214,10 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
else {
substitutedReceiverType = null;
}
return new PropertyDescriptor(
this,
substitutedReceiverType,
expectedThisObject.exists() ? new TransientReceiver(substitutor.substitute(expectedThisObject.getType(), Variance.IN_VARIANCE)) : expectedThisObject,
inType,
outType
);
substitutedDescriptor.setType(inType, outType, substitutedTypeParameters, substitutedExpectedThisObject, substitutedReceiverType);
return substitutedDescriptor;
}
@Override
@@ -244,10 +248,9 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab
newOwner,
Lists.newArrayList(getAnnotations()),
DescriptorUtils.convertModality(modality, makeNonAbstract), visibility, isVar,
expectedThisObject,
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(
propertyDescriptor, Lists.newArrayList(getter.getAnnotations()),
@@ -475,11 +475,10 @@ public class DescriptorResolver {
Modality.FINAL,
resolveVisibilityFromModifiers(objectDeclaration.getModifierList()),
false,
DescriptorUtils.getExpectedThisObjectIfNeeded(containingDeclaration),
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);
JetObjectDeclarationName nameAsDeclaration = objectDeclaration.getNameAsDeclaration();
@@ -518,7 +517,6 @@ public class DescriptorResolver {
resolveModalityFromModifiers(property.getModifierList(), defaultModality),
resolveVisibilityFromModifiers(property.getModifierList()),
isVar,
DescriptorUtils.getExpectedThisObjectIfNeeded(containingDeclaration),
JetPsiUtil.safeName(property.getName())
);
@@ -555,7 +553,7 @@ public class DescriptorResolver {
JetType type = getVariableType(scope2, property, true);
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);
PropertySetterDescriptor setter = resolvePropertySetterDescriptor(scopeWithTypeParameters, property, propertyDescriptor);
@@ -838,14 +836,13 @@ public class DescriptorResolver {
resolveModalityFromModifiers(parameter.getModifierList(), Modality.FINAL),
resolveVisibilityFromModifiers(parameter.getModifierList()),
isMutable,
DescriptorUtils.getExpectedThisObjectIfNeeded(classDescriptor),
name == null ? "<no name>" : name
);
PropertyGetterDescriptor getter = createDefaultGetter(propertyDescriptor);
PropertySetterDescriptor setter = createDefaultSetter(propertyDescriptor);
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);
getter.initialize(propertyDescriptor.getOutType());
@@ -433,15 +433,25 @@ public class CallResolver {
ConstraintSystem constraintSystem = new ConstraintSystemImpl(new DebugConstraintResolutionListener(debugInfo));
for (TypeParameterDescriptor typeParameterDescriptor : candidate.getTypeParameters()) {
constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); // TODO
// If the call is recursive, e.g.
// 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()) {
ResolvedValueArgument valueArgument = entry.getValue();
ValueParameterDescriptor valueParameterDescriptor = entry.getKey();
ValueParameterDescriptor valueParameterDescriptor = candidateWithFreshVariables.getValueParameters().get(entry.getKey().getIndex());
JetType effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor);
@@ -463,25 +473,29 @@ public class CallResolver {
}
}
// Receiver
// Error is already reported if something is missing
ReceiverDescriptor receiverArgument = candidateCall.getReceiverArgument();
ReceiverDescriptor receiverParameter = candidate.getReceiverParameter();
ReceiverDescriptor receiverParameter = candidateWithFreshVariables.getReceiverParameter();
if (receiverArgument.exists() && receiverParameter.exists()) {
constraintSystem.addSubtypingConstraint(receiverArgument.getType(), receiverParameter.getType());
}
// Return type
if (expectedType != NO_EXPECTED_TYPE) {
constraintSystem.addSubtypingConstraint(candidate.getReturnType(), expectedType);
constraintSystem.addSubtypingConstraint(candidateWithFreshVariables.getReturnType(), expectedType);
}
// Solution
ConstraintSystemSolution solution = constraintSystem.solve();
if (solution.getStatus().isSuccessful()) {
D substitute = (D) candidate.substitute(solution.getSubstitutor());
D substitute = (D) candidateWithFreshVariables.substitute(solution.getSubstitutor());
assert substitute != null;
replaceValueParametersWithSubstitutedOnes(candidateCall, substitute);
candidateCall.setResultingDescriptor(substitute);
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
@@ -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)