PY-76814 Add variance field to PyTypeVarType

GitOrigin-RevId: 2e7fe0b149a7412c4fdae924a846f200e82786a0
This commit is contained in:
Daniil Kalinin
2025-06-04 12:17:47 +00:00
committed by intellij-monorepo-bot
parent 751be636a6
commit 90473326ac
4 changed files with 50 additions and 12 deletions
@@ -34,4 +34,10 @@ public interface PyTypeVarType extends PyTypeParameterType, PyInstantiableType<P
* in PEP 484.
*/
@Nullable PyType getBound();
@NotNull Variance getVariance();
enum Variance {
COVARIANT, CONTRAVARIANT, INVARIANT, INFER_VARIANCE
}
}
@@ -31,6 +31,7 @@ import com.jetbrains.python.codeInsight.functionTypeComments.psi.PyFunctionTypeA
import com.jetbrains.python.codeInsight.typeHints.PyTypeHintFile;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.impl.PyEvaluator;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import com.jetbrains.python.psi.impl.stubs.PyClassElementType;
import com.jetbrains.python.psi.impl.stubs.PyTypingAliasStubType;
@@ -1529,7 +1530,8 @@ public final class PyTypingTypeProvider extends PyTypeProviderWithCustomContext<
.toList();
PyExpression boundExpression = assignedCall.getKeywordArgument("bound");
PyType bound = boundExpression == null ? null : Ref.deref(getType(boundExpression, context));
return new PyTypeVarTypeImpl(name, constraints, bound, defaultType);
PyTypeVarType.Variance variance = getTypeVarVarianceFromDeclaration(assignedCall);
return new PyTypeVarTypeImpl(name, constraints, bound, defaultType, variance);
}
case ParamSpec -> {
return new PyParamSpecType(name)
@@ -1543,6 +1545,25 @@ public final class PyTypingTypeProvider extends PyTypeProviderWithCustomContext<
return null;
}
private static @NotNull PyTypeVarType.Variance getTypeVarVarianceFromDeclaration(@NotNull PyCallExpression assignedCall) {
boolean covariant = PyEvaluator.evaluateAsBooleanNoResolve(assignedCall.getKeywordArgument("covariant"), false);
boolean contravariant = PyEvaluator.evaluateAsBooleanNoResolve(assignedCall.getKeywordArgument("contravariant"), false);
boolean inferVariance = PyEvaluator.evaluateAsBooleanNoResolve(assignedCall.getKeywordArgument("infer_variance"), false);
if (covariant && !contravariant) {
return PyTypeVarType.Variance.COVARIANT;
}
else if (contravariant && !covariant) {
return PyTypeVarType.Variance.CONTRAVARIANT;
}
else if (inferVariance) {
return PyTypeVarType.Variance.INFER_VARIANCE;
}
else {
return PyTypeVarType.Variance.INVARIANT;
}
}
@ApiStatus.Internal
public static @Nullable PyAstTypeParameter.Kind getTypeParameterKindFromDeclaration(@NotNull PyCallExpression callExpression,
@NotNull TypeEvalContext context) {
@@ -1600,7 +1621,7 @@ public final class PyTypingTypeProvider extends PyTypeProviderWithCustomContext<
else if (boundExpression != null) {
boundType = Ref.deref(getTypePreventingRecursion(boundExpression, context));
}
return new PyTypeVarTypeImpl(name, constraints, boundType, defaultType)
return new PyTypeVarTypeImpl(name, constraints, boundType, defaultType, PyTypeVarType.Variance.INFER_VARIANCE)
.withScopeOwner(scopeOwner)
.withDeclarationElement(declarationElement);
}
@@ -27,6 +27,7 @@ public class PyGenericType implements PyTypeVarType {
private final @NotNull List<@Nullable PyType> myConstraints;
private final @Nullable PyType myBound;
private final @Nullable Ref<PyType> myDefaultType;
private final @NotNull Variance myVariance;
private final boolean myIsDefinition;
private final @Nullable PyQualifiedNameOwner myDeclarationElement;
private final @Nullable PyQualifiedNameOwner myScopeOwner;
@@ -34,14 +35,16 @@ public class PyGenericType implements PyTypeVarType {
public PyGenericType(@NotNull String name,
@NotNull List<@Nullable PyType> constraints,
@Nullable PyType bound,
@Nullable Ref<PyType> defaultType) {
this(name, constraints, bound, defaultType, false, null, null);
@Nullable Ref<PyType> defaultType,
@NotNull Variance variance) {
this(name, constraints, bound, defaultType, variance, false, null, null);
}
protected PyGenericType(@NotNull String name,
@NotNull List<@Nullable PyType> constraints,
@Nullable PyType bound,
@Nullable Ref<PyType> defaultType,
@NotNull Variance variance,
boolean isDefinition,
@Nullable PyQualifiedNameOwner declarationElement,
@Nullable PyQualifiedNameOwner scopeOwner) {
@@ -49,6 +52,7 @@ public class PyGenericType implements PyTypeVarType {
myConstraints = constraints;
myBound = bound;
myDefaultType = defaultType;
myVariance = variance;
myIsDefinition = isDefinition;
myDeclarationElement = declarationElement;
myScopeOwner = scopeOwner;
@@ -156,6 +160,11 @@ public class PyGenericType implements PyTypeVarType {
return myBound;
}
@Override
public @NotNull Variance getVariance() {
return myVariance;
}
@Override
public @Nullable Ref<PyType> getDefaultType() {
return myDefaultType;
@@ -172,7 +181,7 @@ public class PyGenericType implements PyTypeVarType {
}
public @NotNull PyGenericType withScopeOwner(@Nullable PyQualifiedNameOwner scopeOwner) {
return new PyTypeVarTypeImpl(getName(), getConstraints(), getBound(), getDefaultType(), isDefinition(), getDeclarationElement(), scopeOwner);
return new PyTypeVarTypeImpl(getName(), getConstraints(), getBound(), getDefaultType(), getVariance(), isDefinition(), getDeclarationElement(), scopeOwner);
}
public @NotNull PyGenericType withTargetExpression(@Nullable PyTargetExpression targetExpression) {
@@ -180,16 +189,16 @@ public class PyGenericType implements PyTypeVarType {
}
public @NotNull PyGenericType withDeclarationElement(@Nullable PyQualifiedNameOwner declarationElement) {
return new PyTypeVarTypeImpl(getName(), getConstraints(), getBound(), getDefaultType(), isDefinition(), declarationElement, getScopeOwner());
return new PyTypeVarTypeImpl(getName(), getConstraints(), getBound(), getDefaultType(), getVariance(), isDefinition(), declarationElement, getScopeOwner());
}
@Override
public @NotNull PyGenericType toInstance() {
return myIsDefinition ? new PyTypeVarTypeImpl(myName, myConstraints, myBound, myDefaultType, false, myDeclarationElement, myScopeOwner) : this;
return myIsDefinition ? new PyTypeVarTypeImpl(myName, myConstraints, myBound, myDefaultType, myVariance, false, myDeclarationElement, myScopeOwner) : this;
}
@Override
public @NotNull PyGenericType toClass() {
return myIsDefinition ? this : new PyTypeVarTypeImpl(myName, myConstraints, myBound, myDefaultType, true, myDeclarationElement, myScopeOwner);
return myIsDefinition ? this : new PyTypeVarTypeImpl(myName, myConstraints, myBound, myDefaultType, myVariance, true, myDeclarationElement, myScopeOwner);
}
}
@@ -9,24 +9,26 @@ import java.util.List;
public final class PyTypeVarTypeImpl extends PyGenericType {
public PyTypeVarTypeImpl(@NotNull String name, @Nullable PyType bound) {
this(name, List.of(), bound, null);
this(name, List.of(), bound, null, Variance.INVARIANT);
}
public PyTypeVarTypeImpl(@NotNull String name,
@NotNull List<@Nullable PyType> constraints,
@Nullable PyType bound,
@Nullable Ref<PyType> defaultType) {
super(name, constraints, bound, defaultType);
@Nullable Ref<PyType> defaultType,
@NotNull Variance variance) {
super(name, constraints, bound, defaultType, variance);
}
PyTypeVarTypeImpl(@NotNull String name,
@NotNull List<@Nullable PyType> constraints,
@Nullable PyType bound,
@Nullable Ref<PyType> defaultType,
@NotNull Variance variance,
boolean isDefinition,
@Nullable PyQualifiedNameOwner declarationElement,
@Nullable PyQualifiedNameOwner scopeOwner) {
super(name, constraints, bound, defaultType, isDefinition, declarationElement, scopeOwner);
super(name, constraints, bound, defaultType, variance, isDefinition, declarationElement, scopeOwner);
}
@Override