/// <summary> /// Produce assignment of the input expression. This method is also responsible for assigning /// variables for some pattern-matching temps that can be shared with user variables. /// </summary> protected BoundDecisionDag ShareTempsAndEvaluateInput( BoundExpression loweredInput, BoundDecisionDag decisionDag, Action <BoundExpression> addCode, out BoundExpression savedInputExpression) { var inputDagTemp = BoundDagTemp.ForOriginalInput(loweredInput); if ((loweredInput.Kind == BoundKind.Local || loweredInput.Kind == BoundKind.Parameter) && loweredInput.GetRefKind() == RefKind.None) { // If we're switching on a local variable and there is no when clause (checked by the caller), // we assume the value of the local variable does not change during the execution of the // decision automaton and we just reuse the local variable when we need the input expression. // It is possible for this assumption to be violated by a side-effecting Deconstruct that // modifies the local variable which has been captured in a lambda. Since the language assumes // that functions called by pattern-matching are idempotent and not side-effecting, we feel // justified in taking this assumption in the compiler too. bool tempAssigned = _tempAllocator.TrySetTemp(inputDagTemp, loweredInput); Debug.Assert(tempAssigned); } foreach (BoundDecisionDagNode node in decisionDag.TopologicallySortedNodes) { if (node is BoundWhenDecisionDagNode w) { // We share a slot for a user-declared pattern-matching variable with a pattern temp if there // is no user-written when-clause that could modify the variable before the matching // automaton is done with it (checked by the caller). foreach (BoundPatternBinding binding in w.Bindings) { if (binding.VariableAccess is BoundLocal l) { Debug.Assert(l.LocalSymbol.DeclarationKind == LocalDeclarationKind.PatternVariable); _ = _tempAllocator.TrySetTemp(binding.TempContainingValue, binding.VariableAccess); } } } } if (loweredInput.Type.IsTupleType && loweredInput.Syntax.Kind() == SyntaxKind.TupleExpression && loweredInput is BoundObjectCreationExpression expr && !decisionDag.TopologicallySortedNodes.Any(n => usesOriginalInput(n))) { // If the switch governing expression is a tuple literal whose whole value is not used anywhere, // (though perhaps its component parts are used), then we can save the component parts // and assign them into temps (or perhaps user variables) to avoid the creation of // the tuple altogether. decisionDag = RewriteTupleInput(decisionDag, expr, addCode, out savedInputExpression); }
/// <summary> /// Generates a lowered form of the assignment operator for the given left and right sub-expressions. /// Left and right sub-expressions must be in lowered form. /// </summary> private BoundExpression MakeStaticAssignmentOperator( SyntaxNode syntax, BoundExpression rewrittenLeft, BoundExpression rewrittenRight, bool isRef, TypeSymbol type, bool used) { switch (rewrittenLeft.Kind) { case BoundKind.DynamicIndexerAccess: case BoundKind.DynamicMemberAccess: throw ExceptionUtilities.UnexpectedValue(rewrittenLeft.Kind); case BoundKind.PropertyAccess: { Debug.Assert(!isRef); BoundPropertyAccess propertyAccess = (BoundPropertyAccess)rewrittenLeft; BoundExpression? rewrittenReceiver = propertyAccess.ReceiverOpt; PropertySymbol property = propertyAccess.PropertySymbol; Debug.Assert(!property.IsIndexer); return(MakePropertyAssignment( syntax, rewrittenReceiver, property, ImmutableArray <BoundExpression> .Empty, default(ImmutableArray <RefKind>), false, default(ImmutableArray <int>), rewrittenRight, type, used)); } case BoundKind.IndexerAccess: { Debug.Assert(!isRef); BoundIndexerAccess indexerAccess = (BoundIndexerAccess)rewrittenLeft; BoundExpression? rewrittenReceiver = indexerAccess.ReceiverOpt; ImmutableArray <BoundExpression> arguments = indexerAccess.Arguments; PropertySymbol indexer = indexerAccess.Indexer; Debug.Assert(indexer.IsIndexer || indexer.IsIndexedProperty); return(MakePropertyAssignment( syntax, rewrittenReceiver, indexer, arguments, indexerAccess.ArgumentRefKindsOpt, indexerAccess.Expanded, indexerAccess.ArgsToParamsOpt, rewrittenRight, type, used)); } case BoundKind.Local: case BoundKind.Parameter: case BoundKind.FieldAccess: { Debug.Assert(!isRef || rewrittenLeft.GetRefKind() != RefKind.None); return(new BoundAssignmentOperator( syntax, rewrittenLeft, rewrittenRight, isRef, type)); } case BoundKind.DiscardExpression: { return(rewrittenRight); } case BoundKind.Sequence: // An Index or Range pattern-based indexer, or an interpolated string handler conversion // that uses an indexer argument, produces a sequence with a nested // BoundIndexerAccess. We need to lower the final expression and produce an // update sequence var sequence = (BoundSequence)rewrittenLeft; if (sequence.Value.Kind == BoundKind.IndexerAccess) { return(sequence.Update( sequence.Locals, sequence.SideEffects, MakeStaticAssignmentOperator( syntax, sequence.Value, rewrittenRight, isRef, type, used), type)); } goto default; default: { Debug.Assert(!isRef); return(new BoundAssignmentOperator( syntax, rewrittenLeft, rewrittenRight, type)); } } }
/// <summary> /// Generates a lowered form of the assignment operator for the given left and right sub-expressions. /// Left and right sub-expressions must be in lowered form. /// </summary> private BoundExpression MakeStaticAssignmentOperator( SyntaxNode syntax, BoundExpression rewrittenLeft, BoundExpression rewrittenRight, bool isRef, TypeSymbol type, bool used) { switch (rewrittenLeft.Kind) { case BoundKind.DynamicIndexerAccess: case BoundKind.DynamicMemberAccess: throw ExceptionUtilities.UnexpectedValue(rewrittenLeft.Kind); case BoundKind.PropertyAccess: { Debug.Assert(!isRef); BoundPropertyAccess propertyAccess = (BoundPropertyAccess)rewrittenLeft; BoundExpression rewrittenReceiver = propertyAccess.ReceiverOpt; PropertySymbol property = propertyAccess.PropertySymbol; Debug.Assert(!property.IsIndexer); return(MakePropertyAssignment( syntax, rewrittenReceiver, property, ImmutableArray <BoundExpression> .Empty, default(ImmutableArray <RefKind>), false, default(ImmutableArray <int>), rewrittenRight, type, used)); } case BoundKind.IndexerAccess: { Debug.Assert(!isRef); BoundIndexerAccess indexerAccess = (BoundIndexerAccess)rewrittenLeft; BoundExpression rewrittenReceiver = indexerAccess.ReceiverOpt; ImmutableArray <BoundExpression> rewrittenArguments = indexerAccess.Arguments; PropertySymbol indexer = indexerAccess.Indexer; Debug.Assert(indexer.IsIndexer || indexer.IsIndexedProperty); return(MakePropertyAssignment( syntax, rewrittenReceiver, indexer, rewrittenArguments, indexerAccess.ArgumentRefKindsOpt, indexerAccess.Expanded, indexerAccess.ArgsToParamsOpt, rewrittenRight, type, used)); } case BoundKind.Local: { Debug.Assert(!isRef || ((BoundLocal)rewrittenLeft).LocalSymbol.RefKind != RefKind.None); return(new BoundAssignmentOperator( syntax, rewrittenLeft, rewrittenRight, type, isRef: isRef)); } case BoundKind.Parameter: { Debug.Assert(!isRef || rewrittenLeft.GetRefKind() != RefKind.None); return(new BoundAssignmentOperator( syntax, rewrittenLeft, rewrittenRight, isRef, type)); } case BoundKind.DiscardExpression: { return(rewrittenRight); } default: { Debug.Assert(!isRef); return(new BoundAssignmentOperator( syntax, rewrittenLeft, rewrittenRight, type)); } } }