| File: BoundTree\BoundSwitchStatement.cs | Web Access |
| Project: Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Symbols; namespace Microsoft.CodeAnalysis.CSharp { internal partial class BoundSwitchStatement { public BoundDecisionDag GetDecisionDagForLowering(CSharpCompilation compilation, out LabelSymbol? unreachableDefaultLabel) { unreachableDefaultLabel = null; BoundDecisionDag decisionDag = this.ReachabilityDecisionDag; if (!decisionDag.SuitableForLowering) { LabelSymbol? defaultLabel = null; if (this.DefaultLabel is { } defaultSwitchLabel) { // We get here when there is an explicit 'default:' label // By definition, the switch is exhaustive, but it is possible, that according to // the reachability Dag, the label is not reachable through the Dag (it might still // be reachable through an explicit goto). if (decisionDag.ReachableLabels.Contains(defaultSwitchLabel.Label)) { // The label was reachable. We don't need to worry about lowering Dag saying the same, // because it must. It is safe to jump to BreakLabel for any unmatched input, // otherwise it would be unsafe with reachability Dag as well, and an error about that // (not all code paths return a value, etc.) would be reported elsewhere. defaultLabel = defaultSwitchLabel.Label; } else { // If the code becomes reachable according to the lowering Dag, // we need to throw "unreachable" for code paths that reach the label // directly from the lowering Dag. // An explicit goto, however, should still go the the declared default label without throwing. } } else if (decisionDag.ReachableLabels.Contains(this.BreakLabel)) { // We get here when the switch statement is not exhaustive according to the // reachability Dag. We don't need to worry about lowering Dag saying the same, // because it must. It is safe to jump to BreakLabel for an unmatched input, // otherwise it would be unsafe with reachability Dag as well, and an error about that // (not all code paths return a value, etc.) would be reported elsewhere. defaultLabel = this.BreakLabel; } if (defaultLabel is null) { // The switch statement is exhaustive according to the reachability Dag. If it is // not exhaustive according to lowering Dag (when this label is reachable), // we need to emit throw "unreachable" at this label. defaultLabel = unreachableDefaultLabel = new GeneratedLabelSymbol("unreachableDefault"); } decisionDag = DecisionDagBuilder.CreateDecisionDagForSwitchStatement( compilation, this.Syntax, this.Expression, this.SwitchSections, defaultLabel, BindingDiagnosticBag.Discarded, forLowering: true); Debug.Assert(decisionDag.SuitableForLowering); if (unreachableDefaultLabel is not null) { Debug.Assert(this.DefaultLabel?.Label != unreachableDefaultLabel); Debug.Assert(this.BreakLabel != unreachableDefaultLabel); Debug.Assert(defaultLabel == unreachableDefaultLabel); Debug.Assert(!this.ReachabilityDecisionDag.ReachableLabels.Contains(unreachableDefaultLabel)); if (!decisionDag.ReachableLabels.Contains(unreachableDefaultLabel)) { unreachableDefaultLabel = null; } } } return decisionDag; } } }