LocalRewriter_PatternSwitchStatement.cs 7.4 KB
Newer Older
N
Neal Gafter 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System.Collections.Generic;
using System;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp
{
    internal partial class LocalRewriter
    {
        // PROTOTYPE(typeswitch): as a temporary hack while this code is in development, we
        // only use the new translation machinery when this bool is set to true. If it is false
        // then we use the transitional code which translates a pattern switch into a series of
        // if-then-else statements. Ultimately we need the new translation to be used to generate
        // switch IL instructions for ordinary old-style switch statements.
        private static bool useNewTranslation = false;

        public override BoundNode VisitPatternSwitchStatement(BoundPatternSwitchStatement node)
        {
            // Until this is all implemented, we use a dumb series of if-then-else
            // statements to translate the switch statement.
            if (!useNewTranslation) return VisitPatternSwitchStatement_Ifchain(node);

            var usedLabels = new HashSet<LabelSymbol>();
            var usedTemps = new HashSet<LocalSymbol>();  // PROTOTYPE(typeswitch): worry about deterministic ordering
            var result = ArrayBuilder<BoundStatement>.GetInstance();

            // PROTOTYPE(typeswitch): do we need to do anything with node.ConstantTargetOpt, given that we
            // have the decision tree? If not, can we remove it from the bound trees?

            if (node.DecisionTree.Expression != node.Expression)
            {
                // Store the input expression into a temp
                // PROTOTYPE(typeswitch): do we need to add the temp to the list of used temps?
                result.Add(_factory.Assignment(node.DecisionTree.Expression, node.Expression));
            }

            // output the decision tree part
            LowerDecisionTree(node.DecisionTree, usedLabels, usedTemps, result);
            result.Add(_factory.Goto(node.BreakLabel));

            // output the sections of code (that were reachable)
            foreach (var section in node.SwitchSections)
            {
                ArrayBuilder<BoundStatement> sectionBuilder = null;
                foreach (var label in section.SwitchLabels)
                {
                    if (usedLabels.Contains(label.Label))
                    {
                        if (sectionBuilder == null)
                        {
                            sectionBuilder = ArrayBuilder<BoundStatement>.GetInstance();
                        }

                        sectionBuilder.Add(_factory.Label(label.Label));
                    }
                }

                if (sectionBuilder != null)
                {
                    sectionBuilder.AddRange(VisitList(section.Statements));
                    sectionBuilder.Add(_factory.Goto(node.BreakLabel));
                    result.Add(_factory.Block(section.Locals, sectionBuilder.ToImmutableAndFree()));
                }
            }

            result.Add(_factory.Label(node.BreakLabel));
            return _factory.Block(usedTemps.ToImmutableArray().Concat(node.InnerLocals), node.InnerLocalFunctions, result.ToImmutableAndFree());
        }

        private void LowerDecisionTree(DecisionTree decisionTree, HashSet<LabelSymbol> usedLabels, HashSet<LocalSymbol> usedTemps, ArrayBuilder<BoundStatement> result)
        {
            if (decisionTree == null) return;
            switch (decisionTree.Kind)
            {
                case DecisionTree.DecisionKind.ByType:
                    {
                        LowerDecisionTree((DecisionTree.ByType)decisionTree, usedLabels, usedTemps, result);
                        return;
                    }
                case DecisionTree.DecisionKind.ByValue:
                    {
                        LowerDecisionTree((DecisionTree.ByValue)decisionTree, usedLabels, usedTemps, result);
                        return;
                    }
                case DecisionTree.DecisionKind.Guarded:
                    {
                        LowerDecisionTree((DecisionTree.Guarded)decisionTree, usedLabels, usedTemps, result);
                        return;
                    }
                default:
                    throw ExceptionUtilities.UnexpectedValue(decisionTree.Kind);
            }
        }

        private void LowerDecisionTree(DecisionTree.ByType byType, HashSet<LabelSymbol> usedLabels, HashSet<LocalSymbol> usedTemps, ArrayBuilder<BoundStatement> result)
        {
            var defaultLabel = _factory.GenerateLabel("byTypeDefault");
            if (byType.Type.CanBeAssignedNull())
            {
                var notNullLabel = _factory.GenerateLabel("notNull");
                var inputExpression = byType.Expression;
                var nullValue = _factory.NullOrDefault(byType.Type);
                BoundExpression notNull = byType.Type.IsNullableType()
                    ? this.RewriteNullableNullEquality(_factory.Syntax, BinaryOperatorKind.NullableNullNotEqual, byType.Expression, nullValue, _factory.SpecialType(SpecialType.System_Boolean))
                    : _factory.ObjectNotEqual(byType.Expression, nullValue);
                result.Add(_factory.If(notNull, _factory.Goto(notNullLabel)));
                LowerDecisionTree(byType.WhenNull, usedLabels, usedTemps, result);
                result.Add(_factory.Goto(defaultLabel));
                result.Add(_factory.Label(notNullLabel));
            }
            else
            {
                Debug.Assert(byType.WhenNull == null);
            }

            foreach (var td in byType.TypeAndDecision)
            {
                var type = td.Key;
                var decision = td.Value;
                var failLabel = _factory.GenerateLabel("failedDecision");
                grep("if there is a fresh temp needed, copy to it and add the temp from ", decision.Expression, " to ", usedTemps);
                grep("test for the type ", type, " into that temp and goto failLabel when that fails");
                LowerDecisionTree(decision, usedLabels, usedTemps, result);
                result.Add(_factory.Label(failLabel));
            }

            result.Add(_factory.Label(defaultLabel));
            LowerDecisionTree(byType.Default, usedLabels, usedTemps, result);
        }

        private void LowerDecisionTree(DecisionTree.ByValue byValue, HashSet<LabelSymbol> usedLabels, HashSet<LocalSymbol> usedTemps, ArrayBuilder<BoundStatement> result)
        {
            throw new NotImplementedException();
        }

        private void LowerDecisionTree(DecisionTree.Guarded guarded, HashSet<LabelSymbol> usedLabels, HashSet<LocalSymbol> usedTemps, ArrayBuilder<BoundStatement> result)
        {
            usedLabels.Add(guarded.Label.Label);
            BoundStatement stmt = _factory.Goto(guarded.Label.Label);
            if (guarded.Guard != null)
            {
                stmt = _factory.If(VisitExpression(guarded.Guard), stmt);
            }

            result.Add(stmt);
        }

        private dynamic grep(params object[] args)
        {
            throw null;
        }
    }
}