Example #1
0
        protected override void VisitFinallyBlock(BoundStatement finallyBlock, ref LocalState endState)
        {
            var oldPending1 = SavePending(); // we do not support branches into a finally block
            var oldPending2 = SavePending(); // track only the branches out of the finally block

            base.VisitFinallyBlock(finallyBlock, ref endState);
            RestorePending(oldPending2); // resolve branches that remain within the finally block
            foreach (var branch in PendingBranches.AsEnumerable())
            {
                if (branch.Branch == null)
                {
                    continue;                        // a tracked exception
                }
                var location = new SourceLocation(branch.Branch.Syntax.GetFirstToken());
                switch (branch.Branch.Kind)
                {
                case BoundKind.YieldBreakStatement:
                case BoundKind.YieldReturnStatement:
                    // ERR_BadYieldInFinally reported during initial binding
                    break;

                default:
                    Diagnostics.Add(ErrorCode.ERR_BadFinallyLeave, location);
                    break;
                }
            }

            RestorePending(oldPending1);
        }
        protected override LocalState VisitSwitchStatementDispatch(BoundSwitchStatement node)
        {
            // first, learn from any null tests in the patterns
            int slot = MakeSlot(node.Expression);

            if (slot > 0)
            {
                var originalInputType = node.Expression.Type;
                foreach (var section in node.SwitchSections)
                {
                    foreach (var label in section.SwitchLabels)
                    {
                        LearnFromAnyNullPatterns(slot, originalInputType, label.Pattern);
                    }
                }
            }

            // visit switch header
            var        expressionState = VisitRvalueWithState(node.Expression);
            LocalState initialState    = this.State.Clone();
            var        labelStateMap   = LearnFromDecisionDag(node.Syntax, node.DecisionDag, node.Expression, expressionState, ref initialState);

            foreach (var section in node.SwitchSections)
            {
                foreach (var label in section.SwitchLabels)
                {
                    var labelResult = labelStateMap.TryGetValue(label.Label, out var s1) ? s1 : (state : UnreachableState(), believedReachable : false);
                    SetState(labelResult.state);
                    PendingBranches.Add(new PendingBranch(label, this.State, label.Label));
                }
            }

            labelStateMap.Free();
            return(initialState);
        }
        private void VisitPatternSwitchBlock(BoundPatternSwitchStatement node)
        {
            var initialState     = this.State;
            var afterSwitchState = UnreachableState();
            var switchSections   = node.SwitchSections;
            var iLastSection     = (switchSections.Length - 1);

            // simulate the dispatch (setting pattern variables and jumping to labels) to
            // all reachable switch labels
            foreach (var section in switchSections)
            {
                foreach (var label in section.SwitchLabels)
                {
                    if (label.IsReachable && label != node.DefaultLabel)
                    {
                        SetState(initialState.Clone());
                        // assign pattern variables
                        VisitPattern(null, label.Pattern);
                        SetState(StateWhenTrue);
                        if (label.Guard != null)
                        {
                            VisitCondition(label.Guard);
                            SetState(StateWhenTrue);
                        }

                        PendingBranches.Add(new PendingBranch(label, this.State));
                    }
                }
            }

            // we always consider the default label reachable for flow analysis purposes
            // unless there was a single case that would match every input.
            if (node.DefaultLabel != null)
            {
                if (node.SomeLabelAlwaysMatches)
                {
                    SetUnreachable();
                }
                else
                {
                    SetState(initialState.Clone());
                }

                PendingBranches.Add(new PendingBranch(node.DefaultLabel, this.State));
            }

            // visit switch sections
            for (var iSection = 0; iSection <= iLastSection; iSection++)
            {
                VisitPatternSwitchSection(switchSections[iSection], node.Expression, iSection == iLastSection);
                // Even though it is illegal for the end of a switch section to be reachable, in erroneous
                // code it may be reachable.  We treat that as an implicit break (branch to afterSwitchState).
                Join(ref afterSwitchState, ref this.State);
            }

            SetState(afterSwitchState);
        }
Example #4
0
        protected virtual TLocalState VisitSwitchStatementDispatch(BoundSwitchStatement node)
        {
            // visit switch header
            VisitRvalue(node.Expression);

            TLocalState initialState = this.State.Clone();

            var reachableLabels = node.DecisionDag.ReachableLabels;

            foreach (var section in node.SwitchSections)
            {
                foreach (var label in section.SwitchLabels)
                {
                    if (
                        reachableLabels.Contains(label.Label) ||
                        label.HasErrors ||
                        label == node.DefaultLabel &&
                        node.Expression.ConstantValue == null &&
                        IsTraditionalSwitch(node)
                        )
                    {
                        SetState(initialState.Clone());
                    }
                    else
                    {
                        SetUnreachable();
                    }

                    VisitPattern(label.Pattern);
                    SetState(StateWhenTrue);
                    if (label.WhenClause != null)
                    {
                        VisitCondition(label.WhenClause);
                        SetState(StateWhenTrue);
                    }

                    PendingBranches.Add(new PendingBranch(label, this.State, label.Label));
                }
            }

            TLocalState afterSwitchState = UnreachableState();

            if (
                node.DecisionDag.ReachableLabels.Contains(node.BreakLabel) ||
                (
                    node.DefaultLabel == null &&
                    node.Expression.ConstantValue == null &&
                    IsTraditionalSwitch(node)
                )
                )
            {
                Join(ref afterSwitchState, ref initialState);
            }

            return(afterSwitchState);
        }
Example #5
0
        protected override void LeaveRegion()
        {
            foreach (var pending in PendingBranches.AsEnumerable())
            {
                if (pending.Branch == null || !RegionContains(pending.Branch.Syntax.Span))
                {
                    continue;
                }
                switch (pending.Branch.Kind)
                {
                case BoundKind.GotoStatement:
                    if (_labelsInside.Contains(((BoundGotoStatement)pending.Branch).Label))
                    {
                        continue;
                    }
                    break;

                case BoundKind.BreakStatement:
                    if (_labelsInside.Contains(((BoundBreakStatement)pending.Branch).Label))
                    {
                        continue;
                    }
                    break;

                case BoundKind.ContinueStatement:
                    if (_labelsInside.Contains(((BoundContinueStatement)pending.Branch).Label))
                    {
                        continue;
                    }
                    break;

                case BoundKind.YieldBreakStatement:
                case BoundKind.ReturnStatement:
                    // Return statements are included
                    break;

                case BoundKind.YieldReturnStatement:
                case BoundKind.AwaitExpression:
                case BoundKind.UsingStatement:
                case BoundKind.ForEachStatement when((BoundForEachStatement)pending.Branch).AwaitOpt != null:
                    // We don't do anything with yield return statements, async using statement, async foreach statement, or await expressions;
                    // they are treated as if they are not jumps.
                    continue;

                default:
                    throw ExceptionUtilities.UnexpectedValue(pending.Branch.Kind);
                }
                _branchesOutOf.Add((StatementSyntax)pending.Branch.Syntax);
            }

            base.LeaveRegion();
        }
        private void VisitSwitchBlock(BoundSwitchStatement node)
        {
            var initialState    = State.Clone();
            var reachableLabels = node.DecisionDag.ReachableLabels;

            foreach (var section in node.SwitchSections)
            {
                foreach (var label in section.SwitchLabels)
                {
                    if (reachableLabels.Contains(label.Label) || label.HasErrors ||
                        label == node.DefaultLabel && node.Expression.ConstantValue == null && IsTraditionalSwitch(node))
                    {
                        SetState(initialState.Clone());
                    }
                    else
                    {
                        SetUnreachable();
                    }

                    VisitPattern(label.Pattern);
                    SetState(StateWhenTrue);
                    if (label.WhenClause != null)
                    {
                        VisitCondition(label.WhenClause);
                        SetState(StateWhenTrue);
                    }

                    PendingBranches.Add(new PendingBranch(label, this.State, label.Label));
                }
            }

            // visit switch sections
            var afterSwitchState = UnreachableState();
            var switchSections   = node.SwitchSections;
            var iLastSection     = (switchSections.Length - 1);

            for (var iSection = 0; iSection <= iLastSection; iSection++)
            {
                VisitSwitchSection(switchSections[iSection], iSection == iLastSection);
                // Even though it is illegal for the end of a switch section to be reachable, in erroneous
                // code it may be reachable.  We treat that as an implicit break (branch to afterSwitchState).
                Join(ref afterSwitchState, ref this.State);
            }

            if (reachableLabels.Contains(node.BreakLabel) || node.DefaultLabel == null && IsTraditionalSwitch(node))
            {
                Join(ref afterSwitchState, ref initialState);
            }

            ResolveBreaks(afterSwitchState, node.BreakLabel);
        }
Example #7
0
        public override BoundNode VisitSwitchDispatch(BoundSwitchDispatch node)
        {
            VisitRvalue(node.Expression);
            var state = this.State.Clone();

            PendingBranches.Add(new PendingBranch(node, state, node.DefaultLabel));
            foreach ((_, LabelSymbol label) in node.Cases)
            {
                PendingBranches.Add(new PendingBranch(node, state, label));
            }

            SetUnreachable();
            return(null);
        }
        protected override LocalState VisitSwitchStatementDispatch(BoundSwitchStatement node)
        {
            // first, learn from any null tests in the patterns
            int slot = node.Expression.IsSuppressed ? GetOrCreatePlaceholderSlot(node.Expression) : MakeSlot(node.Expression);

            if (slot > 0)
            {
                var originalInputType = node.Expression.Type;
                foreach (var section in node.SwitchSections)
                {
                    foreach (var label in section.SwitchLabels)
                    {
                        LearnFromAnyNullPatterns(slot, originalInputType, label.Pattern);
                    }
                }
            }

            // visit switch header
            Visit(node.Expression);
            var expressionState = ResultType;
            var initialState    = PossiblyConditionalState.Create(this);

            DeclareLocals(node.InnerLocals);
            foreach (var section in node.SwitchSections)
            {
                // locals can be alive across jumps in the switch sections, so we declare them early.
                DeclareLocals(section.Locals);
            }

            var labelStateMap = LearnFromDecisionDag(node.Syntax, node.DecisionDag, node.Expression, expressionState, ref initialState);

            foreach (var section in node.SwitchSections)
            {
                foreach (var label in section.SwitchLabels)
                {
                    var labelResult = labelStateMap.TryGetValue(label.Label, out var s1) ? s1 : (state : UnreachableState(), believedReachable : false);
                    SetState(labelResult.state);
                    PendingBranches.Add(new PendingBranch(label, this.State, label.Label));
                }
            }

            var afterSwitchState = labelStateMap.TryGetValue(node.BreakLabel, out var stateAndReachable) ? stateAndReachable.state : UnreachableState();

            labelStateMap.Free();
            return(afterSwitchState);
        }
Example #9
0
        public override BoundNode VisitReturnStatement(BoundReturnStatement node)
        {
            var result = base.VisitReturnStatement(node);

            // After processing a return statement, the very last pending branch is for that return statement.
            // If it is returning an allocated object, consider it to be disposed.

            if (result != null)
            {
                switch (result.Kind)
                {
                case HijackedBoundKindForValueHolder:
                {
                    var holder       = (BoundValueHolder)result;
                    var returnBranch = PendingBranches.Last();
                    foreach (var c in holder.value.creations)
                    {
                        returnBranch.State.possiblyUndisposedCreations.Remove(c);
                        if (returnBranch.State.possiblyDisposedCreations.Contains(c))
                        {
                            // TODO: error: returning a value that may already have been disposed.
                        }
                    }
                    break;
                }

                case BoundKind.NewT:
                case BoundKind.ObjectCreationExpression:
                    PendingBranches.Last().State.possiblyUndisposedCreations.Remove((BoundExpression)result);
                    break;

                default:
                    break;
                }
            }

            return(result);
        }
        protected override void LeaveRegion()
        {
            if (this.IsConditionalState)
            {
                // If the region is in a condition, then the state will be split and state.Assigned will
                // be null.  Merge to get sensible results.
                _endOfRegionState = StateWhenTrue.Clone();
                Join(ref _endOfRegionState, ref StateWhenFalse);
            }
            else
            {
                _endOfRegionState = this.State.Clone();
            }

            foreach (var branch in PendingBranches.AsEnumerable())
            {
                if (branch.Branch != null && RegionContains(branch.Branch.Syntax.Span) && !_labelsInside.Contains(branch.Label))
                {
                    Join(ref _endOfRegionState, ref branch.State);
                }
            }

            base.LeaveRegion();
        }
Example #11
0
        public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement localFunc)
        {
            var oldSymbol       = this.currentSymbol;
            var localFuncSymbol = localFunc.Symbol;

            this.currentSymbol = localFuncSymbol;

            var oldPending = SavePending(); // we do not support branches into a lambda

            // SPEC: The entry point to a local function is always reachable.
            // Captured variables are definitely assigned if they are definitely assigned on
            // all branches into the local function.

            var savedState = this.State;

            this.State = this.TopState();

            if (!localFunc.WasCompilerGenerated)
            {
                EnterParameters(localFuncSymbol.Parameters);
            }

            // Captured variables are definitely assigned if they are assigned on
            // all branches into the local function, so we store all reads from
            // possibly unassigned captured variables and later report definite
            // assignment errors if any of the captured variables is not assigned
            // on a particular branch.
            //
            // Assignments to captured variables are also recorded, as calls to local functions
            // definitely assign captured variables if the variables are definitely assigned at
            // all branches out of the local function.

            var usages   = GetOrCreateLocalFuncUsages(localFuncSymbol);
            var oldReads = usages.ReadVars.Clone();

            usages.ReadVars.Clear();

            var oldPending2 = SavePending();

            // If this is an iterator, there's an implicit branch before the first statement
            // of the function where the enumerable is returned.
            if (localFuncSymbol.IsIterator)
            {
                PendingBranches.Add(new PendingBranch(null, this.State, null));
            }

            VisitAlways(localFunc.Body);
            RestorePending(oldPending2); // process any forward branches within the lambda body
            ImmutableArray <PendingBranch> pendingReturns = RemoveReturns();

            RestorePending(oldPending);

            Location location = null;

            if (!localFuncSymbol.Locations.IsDefaultOrEmpty)
            {
                location = localFuncSymbol.Locations[0];
            }

            LeaveParameters(localFuncSymbol.Parameters, localFunc.Syntax, location);

            // Intersect the state of all branches out of the local function
            LocalState stateAtReturn = this.State;

            foreach (PendingBranch pending in pendingReturns)
            {
                this.State = pending.State;
                BoundNode branch = pending.Branch;

                // Pass the local function identifier as a location if the branch
                // is null or compiler generated.
                LeaveParameters(localFuncSymbol.Parameters,
                                branch?.Syntax,
                                branch?.WasCompilerGenerated == false ? null : location);

                Join(ref stateAtReturn, ref this.State);
            }

            // Check for changes to the possibly unassigned and assigned sets
            // of captured variables
            if (RecordChangedVars(ref usages.WrittenVars,
                                  ref stateAtReturn,
                                  ref oldReads,
                                  ref usages.ReadVars) &&
                usages.LocalFuncVisited)
            {
                // If the sets have changed and we already used the results
                // of this local function in another computation, the previous
                // calculations may be invalid. We need to analyze until we
                // reach a fixed-point. The previous writes are always valid,
                // so they are stored in usages.WrittenVars, while the reads
                // may be invalidated by new writes, so we throw the results out.
                stateChangedAfterUse    = true;
                usages.LocalFuncVisited = false;
            }

            this.State         = savedState;
            this.currentSymbol = oldSymbol;

            return(null);
        }
        public override BoundNode?VisitLocalFunctionStatement(BoundLocalFunctionStatement localFunc)
        {
            var oldSymbol       = this.CurrentSymbol;
            var localFuncSymbol = localFunc.Symbol;

            this.CurrentSymbol = localFuncSymbol;

            var oldPending = SavePending(); // we do not support branches into a lambda

            // SPEC: The entry point to a local function is always reachable.
            // Captured variables are definitely assigned if they are definitely assigned on
            // all branches into the local function.

            var savedState = this.State;

            this.State = this.TopState();

            if (!localFunc.WasCompilerGenerated)
            {
                EnterParameters(localFuncSymbol.Parameters);
            }

            // State changes to captured variables are recorded, as calls to local functions
            // transition the state of captured variables if the variables have state changes
            // across all branches leaving the local function

            var localFunctionState      = GetOrCreateLocalFuncUsages(localFuncSymbol);
            var savedLocalFunctionState = LocalFunctionStart(localFunctionState);

            var oldPending2 = SavePending();

            // If this is an iterator, there's an implicit branch before the first statement
            // of the function where the enumerable is returned.
            if (localFuncSymbol.IsIterator)
            {
                PendingBranches.Add(new PendingBranch(null, this.State, null));
            }

            VisitAlways(localFunc.Body);
            RestorePending(oldPending2); // process any forward branches within the lambda body
            ImmutableArray <PendingBranch> pendingReturns = RemoveReturns();

            RestorePending(oldPending);

            Location?location = null;

            if (!localFuncSymbol.Locations.IsDefaultOrEmpty)
            {
                location = localFuncSymbol.Locations[0];
            }

            LeaveParameters(localFuncSymbol.Parameters, localFunc.Syntax, location);

            // Intersect the state of all branches out of the local function
            var stateAtReturn = this.State;

            foreach (PendingBranch pending in pendingReturns)
            {
                this.State = pending.State;
                BoundNode branch = pending.Branch;

                // Pass the local function identifier as a location if the branch
                // is null or compiler generated.
                LeaveParameters(localFuncSymbol.Parameters,
                                branch?.Syntax,
                                branch?.WasCompilerGenerated == false ? null : location);

                Join(ref stateAtReturn, ref this.State);
            }

            // Record any changes to the state of captured variables
            if (RecordStateChange(
                    savedLocalFunctionState,
                    localFunctionState,
                    ref stateAtReturn) &&
                localFunctionState.Visited)
            {
                // If the sets have changed and we already used the results
                // of this local function in another computation, the previous
                // calculations may be invalid. We need to analyze until we
                // reach a fixed-point.
                stateChangedAfterUse       = true;
                localFunctionState.Visited = false;
            }

            this.State         = savedState;
            this.CurrentSymbol = oldSymbol;

            return(null);
        }
        public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement localFunc)
        {
            var oldMethodOrLambda = this.currentMethodOrLambda;
            var localFuncSymbol   = localFunc.Symbol;

            this.currentMethodOrLambda = localFuncSymbol;

            var oldPending = SavePending(); // we do not support branches into a lambda

            // Local functions don't affect outer state and are analyzed
            // with everything unassigned and reachable
            var savedState = this.State;

            this.State = this.ReachableState();

            var usages   = GetOrCreateLocalFuncUsages(localFuncSymbol);
            var oldReads = usages.ReadVars;

            usages.ReadVars = BitVector.Empty;

            if (!localFunc.WasCompilerGenerated)
            {
                EnterParameters(localFuncSymbol.Parameters);
            }

            var oldPending2 = SavePending();

            // If this is an iterator, there's an implicit branch before the first statement
            // of the function where the enumerable is returned.
            if (localFuncSymbol.IsIterator)
            {
                PendingBranches.Add(new PendingBranch(null, this.State));
            }

            VisitAlways(localFunc.Body);
            RestorePending(oldPending2); // process any forward branches within the lambda body
            ImmutableArray <PendingBranch> pendingReturns = RemoveReturns();

            RestorePending(oldPending);

            Location location = null;

            if (!localFuncSymbol.Locations.IsDefaultOrEmpty)
            {
                location = localFuncSymbol.Locations[0];
            }

            LeaveParameters(localFuncSymbol.Parameters, localFunc.Syntax, location);

            LocalState stateAtReturn = this.State;

            foreach (PendingBranch pending in pendingReturns)
            {
                this.State = pending.State;
                BoundNode branch = pending.Branch;
                LeaveParameters(localFuncSymbol.Parameters, branch?.Syntax,
                                branch?.WasCompilerGenerated == true
                                    ? location : null);
                IntersectWith(ref stateAtReturn, ref this.State);
            }

            // Check for changes to the read and write sets
            if (RecordChangedVars(ref usages.WrittenVars,
                                  ref stateAtReturn,
                                  ref oldReads,
                                  ref usages.ReadVars) &&
                usages.LocalFuncVisited)
            {
                stateChangedAfterUse    = true;
                usages.LocalFuncVisited = false;
            }

            this.State = savedState;
            this.currentMethodOrLambda = oldMethodOrLambda;

            return(null);
        }