[python] Rm obsolete registry key python.use.better.control.flow.type.inference

GitOrigin-RevId: 47ca59e692de12376ca17604b8b9ab2439882a45
This commit is contained in:
Petr
2025-08-19 17:37:04 +00:00
committed by intellij-monorepo-bot
parent 55f264d1e3
commit dd58eaacfc
4 changed files with 7 additions and 119 deletions
@@ -24,7 +24,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
@@ -50,21 +49,9 @@ public sealed class TypeEvalContext {
private final ThreadLocal<ProcessingContext> myProcessingContext = ThreadLocal.withInitial(ProcessingContext::new);
protected final Map<PyTypedElement, PyType> myEvaluated = createMap();
protected final Map<PyCallable, PyType> myEvaluatedReturn = createMap();
protected final Map<Pair<PyExpression, Object>, PyType> contextTypeCache = createMap();
/**
* AssumptionContext invariant requires that if type is in the map,
* it's dependencies are also in the map, so we can't use softValueMap.
* Temporary solution until we know assumeType works as expected.
* @see TypeEvalContext#assumeType(PyTypedElement, PyType, Function)
*/
private static <T> Map<T, PyType> createMap() {
if (Registry.is("python.use.better.control.flow.type.inference")) {
return new ConcurrentHashMap<>();
}
return CollectionFactory.createConcurrentSoftValueMap();
}
protected final Map<PyTypedElement, PyType> myEvaluated = CollectionFactory.createConcurrentSoftValueMap();
protected final Map<PyCallable, PyType> myEvaluatedReturn = CollectionFactory.createConcurrentSoftValueMap();
protected final Map<Pair<PyExpression, Object>, PyType> contextTypeCache = CollectionFactory.createConcurrentSoftValueMap();
private TypeEvalContext(boolean allowDataFlow, boolean allowStubToAST, boolean allowCallContext, @Nullable PsiFile origin) {
myConstraints = new TypeEvalConstraints(allowDataFlow, allowStubToAST, allowCallContext, origin);
@@ -493,9 +493,6 @@
<categoryKey>INTN.category.python</categoryKey>
</intentionAction>
<registryKey defaultValue="false"
description="Enable experimental better handling of recursive cases in type inference"
key="python.use.better.control.flow.type.inference"/>
<registryKey defaultValue="true"
description="Enable pyi stubs distributed with numpy (see https://github.com/numpy/numpy/issues/18565)"
key="enable.numpy.pyi.stubs"/>
@@ -2,13 +2,11 @@
package com.jetbrains.python.psi.impl;
import com.intellij.codeInsight.controlflow.ConditionalInstruction;
import com.intellij.codeInsight.controlflow.ControlFlowUtil;
import com.intellij.codeInsight.controlflow.Instruction;
import com.intellij.diagnostic.PluginException;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.FileContextUtil;
import com.intellij.psi.util.PsiTreeUtil;
@@ -17,7 +15,6 @@ import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PythonRuntimeService;
import com.jetbrains.python.ast.PyAstFunction;
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
import com.jetbrains.python.codeInsight.controlflow.PyTypeAssertionEvaluator;
import com.jetbrains.python.codeInsight.controlflow.ReadWriteInstruction;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
@@ -485,87 +482,6 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
@NotNull TypeEvalContext context,
@NotNull PyExpression anchor,
@NotNull ScopeOwner scopeOwner) {
if (!Registry.is("python.use.better.control.flow.type.inference")) {
return getTypeByControlFlowOld(name, context, anchor, scopeOwner);
}
final PyAugAssignmentStatement augAssignment = PsiTreeUtil.getParentOfType(anchor, PyAugAssignmentStatement.class);
final PyElement element = augAssignment != null ? augAssignment : anchor;
final Instruction[] flow = ControlFlowCache.getControlFlow(scopeOwner).getInstructions();
final int thisInstructionIdx = ControlFlowUtil.findInstructionNumberByElement(flow, element);
if (thisInstructionIdx == -1) return null;
final Instruction thisInstruction = flow[thisInstructionIdx];
final List<Instruction> defs = PyDefUseUtil.getLatestDefs(scopeOwner, name, element, true, false, context);
// null means empty set of possible types, Ref(null) means Any
final @Nullable Ref<PyType> typeOfEarlierDefinitions = StreamEx.of(defs)
.filter(def -> def.num() < thisInstruction.num())
.map(def -> getTypeFromInstruction(context, anchor, def))
.nonNull()
.collect(PyTypeUtil.toUnionFromRef());
// If earlier definitions were not found, variable may be unbound. Choose Any as type.
PyType deducedType = Ref.deref(typeOfEarlierDefinitions);
final var laterDefs = StreamEx.of(defs).filter(def -> def.num() > thisInstruction.num()).toList();
if (laterDefs.isEmpty()) {
return deducedType;
}
for (int i = 0; i < MAX_CFG_ITERATIONS; i++) {
final @Nullable Ref<PyType> typeOfLaterDefinitions = context.assumeType(anchor, deducedType, (ctx) -> {
return StreamEx.of(laterDefs)
.map(def -> getTypeFromInstruction(ctx, anchor, def))
.nonNull()
.collect(PyTypeUtil.toUnionFromRef());
});
if (typeOfLaterDefinitions == null) {
return deducedType;
}
PyType newType = PyUnionType.union(deducedType, typeOfLaterDefinitions.get());
if (Objects.equals(deducedType, newType)) {
return deducedType;
}
deducedType = newType;
}
return deducedType;
}
private static @Nullable Ref<PyType> getTypeFromInstruction(@NotNull TypeEvalContext context,
@NotNull PyExpression anchor,
@NotNull Instruction instr) {
if (instr instanceof ReadWriteInstruction readWriteInstruction) {
return readWriteInstruction.getType(context, anchor);
}
if (instr instanceof ConditionalInstruction conditionalInstruction) {
final PyType conditionType = context.getType((PyTypedElement)conditionalInstruction.getCondition());
if (conditionType instanceof PyNarrowedType narrowedType && narrowedType.isBound()) {
var arguments = narrowedType.getOriginal().getArguments(null);
if (!arguments.isEmpty()) {
var firstArgument = arguments.get(0);
PyType type = narrowedType.getNarrowedType();
if (firstArgument instanceof PyReferenceExpression && type != null) {
@Nullable PyType initial = context.getType(firstArgument);
boolean positive = conditionalInstruction.getResult() ^ narrowedType.getNegated();
if (narrowedType.getTypeIs()) {
return PyTypeAssertionEvaluator.createAssertionType(initial, type, positive, context);
}
return Ref.create((positive) ? type : initial);
}
}
}
}
return null;
}
private static PyType getTypeByControlFlowOld(@NotNull String name,
@NotNull TypeEvalContext context,
@NotNull PyExpression anchor,
@NotNull ScopeOwner scopeOwner) {
final PyAugAssignmentStatement augAssignment = PsiTreeUtil.getParentOfType(anchor, PyAugAssignmentStatement.class);
final PyElement element = augAssignment != null ? augAssignment : anchor;
try {
@@ -22,7 +22,6 @@ import com.intellij.codeInsight.controlflow.Instruction;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.Version;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.QualifiedName;
import com.jetbrains.python.PyLanguageFacadeKt;
@@ -86,7 +85,7 @@ public final class PyDefUseUtil {
result.add(typeGuardInstruction);
return ControlFlowUtil.Operation.CONTINUE;
}
if (isNotBackEdge(instruction.num(), startNum) &&
if (instruction.num() < startNum &&
context.getOrigin() == callInstruction.getElement().getContainingFile()) {
var newContext = (MAX_CONTROL_FLOW_SIZE > instructions.length)
? TypeEvalContext.codeAnalysis(context.getOrigin().getProject(), context.getOrigin())
@@ -95,7 +94,7 @@ public final class PyDefUseUtil {
}
}
final PsiElement element = instruction.getElement();
if (isNotBackEdge(instruction.num(), startNum)
if (instruction.num() < startNum
&& acceptTypeAssertions && instruction instanceof ConditionalInstruction conditionalInstruction) {
if (conditionalInstruction.getCondition() instanceof PyTypedElement typedElement && context.getOrigin() == typedElement.getContainingFile()) {
var newContext = (MAX_CONTROL_FLOW_SIZE > instructions.length)
@@ -110,8 +109,8 @@ public final class PyDefUseUtil {
}
if (instruction instanceof ReadWriteInstruction rwInstruction) {
final ReadWriteInstruction.ACCESS access = rwInstruction.getAccess();
if (access.isWriteAccess() ||
acceptTypeAssertions && access.isAssertTypeAccess() && isNotBackEdge(instruction.num(), startNum)) {
if (access.isWriteAccess() ||
acceptTypeAssertions && access.isAssertTypeAccess() && instruction.num() < startNum) {
final String name = elementName(element);
if (Comparing.strEqual(name, varName)) {
if (isReachableWithVersionChecks(rwInstruction, languageLevel)) {
@@ -134,17 +133,6 @@ public final class PyDefUseUtil {
return new ArrayList<>(result);
}
/**
* New analysis handles back edges separately.
* @see com.jetbrains.python.psi.impl.PyReferenceExpressionImpl#getTypeByControlFlow(String, TypeEvalContext, PyExpression, ScopeOwner)
*/
private static boolean isNotBackEdge(int instNum, int startNum) {
if (Registry.is("python.use.better.control.flow.type.inference")) {
return true;
}
return instNum < startNum;
}
private static int findStartInstructionId(@NotNull PsiElement startAnchor, Instruction @NotNull [] instructions) {
PsiElement realCfgAnchor = startAnchor;
final PyAugAssignmentStatement augAssignment = PyAugAssignmentStatementNavigator.getStatementByTarget(startAnchor);