diff --git a/python/python-psi-api/src/com/jetbrains/python/psi/types/TypeEvalContext.java b/python/python-psi-api/src/com/jetbrains/python/psi/types/TypeEvalContext.java index c85b551ee96c..c8909daf62c7 100644 --- a/python/python-psi-api/src/com/jetbrains/python/psi/types/TypeEvalContext.java +++ b/python/python-psi-api/src/com/jetbrains/python/psi/types/TypeEvalContext.java @@ -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 myProcessingContext = ThreadLocal.withInitial(ProcessingContext::new); - protected final Map myEvaluated = createMap(); - protected final Map myEvaluatedReturn = createMap(); - protected final Map, 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 Map createMap() { - if (Registry.is("python.use.better.control.flow.type.inference")) { - return new ConcurrentHashMap<>(); - } - return CollectionFactory.createConcurrentSoftValueMap(); - } + protected final Map myEvaluated = CollectionFactory.createConcurrentSoftValueMap(); + protected final Map myEvaluatedReturn = CollectionFactory.createConcurrentSoftValueMap(); + protected final Map, PyType> contextTypeCache = CollectionFactory.createConcurrentSoftValueMap(); private TypeEvalContext(boolean allowDataFlow, boolean allowStubToAST, boolean allowCallContext, @Nullable PsiFile origin) { myConstraints = new TypeEvalConstraints(allowDataFlow, allowStubToAST, allowCallContext, origin); diff --git a/python/python-psi-impl/resources/intellij.python.psi.impl.xml b/python/python-psi-impl/resources/intellij.python.psi.impl.xml index 80092c77feb9..c059de3b9235 100644 --- a/python/python-psi-impl/resources/intellij.python.psi.impl.xml +++ b/python/python-psi-impl/resources/intellij.python.psi.impl.xml @@ -493,9 +493,6 @@ INTN.category.python - diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java index 515a5309ed16..18d970708a1f 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java @@ -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 defs = PyDefUseUtil.getLatestDefs(scopeOwner, name, element, true, false, context); - - // null means empty set of possible types, Ref(null) means Any - final @Nullable Ref 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 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 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 { diff --git a/python/python-psi-impl/src/com/jetbrains/python/refactoring/PyDefUseUtil.java b/python/python-psi-impl/src/com/jetbrains/python/refactoring/PyDefUseUtil.java index 65280b0e5907..c6810b27a13f 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/refactoring/PyDefUseUtil.java +++ b/python/python-psi-impl/src/com/jetbrains/python/refactoring/PyDefUseUtil.java @@ -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);