use TypeCheckingProcedure for generating constraints
(in ConstraintSystemImpl)
This commit is contained in:
+14
-8
@@ -26,7 +26,10 @@ import org.jetbrains.jet.lang.resolve.BindingTrace;
|
||||
import org.jetbrains.jet.lang.resolve.TemporaryBindingTrace;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.context.*;
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallImpl;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace;
|
||||
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
|
||||
import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
|
||||
import org.jetbrains.jet.lang.resolve.constants.ConstantUtils;
|
||||
@@ -149,7 +152,6 @@ public class CallExpressionResolver {
|
||||
@NotNull Call call, @NotNull JetExpression callExpression, @NotNull ReceiverValue receiver,
|
||||
@NotNull ResolutionContext context, @NotNull ResolveMode resolveMode, @NotNull boolean[] result
|
||||
) {
|
||||
|
||||
CallResolver callResolver = expressionTypingServices.getCallResolver();
|
||||
OverloadResolutionResults<FunctionDescriptor> results = callResolver.resolveFunctionCall(
|
||||
BasicCallResolutionContext.create(context, call, resolveMode));
|
||||
@@ -157,10 +159,14 @@ public class CallExpressionResolver {
|
||||
checkSuper(receiver, results, context.trace, callExpression);
|
||||
result[0] = true;
|
||||
if (results.isSingleResult() && resolveMode == ResolveMode.TOP_LEVEL_CALL) {
|
||||
if (CallResolverUtil.hasReturnTypeDependentOnNotInferredParams(results.getResultingCall())) {
|
||||
return null;
|
||||
}
|
||||
ResolvedCallImpl<FunctionDescriptor> callToComplete = results.getResultingCall().getResolvedCallToComplete();
|
||||
if (CallResolverUtil.hasReturnTypeDependentOnNotInferredParams(callToComplete)) return null;
|
||||
|
||||
// Expected type mismatch was reported before as 'TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH'
|
||||
ConstraintSystem constraintSystem = callToComplete.getConstraintSystem();
|
||||
if (constraintSystem != null && constraintSystem.hasExpectedTypeMismatch()) return null;
|
||||
}
|
||||
|
||||
return results.isSingleResult() ? results.getResultingCall() : null;
|
||||
}
|
||||
result[0] = false;
|
||||
@@ -169,8 +175,8 @@ public class CallExpressionResolver {
|
||||
|
||||
@Nullable
|
||||
private JetType getVariableType(@NotNull JetSimpleNameExpression nameExpression, @NotNull ReceiverValue receiver,
|
||||
@Nullable ASTNode callOperationNode, @NotNull ResolutionContext context, @NotNull boolean[] result) {
|
||||
|
||||
@Nullable ASTNode callOperationNode, @NotNull ResolutionContext context, @NotNull boolean[] result
|
||||
) {
|
||||
TemporaryBindingTrace traceForVariable = TemporaryBindingTrace.create(
|
||||
context.trace, "trace to resolve as local variable or property", nameExpression);
|
||||
CallResolver callResolver = expressionTypingServices.getCallResolver();
|
||||
@@ -207,8 +213,8 @@ public class CallExpressionResolver {
|
||||
|
||||
@NotNull
|
||||
public JetTypeInfo getSimpleNameExpressionTypeInfo(@NotNull JetSimpleNameExpression nameExpression, @NotNull ReceiverValue receiver,
|
||||
@Nullable ASTNode callOperationNode, @NotNull ResolutionContext context) {
|
||||
|
||||
@Nullable ASTNode callOperationNode, @NotNull ResolutionContext context
|
||||
) {
|
||||
boolean[] result = new boolean[1];
|
||||
|
||||
TemporaryBindingTrace traceForVariable = TemporaryBindingTrace.create(context.trace, "trace to resolve as variable", nameExpression);
|
||||
|
||||
@@ -307,11 +307,10 @@ public class CallResolver {
|
||||
@NotNull OverloadResolutionResults<D> results,
|
||||
@NotNull TracingStrategy tracing
|
||||
) {
|
||||
if (results.getResultCode() != OverloadResolutionResults.Code.INCOMPLETE_TYPE_INFERENCE) return;
|
||||
|
||||
//todo[ResolvedCallImpl] get rid of instanceof & cast
|
||||
if (!results.isSingleResult() || !(results.getResultingCall() instanceof ResolvedCallImpl)) {
|
||||
argumentTypeResolver.checkTypesWithNoCallee(context, RESOLVE_FUNCTION_ARGUMENTS);
|
||||
if (!results.isSingleResult()) {
|
||||
if (results.getResultCode() == OverloadResolutionResults.Code.INCOMPLETE_TYPE_INFERENCE) {
|
||||
argumentTypeResolver.checkTypesWithNoCallee(context, RESOLVE_FUNCTION_ARGUMENTS);
|
||||
}
|
||||
return;
|
||||
}
|
||||
CallCandidateResolutionContext<D> candidateContext = CallCandidateResolutionContext.createForCallBeingAnalyzed(
|
||||
|
||||
@@ -106,7 +106,9 @@ public class CallResolverUtil {
|
||||
return new JetTypeImpl(type.getAnnotations(), type.getConstructor(), type.isNullable(), newArguments, type.getMemberScope());
|
||||
}
|
||||
|
||||
public static <D extends CallableDescriptor> boolean hasReturnTypeDependentOnNotInferredParams(@NotNull ResolvedCall<D> resolvedCall) {
|
||||
public static <D extends CallableDescriptor> boolean hasReturnTypeDependentOnNotInferredParams(
|
||||
@NotNull ResolvedCallImpl<D> resolvedCall
|
||||
) {
|
||||
//todo[ResolvedCallImpl]
|
||||
if (!(resolvedCall instanceof ResolvedCallImpl)) return false;
|
||||
ResolvedCallImpl call = (ResolvedCallImpl) resolvedCall;
|
||||
|
||||
@@ -197,9 +197,8 @@ public class CandidateResolver {
|
||||
CallCandidateResolutionContext<D> context
|
||||
) {
|
||||
ResolvedCallImpl<D> resolvedCall = context.candidateCall;
|
||||
assert resolvedCall.hasIncompleteTypeParameters();
|
||||
ConstraintSystem constraintSystem = resolvedCall.getConstraintSystem();
|
||||
assert constraintSystem != null;
|
||||
if (!resolvedCall.hasIncompleteTypeParameters() || constraintSystem == null) return;
|
||||
|
||||
// constraints for function literals
|
||||
// Value parameters
|
||||
@@ -237,11 +236,10 @@ public class CandidateResolver {
|
||||
List<JetType> argumentTypes = checkValueArgumentTypes(context, resolvedCall, context.trace,
|
||||
RESOLVE_FUNCTION_ARGUMENTS).argumentTypes;
|
||||
JetType receiverType = resolvedCall.getReceiverArgument().exists() ? resolvedCall.getReceiverArgument().getType() : null;
|
||||
context.tracing.typeInferenceFailed(context.trace,
|
||||
InferenceErrorData
|
||||
.create(descriptor, constraintSystem, argumentTypes, receiverType,
|
||||
context.expectedType),
|
||||
constraintSystemWithoutExpectedTypeConstraint);
|
||||
InferenceErrorData.ExtendedInferenceErrorData errorData = InferenceErrorData
|
||||
.create(descriptor, constraintSystem, argumentTypes, receiverType, context.expectedType);
|
||||
|
||||
context.tracing.typeInferenceFailed(context.trace, errorData, constraintSystemWithoutExpectedTypeConstraint);
|
||||
resolvedCall.addStatus(ResolutionStatus.OTHER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
+128
-166
@@ -22,27 +22,37 @@ import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
|
||||
import org.jetbrains.jet.lang.types.*;
|
||||
import org.jetbrains.jet.lang.types.checker.TypeCheckingProcedure;
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.TypeConstraintsImpl.ConstraintKind;
|
||||
import org.jetbrains.jet.lang.types.checker.TypingConstraints;
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.jetbrains.jet.lang.resolve.calls.CallResolverUtil.*;
|
||||
import static org.jetbrains.jet.lang.resolve.calls.inference.TypeConstraintsImpl.ConstraintKind.*;
|
||||
import static org.jetbrains.jet.lang.types.Variance.*;
|
||||
import static org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.EQUAL;
|
||||
import static org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.SUB_TYPE;
|
||||
import static org.jetbrains.jet.lang.resolve.calls.inference.TypeConstraintsImpl.BoundKind;
|
||||
import static org.jetbrains.jet.lang.resolve.calls.inference.TypeConstraintsImpl.BoundKind.*;
|
||||
|
||||
public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
|
||||
public static enum ConstraintKind {
|
||||
SUB_TYPE, EQUAL
|
||||
}
|
||||
|
||||
private final Map<TypeParameterDescriptor, TypeConstraintsImpl> typeParameterConstraints = Maps.newLinkedHashMap();
|
||||
private final Set<ConstraintPosition> errorConstraintPositions = Sets.newHashSet();
|
||||
private final TypeSubstitutor resultingSubstitutor;
|
||||
private final TypeSubstitutor currentSubstitutor;
|
||||
private boolean hasErrorInConstrainingTypes;
|
||||
private boolean hasExpectedTypeMismatch;
|
||||
|
||||
public ConstraintSystemImpl() {
|
||||
this.resultingSubstitutor = createTypeSubstitutorWithDefaultForUnknownTypeParameter(new TypeProjection(CANT_INFER));
|
||||
@@ -58,7 +68,7 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
TypeParameterDescriptor descriptor = (TypeParameterDescriptor) declarationDescriptor;
|
||||
|
||||
JetType value = ConstraintsUtil.getValue(getTypeConstraints(descriptor));
|
||||
if (value != null && !TypeUtils.dependsOnTypeParameterConstructors(value, Collections.singleton(
|
||||
if (value != null && !TypeUtils.dependsOnTypeConstructors(value, Collections.singleton(
|
||||
DONT_CARE.getConstructor()))) {
|
||||
return new TypeProjection(value);
|
||||
}
|
||||
@@ -93,7 +103,12 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
|
||||
@Override
|
||||
public boolean hasExpectedTypeMismatch() {
|
||||
return errorConstraintPositions.size() == 1 && errorConstraintPositions.contains(ConstraintPosition.EXPECTED_TYPE_POSITION);
|
||||
return hasExpectedTypeMismatch
|
||||
|| (errorConstraintPositions.size() == 1 && errorConstraintPositions.contains(ConstraintPosition.EXPECTED_TYPE_POSITION));
|
||||
}
|
||||
|
||||
public void setHasExpectedTypeMismatch() {
|
||||
hasExpectedTypeMismatch = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -142,7 +157,7 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
@NotNull JetType subjectType,
|
||||
@NotNull ConstraintPosition constraintPosition
|
||||
) {
|
||||
addConstraint(SUPER_TYPE, subjectType, constrainingType, constraintPosition);
|
||||
addConstraint(SUB_TYPE, subjectType, constrainingType, constraintPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -151,34 +166,82 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
@NotNull JetType subjectType,
|
||||
@NotNull ConstraintPosition constraintPosition
|
||||
) {
|
||||
addConstraint(SUB_TYPE, subjectType, constrainingType, constraintPosition);
|
||||
addConstraint(SUB_TYPE, constrainingType, subjectType, constraintPosition);
|
||||
}
|
||||
|
||||
private void addConstraint(@NotNull ConstraintKind constraintKind,
|
||||
@NotNull JetType subjectType,
|
||||
@Nullable JetType constrainingType,
|
||||
@NotNull ConstraintPosition constraintPosition) {
|
||||
private void addConstraint(
|
||||
@NotNull ConstraintKind constraintKind,
|
||||
@Nullable JetType subType,
|
||||
@Nullable JetType superType,
|
||||
@NotNull final ConstraintPosition constraintPosition
|
||||
) {
|
||||
TypeCheckingProcedure typeCheckingProcedure = new TypeCheckingProcedure(new TypingConstraints() {
|
||||
@Override
|
||||
public boolean assertEqualTypes(
|
||||
@NotNull JetType a, @NotNull JetType b, @NotNull TypeCheckingProcedure typeCheckingProcedure
|
||||
) {
|
||||
doAddConstraint(EQUAL, a, b, constraintPosition, typeCheckingProcedure);
|
||||
return true;
|
||||
|
||||
if (constrainingType == TypeUtils.NO_EXPECTED_TYPE
|
||||
|| constrainingType == DONT_CARE
|
||||
|| constrainingType == CANT_INFER) {
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean assertEqualTypeConstructors(
|
||||
@NotNull TypeConstructor a, @NotNull TypeConstructor b
|
||||
) {
|
||||
throw new IllegalStateException("'assertEqualTypeConstructors' shouldn't be invoked inside 'isSubtypeOf'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean assertSubtype(
|
||||
@NotNull JetType subtype, @NotNull JetType supertype, @NotNull TypeCheckingProcedure typeCheckingProcedure
|
||||
) {
|
||||
doAddConstraint(SUB_TYPE, subtype, supertype, constraintPosition, typeCheckingProcedure);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean noCorrespondingSupertype(
|
||||
@NotNull JetType subtype, @NotNull JetType supertype
|
||||
) {
|
||||
errorConstraintPositions.add(constraintPosition);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
doAddConstraint(constraintKind, subType, superType, constraintPosition, typeCheckingProcedure);
|
||||
}
|
||||
|
||||
private boolean isErrorOrSpecialType(@Nullable JetType type) {
|
||||
if (type == TypeUtils.NO_EXPECTED_TYPE
|
||||
|| type == DONT_CARE
|
||||
|| type == CANT_INFER) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (constrainingType == null || (ErrorUtils.isErrorType(constrainingType) && constrainingType != PLACEHOLDER_FUNCTION_TYPE)) {
|
||||
if (type == null || ((ErrorUtils.isErrorType(type) && type != PLACEHOLDER_FUNCTION_TYPE))) {
|
||||
hasErrorInConstrainingTypes = true;
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
assert subjectType != TypeUtils.NO_EXPECTED_TYPE : "Subject type shouldn't be NO_EXPECTED_TYPE (in position " + constraintPosition + " )";
|
||||
if (ErrorUtils.isErrorType(subjectType)) return;
|
||||
private void doAddConstraint(
|
||||
@NotNull ConstraintKind constraintKind,
|
||||
@Nullable JetType subType,
|
||||
@Nullable JetType superType,
|
||||
@NotNull ConstraintPosition constraintPosition,
|
||||
@NotNull TypeCheckingProcedure typeCheckingProcedure
|
||||
) {
|
||||
|
||||
DeclarationDescriptor subjectTypeDescriptor = subjectType.getConstructor().getDeclarationDescriptor();
|
||||
if (isErrorOrSpecialType(subType) || isErrorOrSpecialType(superType)) return;
|
||||
assert subType != null && superType != null;
|
||||
|
||||
assert superType != PLACEHOLDER_FUNCTION_TYPE : "The type for " + constraintPosition + " shouldn't be a placeholder for function type";
|
||||
|
||||
KotlinBuiltIns kotlinBuiltIns = KotlinBuiltIns.getInstance();
|
||||
if (constrainingType == PLACEHOLDER_FUNCTION_TYPE) {
|
||||
if (!kotlinBuiltIns.isFunctionOrExtensionFunctionType(subjectType)) {
|
||||
if (subjectTypeDescriptor instanceof TypeParameterDescriptor && typeParameterConstraints.get(subjectTypeDescriptor) != null) {
|
||||
if (subType == PLACEHOLDER_FUNCTION_TYPE) {
|
||||
if (!kotlinBuiltIns.isFunctionOrExtensionFunctionType(superType)) {
|
||||
if (isMyTypeVariable(superType)) {
|
||||
// a constraint binds type parameter and any function type, so there is no new info and no error
|
||||
return;
|
||||
}
|
||||
@@ -191,159 +254,44 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
// function literal without declaring receiver type { x -> ... }
|
||||
// can be considered as extension function if one is expected
|
||||
// (special type constructor for function/ extension function should be introduced like PLACEHOLDER_FUNCTION_TYPE)
|
||||
if (constraintKind == SUB_TYPE && kotlinBuiltIns.isFunctionType(constrainingType) && kotlinBuiltIns.isExtensionFunctionType(subjectType)) {
|
||||
constrainingType = createCorrespondingExtensionFunctionType(constrainingType, DONT_CARE);
|
||||
if (constraintKind == SUB_TYPE && kotlinBuiltIns.isFunctionType(subType) && kotlinBuiltIns.isExtensionFunctionType(superType)) {
|
||||
subType = createCorrespondingExtensionFunctionType(subType, DONT_CARE);
|
||||
}
|
||||
|
||||
DeclarationDescriptor constrainingTypeDescriptor = constrainingType.getConstructor().getDeclarationDescriptor();
|
||||
// can be equal for the recursive invocations:
|
||||
// fun <T> foo(i: Int) : T { ... return foo(i); } => T <: T
|
||||
if (subType == superType) return;
|
||||
|
||||
if (subjectTypeDescriptor instanceof TypeParameterDescriptor) {
|
||||
TypeParameterDescriptor typeParameter = (TypeParameterDescriptor) subjectTypeDescriptor;
|
||||
TypeConstraintsImpl typeConstraints = typeParameterConstraints.get(typeParameter);
|
||||
if (typeConstraints != null) {
|
||||
if (TypeUtils.dependsOnTypeParameterConstructors(constrainingType, Collections.singleton(DONT_CARE.getConstructor()))) {
|
||||
return;
|
||||
}
|
||||
if (subjectType.isNullable() && constrainingType.isNullable()) {
|
||||
constrainingType = TypeUtils.makeNotNullable(constrainingType);
|
||||
}
|
||||
typeConstraints.addBound(constraintKind, constrainingType);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (constrainingTypeDescriptor instanceof TypeParameterDescriptor) {
|
||||
assert typeParameterConstraints.get(constrainingTypeDescriptor) == null : "Constraining type contains type variable " + constrainingTypeDescriptor.getName();
|
||||
}
|
||||
if (constraintKind == SUB_TYPE && kotlinBuiltIns.isNothingOrNullableNothing(constrainingType)) {
|
||||
// following constraints are always true:
|
||||
// 'Nothing' is a subtype of any type
|
||||
if (!constrainingType.isNullable()) return;
|
||||
// 'Nothing?' is a subtype of nullable type
|
||||
if (subjectType.isNullable()) return;
|
||||
}
|
||||
if (!(constrainingTypeDescriptor instanceof ClassDescriptor) || !(subjectTypeDescriptor instanceof ClassDescriptor)) {
|
||||
errorConstraintPositions.add(constraintPosition);
|
||||
assert !isMyTypeVariable(subType) || !isMyTypeVariable(superType) :
|
||||
"The constraint shouldn't contain different type variables on both sides: " + subType + " <: " + superType;
|
||||
|
||||
|
||||
if (isMyTypeVariable(subType)) {
|
||||
generateTypeParameterConstraint(subType, superType, constraintKind == SUB_TYPE ? UPPER_BOUND : EXACT_BOUND);
|
||||
return;
|
||||
}
|
||||
switch (constraintKind) {
|
||||
case SUB_TYPE: {
|
||||
if (kotlinBuiltIns.isNothingOrNullableNothing(constrainingType)) break;
|
||||
JetType correspondingSupertype = TypeCheckingProcedure.findCorrespondingSupertype(constrainingType, subjectType);
|
||||
if (correspondingSupertype != null) {
|
||||
constrainingType = correspondingSupertype;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SUPER_TYPE: {
|
||||
if (kotlinBuiltIns.isNothingOrNullableNothing(subjectType)) break;
|
||||
JetType correspondingSupertype = TypeCheckingProcedure.findCorrespondingSupertype(subjectType, constrainingType);
|
||||
if (correspondingSupertype != null) {
|
||||
subjectType = correspondingSupertype;
|
||||
}
|
||||
}
|
||||
case EQUAL: //nothing
|
||||
}
|
||||
if (constrainingType.getConstructor() != subjectType.getConstructor()) {
|
||||
errorConstraintPositions.add(constraintPosition);
|
||||
if (isMyTypeVariable(superType)) {
|
||||
generateTypeParameterConstraint(superType, subType, constraintKind == SUB_TYPE ? LOWER_BOUND : EXACT_BOUND);
|
||||
return;
|
||||
}
|
||||
TypeConstructor typeConstructor = subjectType.getConstructor();
|
||||
List<TypeProjection> subjectArguments = subjectType.getArguments();
|
||||
List<TypeProjection> constrainingArguments = constrainingType.getArguments();
|
||||
List<TypeParameterDescriptor> parameters = typeConstructor.getParameters();
|
||||
for (int i = 0; i < subjectArguments.size(); i++) {
|
||||
Variance typeParameterVariance = parameters.get(i).getVariance();
|
||||
TypeProjection subjectArgument = subjectArguments.get(i);
|
||||
TypeProjection constrainingArgument = constrainingArguments.get(i);
|
||||
|
||||
ConstraintKind typeParameterConstraintKind = getTypeParameterConstraintKind(typeParameterVariance,
|
||||
subjectArgument, constrainingArgument, constraintKind);
|
||||
|
||||
addConstraint(typeParameterConstraintKind, subjectArgument.getType(), constrainingArgument.getType(), constraintPosition);
|
||||
}
|
||||
// if superType is nullable and subType is not nullable, unsafe call error will be generated later,
|
||||
// but constraint system should be solved anyway
|
||||
typeCheckingProcedure.isSubtypeOf(TypeUtils.makeNotNullable(subType), TypeUtils.makeNotNullable(superType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines what constraint (supertype, subtype or equal) should be generated for type parameter {@code T} in a constraint like (in this example subtype one): <br/>
|
||||
* {@code MyClass<in/out/- A> <: MyClass<in/out/- B>}, where {@code MyClass<in/out/- T>} is declared. <br/>
|
||||
*
|
||||
* The parameters description is given according to the example above.
|
||||
* @param typeParameterVariance declared variance of T
|
||||
* @param subjectTypeProjection {@code in/out/- A}
|
||||
* @param constrainingTypeProjection {@code in/out/- B}
|
||||
* @param upperConstraintKind kind of the constraint {@code MyClass<...A> <: MyClass<...B>} (subtype in this example).
|
||||
* @return kind of constraint to be generated: {@code A <: B} (subtype), {@code A >: B} (supertype) or {@code A = B} (equal).
|
||||
*/
|
||||
@NotNull
|
||||
private static ConstraintKind getTypeParameterConstraintKind(
|
||||
@NotNull Variance typeParameterVariance,
|
||||
@NotNull TypeProjection subjectTypeProjection,
|
||||
@NotNull TypeProjection constrainingTypeProjection,
|
||||
@NotNull ConstraintKind upperConstraintKind
|
||||
private void generateTypeParameterConstraint(
|
||||
@NotNull JetType parameterType,
|
||||
@NotNull JetType constrainingType,
|
||||
@NotNull BoundKind boundKind
|
||||
) {
|
||||
// If variance of type parameter is non-trivial, it should be taken into consideration to infer result constraint type.
|
||||
// Otherwise when type parameter declared as INVARIANT, there might be non-trivial use-site variance of a supertype.
|
||||
//
|
||||
// Example: Let class MyClass<T> is declared.
|
||||
//
|
||||
// If super type has 'out' projection:
|
||||
// MyClass<A> <: MyClass<out B>,
|
||||
// then constraint A <: B can be generated.
|
||||
//
|
||||
// If super type has 'in' projection:
|
||||
// MyClass<A> <: MyClass<in B>,
|
||||
// then constraint A >: B can be generated.
|
||||
//
|
||||
// Otherwise constraint A = B should be generated.
|
||||
TypeConstraintsImpl typeConstraints = getTypeConstraints(parameterType);
|
||||
assert typeConstraints != null : "constraint should be generated only for type variables";
|
||||
|
||||
Variance varianceForTypeParameter;
|
||||
if (typeParameterVariance != INVARIANT) {
|
||||
varianceForTypeParameter = typeParameterVariance;
|
||||
if (parameterType.isNullable()) {
|
||||
// For parameter type T constraint T? <: Int? should transform to T <: Int
|
||||
constrainingType = TypeUtils.makeNotNullable(constrainingType);
|
||||
}
|
||||
else if (upperConstraintKind == SUPER_TYPE) {
|
||||
varianceForTypeParameter = constrainingTypeProjection.getProjectionKind();
|
||||
}
|
||||
else if (upperConstraintKind == SUB_TYPE) {
|
||||
varianceForTypeParameter = subjectTypeProjection.getProjectionKind();
|
||||
}
|
||||
else {
|
||||
varianceForTypeParameter = INVARIANT;
|
||||
}
|
||||
|
||||
return getTypeParameterConstraintKind(varianceForTypeParameter, upperConstraintKind);
|
||||
}
|
||||
|
||||
/**
|
||||
* Let class {@code MyClass<T, out R, in S>} is declared.<br/><br/>
|
||||
*
|
||||
* If upperConstraintKind is SUPER_TYPE:
|
||||
* {@code MyClass<A, B, C> <: MyClass<D, E, F>}, <br/>
|
||||
* then constraints {@code A = D, B <: E, C >: F} are generated. <br/><br/>
|
||||
*
|
||||
* If upperConstraintKind is SUB_TYPE:
|
||||
* {@code MyClass<A, B, C> >: MyClass<D, E, F>}, <br/>
|
||||
* then constraints {@code A = D, B >: E, C <: F} are generated. <br/><br/>
|
||||
*
|
||||
* If upperConstraintKind is EQUAL:
|
||||
* {@code MyClass<A, B, C> = MyClass<D, E, F>}, <br/>
|
||||
* then equality constraints {@code A = D, B = E, C = F} are generated. <br/><br/>
|
||||
*
|
||||
* Method getTypeParameterConstraintKind gets upperConstraintKind and variance of type parameter
|
||||
* (INVARIANT for T, OUT_VARIANCE for R in example above) and returns kind of constraint for corresponding type parameter.
|
||||
*/
|
||||
@NotNull
|
||||
private static ConstraintKind getTypeParameterConstraintKind(
|
||||
@NotNull Variance typeParameterVariance,
|
||||
@NotNull ConstraintKind upperConstraintKind
|
||||
) {
|
||||
if (upperConstraintKind == EQUAL || typeParameterVariance == INVARIANT) {
|
||||
return EQUAL;
|
||||
}
|
||||
if ((upperConstraintKind == SUB_TYPE && typeParameterVariance == OUT_VARIANCE) ||
|
||||
(upperConstraintKind == SUPER_TYPE && typeParameterVariance == IN_VARIANCE)) {
|
||||
return SUB_TYPE;
|
||||
}
|
||||
return SUPER_TYPE;
|
||||
typeConstraints.addBound(boundKind, constrainingType);
|
||||
}
|
||||
|
||||
public void processDeclaredBoundConstraints() {
|
||||
@@ -371,6 +319,20 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
return typeParameterConstraints.get(typeVariable);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private TypeConstraintsImpl getTypeConstraints(@NotNull JetType type) {
|
||||
ClassifierDescriptor parameterDescriptor = type.getConstructor().getDeclarationDescriptor();
|
||||
if (parameterDescriptor instanceof TypeParameterDescriptor) {
|
||||
return typeParameterConstraints.get(parameterDescriptor);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isMyTypeVariable(@NotNull JetType type) {
|
||||
ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
|
||||
return descriptor instanceof TypeParameterDescriptor && typeParameterConstraints.get(descriptor) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccessful() {
|
||||
return !hasContradiction() && !hasUnknownParameters();
|
||||
|
||||
+7
-7
@@ -39,15 +39,15 @@ public class TypeConstraintsImpl implements TypeConstraints {
|
||||
return varianceOfPosition;
|
||||
}
|
||||
|
||||
public void addBound(@NotNull ConstraintKind constraintKind, @NotNull JetType type) {
|
||||
switch (constraintKind) {
|
||||
case SUB_TYPE:
|
||||
public void addBound(@NotNull BoundKind boundKind, @NotNull JetType type) {
|
||||
switch (boundKind) {
|
||||
case LOWER_BOUND:
|
||||
lowerBounds.add(type);
|
||||
break;
|
||||
case SUPER_TYPE:
|
||||
case UPPER_BOUND:
|
||||
upperBounds.add(type);
|
||||
break;
|
||||
case EQUAL:
|
||||
case EXACT_BOUND:
|
||||
exactBounds.add(type);
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ public class TypeConstraintsImpl implements TypeConstraints {
|
||||
return typeConstraints;
|
||||
}
|
||||
|
||||
public static enum ConstraintKind {
|
||||
SUPER_TYPE, SUB_TYPE, EQUAL
|
||||
public static enum BoundKind {
|
||||
LOWER_BOUND, UPPER_BOUND, EXACT_BOUND
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -25,6 +25,7 @@ import org.jetbrains.jet.lang.diagnostics.Errors;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.BindingTrace;
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem;
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl;
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.InferenceErrorData;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
|
||||
@@ -221,6 +222,10 @@ public class TracingStrategyImpl implements TracingStrategy {
|
||||
}
|
||||
assert data.expectedType != null;
|
||||
trace.report(TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH.on(reference, returnType, data.expectedType));
|
||||
//todo[ConstraintSystemImpl]
|
||||
if (constraintSystem instanceof ConstraintSystemImpl) {
|
||||
((ConstraintSystemImpl) constraintSystem).setHasExpectedTypeMismatch();
|
||||
}
|
||||
}
|
||||
else if (constraintSystem.hasTypeConstructorMismatch()) {
|
||||
trace.report(TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH.on(reference, data));
|
||||
|
||||
@@ -465,7 +465,7 @@ public class TypeUtils {
|
||||
}
|
||||
|
||||
public static boolean dependsOnTypeParameters(@NotNull JetType type, @NotNull Collection<TypeParameterDescriptor> typeParameters) {
|
||||
return dependsOnTypeParameterConstructors(type, Collections2
|
||||
return dependsOnTypeConstructors(type, Collections2
|
||||
.transform(typeParameters, new Function<TypeParameterDescriptor, TypeConstructor>() {
|
||||
@Override
|
||||
public TypeConstructor apply(@Nullable TypeParameterDescriptor typeParameterDescriptor) {
|
||||
@@ -475,10 +475,10 @@ public class TypeUtils {
|
||||
}));
|
||||
}
|
||||
|
||||
public static boolean dependsOnTypeParameterConstructors(@NotNull JetType type, @NotNull Collection<TypeConstructor> typeParameterConstructors) {
|
||||
public static boolean dependsOnTypeConstructors(@NotNull JetType type, @NotNull Collection<TypeConstructor> typeParameterConstructors) {
|
||||
if (typeParameterConstructors.contains(type.getConstructor())) return true;
|
||||
for (TypeProjection typeProjection : type.getArguments()) {
|
||||
if (dependsOnTypeParameterConstructors(typeProjection.getType(), typeParameterConstructors)) {
|
||||
if (dependsOnTypeConstructors(typeProjection.getType(), typeParameterConstructors)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package b
|
||||
|
||||
fun <T, R> foo(<!UNUSED_PARAMETER!>map<!>: Map<T, R>) : R = throw Exception()
|
||||
|
||||
fun <F, G> getMap() : Map<F, G> = throw Exception()
|
||||
|
||||
fun bar123() {
|
||||
<!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>foo<!>(<!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>getMap<!>(
|
||||
<!SYNTAX!><!>}
|
||||
|
||||
@@ -28,7 +28,7 @@ fun test1(int: Int, any: Any) {
|
||||
val a7 : MyList<in Any> = getMyList(int)
|
||||
|
||||
val a8 : MyList<in Any> = <!TYPE_MISMATCH!>getMyListToReadFrom<Int>(int)<!>
|
||||
val a9 : MyList<in Any> = <!TYPE_MISMATCH!>getMyListToReadFrom(int)<!>
|
||||
val a9 : MyList<in Any> = <!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>getMyListToReadFrom<!>(int)
|
||||
|
||||
val a10 : MyList<out Int> = <!TYPE_MISMATCH!>getMyList<Any>(any)<!>
|
||||
val a11 : MyList<out Int> = <!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>getMyList<!>(any)
|
||||
@@ -58,9 +58,9 @@ fun test1(int: Int, any: Any) {
|
||||
writeToMyList(getMyListToWriteTo(any), int)
|
||||
<!TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS!>writeToMyList<!>(getMyListToWriteTo(int), any)
|
||||
|
||||
readFromMyList(<!TYPE_MISMATCH!>getMyListToWriteTo(any)<!>, any)
|
||||
readFromMyList(getMyListToWriteTo(any), any)
|
||||
|
||||
writeToMyList(<!TYPE_MISMATCH!>getMyListToReadFrom(any)<!>, any)
|
||||
<!TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS!>writeToMyList<!>(getMyListToReadFrom(any), any)
|
||||
|
||||
use(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
class Item(val name: String, val rating: Int): Comparable<Item> {
|
||||
public override fun compareTo(other: Item): Int {
|
||||
return compareBy(this, other, { rating }, { name })
|
||||
}
|
||||
}
|
||||
|
||||
// from standard library
|
||||
inline fun <T : Any> compareBy(<!UNUSED_PARAMETER!>a<!>: T?, <!UNUSED_PARAMETER!>b<!>: T?,
|
||||
vararg <!UNUSED_PARAMETER!>functions<!>: T.() -> Comparable<*>?): Int = throw Exception()
|
||||
@@ -6,7 +6,7 @@ fun bar<T>(a: T, b: Map<T, String>) = b.get(a)
|
||||
|
||||
fun test(a: Int) {
|
||||
foo(a, null)
|
||||
<!TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH!>bar<!>(a, null)
|
||||
bar(a, <!ERROR_COMPILE_TIME_VALUE!>null<!>)
|
||||
}
|
||||
fun test1(a: Int) {
|
||||
<!UNREACHABLE_CODE!>foo(a, throw Exception())<!>
|
||||
|
||||
@@ -2007,6 +2007,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
||||
doTest("compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/namedFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noTypeParamsInReturnType.kt")
|
||||
public void testNoTypeParamsInReturnType() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/noTypeParamsInReturnType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("typeReferenceError.kt")
|
||||
public void testTypeReferenceError() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/typeReferenceError.kt");
|
||||
@@ -2168,6 +2173,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/inference/regressions"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("compareBy.kt")
|
||||
public void testCompareBy() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/inference/regressions/compareBy.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt1029.kt")
|
||||
public void testKt1029() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/inference/regressions/kt1029.kt");
|
||||
|
||||
Reference in New Issue
Block a user