예제 #1
0
        private DecisionTree AddByValue(DecisionTree.ByValue byValue, BoundConstantPattern value, DecisionMaker makeDecision)
        {
            Debug.Assert(value.Value.Type.Equals(byValue.Type, TypeCompareKind.IgnoreDynamicAndTupleNames));
            if (byValue.Default != null)
            {
                return(AddByValue(byValue.Default, value, makeDecision));
            }

            // For error recovery, to avoid "unreachable code" diagnostics when there is a bad case
            // label, we use the case label itself as the value key.
            object       valueKey = value.ConstantValue?.Value ?? value;
            DecisionTree valueDecision;

            if (byValue.ValueAndDecision.TryGetValue(valueKey, out valueDecision))
            {
                valueDecision = Add(valueDecision, makeDecision);
            }
            else
            {
                valueDecision = makeDecision(byValue.Expression, byValue.Type);
                byValue.ValueAndDecision.Add(valueKey, valueDecision);
            }

            if (byValue.Type.SpecialType == SpecialType.System_Boolean &&
                byValue.ValueAndDecision.Count == 2 &&
                byValue.ValueAndDecision.Values.All(d => d.MatchIsComplete))
            {
                byValue.MatchIsComplete = true;
            }

            return(valueDecision);
        }
예제 #2
0
        private DecisionTree AddByValue(DecisionTree.ByValue byValue, BoundConstantPattern value, DecisionMaker makeDecision)
        {
            Debug.Assert(value.Value.Type.Equals(byValue.Type, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes));
            if (byValue.Default != null)
            {
                return(AddByValue(byValue.Default, value, makeDecision));
            }

            Debug.Assert(value.ConstantValue != null);
            object       valueKey = value.ConstantValue.Value;
            DecisionTree valueDecision;

            if (byValue.ValueAndDecision.TryGetValue(valueKey, out valueDecision))
            {
                valueDecision = Add(valueDecision, makeDecision);
            }
            else
            {
                valueDecision = makeDecision(byValue.Expression, byValue.Type);
                byValue.ValueAndDecision.Add(valueKey, valueDecision);
            }

            if (byValue.Type.SpecialType == SpecialType.System_Boolean &&
                byValue.ValueAndDecision.Count == 2 &&
                byValue.ValueAndDecision.Values.All(d => d.MatchIsComplete))
            {
                byValue.MatchIsComplete = true;
            }

            return(valueDecision);
        }
예제 #3
0
            private void LowerConstantValueDecision(DecisionTree.ByValue byValue)
            {
                var value = byValue.Expression.ConstantValue.Value;

                Debug.Assert(value != null);

                // because we are switching on a constant, the decision tree builder does not produce a (nonempty) ByValue
                Debug.Assert(byValue.ValueAndDecision.Count == 0);

                LowerDecisionTree(byValue.Expression, byValue.Default);
            }
예제 #4
0
            private void LowerBooleanSwitch(DecisionTree.ByValue byValue)
            {
                switch (byValue.ValueAndDecision.Count)
                {
                case 0:
                {
                    // this should have been handled in the caller.
                    throw ExceptionUtilities.Unreachable;
                }

                case 1:
                {
                    DecisionTree decision;
                    bool         onBoolean = byValue.ValueAndDecision.TryGetValue(true, out decision);
                    if (!onBoolean)
                    {
                        byValue.ValueAndDecision.TryGetValue(false, out decision);
                    }

                    Debug.Assert(decision != null);
                    var onOther = _factory.GenerateLabel("on" + !onBoolean);
                    _loweredDecisionTree.Add(_factory.ConditionalGoto(byValue.Expression, onOther, !onBoolean));
                    LowerDecisionTree(byValue.Expression, decision);
                    // if we fall through here, that means the match was not complete and we invoke the default part
                    _loweredDecisionTree.Add(_factory.Label(onOther));
                    LowerDecisionTree(byValue.Expression, byValue.Default);
                    break;
                }

                case 2:
                {
                    DecisionTree trueDecision, falseDecision;
                    bool         hasTrue  = byValue.ValueAndDecision.TryGetValue(true, out trueDecision);
                    bool         hasFalse = byValue.ValueAndDecision.TryGetValue(false, out falseDecision);
                    Debug.Assert(hasTrue && hasFalse);
                    var tryAnother = _factory.GenerateLabel("tryAnother");
                    var onFalse    = _factory.GenerateLabel("onFalse");
                    _loweredDecisionTree.Add(_factory.ConditionalGoto(byValue.Expression, onFalse, false));
                    LowerDecisionTree(byValue.Expression, trueDecision);
                    _loweredDecisionTree.Add(_factory.Goto(tryAnother));
                    _loweredDecisionTree.Add(_factory.Label(onFalse));
                    LowerDecisionTree(byValue.Expression, falseDecision);
                    _loweredDecisionTree.Add(_factory.Label(tryAnother));
                    // if both true and false (i.e. all values) are fully handled, there should be no default.
                    Debug.Assert(!trueDecision.MatchIsComplete || !falseDecision.MatchIsComplete || byValue.Default == null);
                    LowerDecisionTree(byValue.Expression, byValue.Default);
                    break;
                }

                default:
                    throw ExceptionUtilities.UnexpectedValue(byValue.ValueAndDecision.Count);
                }
            }
            private void LowerConstantValueDecision(DecisionTree.ByValue byValue)
            {
                var value = byValue.Expression.ConstantValue.Value;

                Debug.Assert(value != null);
                DecisionTree onValue;

                if (byValue.ValueAndDecision.TryGetValue(value, out onValue))
                {
                    LowerDecisionTree(byValue.Expression, onValue);
                    if (onValue.MatchIsComplete)
                    {
                        return;
                    }
                }

                LowerDecisionTree(byValue.Expression, byValue.Default);
            }
예제 #6
0
        private DecisionTree Add(DecisionTree.ByValue byValue, DecisionMaker makeDecision)
        {
            DecisionTree result;

            if (byValue.Default != null)
            {
                result = Add(byValue.Default, makeDecision);
            }
            else
            {
                result = byValue.Default = makeDecision(byValue.Expression, byValue.Type);
            }
            if (byValue.Default.MatchIsComplete)
            {
                byValue.MatchIsComplete = true;
            }

            return(result);
        }
예제 #7
0
            private void LowerDecisionTree(DecisionTree.ByValue byValue)
            {
                if (byValue.Expression.ConstantValue != null)
                {
                    LowerConstantValueDecision(byValue);
                    return;
                }

                if (byValue.ValueAndDecision.Count == 0)
                {
                    LowerDecisionTree(byValue.Expression, byValue.Default);
                    return;
                }

                if (byValue.Type.SpecialType == SpecialType.System_Boolean)
                {
                    LowerBooleanSwitch(byValue);
                }
                else
                {
                    LowerBasicSwitch(byValue);
                }
            }
예제 #8
0
        private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern value, DecisionMaker makeDecision)
        {
            if (byType.Default != null)
            {
                try
                {
                    return(AddByValue(byType.Default, value, makeDecision));
                }
                finally
                {
                    if (byType.Default.MatchIsComplete)
                    {
                        // This code may be unreachable due to https://github.com/dotnet/roslyn/issues/16878
                        byType.MatchIsComplete = true;
                    }
                }
            }

            if (value.ConstantValue == ConstantValue.Null)
            {
                // This should not occur, as the caller will have invoked AddByNull instead.
                throw ExceptionUtilities.Unreachable;
            }

            if ((object)value.Value.Type == null)
            {
                return(null);
            }

            foreach (var kvp in byType.TypeAndDecision)
            {
                var matchedType = kvp.Key;
                var decision    = kvp.Value;

                // See if the test is already subsumed
                switch (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics))
                {
                case true:
                    if (decision.MatchIsComplete)
                    {
                        return(null);
                    }

                    continue;

                case false:
                case null:
                    continue;
                }
            }

            DecisionTree forType = null;

            // This new type test should logically be last. However it might be the same type as the one that is already
            // last. In that case we can produce better code by piggy-backing our new case on to the last decision.
            // Also, the last one might be a non-overlapping type, in which case we can piggy-back onto the second-last
            // type test.
            for (int i = byType.TypeAndDecision.Count - 1; i >= 0; i--)
            {
                var kvp         = byType.TypeAndDecision[i];
                var matchedType = kvp.Key;
                var decision    = kvp.Value;
                if (matchedType.Equals(value.Value.Type, TypeCompareKind.IgnoreDynamicAndTupleNames))
                {
                    forType = decision;
                    break;
                }
                else if (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics) != false)
                {
                    // because there is overlap, we cannot reuse some earlier entry
                    break;
                }
            }

            // if we did not piggy-back, then create a new decision tree node for the type.
            if (forType == null)
            {
                var type               = value.Value.Type;
                var localSymbol        = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatching, Syntax, false, RefKind.None);
                var narrowedExpression = new BoundLocal(Syntax, localSymbol, null, type);
                forType = new DecisionTree.ByValue(narrowedExpression, value.Value.Type.TupleUnderlyingTypeOrSelf(), localSymbol);
                byType.TypeAndDecision.Add(new KeyValuePair <TypeSymbol, DecisionTree>(value.Value.Type, forType));
            }

            return(AddByValue(forType, value, makeDecision));
        }
예제 #9
0
            // For switch statements, we have an option of completely rewriting the switch header
            // and switch sections into simpler constructs, i.e. we can rewrite the switch header
            // using bound conditional goto statements and the rewrite the switch sections into
            // bound labeled statements.
            //
            // However, all the logic for emitting the switch jump tables is language agnostic
            // and includes IL optimizations. Hence we delay the switch jump table generation
            // till the emit phase. This way we also get additional benefit of sharing this code
            // between both VB and C# compilers.
            //
            // For string switch statements, we need to determine if we are generating a hash
            // table based jump table or a non hash jump table, i.e. linear string comparisons
            // with each case label. We use the Dev10 Heuristic to determine this
            // (see SwitchStringJumpTableEmitter.ShouldGenerateHashTableSwitch() for details).
            // If we are generating a hash table based jump table, we use a simple
            // hash function to hash the string constants corresponding to the case labels.
            // See SwitchStringJumpTableEmitter.ComputeStringHash().
            // We need to emit this same function to compute the hash value into the compiler generated
            // <PrivateImplementationDetails> class.
            // If we have at least one string switch statement in a module that needs a
            // hash table based jump table, we generate a single public string hash synthesized method
            // that is shared across the module.
            private void LowerBasicSwitch(DecisionTree.ByValue byValue)
            {
                var switchSections = ArrayBuilder <BoundSwitchSection> .GetInstance();

                var noValueMatches       = _factory.GenerateLabel("noValueMatches");
                var underlyingSwitchType = byValue.Type.IsEnumType() ? byValue.Type.GetEnumUnderlyingType() : byValue.Type;

                foreach (var vd in byValue.ValueAndDecision)
                {
                    var value              = vd.Key;
                    var decision           = vd.Value;
                    var constantValue      = ConstantValue.Create(value, underlyingSwitchType.SpecialType);
                    var constantExpression = new BoundLiteral(_factory.Syntax, constantValue, underlyingSwitchType);
                    var label              = _factory.GenerateLabel("case+" + value);
                    var switchLabel        = new BoundSwitchLabel(_factory.Syntax, label, constantExpression, constantValue);
                    var forValue           = ArrayBuilder <BoundStatement> .GetInstance();

                    LowerDecisionTree(byValue.Expression, decision, forValue);
                    if (!decision.MatchIsComplete)
                    {
                        forValue.Add(_factory.Goto(noValueMatches));
                    }

                    var section = new BoundSwitchSection(_factory.Syntax, ImmutableArray.Create(switchLabel), forValue.ToImmutableAndFree());
                    switchSections.Add(section);
                }

                var          rewrittenSections = switchSections.ToImmutableAndFree();
                MethodSymbol stringEquality    = null;

                if (underlyingSwitchType.SpecialType == SpecialType.System_String)
                {
                    _localRewriter.EnsureStringHashFunction(rewrittenSections, _factory.Syntax);
                    stringEquality = _localRewriter.UnsafeGetSpecialTypeMethod(_factory.Syntax, SpecialMember.System_String__op_Equality);
                }

                // The BoundSwitchStatement requires a constant target when there are no sections, so we accomodate that here.
                var constantTarget  = rewrittenSections.IsEmpty ? noValueMatches : null;
                var switchStatement = new BoundSwitchStatement(
                    _factory.Syntax, null, _factory.Convert(underlyingSwitchType, byValue.Expression),
                    constantTarget,
                    ImmutableArray <LocalSymbol> .Empty, ImmutableArray <LocalFunctionSymbol> .Empty,
                    rewrittenSections, noValueMatches, stringEquality);

                // The bound switch statement implicitly defines the label noValueMatches at the end, so we do not add it explicitly.

                switch (underlyingSwitchType.SpecialType)
                {
                case SpecialType.System_Boolean:
                    // boolean switch is handled in LowerBooleanSwitch, not here.
                    throw ExceptionUtilities.Unreachable;

                case SpecialType.System_String:
                case SpecialType.System_Byte:
                case SpecialType.System_Char:
                case SpecialType.System_Int16:
                case SpecialType.System_Int32:
                case SpecialType.System_Int64:
                case SpecialType.System_SByte:
                case SpecialType.System_UInt16:
                case SpecialType.System_UInt32:
                case SpecialType.System_UInt64:
                {
                    // emit knows how to efficiently generate code for these kinds of switches.
                    _loweredDecisionTree.Add(switchStatement);
                    break;
                }

                default:
                {
                    // other types, such as float, double, and decimal, are not currently
                    // handled by emit and must be lowered here.
                    _loweredDecisionTree.Add(LowerNonprimitiveSwitch(switchStatement));
                    break;
                }
                }

                LowerDecisionTree(byValue.Expression, byValue.Default);
            }
예제 #10
0
        private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern value, DecisionMaker makeDecision)
        {
            if (byType.Default != null)
            {
                try
                {
                    return(AddByValue(byType.Default, value, makeDecision));
                }
                finally
                {
                    if (byType.Default.MatchIsComplete)
                    {
                        byType.MatchIsComplete = true;
                    }
                }
            }

            if (value.ConstantValue == ConstantValue.Null)
            {
                return(byType.Expression.ConstantValue?.IsNull == false
                    ? null : AddByNull((DecisionTree)byType, makeDecision));
            }

            foreach (var kvp in byType.TypeAndDecision)
            {
                var matchedType = kvp.Key;
                var decision    = kvp.Value;

                // See if the test is already subsumed
                switch (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics))
                {
                case true:
                    if (decision.MatchIsComplete)
                    {
                        return(null);
                    }

                    continue;

                case false:
                case null:
                    continue;
                }
            }

            DecisionTree forType = null;

            // Find an existing decision tree for the expression's type. Since this new test
            // should logically be last, we look for the last one we can piggy-back it onto.
            for (int i = byType.TypeAndDecision.Count - 1; i >= 0 && forType == null; i--)
            {
                var kvp         = byType.TypeAndDecision[i];
                var matchedType = kvp.Key;
                var decision    = kvp.Value;
                if (matchedType.TupleUnderlyingTypeOrSelf() == value.Value.Type.TupleUnderlyingTypeOrSelf())
                {
                    forType = decision;
                    break;
                }
                else if (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics) != false)
                {
                    break;
                }
            }

            if (forType == null)
            {
                var type               = value.Value.Type;
                var localSymbol        = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatchingTemp, Syntax, false, RefKind.None);
                var narrowedExpression = new BoundLocal(Syntax, localSymbol, null, type);
                forType = new DecisionTree.ByValue(narrowedExpression, value.Value.Type.TupleUnderlyingTypeOrSelf(), localSymbol);
                byType.TypeAndDecision.Add(new KeyValuePair <TypeSymbol, DecisionTree>(value.Value.Type, forType));
            }

            return(AddByValue(forType, value, makeDecision));
        }
예제 #11
0
        private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern value, DecisionMaker makeDecision)
        {
            if (byType.Default != null)
            {
                try
                {
                    return(AddByValue(byType.Default, value, makeDecision));
                }
                finally
                {
                    if (byType.Default.MatchIsComplete)
                    {
                        // This code may be unreachable due to https://github.com/dotnet/roslyn/issues/16878
                        byType.MatchIsComplete = true;
                    }
                }
            }

            if (value.ConstantValue == ConstantValue.Null)
            {
                // This should not occur, as the caller will have invoked AddByNull instead.
                throw ExceptionUtilities.Unreachable;
            }

            if ((object)value.Value.Type == null || value.ConstantValue == null)
            {
                return(null);
            }

            foreach (var kvp in byType.TypeAndDecision)
            {
                var matchedType = kvp.Key;
                var decision    = kvp.Value;

                // See if the test is already subsumed
                switch (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics))
                {
                case true:
                    if (decision.MatchIsComplete)
                    {
                        // Subsumed case have been eliminated by semantic analysis.
                        Debug.Assert(false);
                        return(null);
                    }

                    continue;

                case false:
                case null:
                    continue;
                }
            }

            DecisionTree forType = null;

            // This new type test should logically be last. However it might be the same type as the one that is already
            // last. In that case we can produce better code by piggy-backing our new case on to the last decision.
            // Also, the last one might be a non-overlapping type, in which case we can piggy-back onto the second-last
            // type test.
            for (int i = byType.TypeAndDecision.Count - 1; i >= 0; i--)
            {
                var kvp         = byType.TypeAndDecision[i];
                var matchedType = kvp.Key;
                var decision    = kvp.Value;
                if (matchedType.Equals(value.Value.Type, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes))
                {
                    forType = decision;
                    break;
                }
                switch (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics))
                {
                case true:
                    if (decision.MatchIsComplete)
                    {
                        // we should have reported this case as subsumed already.
                        Debug.Assert(false);
                        return(null);
                    }
                    else
                    {
                        goto case null;
                    }

                case false:
                    continue;

                case null:
                    // because there is overlap, we cannot reuse some earlier entry
                    goto noReuse;
                }
            }
            noReuse :;

            // if we did not piggy-back, then create a new decision tree node for the type.
            if (forType == null)
            {
                var type = value.Value.Type;
                if (byType.Type.Equals(type, TypeCompareKind.AllIgnoreOptions))
                {
                    // reuse the input expression when we have an equivalent type to reduce the number of generated temps
                    forType = new DecisionTree.ByValue(byType.Expression, type.TupleUnderlyingTypeOrSelf(), null);
                }
                else
                {
                    var narrowedExpression = GetBoundPatternMatchingLocal(type);
                    forType = new DecisionTree.ByValue(narrowedExpression, type.TupleUnderlyingTypeOrSelf(), narrowedExpression.LocalSymbol);
                }

                byType.TypeAndDecision.Add(new KeyValuePair <TypeSymbol, DecisionTree>(type, forType));
            }

            return(AddByValue(forType, value, makeDecision));
        }
예제 #12
0
        private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern value, DecisionMaker makeDecision)
        {
            if (byType.Default != null)
            {
                try
                {
                    return AddByValue(byType.Default, value, makeDecision);
                }
                finally
                {
                    if (byType.Default.MatchIsComplete)
                    {
                        byType.MatchIsComplete = true;
                    }
                }
            }

            if (value.ConstantValue == ConstantValue.Null)
            {
                return byType.Expression.ConstantValue?.IsNull == false
                    ? null : AddByNull((DecisionTree)byType, makeDecision);
            }

            foreach (var kvp in byType.TypeAndDecision)
            {
                var matchedType = kvp.Key;
                var decision = kvp.Value;

                // See if the test is already subsumed
                switch (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics))
                {
                    case true:
                        if (decision.MatchIsComplete)
                        {
                            return null;
                        }

                        continue;
                    case false:
                    case null:
                        continue;
                }
            }

            DecisionTree forType = null;

            // Find an existing decision tree for the expression's type. Since this new test
            // should logically be last, we look for the last one we can piggy-back it onto.
            for (int i = byType.TypeAndDecision.Count - 1; i >= 0 && forType == null; i--)
            {
                var kvp = byType.TypeAndDecision[i];
                var matchedType = kvp.Key;
                var decision = kvp.Value;
                if (matchedType.TupleUnderlyingTypeOrSelf() == value.Value.Type.TupleUnderlyingTypeOrSelf())
                {
                    forType = decision;
                    break;
                }
                else if (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics) != false)
                {
                    break;
                }
            }

            if (forType == null)
            {
                var type = value.Value.Type;
                var localSymbol = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatchingTemp, Syntax, false, RefKind.None);
                var narrowedExpression = new BoundLocal(Syntax, localSymbol, null, type);
                forType = new DecisionTree.ByValue(narrowedExpression, value.Value.Type.TupleUnderlyingTypeOrSelf(), localSymbol);
                byType.TypeAndDecision.Add(new KeyValuePair<TypeSymbol, DecisionTree>(value.Value.Type, forType));
            }

            return AddByValue(forType, value, makeDecision);
        }