LocalRewriter_PatternSwitchStatement.cs 7.8 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
// 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);
N
Neal Gafter 已提交
113
                result.Add(_factory.ConditionalGoto(notNull, notNullLabel, true));
N
Neal Gafter 已提交
114 115 116 117 118 119 120 121 122 123 124 125 126 127
                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");
N
Neal Gafter 已提交
128 129
                var testAndCopy = TypeTestAndCopyToTemp(byType.Expression, decision.Expression);
                result.Add(_factory.ConditionalGoto(testAndCopy, failLabel, false));
N
Neal Gafter 已提交
130 131 132 133 134 135 136 137
                LowerDecisionTree(decision, usedLabels, usedTemps, result);
                result.Add(_factory.Label(failLabel));
            }

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

N
Neal Gafter 已提交
138 139 140 141 142 143 144 145 146 147 148 149
        private BoundExpression TypeTestAndCopyToTemp(BoundExpression input, BoundExpression temp)
        {
            if (input == temp)
            {
                // if the expression is the same, no need to type test and copy to temp
                return _factory.Literal(true);
            }

            Debug.Assert(temp.Kind == BoundKind.Local);
            return MakeDeclarationPattern(_factory.Syntax, input, ((BoundLocal)temp).LocalSymbol);
        }

N
Neal Gafter 已提交
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
        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;
        }
    }
}