private BoundExpression LowerSwitchExpression(BoundSwitchExpression node) { _factory.Syntax = node.Syntax; var result = ArrayBuilder <BoundStatement> .GetInstance(); var outerVariables = ArrayBuilder <LocalSymbol> .GetInstance(); var loweredSwitchGoverningExpression = _localRewriter.VisitExpression(node.Expression); BoundDecisionDag decisionDag = ShareTempsIfPossibleAndEvaluateInput( node.DecisionDag, loweredSwitchGoverningExpression, result, out BoundExpression savedInputExpression); Debug.Assert(savedInputExpression != null); // lower the decision dag. (ImmutableArray <BoundStatement> loweredDag, ImmutableDictionary <SyntaxNode, ImmutableArray <BoundStatement> > switchSections) = LowerDecisionDag(decisionDag); // then add the rest of the lowered dag that references that input result.Add(_factory.Block(loweredDag)); // A branch to the default label when no switch case matches is included in the // decision tree, so the code in result is unreachable at this point. // Lower each switch expression arm LocalSymbol resultTemp = _factory.SynthesizedLocal(node.Type, node.Syntax, kind: SynthesizedLocalKind.SwitchCasePatternMatching); LabelSymbol afterSwitchExpression = _factory.GenerateLabel("afterSwitchExpression"); foreach (BoundSwitchExpressionArm arm in node.SwitchArms) { _factory.Syntax = arm.Syntax; var sectionBuilder = ArrayBuilder <BoundStatement> .GetInstance(); sectionBuilder.AddRange(switchSections[arm.Syntax]); sectionBuilder.Add(_factory.Label(arm.Label)); sectionBuilder.Add(_factory.Assignment(_factory.Local(resultTemp), _localRewriter.VisitExpression(arm.Value))); sectionBuilder.Add(_factory.Goto(afterSwitchExpression)); var statements = sectionBuilder.ToImmutableAndFree(); if (arm.Locals.IsEmpty) { result.Add(_factory.StatementList(statements)); } else { // Lifetime of these locals is expanded to the entire switch body, as it is possible to // share them as temps in the decision dag. outerVariables.AddRange(arm.Locals); // Note the language scope of the locals, even though they are included for the purposes of // lifetime analysis in the enclosing scope. result.Add(new BoundScope(arm.Syntax, arm.Locals, statements)); } } _factory.Syntax = node.Syntax; if (node.DefaultLabel != null) { result.Add(_factory.Label(node.DefaultLabel)); var objectType = _factory.SpecialType(SpecialType.System_Object); var thrownExpression = (implicitConversionExists(savedInputExpression, objectType) && _factory.WellKnownMember(WellKnownMember.System_Runtime_CompilerServices_SwitchExpressionException__ctorObject, isOptional: true) is MethodSymbol exception1) ? _factory.New(exception1, _factory.Convert(objectType, savedInputExpression)) : (_factory.WellKnownMember(WellKnownMember.System_Runtime_CompilerServices_SwitchExpressionException__ctor, isOptional: true) is MethodSymbol exception0) ? _factory.New(exception0) : _factory.New(_factory.WellKnownMethod(WellKnownMember.System_InvalidOperationException__ctor)); result.Add(_factory.Throw(thrownExpression)); } result.Add(_factory.Label(afterSwitchExpression)); outerVariables.Add(resultTemp); outerVariables.AddRange(_tempAllocator.AllTemps()); return(_factory.SpillSequence(outerVariables.ToImmutableAndFree(), result.ToImmutableAndFree(), _factory.Local(resultTemp))); bool implicitConversionExists(BoundExpression expression, TypeSymbol type) { HashSet <DiagnosticInfo> discarded = null; Conversion c = _localRewriter._compilation.Conversions.ClassifyConversionFromExpression(expression, type, ref discarded); return(c.IsImplicit); } }
private SwitchExpressionLocalRewriter(BoundSwitchExpression node, LocalRewriter localRewriter) : base(node.Syntax, localRewriter, node.SwitchArms.SelectAsArray(arm => arm.Syntax), isSwitchStatement: false) { }