public PossiblyConditionalState(LocalState stateWhenTrue, LocalState stateWhenFalse) { StateWhenTrue = stateWhenTrue.Clone(); StateWhenFalse = stateWhenFalse.Clone(); IsConditionalState = true; State = default; }
protected override bool IntersectWith(ref LocalState self, ref LocalState other) { if (!other.Reachable) { return(false); } if (!self.Reachable) { self = other.Clone(); return(true); } return(self.IntersectWith(other)); }
LearnFromDecisionDag( SyntaxNode node, BoundDecisionDag decisionDag, BoundExpression expression, TypeWithState expressionType, ref LocalState initialState) { // We reuse the slot at the beginning of a switch (or is-pattern expression), pretending that we are // not copying the input to evaluate the patterns. In this way we infer non-nullability of the original // variable's parts based on matched pattern parts. Mutations in `when` clauses can show the inaccuracy // of analysis based on this choice. var rootTemp = BoundDagTemp.ForOriginalInput(expression); int originalInputSlot = MakeSlot(expression); if (originalInputSlot <= 0) { originalInputSlot = makeDagTempSlot(expressionType.ToTypeWithAnnotations(), rootTemp); initialState[originalInputSlot] = expressionType.State; } var tempMap = PooledDictionary <BoundDagTemp, (int slot, TypeSymbol type)> .GetInstance(); Debug.Assert(originalInputSlot > 0); tempMap.Add(rootTemp, (originalInputSlot, expressionType.Type)); var nodeStateMap = PooledDictionary <BoundDecisionDagNode, (LocalState state, bool believedReachable)> .GetInstance(); nodeStateMap.Add(decisionDag.RootNode, (state: initialState.Clone(), believedReachable: true)); var labelStateMap = PooledDictionary <LabelSymbol, (LocalState state, bool believedReachable)> .GetInstance(); foreach (var dagNode in decisionDag.TopologicallySortedNodes) { bool found = nodeStateMap.TryGetValue(dagNode, out var nodeStateAndBelievedReachable); Debug.Assert(found); // the topologically sorted nodes should contain only reachable nodes (LocalState nodeState, bool nodeBelievedReachable) = nodeStateAndBelievedReachable; SetState(nodeState); switch (dagNode) { case BoundEvaluationDecisionDagNode p: { var evaluation = p.Evaluation; (int inputSlot, TypeSymbol inputType) = tempMap.TryGetValue(evaluation.Input, out var slotAndType) ? slotAndType : throw ExceptionUtilities.Unreachable; Debug.Assert(inputSlot > 0); var inputState = this.State[inputSlot]; switch (evaluation) { case BoundDagDeconstructEvaluation e: { // https://github.com/dotnet/roslyn/issues/34232 // We may need to recompute the Deconstruct method for a deconstruction if // the receiver type has changed (e.g. its nested nullability). var method = e.DeconstructMethod; int extensionExtra = method.IsStatic ? 1 : 0; for (int i = 0; i < method.ParameterCount - extensionExtra; i++) { var parameterType = method.Parameters[i + extensionExtra].TypeWithAnnotations; var output = new BoundDagTemp(e.Syntax, parameterType.Type, e, i); int outputSlot = makeDagTempSlot(parameterType, output); Debug.Assert(outputSlot > 0); addToTempMap(output, outputSlot, parameterType.Type); } break; } case BoundDagTypeEvaluation e: { var output = new BoundDagTemp(e.Syntax, e.Type, e); HashSet <DiagnosticInfo> discardedDiagnostics = null; int outputSlot; switch (_conversions.WithNullability(false).ClassifyConversionFromType(inputType, e.Type, ref discardedDiagnostics).Kind) { case ConversionKind.Identity: case ConversionKind.ImplicitReference: case ConversionKind.NoConversion: case ConversionKind.ExplicitReference: outputSlot = inputSlot; break; case ConversionKind.ExplicitNullable when AreNullableAndUnderlyingTypes(inputType, e.Type, out _): outputSlot = GetNullableOfTValueSlot(inputType, inputSlot, out _, forceSlotEvenIfEmpty: true); if (outputSlot < 0) { goto default; } break; default: outputSlot = makeDagTempSlot(TypeWithAnnotations.Create(e.Type, NullableAnnotation.NotAnnotated), output); break; } State[outputSlot] = NullableFlowState.NotNull; var outputType = TypeWithState.Create(e.Type, inputState); addToTempMap(output, outputSlot, outputType.Type); break; } case BoundDagFieldEvaluation e: { Debug.Assert(inputSlot > 0); var field = (FieldSymbol)AsMemberOfType(inputType, e.Field); int outputSlot = GetOrCreateSlot(field, inputSlot, forceSlotEvenIfEmpty: true); Debug.Assert(outputSlot > 0); var type = field.Type; var output = new BoundDagTemp(e.Syntax, type, e); addToTempMap(output, outputSlot, type); break; } case BoundDagPropertyEvaluation e: { Debug.Assert(inputSlot > 0); var property = (PropertySymbol)AsMemberOfType(inputType, e.Property); var type = property.TypeWithAnnotations; var output = new BoundDagTemp(e.Syntax, type.Type, e); int outputSlot = GetOrCreateSlot(property, inputSlot, forceSlotEvenIfEmpty: true); if (outputSlot <= 0) { // This is needed due to https://github.com/dotnet/roslyn/issues/29619 outputSlot = makeDagTempSlot(type, output); } Debug.Assert(outputSlot > 0); addToTempMap(output, outputSlot, type.Type); break; } case BoundDagIndexEvaluation e: { var type = TypeWithAnnotations.Create(e.Property.Type, NullableAnnotation.Annotated); var output = new BoundDagTemp(e.Syntax, type.Type, e); int outputSlot = makeDagTempSlot(type, output); Debug.Assert(outputSlot > 0); addToTempMap(output, outputSlot, type.Type); break; } default: throw ExceptionUtilities.UnexpectedValue(p.Evaluation.Kind); } gotoNode(p.Next, this.State, nodeBelievedReachable); break; } case BoundTestDecisionDagNode p: { var test = p.Test; bool foundTemp = tempMap.TryGetValue(test.Input, out var slotAndType); Debug.Assert(foundTemp); (int inputSlot, TypeSymbol inputType) = slotAndType; var inputState = this.State[inputSlot]; Split(); switch (test) { case BoundDagTypeTest t: if (inputSlot > 0) { learnFromNonNullTest(inputSlot, ref this.StateWhenTrue); } gotoNode(p.WhenTrue, this.StateWhenTrue, nodeBelievedReachable); gotoNode(p.WhenFalse, this.StateWhenFalse, nodeBelievedReachable); break; case BoundDagNonNullTest t: if (inputSlot > 0) { learnFromNonNullTest(inputSlot, ref this.StateWhenTrue); } gotoNode(p.WhenTrue, this.StateWhenTrue, nodeBelievedReachable); gotoNode(p.WhenFalse, this.StateWhenFalse, nodeBelievedReachable & inputState.MayBeNull()); break; case BoundDagExplicitNullTest t: if (inputSlot > 0) { LearnFromNullTest(inputSlot, inputType, ref this.StateWhenTrue); learnFromNonNullTest(inputSlot, ref this.StateWhenFalse); } gotoNode(p.WhenTrue, this.StateWhenTrue, nodeBelievedReachable); gotoNode(p.WhenFalse, this.StateWhenFalse, nodeBelievedReachable); break; case BoundDagValueTest t: Debug.Assert(t.Value != ConstantValue.Null); if (inputSlot > 0) { learnFromNonNullTest(inputSlot, ref this.StateWhenTrue); } gotoNode(p.WhenTrue, this.StateWhenTrue, nodeBelievedReachable); gotoNode(p.WhenFalse, this.StateWhenFalse, nodeBelievedReachable); break; default: throw ExceptionUtilities.UnexpectedValue(test.Kind); } break; } case BoundLeafDecisionDagNode d: // We have one leaf decision dag node per reachable label labelStateMap.Add(d.Label, (this.State, nodeBelievedReachable)); break; case BoundWhenDecisionDagNode w: // bind the pattern variables, inferring their types as well foreach (var binding in w.Bindings) { var variableAccess = binding.VariableAccess; var tempSource = binding.TempContainingValue; var foundTemp = tempMap.TryGetValue(tempSource, out var tempSlotAndType); Debug.Assert(foundTemp); var(tempSlot, tempType) = tempSlotAndType; var tempState = this.State[tempSlot]; if (variableAccess is BoundLocal { LocalSymbol: SourceLocalSymbol { IsVar: true } local }) { var inferredType = TypeWithState.Create(tempType, tempState).ToTypeWithAnnotations(); if (_variableTypes.TryGetValue(local, out var existingType)) { // merge inferred nullable annotation from different branches of the decision tree _variableTypes[local] = TypeWithAnnotations.Create(existingType.Type, existingType.NullableAnnotation.Join(inferredType.NullableAnnotation)); } else { _variableTypes[local] = inferredType; } int localSlot = GetOrCreateSlot(local, forceSlotEvenIfEmpty: true); this.State[localSlot] = tempState; }
public LocalFunctionState(LocalState unreachableState) : base(unreachableState.Clone(), unreachableState.Clone()) { }
public PossiblyConditionalState(LocalState state) { StateWhenTrue = StateWhenFalse = default; IsConditionalState = false; State = state.Clone(); }