private static int AddFlow(NullabilityNode node, NullabilityNode sink, int maxNewFlow) { if (maxNewFlow == 0 || node.Visited) { return(0); } node.Visited = true; if (node == sink) { return(maxNewFlow); } foreach (NullabilityEdge edge in node.OutgoingEdges) { int newFlow = AddFlow(edge.Target, sink, Math.Min(maxNewFlow, edge.Capacity)); if (newFlow > 0) { edge.Capacity -= newFlow; edge.ReverseCapacity += newFlow; return(newFlow); } } foreach (NullabilityEdge edge in node.IncomingEdges) { int newFlow = AddFlow(edge.Source, sink, Math.Min(maxNewFlow, edge.ReverseCapacity)); if (newFlow > 0) { edge.ReverseCapacity -= newFlow; edge.Capacity += newFlow; return(newFlow); } } return(0); }
private TypeWithNode(ITypeSymbol?type, NullabilityNode node, IReadOnlyList <TypeWithNode>?typeArguments, string?flowLabel) : this(type, node, typeArguments) { #if DEBUG this.FlowLabel = flowLabel; #endif }
public TypeWithNode(ITypeSymbol?type, NullabilityNode node, IReadOnlyList <TypeWithNode>?typeArguments = null) { this.Type = type; this.Node = node; this.TypeArguments = typeArguments ?? emptyTypeArguments; Debug.Assert(this.TypeArguments.Count == type.FullArity()); }
private static bool AddFlow(NullabilityNode node, NullabilityNode source) { if (node.Visited) { return(false); } node.Visited = true; if (node == source) { return(true); } var predecessors = node.ResidualGraphPredecessors; for (int i = 0; i < predecessors.Count; i++) { var prevNode = predecessors[i]; if (AddFlow(prevNode, source)) { // Remove the edge from the residual graph predecessors.SwapRemoveAt(i); // and instead add the reverse edge prevNode.ResidualGraphPredecessors.Add(node); return(true); } } return(false); }
private void InferNullable(NullabilityNode node) { Debug.Assert(node.NullType == NullType.Infer || node.NullType == NullType.Nullable); node.NullType = NullType.Nullable; foreach (var edge in node.OutgoingEdges) { if (edge.Target.NullType == NullType.Infer) { InferNullable(edge.Target); } } }
private void InferNonNull(NullabilityNode node) { Debug.Assert(node.NullType == NullType.Infer || node.NullType == NullType.NonNull); node.NullType = NullType.NonNull; foreach (var edge in node.IncomingEdges) { if (edge.Source.NullType == NullType.Infer) { InferNonNull(edge.Source); } } }
private void InferNonNullUsingResidualGraph(NullabilityNode node) { Debug.Assert(node.NullType == NullType.Infer || node.NullType == NullType.NonNull); node.NullType = NullType.NonNull; foreach (var pred in node.ResidualGraphPredecessors) { if (pred.NullType == NullType.Infer) { InferNonNullUsingResidualGraph(pred); } } }
public static int Compute(IEnumerable <NullabilityNode> allNodes, NullabilityNode source, NullabilityNode sink, CancellationToken cancellationToken) { Debug.Assert(source != sink); int maxFlow = 0; ResetVisited(allNodes); while (AddFlow(sink, source)) { cancellationToken.ThrowIfCancellationRequested(); maxFlow += 1; ResetVisited(allNodes); } return(maxFlow); }
public static int Compute(IEnumerable <NullabilityNode> allTypes, NullabilityNode source, NullabilityNode sink, CancellationToken cancellationToken) { Debug.Assert(source != sink); int maxFlow = 0; int newFlow; ResetVisited(allTypes); while ((newFlow = AddFlow(source, sink, int.MaxValue)) > 0) { cancellationToken.ThrowIfCancellationRequested(); maxFlow += newFlow; ResetVisited(allTypes); } return(maxFlow); }
private void InferNullable(NullabilityNode node, bool ignoreEdgesWithoutCapacity = false) { if (node.NullType != NullType.Infer) { return; } node.NullType = NullType.Nullable; foreach (var edge in node.OutgoingEdges) { if (ignoreEdgesWithoutCapacity == false || edge.Capacity > 0) { InferNullable(edge.Target, ignoreEdgesWithoutCapacity); } } }
private void InferNonNull(NullabilityNode node, bool ignoreEdgesWithoutCapacity = false) { if (node.NullType != NullType.Infer) { return; } node.NullType = NullType.NonNull; foreach (var edge in node.IncomingEdges) { if (ignoreEdgesWithoutCapacity == false || edge.Capacity > 0) { InferNonNull(edge.Source, ignoreEdgesWithoutCapacity); } } }
/// <summary> /// Replace with node with another node. /// All future attempts to create an edge involving this node, will instead create an edge with the other node. /// This method can only be used in the NodeBuilding phase, as otherwise there might already be edges registered; /// which will not be re-pointed. /// </summary> internal void ReplaceWith(NullabilityNode other) { if (this.replacement != null) { this.replacement.ReplaceWith(other); return; } while (other.replacement != null) { other = other.replacement; } Debug.Assert(this.NullType == other.NullType || this.NullType == NullType.Infer || other.NullType == NullType.Infer); // Replacements must be performed before the edges are registered. Debug.Assert(this.IncomingEdges.Count == 0); Debug.Assert(this.OutgoingEdges.Count == 0); this.replacement = other; }
public void SetNode(AccessPath path, NullabilityNode newNode, bool clearMembers) { switch (path.Root) { case AccessPathRoot.This: thisPath = Visit(thisPath, 0); break; case AccessPathRoot.Local: if (!locals.TryGetValue(path.Symbols[0], out var localPathNode)) { localPathNode = new PathNode(typeSystem.NonNullNode, emptyMembers); } localPathNode = Visit(localPathNode, 1); locals[path.Symbols[0]] = localPathNode; break; default: throw new NotSupportedException(); } PathNode Visit(PathNode input, int index) { if (index == path.Symbols.Length) { if (clearMembers) { return(new PathNode(newNode, emptyMembers)); } else { return(new PathNode(newNode, input.Members)); } } var member = path.Symbols[index]; if (!input.Members.TryGetValue(member, out var childNode)) { childNode = new PathNode(typeSystem.NonNullNode, emptyMembers); } childNode = Visit(childNode, index + 1); return(new PathNode(typeSystem.NonNullNode, input.Members.SetItem(member, childNode))); } }
public PathNode(NullabilityNode nullability, ImmutableDictionary <ISymbol, PathNode> members) { this.Nullability = nullability; this.Members = members; }
public SpecialNodes(NullabilityNode nullableNode, NullabilityNode nonNullNode, NullabilityNode obliviousNode) { this.NullableNode = nullableNode; this.NonNullNode = nonNullNode; this.ObliviousNode = obliviousNode; }
/// <summary> /// Replaces the top-level nullability. /// </summary> internal TypeWithNode WithNode(NullabilityNode newNode) { return(new TypeWithNode(Type, newNode, TypeArguments)); }
/// <summary> /// Represents the subtype relation "subType <: superType". /// This means that values of type subType can be assigned to variables of type superType. /// </summary> public NullabilityEdge(NullabilityNode source, NullabilityNode target) { this.Source = source ?? throw new ArgumentNullException(nameof(source)); this.Target = target ?? throw new ArgumentNullException(nameof(target)); }
internal TypeWithNode WithFlowState(NullabilityNode flowNode, string?flowLabel) { return(new TypeWithNode(Type, flowNode, TypeArguments, flowLabel)); }