Example #1
0
        public static DecisionTree Create(BoundExpression expression, TypeSymbol type, Symbol enclosingSymbol)
        {
            Debug.Assert(expression.Type == type);
            LocalSymbol temp = null;
            if (expression.ConstantValue == null)
            {
                // Unless it is a constant, the decision tree acts on a copy of the input expression.
                // We create a temp to represent that copy. Lowering will assign into this temp.
                temp = new SynthesizedLocal(enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatchingTemp, expression.Syntax, false, RefKind.None);
                expression = new BoundLocal(expression.Syntax, temp, null, type);
            }

            if (expression.Type.CanBeAssignedNull())
            {
                // We need the ByType decision tree to separate null from non-null values.
                // Note that, for the purpose of the decision tree (and subsumption), we
                // ignore the fact that the input may be a constant, and therefore always
                // or never null.
                return new ByType(expression, type, temp);
            }
            else
            {
                // If it is a (e.g. builtin) value type, we can switch on its (constant) values.
                // If it isn't a builtin, in practice we will only use the Default part of the
                // ByValue.
                return new ByValue(expression, type, temp);
            }
        }
        public Conversion ClassifyConversionFromExpression(BoundExpression sourceExpression, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert(sourceExpression != null);
            Debug.Assert((object)destination != null);

            return ClassifyConversionFromExpression(sourceExpression, sourceExpression.Type, destination, ref useSiteDiagnostics);
        }
 /// <summary>
 /// Lower the given decision tree into the given statement builder.
 /// </summary>
 public void LowerDecisionTree(BoundExpression expression, DecisionTree decisionTree, ArrayBuilder<BoundStatement> loweredDecisionTree)
 {
     var oldLoweredDecisionTree = this._loweredDecisionTree;
     this._loweredDecisionTree = loweredDecisionTree;
     LowerDecisionTree(expression, decisionTree);
     this._loweredDecisionTree = oldLoweredDecisionTree;
 }
 /// <summary>
 /// If a ref variable is to be spilled, sometimes that causes us to need to spill
 /// the thing the ref variable was initialized with.  For example, if the variable
 /// was initialized with "structVariable.field", then the struct variable needs to
 /// be spilled.  This method adds to the spill set things that need to be spilled
 /// based on the given refInitializer expression.
 /// </summary>
 private void AddSpillsForRef(BoundExpression refInitializer, IEnumerable<CSharpSyntaxNode> locations)
 {
     while (true)
     {
         if (refInitializer == null) return;
         switch (refInitializer.Kind)
         {
             case BoundKind.Local:
                 var local = (BoundLocal)refInitializer;
                 if (!variablesCaptured.ContainsKey(local.LocalSymbol))
                 {
                     foreach (var loc in locations) variablesCaptured.Add(local.LocalSymbol, loc);
                     if (local.LocalSymbol.RefKind != RefKind.None)
                     {
                         refInitializer = refLocalInitializers[local.LocalSymbol];
                         continue;
                     }
                 }
                 return;
             case BoundKind.FieldAccess:
                 var field = (BoundFieldAccess)refInitializer;
                 if (!field.FieldSymbol.IsStatic && field.FieldSymbol.ContainingType.IsValueType)
                 {
                     refInitializer = field.ReceiverOpt;
                     continue;
                 }
                 return;
             default:
                 return;
         }
     }
 }
        private BoundExpression MakePropertyGetAccess(
            SyntaxNode syntax,
            BoundExpression rewrittenReceiver,
            PropertySymbol property,
            ImmutableArray<BoundExpression> rewrittenArguments,
            MethodSymbol getMethodOpt = null,
            BoundPropertyAccess oldNodeOpt = null)
        {
            if (_inExpressionLambda && rewrittenArguments.IsEmpty)
            {
                return oldNodeOpt != null ?
                    oldNodeOpt.Update(rewrittenReceiver, property, LookupResultKind.Viable, property.Type) :
                    new BoundPropertyAccess(syntax, rewrittenReceiver, property, LookupResultKind.Viable, property.Type);
            }
            else
            {
                var getMethod = getMethodOpt ?? property.GetOwnOrInheritedGetMethod();

                Debug.Assert((object)getMethod != null);
                Debug.Assert(getMethod.ParameterCount == rewrittenArguments.Length);
                Debug.Assert(((object)getMethodOpt == null) || ReferenceEquals(getMethod, getMethodOpt));

                return BoundCall.Synthesized(
                    syntax,
                    rewrittenReceiver,
                    getMethod,
                    rewrittenArguments);
            }
        }
        // Rewrite collection initializer add method calls:
        // 2) new List<int> { 1 };
        //                    ~
        private void AddCollectionInitializers(ref ArrayBuilder<BoundExpression> dynamicSiteInitializers, ArrayBuilder<BoundExpression> result, BoundExpression rewrittenReceiver, ImmutableArray<BoundExpression> initializers)
        {
            Debug.Assert(rewrittenReceiver != null || _inExpressionLambda);

            foreach (var initializer in initializers)
            {
                // In general bound initializers may contain bad expressions or element initializers.
                // We don't lower them if they contain errors, so it's safe to assume an element initializer.

                BoundExpression rewrittenInitializer;
                if (initializer.Kind == BoundKind.CollectionElementInitializer)
                {
                    rewrittenInitializer = MakeCollectionInitializer(rewrittenReceiver, (BoundCollectionElementInitializer)initializer);
                }
                else
                {
                    Debug.Assert(!_inExpressionLambda);
                    Debug.Assert(initializer.Kind == BoundKind.DynamicCollectionElementInitializer);

                    rewrittenInitializer = MakeDynamicCollectionInitializer(rewrittenReceiver, (BoundDynamicCollectionElementInitializer)initializer);
                }

                // the call to Add may be omitted
                if (rewrittenInitializer != null)
                {
                    result.Add(rewrittenInitializer);
                }
            }
        }
Example #7
0
        private BoundExpression MakeFieldAccess(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenReceiver,
            FieldSymbol fieldSymbol,
            ConstantValue constantValueOpt,
            LookupResultKind resultKind,
            TypeSymbol type,
            BoundFieldAccess oldNodeOpt = null)
        {

            if (fieldSymbol.IsTupleField)
            {
                return MakeTupleFieldAccess(syntax, fieldSymbol, rewrittenReceiver, constantValueOpt, resultKind);
            }
            
            BoundExpression result = oldNodeOpt != null ?
                oldNodeOpt.Update(rewrittenReceiver, fieldSymbol, constantValueOpt, resultKind, type) :
                new BoundFieldAccess(syntax, rewrittenReceiver, fieldSymbol, constantValueOpt, resultKind, type);

            if (fieldSymbol.IsFixed)
            {
                // a reference to a fixed buffer is translated into its address
                result = new BoundConversion(syntax,
                    new BoundAddressOfOperator(syntax, result, syntax != null && SyntaxFacts.IsFixedStatementExpression(syntax), type, false),
                    new Conversion(ConversionKind.PointerToPointer), false, false, default(ConstantValue), type, false);
            }

            return result;
        }
        /// <summary>
        /// Spill an expression list with a receiver (e.g. array access, method call), where at least one of the
        /// receiver or the arguments contains an await expression.
        /// </summary>
        private Tuple<BoundExpression, ReadOnlyArray<BoundExpression>> SpillExpressionsWithReceiver(
            BoundExpression receiverOpt,
            ReadOnlyArray<BoundExpression> expressions,
            SpillBuilder spillBuilder,
            ReadOnlyArray<RefKind> refKindsOpt)
        {
            if (receiverOpt == null)
            {
                return Tuple.Create(default(BoundExpression), SpillExpressionList(spillBuilder, expressions));
            }

            // We have a non-null receiver, and an expression of the form:
            //     receiver[index1, index2, ..., indexN]
            //     or:
            //     receiver(arg1, arg2, ... argN)

            // Build a list containing the receiver and all expressions (in that order)
            var allExpressions = ReadOnlyArray<BoundExpression>.CreateFrom(receiverOpt).Concat(expressions);
            var allRefKinds = (refKindsOpt != null)
                ? ReadOnlyArray<RefKind>.CreateFrom(RefKind.None).Concat(refKindsOpt)
                : ReadOnlyArray<RefKind>.Empty;

            // Spill the expressions (and possibly the receiver):
            var allSpilledExpressions = SpillExpressionList(spillBuilder, allExpressions, allRefKinds);

            var spilledReceiver = allSpilledExpressions.First();
            var spilledArguments = allSpilledExpressions.RemoveFirst();
            return Tuple.Create(spilledReceiver, spilledArguments);
        }
        private BoundExpression VisitUnusedExpression(BoundExpression expression)
        {
            if (expression.HasErrors)
            {
                return expression;
            }

            switch (expression.Kind)
            {
                case BoundKind.AssignmentOperator:
                    // Avoid extra temporary by indicating the expression value is not used.
                    var assignmentOperator = (BoundAssignmentOperator)expression;
                    return VisitAssignmentOperator(assignmentOperator, used: false);

                case BoundKind.CompoundAssignmentOperator:
                    var compoundAssignmentOperator = (BoundCompoundAssignmentOperator)expression;
                    return VisitCompoundAssignmentOperator(compoundAssignmentOperator, false);

                case BoundKind.Call:
                    var call = (BoundCall)expression;
                    if (call.Method.CallsAreOmitted(call.SyntaxTree))
                    {
                        return null;
                    }

                    break;

                case BoundKind.DynamicInvocation:
                    // TODO (tomat): circumvents logic in VisitExpression...
                    return VisitDynamicInvocation((BoundDynamicInvocation)expression, resultDiscarded: true);
            }
            return VisitExpression(expression);
        }
Example #10
0
 internal BoundSpillSequence2 Update(BoundExpression value)
 {
     var result = new BoundSpillSequence2(value);
     result.locals = this.locals;
     result.statements = this.statements;
     return result;
 }
            private BoundExpression SelectField(SimpleNameSyntax node, BoundExpression receiver, string name, DiagnosticBag diagnostics)
            {
                var receiverType = receiver.Type as NamedTypeSymbol;
                if ((object)receiverType == null || !receiverType.IsAnonymousType)
                {
                    // We only construct transparent query variables using anonymous types, so if we're trying to navigate through
                    // some other type, we must have some hinky query API where the types don't match up as expected.
                    // We should report this as an error of some sort.
                    // TODO: DevDiv #737822 - reword error message and add test.
                    var info = new CSDiagnosticInfo(ErrorCode.ERR_UnsupportedTransparentIdentifierAccess, name, receiver.ExpressionSymbol ?? receiverType);
                    Error(diagnostics, info, node);
                    return new BoundBadExpression(
                        node,
                        LookupResultKind.Empty,
                        ImmutableArray.Create<Symbol>(receiver.ExpressionSymbol),
                        ImmutableArray.Create<BoundNode>(receiver),
                        new ExtendedErrorTypeSymbol(this.Compilation, "", 0, info));
                }

                LookupResult lookupResult = LookupResult.GetInstance();
                LookupOptions options = LookupOptions.MustBeInstance;
                HashSet<DiagnosticInfo> useSiteDiagnostics = null;
                LookupMembersWithFallback(lookupResult, receiver.Type, name, 0, ref useSiteDiagnostics, basesBeingResolved: null, options: options);
                diagnostics.Add(node, useSiteDiagnostics);

                var result = BindMemberOfType(node, node, name, 0, receiver, default(SeparatedSyntaxList<TypeSyntax>), default(ImmutableArray<TypeSymbol>), lookupResult, BoundMethodGroupFlags.None, diagnostics);
                result.WasCompilerGenerated = true;
                lookupResult.Free();
                return result;
            }
 private void CheckReceiverIfField(BoundExpression receiverOpt)
 {
     if (receiverOpt != null && receiverOpt.Kind == BoundKind.FieldAccess)
     {
         CheckFieldAsReceiver((BoundFieldAccess)receiverOpt);
     }
 }
Example #13
0
        private BoundExpression MakeIsDeclarationPattern(BoundDeclarationPattern loweredPattern, BoundExpression loweredInput)
        {
            Debug.Assert(((object)loweredPattern.Variable == null && loweredPattern.VariableAccess.Kind == BoundKind.DiscardedExpression) ||
                         loweredPattern.Variable.GetTypeOrReturnType() == loweredPattern.DeclaredType.Type);

            if (loweredPattern.IsVar)
            {
                var result = _factory.Literal(true);

                if (loweredPattern.VariableAccess.Kind == BoundKind.DiscardedExpression)
                {
                    return result;
                }

                Debug.Assert((object)loweredPattern.Variable != null && loweredInput.Type == loweredPattern.Variable.GetTypeOrReturnType());

                var assignment = _factory.AssignmentExpression(loweredPattern.VariableAccess, loweredInput);
                return _factory.MakeSequence(assignment, result);
            }

            if (loweredPattern.VariableAccess.Kind == BoundKind.DiscardedExpression)
            {
                LocalSymbol temp;
                BoundLocal discard = _factory.MakeTempForDiscard((BoundDiscardedExpression)loweredPattern.VariableAccess, out temp);

                return _factory.Sequence(ImmutableArray.Create(temp),
                         sideEffects: ImmutableArray<BoundExpression>.Empty,
                         result: MakeIsDeclarationPattern(loweredPattern.Syntax, loweredInput, discard, requiresNullTest: true));
            }

            return MakeIsDeclarationPattern(loweredPattern.Syntax, loweredInput, loweredPattern.VariableAccess, requiresNullTest: true);
        }
        private static BoundExpression RewriteConditionalOperator(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenCondition,
            BoundExpression rewrittenConsequence,
            BoundExpression rewrittenAlternative,
            ConstantValue constantValueOpt,
            TypeSymbol rewrittenType)
        {
            // NOTE: This optimization assumes that a constant has no side effects. In the future we 
            // might wish to represent nodes that are known to the optimizer as having constant
            // values as a sequence of side effects and a constant value; in that case the result
            // of this should be a sequence containing the side effect and the consequence or alternative.

            ConstantValue conditionConstantValue = rewrittenCondition.ConstantValue;
            if (conditionConstantValue == ConstantValue.True)
            {
                return rewrittenConsequence;
            }
            else if (conditionConstantValue == ConstantValue.False)
            {
                return rewrittenAlternative;
            }
            else
            {
                return new BoundConditionalOperator(
                    syntax,
                    rewrittenCondition,
                    rewrittenConsequence,
                    rewrittenAlternative,
                    constantValueOpt,
                    rewrittenType);
            }
        }
Example #15
0
 public LoweredDynamicOperation(SyntheticBoundNodeFactory factory, BoundExpression siteInitialization, BoundExpression siteInvocation, TypeSymbol resultType)
 {
     this.Factory = factory;
     this.resultType = resultType;
     this.SiteInitialization = siteInitialization;
     this.SiteInvocation = siteInvocation;
 }
 internal SubsumptionDiagnosticBuilder(Symbol enclosingSymbol,
                                        Conversions conversions,
                                        BoundExpression expression)
     : base(enclosingSymbol, conversions)
 {
     _subsumptionTree = DecisionTree.Create(expression, expression.Type, enclosingSymbol);
 }
Example #17
0
 private void PopulateHelper(BoundExpression receiverOpt, LookupResultKind resultKind, DiagnosticInfo error)
 {
     VerifyClear();
     this.Receiver = receiverOpt;
     this.Error = error;
     this.ResultKind = resultKind;
 }
Example #18
0
            internal BinderWithConditionalReceiver(Binder next, BoundExpression receiverExpression)
                : base(next)
            {
                Debug.Assert(receiverExpression != null);

                _receiverExpression = receiverExpression;
            }
        private BoundStatement RewriteWhileStatement(
            BoundLoopStatement loop,
            BoundExpression rewrittenCondition,
            BoundStatement rewrittenBody,
            GeneratedLabelSymbol breakLabel,
            GeneratedLabelSymbol continueLabel,
            bool hasErrors)
        {
            Debug.Assert(loop.Kind == BoundKind.WhileStatement || loop.Kind == BoundKind.ForEachStatement);

            // while (condition) 
            //   body;
            //
            // becomes
            //
            // goto continue;
            // start: 
            // {
            //     body
            //     continue:
            //     GotoIfTrue condition start;
            // }
            // break:

            SyntaxNode syntax = loop.Syntax;
            var startLabel = new GeneratedLabelSymbol("start");
            BoundStatement ifConditionGotoStart = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel);
            BoundStatement gotoContinue = new BoundGotoStatement(syntax, continueLabel);

            if (this.Instrument && !loop.WasCompilerGenerated)
            {
                switch (loop.Kind)
                {
                    case BoundKind.WhileStatement:
                        ifConditionGotoStart = _instrumenter.InstrumentWhileStatementConditionalGotoStartOrBreak((BoundWhileStatement)loop, ifConditionGotoStart);
                        break;

                    case BoundKind.ForEachStatement:
                        ifConditionGotoStart = _instrumenter.InstrumentForEachStatementConditionalGotoStart((BoundForEachStatement)loop, ifConditionGotoStart);
                        break;

                    default:
                        throw ExceptionUtilities.UnexpectedValue(loop.Kind);
                }

                // mark the initial jump as hidden. We do it to tell that this is not a part of previous statement. This
                // jump may be a target of another jump (for example if loops are nested) and that would give the
                // impression that the previous statement is being re-executed.
                gotoContinue = new BoundSequencePoint(null, gotoContinue);
            }

            return BoundStatementList.Synthesized(syntax, hasErrors,
                gotoContinue,
                new BoundLabelStatement(syntax, startLabel),
                rewrittenBody,
                new BoundLabelStatement(syntax, continueLabel),
                ifConditionGotoStart,
                new BoundLabelStatement(syntax, breakLabel));
        }
        private static BoundStatement RewriteIfStatement(
            CSharpSyntaxNode syntax,
            ImmutableArray<LocalSymbol> locals,
            BoundExpression rewrittenCondition,
            BoundStatement rewrittenConsequence,
            BoundStatement rewrittenAlternativeOpt,
            bool hasErrors)
        {
            var afterif = new GeneratedLabelSymbol("afterif");
            var builder = ArrayBuilder<BoundStatement>.GetInstance();

            if (rewrittenAlternativeOpt == null)
            {
                // if (condition) 
                //   consequence;  
                //
                // becomes
                //
                // GotoIfFalse condition afterif;
                // consequence;
                // afterif:

                builder.Add(new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, afterif));
                builder.Add(rewrittenConsequence);
            }
            else
            {
                // if (condition)
                //     consequence;
                // else 
                //     alternative
                //
                // becomes
                //
                // GotoIfFalse condition alt;
                // consequence
                // goto afterif;
                // alt:
                // alternative;
                // afterif:

                var alt = new GeneratedLabelSymbol("alternative");

                builder.Add(new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, alt));
                builder.Add(rewrittenConsequence);
                builder.Add(new BoundGotoStatement(syntax, afterif));
                builder.Add(new BoundLabelStatement(syntax, alt));
                builder.Add(rewrittenAlternativeOpt);
            }

            builder.Add(new BoundLabelStatement(syntax, afterif));

            if (!locals.IsDefaultOrEmpty)
            {
                return new BoundBlock(syntax, locals, builder.ToImmutableAndFree(), hasErrors);
            }

            return new BoundStatementList(syntax, builder.ToImmutableAndFree(), hasErrors);
        }
 protected BoundExpression CreateConversion(
     BoundExpression source,
     Conversion conversion,
     TypeSymbol destination,
     DiagnosticBag diagnostics)
 {
     return CreateConversion(source.Syntax, source, conversion, isCast: false, destination: destination, diagnostics: diagnostics);
 }
 private BoundNode SpillAssignmentOperator(BoundAssignmentOperator node, BoundExpression left, BoundSpillSequence right)
 {
     var spillBuilder = new SpillBuilder();
     var spilledLeftNode = SpillLValue(left, spillBuilder);
     var innerSpill = node.Update(spilledLeftNode, right.Value, node.RefKind, node.Type);
     spillBuilder.AddSpill(right);
     return spillBuilder.BuildSequenceAndFree(F, innerSpill);
 }
 public LoweredDynamicOperation(SyntheticBoundNodeFactory factory, BoundExpression siteInitialization, BoundExpression siteInvocation, TypeSymbol resultType, ImmutableArray<LocalSymbol> temps)
 {
     _factory = factory;
     _resultType = resultType;
     _temps = temps;
     this.SiteInitialization = siteInitialization;
     this.SiteInvocation = siteInvocation;
 }
Example #24
0
 protected override void WriteArgument(BoundExpression arg, RefKind refKind, MethodSymbol method)
 {
     // ref parameter does not "always" assign.
     if (refKind == RefKind.Out)
     {
         Assign(arg, value: null);
     }
 }
Example #25
0
 public DecisionTree(BoundExpression expression, TypeSymbol type, LocalSymbol temp)
 {
     this.Expression = expression;
     this.Type = type;
     this.Temp = temp;
     Debug.Assert(this.Expression != null);
     Debug.Assert(this.Type != null);
 }
Example #26
0
 public BoundFieldAccess Update(
     BoundExpression receiver,
     FieldSymbol fieldSymbol,
     ConstantValue constantValueOpt,
     LookupResultKind resultKind,
     TypeSymbol typeSymbol)
 {
     return this.Update(receiver, fieldSymbol, constantValueOpt, resultKind, this.IsByValue, typeSymbol);
 }
Example #27
0
 internal void PopulateWithSingleMethod(
     BoundExpression receiverOpt,
     MethodSymbol method,
     LookupResultKind resultKind = LookupResultKind.Viable,
     DiagnosticInfo error = null)
 {
     this.PopulateHelper(receiverOpt, resultKind, error);
     this.Methods.Add(method);
 }
 private BoundExpression MakeUnaryOperator(
     UnaryOperatorKind kind,
     CSharpSyntaxNode syntax,
     MethodSymbol method,
     BoundExpression loweredOperand,
     TypeSymbol type)
 {
     return MakeUnaryOperator(null, kind, syntax, method, loweredOperand, type);
 }
Example #29
0
        protected override void VisitPatternSwitchSection(BoundPatternSwitchSection node, BoundExpression switchExpression, bool isLastSection)
        {
            foreach (var label in node.SwitchLabels)
            {
                NoteDeclaredPatternVariables(label.Pattern);
            }

            base.VisitPatternSwitchSection(node, switchExpression, isLastSection);
        }
Example #30
0
 public BoundFieldAccess(
     CSharpSyntaxNode syntax,
     BoundExpression receiver,
     FieldSymbol fieldSymbol,
     ConstantValue constantValueOpt,
     bool hasErrors = false)
     : this(syntax, receiver, fieldSymbol, constantValueOpt, LookupResultKind.Viable, fieldSymbol.Type, hasErrors)
 {
 }
Example #31
0
        private BoundStatement GenerateAwaitOnCompleted(
            TypeSymbol loweredAwaiterType,
            LocalSymbol awaiterTemp
            )
        {
            // this.builder.AwaitOnCompleted<TAwaiter,TSM>(ref $awaiterTemp, ref this)
            //    or
            // this.builder.AwaitOnCompleted<TAwaiter,TSM>(ref $awaiterArrayTemp[0], ref this)

            LocalSymbol thisTemp =
                (F.CurrentType.TypeKind == TypeKind.Class)
                    ? F.SynthesizedLocal(F.CurrentType)
                    : null;

            var discardedUseSiteInfo = CompoundUseSiteInfo <AssemblySymbol> .Discarded;
            var useUnsafeOnCompleted =
                F.Compilation.Conversions.ClassifyImplicitConversionFromType(
                    loweredAwaiterType,
                    F.Compilation.GetWellKnownType(
                        WellKnownType.System_Runtime_CompilerServices_ICriticalNotifyCompletion
                        ),
                    ref discardedUseSiteInfo
                    ).IsImplicit;

            var onCompleted = (
                useUnsafeOnCompleted
                    ? _asyncMethodBuilderMemberCollection.AwaitUnsafeOnCompleted
                    : _asyncMethodBuilderMemberCollection.AwaitOnCompleted
                ).Construct(loweredAwaiterType, F.This().Type);

            if (_asyncMethodBuilderMemberCollection.CheckGenericMethodConstraints)
            {
                onCompleted.CheckConstraints(
                    new ConstraintsHelper.CheckConstraintsArgs(
                        F.Compilation,
                        F.Compilation.Conversions,
                        includeNullability: false,
                        F.Syntax.Location,
                        this.Diagnostics
                        )
                    );
            }

            BoundExpression result = F.Call(
                F.Field(F.This(), _asyncMethodBuilderField),
                onCompleted,
                F.Local(awaiterTemp),
                F.This(thisTemp)
                );

            if (thisTemp != null)
            {
                result = F.Sequence(
                    ImmutableArray.Create(thisTemp),
                    ImmutableArray.Create <BoundExpression>(
                        F.AssignmentExpression(F.Local(thisTemp), F.This())
                        ),
                    result
                    );
            }

            return(F.ExpressionStatement(result));
        }
Example #32
0
        private BoundExpression LowerLiftedUnaryOperator(
            UnaryOperatorKind kind,
            SyntaxNode syntax,
            MethodSymbol method,
            BoundExpression loweredOperand,
            TypeSymbol type)
        {
            // First, an optimization. If we know that the operand is always null then
            // we can simply lower to the alternative.

            BoundExpression optimized = OptimizeLiftedUnaryOperator(kind, syntax, method, loweredOperand, type);

            if (optimized != null)
            {
                return(optimized);
            }

            // We do not know whether the operand is null or non-null, so we generate:
            //
            // S? temp = operand;
            // R? r = temp.HasValue ?
            //        new R?(OP(temp.GetValueOrDefault())) :
            //        default(R?);

            BoundAssignmentOperator tempAssignment;
            BoundLocal   boundTemp         = _factory.StoreToTemp(loweredOperand, out tempAssignment);
            MethodSymbol getValueOrDefault = UnsafeGetNullableMethod(syntax, boundTemp.Type, SpecialMember.System_Nullable_T_GetValueOrDefault);

            // temp.HasValue
            BoundExpression condition = MakeNullableHasValue(syntax, boundTemp);

            // temp.GetValueOrDefault()
            BoundExpression call_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault);

            // new R?(temp.GetValueOrDefault())
            BoundExpression consequence = GetLiftedUnaryOperatorConsequence(kind, syntax, method, type, call_GetValueOrDefault);

            // default(R?)
            BoundExpression alternative = new BoundDefaultExpression(syntax, null, type);

            // temp.HasValue ?
            //          new R?(OP(temp.GetValueOrDefault())) :
            //          default(R?);
            BoundExpression conditionalExpression = RewriteConditionalOperator(
                syntax: syntax,
                rewrittenCondition: condition,
                rewrittenConsequence: consequence,
                rewrittenAlternative: alternative,
                constantValueOpt: null,
                rewrittenType: type);

            // temp = operand;
            // temp.HasValue ?
            //          new R?(OP(temp.GetValueOrDefault())) :
            //          default(R?);
            return(new BoundSequence(
                       syntax: syntax,
                       locals: ImmutableArray.Create <LocalSymbol>(boundTemp.LocalSymbol),
                       sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment),
                       value: conditionalExpression,
                       type: type));
        }
 private static bool IsNullOrEmptyStringConstant(BoundExpression operand)
 {
     return((operand.ConstantValue != null && string.IsNullOrEmpty(operand.ConstantValue.StringValue)) ||
            operand.IsDefaultValue());
 }
        private BoundExpression Spill(
            BoundSpillSequenceBuilder builder,
            BoundExpression expression,
            RefKind refKind      = RefKind.None,
            bool sideEffectsOnly = false)
        {
            Debug.Assert(builder != null);

            while (true)
            {
                switch (expression.Kind)
                {
                case BoundKind.ArrayInitialization:
                    Debug.Assert(refKind == RefKind.None);
                    Debug.Assert(!sideEffectsOnly);
                    var arrayInitialization = (BoundArrayInitialization)expression;
                    var newInitializers     = VisitExpressionList(ref builder, arrayInitialization.Initializers, forceSpill: true);
                    return(arrayInitialization.Update(newInitializers));

                case BoundKind.ArgListOperator:
                    Debug.Assert(refKind == RefKind.None);
                    Debug.Assert(!sideEffectsOnly);
                    var argumentList = (BoundArgListOperator)expression;
                    var newArgs      = VisitExpressionList(ref builder, argumentList.Arguments, argumentList.ArgumentRefKindsOpt, forceSpill: true);
                    return(argumentList.Update(newArgs, argumentList.ArgumentRefKindsOpt, argumentList.Type));

                case SpillSequenceBuilder:
                    var sequenceBuilder = (BoundSpillSequenceBuilder)expression;
                    builder.Include(sequenceBuilder);
                    expression = sequenceBuilder.Value;
                    continue;

                case BoundKind.Sequence:
                    // We don't need promote short-lived variables defined by the sequence to long-lived,
                    // since neither the side-effects nor the value of the sequence contains await
                    // (otherwise it would be converted to a SpillSequenceBuilder).
                    var sequence = (BoundSequence)expression;
                    builder.AddLocals(sequence.Locals);
                    builder.AddExpressions(sequence.SideEffects);
                    expression = sequence.Value;
                    continue;

                case BoundKind.ThisReference:
                case BoundKind.BaseReference:
                    if (refKind != RefKind.None || expression.Type.IsReferenceType)
                    {
                        return(expression);
                    }

                    goto default;

                case BoundKind.Parameter:
                    if (refKind != RefKind.None)
                    {
                        return(expression);
                    }

                    goto default;

                case BoundKind.Local:
                    var local = (BoundLocal)expression;
                    if (local.LocalSymbol.SynthesizedKind == SynthesizedLocalKind.AwaitSpill || refKind != RefKind.None)
                    {
                        return(local);
                    }

                    goto default;

                case BoundKind.FieldAccess:
                    var field = (BoundFieldAccess)expression;
                    if (field.FieldSymbol.IsReadOnly)
                    {
                        if (field.FieldSymbol.IsStatic)
                        {
                            return(field);
                        }
                        if (field.FieldSymbol.ContainingType.IsValueType)
                        {
                            goto default;
                        }
                        // save the receiver; can get the field later.
                        var receiver = Spill(builder, field.ReceiverOpt, (refKind != RefKind.None && field.FieldSymbol.Type.IsReferenceType) ? refKind : RefKind.None, sideEffectsOnly);
                        return(field.Update(receiver, field.FieldSymbol, field.ConstantValueOpt, field.ResultKind, field.Type));
                    }
                    goto default;

                case BoundKind.Call:
                    var call = (BoundCall)expression;
                    if (refKind != RefKind.None)
                    {
                        Debug.Assert(call.Method.RefKind != RefKind.None);
                        _F.Diagnostics.Add(ErrorCode.ERR_RefReturningCallAndAwait, _F.Syntax.Location, call.Method);
                        refKind = RefKind.None;     // Switch the RefKind to avoid asserting later in the pipeline
                    }
                    goto default;

                case BoundKind.Literal:
                case BoundKind.TypeExpression:
                    return(expression);

                case BoundKind.ConditionalReceiver:
                    // we will rewrite this as a part of rewriting whole LoweredConditionalAccess
                    // later, if needed
                    return(expression);

                default:
                    if (expression.Type.SpecialType == SpecialType.System_Void || sideEffectsOnly)
                    {
                        builder.AddStatement(_F.ExpressionStatement(expression));
                        return(null);
                    }
                    else
                    {
                        BoundAssignmentOperator assignToTemp;
                        Debug.Assert(_F.Syntax.IsKind(SyntaxKind.AwaitExpression));

                        var replacement = _F.StoreToTemp(
                            expression,
                            out assignToTemp,
                            refKind: refKind,
                            kind: SynthesizedLocalKind.AwaitSpill,
                            syntaxOpt: _F.Syntax);

                        builder.AddLocal(replacement.LocalSymbol, _F.Diagnostics);
                        builder.AddStatement(_F.ExpressionStatement(assignToTemp));
                        return(replacement);
                    }
                }
            }
        }
 private static RefKind ReceiverSpillRefKind(BoundExpression receiver)
 {
     return(LocalRewriter.WouldBeAssignableIfUsedAsMethodReceiver(receiver) ?
            RefKind.Ref :
            RefKind.None);
 }
        private BoundExpression RewriteStringConcatenationTwoExprs(CSharpSyntaxNode syntax, BoundExpression loweredLeft, BoundExpression loweredRight)
        {
            SpecialMember member = (loweredLeft.Type.SpecialType == SpecialType.System_String && loweredRight.Type.SpecialType == SpecialType.System_String) ?
                                   SpecialMember.System_String__ConcatStringString :
                                   SpecialMember.System_String__ConcatObjectObject;

            var method = GetSpecialTypeMethod(syntax, member);

            Debug.Assert((object)method != null);

            return((BoundExpression)BoundCall.Synthesized(syntax, null, method, loweredLeft, loweredRight));
        }
 protected abstract bool TryGetReceiverAndMember(BoundExpression expr, out BoundExpression?receiver, [NotNullWhen(true)] out Symbol?member);
        private BoundExpression HoistExpression(
            BoundExpression expr,
            AwaitExpressionSyntax awaitSyntaxOpt,
            int syntaxOffset,
            bool isRef,
            ArrayBuilder <BoundExpression> sideEffects,
            ArrayBuilder <StateMachineFieldSymbol> hoistedFields,
            ref bool needsSacrificialEvaluation)
        {
            switch (expr.Kind)
            {
            case BoundKind.ArrayAccess:
            {
                var             array      = (BoundArrayAccess)expr;
                BoundExpression expression = HoistExpression(array.Expression, awaitSyntaxOpt, syntaxOffset, false, sideEffects, hoistedFields, ref needsSacrificialEvaluation);
                var             indices    = ArrayBuilder <BoundExpression> .GetInstance();

                foreach (var index in array.Indices)
                {
                    indices.Add(HoistExpression(index, awaitSyntaxOpt, syntaxOffset, false, sideEffects, hoistedFields, ref needsSacrificialEvaluation));
                }

                needsSacrificialEvaluation = true;         // need to force array index out of bounds exceptions
                return(array.Update(expression, indices.ToImmutableAndFree(), array.Type));
            }

            case BoundKind.FieldAccess:
            {
                var field = (BoundFieldAccess)expr;
                if (field.FieldSymbol.IsStatic)
                {
                    // the address of a static field, and the value of a readonly static field, is stable
                    if (isRef || field.FieldSymbol.IsReadOnly)
                    {
                        return(expr);
                    }
                    goto default;
                }

                if (!isRef)
                {
                    goto default;
                }

                var isFieldOfStruct = !field.FieldSymbol.ContainingType.IsReferenceType;

                var receiver = HoistExpression(field.ReceiverOpt, awaitSyntaxOpt, syntaxOffset, isFieldOfStruct, sideEffects, hoistedFields, ref needsSacrificialEvaluation);
                if (receiver.Kind != BoundKind.ThisReference && !isFieldOfStruct)
                {
                    needsSacrificialEvaluation = true;         // need the null check in field receiver
                }

                return(F.Field(receiver, field.FieldSymbol));
            }

            case BoundKind.ThisReference:
            case BoundKind.BaseReference:
            case BoundKind.DefaultOperator:
                return(expr);

            default:
                if (expr.ConstantValue != null)
                {
                    return(expr);
                }

                if (isRef)
                {
                    throw ExceptionUtilities.UnexpectedValue(expr.Kind);
                }

                TypeSymbol fieldType = expr.Type;
                StateMachineFieldSymbol hoistedField;
                if (F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug)
                {
                    const SynthesizedLocalKind kind = SynthesizedLocalKind.AwaitByRefSpill;

                    Debug.Assert(awaitSyntaxOpt != null);

                    int ordinal = synthesizedLocalOrdinals.AssignLocalOrdinal(kind, syntaxOffset);
                    var id      = new LocalDebugId(syntaxOffset, ordinal);

                    // Editing await expression is not allowed. Thus all spilled fields will be present in the previous state machine.
                    // However, it may happen that the type changes, in which case we need to allocate a new slot.
                    int slotIndex = -1;
                    if (slotAllocatorOpt != null)
                    {
                        slotIndex = slotAllocatorOpt.GetPreviousHoistedLocalSlotIndex(awaitSyntaxOpt, (Cci.ITypeReference)fieldType, kind, id);
                    }

                    if (slotIndex == -1)
                    {
                        slotIndex = nextFreeHoistedLocalSlot++;
                    }

                    string fieldName = GeneratedNames.MakeHoistedLocalFieldName(kind, slotIndex);
                    hoistedField = F.StateMachineField(expr.Type, fieldName, new LocalSlotDebugInfo(kind, id), slotIndex);
                }
                else
                {
                    hoistedField = GetOrAllocateReusableHoistedField(fieldType);
                }

                hoistedFields.Add(hoistedField);

                var replacement = F.Field(F.This(), hoistedField);
                sideEffects.Add(F.AssignmentExpression(replacement, expr));
                return(replacement);
            }
        }
Example #39
0
        protected override void PropertySetter(BoundExpression node, BoundExpression receiver, MethodSymbol setter, BoundExpression value = null)
        {
            base.PropertySetter(node, receiver, setter, value);

            if (receiver is null || receiver is BoundThisReference)
            {
                ApplyMemberPostConditions(setter.ContainingType, setter.NotNullMembers, notNullWhenTrueMembers: default, notNullWhenFalseMembers: default);
Example #40
0
        /// <remarks>
        /// This method implements best type inference for the conditional operator ?:.
        /// NOTE: If either expression is an error type, we return error type as the inference result.
        /// </remarks>
        public static TypeSymbol InferBestTypeForConditionalOperator(
            BoundExpression expr1,
            BoundExpression expr2,
            ConversionsBase conversions,
            out bool hadMultipleCandidates,
            out bool hadNullabilityMismatch,
            ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            // SPEC:    The second and third operands, x and y, of the ?: operator control the type of the conditional expression.
            // SPEC:    •	If x has type X and y has type Y then
            // SPEC:        o	If an implicit conversion (§6.1) exists from X to Y, but not from Y to X, then Y is the type of the conditional expression.
            // SPEC:        o	If an implicit conversion (§6.1) exists from Y to X, but not from X to Y, then X is the type of the conditional expression.
            // SPEC:        o	Otherwise, no expression type can be determined, and a compile-time error occurs.
            // SPEC:    •	If only one of x and y has a type, and both x and y, are implicitly convertible to that type, then that is the type of the conditional expression.
            // SPEC:    •	Otherwise, no expression type can be determined, and a compile-time error occurs.

            // A type is a candidate if all expressions are convertible to that type.
            ArrayBuilder <TypeSymbol> candidateTypes = ArrayBuilder <TypeSymbol> .GetInstance();

            try
            {
                var        conversionsWithoutNullability = conversions.WithNullability(false);
                TypeSymbol type1 = expr1.Type;

                if ((object)type1 != null)
                {
                    if (type1.IsErrorType())
                    {
                        hadMultipleCandidates  = false;
                        hadNullabilityMismatch = false;
                        return(type1);
                    }

                    if (conversionsWithoutNullability.ClassifyImplicitConversionFromExpression(expr2, type1, ref useSiteDiagnostics).Exists)
                    {
                        candidateTypes.Add(type1);
                    }
                }

                TypeSymbol type2 = expr2.Type;

                if ((object)type2 != null)
                {
                    if (type2.IsErrorType())
                    {
                        hadMultipleCandidates  = false;
                        hadNullabilityMismatch = false;
                        return(type2);
                    }

                    if (conversionsWithoutNullability.ClassifyImplicitConversionFromExpression(expr1, type2, ref useSiteDiagnostics).Exists)
                    {
                        candidateTypes.Add(type2);
                    }
                }

                hadMultipleCandidates = candidateTypes.Count > 1;

                return(GetBestType(candidateTypes, conversions, out hadNullabilityMismatch, ref useSiteDiagnostics));
            }
            finally
            {
                candidateTypes.Free();
            }
        }
        /// <summary>
        /// The strategy of this rewrite is to do rewrite "locally".
        /// We analyze arguments of the concat in a shallow fasion assuming that
        /// lowering and optimizations (including this one) is already done for the arguments.
        /// Based on the arguments we select the most appropriate pattern for the current node.
        ///
        /// NOTE: it is not guaranteed that the node that we chose will be the most optimal since we have only
        ///       local information - i.e. we look at the arguments, but we do not know about siblings.
        ///       When we move to the parent, the node may be rewritten by this or some another optimization.
        ///
        /// Example:
        ///     result = ( "abc" + "def" + null ?? expr1 + "moo" + "baz" ) + expr2
        ///
        /// Will rewrite into:
        ///     result = Concat("abcdef", expr2)
        ///
        /// However there will be transient nodes like  Concat(expr1 + "moo")  that will not be present in the
        /// resulting tree.
        ///
        /// </summary>
        private BoundExpression RewriteStringConcatenation(CSharpSyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type)
        {
            Debug.Assert(
                operatorKind == BinaryOperatorKind.StringConcatenation ||
                operatorKind == BinaryOperatorKind.StringAndObjectConcatenation ||
                operatorKind == BinaryOperatorKind.ObjectAndStringConcatenation);

            if (inExpressionLambda)
            {
                return(RewriteStringConcatInExpressionLambda(syntax, operatorKind, loweredLeft, loweredRight, type));
            }

            // try fold two args without flattening.
            var folded = TryFoldTwoConcatOperands(syntax, loweredLeft, loweredRight);

            if (folded != null)
            {
                return(folded);
            }

            // flatten and merge -  ( expr1 + "A" ) + ("B" + expr2) ===> (expr1 + "AB" + expr2)
            ArrayBuilder <BoundExpression> leftFlattened = ArrayBuilder <BoundExpression> .GetInstance();

            ArrayBuilder <BoundExpression> rightFlattened = ArrayBuilder <BoundExpression> .GetInstance();

            FlattenConcatArg(loweredLeft, leftFlattened);
            FlattenConcatArg(loweredRight, rightFlattened);

            if (leftFlattened.Any() && rightFlattened.Any())
            {
                folded = TryFoldTwoConcatOperands(syntax, leftFlattened.Last(), rightFlattened.First());
                if (folded != null)
                {
                    rightFlattened[0] = folded;
                    leftFlattened.RemoveLast();
                }
            }

            leftFlattened.AddRange(rightFlattened);
            rightFlattened.Free();

            BoundExpression result;

            switch (leftFlattened.Count)
            {
            case 0:
                result = factory.StringLiteral(string.Empty);
                break;

            case 1:
                result = leftFlattened[0];
                break;

            case 2:
                var left  = leftFlattened[0];
                var right = leftFlattened[1];
                result = RewriteStringConcatenationTwoExprs(syntax, left, right);
                break;

            case 3:
                var first  = leftFlattened[0];
                var second = leftFlattened[1];
                var third  = leftFlattened[2];
                result = RewriteStringConcatenationThreeExprs(syntax, first, second, third);
                break;

            default:
                result = RewriteStringConcatenationManyExprs(syntax, leftFlattened.ToImmutable());
                break;
            }

            leftFlattened.Free();
            return(result);
        }
Example #42
0
        private BoundExpression MakeBuiltInIncrementOperator(BoundIncrementOperator node, BoundExpression rewrittenValueToIncrement)
        {
            BoundExpression result;
            // If we have a built-in increment or decrement then things get a bit trickier. Suppose for example we have
            // a user-defined conversion from X to short and from short to X, but no user-defined increment operator on
            // X.  The increment portion of "++x" is then: (X)(short)((int)(short)x + 1). That is, first x must be
            // converted to short via an implicit user- defined conversion, then to int via an implicit numeric
            // conversion, then the addition is performed in integers. The resulting integer is converted back to short,
            // and then the short is converted to X.

            // This is the input and output type of the unary increment operator we're going to call.
            // That is, "short" in the example above.
            TypeSymbol unaryOperandType = GetUnaryOperatorType(node);

            // This is the kind of binary operator that we're going to realize the unary operator
            // as. That is, "int + int --> int" in the example above.
            BinaryOperatorKind binaryOperatorKind = GetCorrespondingBinaryOperator(node);

            binaryOperatorKind |= IsIncrement(node) ? BinaryOperatorKind.Addition : BinaryOperatorKind.Subtraction;

            // The "1" in the example above.
            ConstantValue constantOne = GetConstantOneForBinOp(binaryOperatorKind);

            Debug.Assert(constantOne != null);
            Debug.Assert(constantOne.SpecialType != SpecialType.None);
            Debug.Assert(binaryOperatorKind.OperandTypes() != 0);

            // The input/output type of the binary operand. "int" in the example.
            TypeSymbol binaryOperandType = _compilation.GetSpecialType(constantOne.SpecialType);

            // 1
            BoundExpression boundOne = MakeLiteral(
                syntax: node.Syntax,
                constantValue: constantOne,
                type: binaryOperandType);

            if (binaryOperatorKind.IsLifted())
            {
                binaryOperandType = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(binaryOperandType);
                MethodSymbol ctor = UnsafeGetNullableMethod(node.Syntax, binaryOperandType, SpecialMember.System_Nullable_T__ctor);
                boundOne = new BoundObjectCreationExpression(node.Syntax, ctor, boundOne);
            }

            // Now we construct the other operand to the binary addition. We start with just plain "x".
            BoundExpression binaryOperand = rewrittenValueToIncrement;

            bool @checked = node.OperatorKind.IsChecked();

            // If we need to make a conversion from the original operand type to the operand type of the
            // underlying increment operation, do it now.
            if (!node.OperandConversion.IsIdentity)
            {
                // (short)x
                binaryOperand = MakeConversionNode(
                    syntax: node.Syntax,
                    rewrittenOperand: binaryOperand,
                    conversion: node.OperandConversion,
                    rewrittenType: unaryOperandType,
                    @checked: @checked);
            }

            // Early-out for pointer increment - we don't need to convert the operands to a common type.
            if (node.OperatorKind.OperandTypes() == UnaryOperatorKind.Pointer)
            {
                Debug.Assert(binaryOperatorKind.OperandTypes() == BinaryOperatorKind.PointerAndInt);
                Debug.Assert(binaryOperand.Type.IsPointerType());
                Debug.Assert(boundOne.Type.SpecialType == SpecialType.System_Int32);
                return(MakeBinaryOperator(node.Syntax, binaryOperatorKind, binaryOperand, boundOne, binaryOperand.Type, method: null));
            }

            // If we need to make a conversion from the unary operator type to the binary operator type,
            // do it now.

            // (int)(short)x
            binaryOperand = MakeConversionNode(binaryOperand, binaryOperandType, @checked);

            // Perform the addition.

            // (int)(short)x + 1
            BoundExpression binOp;

            if (unaryOperandType.SpecialType == SpecialType.System_Decimal)
            {
                binOp = MakeDecimalIncDecOperator(node.Syntax, binaryOperatorKind, binaryOperand);
            }
            else if (unaryOperandType.IsNullableType() && unaryOperandType.GetNullableUnderlyingType().SpecialType == SpecialType.System_Decimal)
            {
                binOp = MakeLiftedDecimalIncDecOperator(node.Syntax, binaryOperatorKind, binaryOperand);
            }
            else
            {
                binOp = MakeBinaryOperator(node.Syntax, binaryOperatorKind, binaryOperand, boundOne, binaryOperandType, method: null);
            }

            // Generate the conversion back to the type of the unary operator.

            // (short)((int)(short)x + 1)
            result = MakeConversionNode(binOp, unaryOperandType, @checked);
            return(result);
        }
Example #43
0
        private BoundExpression MakeUnaryOperator(
            BoundUnaryOperator oldNode,
            UnaryOperatorKind kind,
            SyntaxNode syntax,
            MethodSymbol method,
            BoundExpression loweredOperand,
            TypeSymbol type)
        {
            if (kind.IsDynamic())
            {
                Debug.Assert(kind == UnaryOperatorKind.DynamicTrue && type.SpecialType == SpecialType.System_Boolean || type.IsDynamic());
                Debug.Assert((object)method == null);

                // Logical operators on boxed Boolean constants:
                var constant = UnboxConstant(loweredOperand);
                if (constant == ConstantValue.True || constant == ConstantValue.False)
                {
                    if (kind == UnaryOperatorKind.DynamicTrue)
                    {
                        return(_factory.Literal(constant.BooleanValue));
                    }
                    else if (kind == UnaryOperatorKind.DynamicLogicalNegation)
                    {
                        return(MakeConversionNode(_factory.Literal(!constant.BooleanValue), type, @checked: false));
                    }
                }

                return(_dynamicFactory.MakeDynamicUnaryOperator(kind, loweredOperand, type).ToExpression());
            }
            else if (kind.IsLifted())
            {
                if (!_inExpressionLambda)
                {
                    return(LowerLiftedUnaryOperator(kind, syntax, method, loweredOperand, type));
                }
            }
            else if (kind.IsUserDefined())
            {
                Debug.Assert((object)method != null);
                Debug.Assert(type == method.ReturnType);
                if (!_inExpressionLambda || kind == UnaryOperatorKind.UserDefinedTrue || kind == UnaryOperatorKind.UserDefinedFalse)
                {
                    return(BoundCall.Synthesized(syntax, null, method, loweredOperand));
                }
            }
            else if (kind.Operator() == UnaryOperatorKind.UnaryPlus)
            {
                // We do not call the operator even for decimal; we simply optimize it away entirely.
                return(loweredOperand);
            }

            if (kind == UnaryOperatorKind.EnumBitwiseComplement)
            {
                var underlyingType       = loweredOperand.Type.GetEnumUnderlyingType();
                var upconvertSpecialType = Binder.GetEnumPromotedType(underlyingType.SpecialType);
                var upconvertType        = upconvertSpecialType == underlyingType.SpecialType ?
                                           underlyingType :
                                           _compilation.GetSpecialType(upconvertSpecialType);


                var newOperand            = MakeConversionNode(loweredOperand, upconvertType, false);
                UnaryOperatorKind newKind = kind.Operator().WithType(upconvertSpecialType);

                var newNode = (oldNode != null) ?
                              oldNode.Update(
                    newKind,
                    newOperand,
                    oldNode.ConstantValueOpt,
                    method,
                    newOperand.ResultKind,
                    upconvertType) :
                              new BoundUnaryOperator(
                    syntax,
                    newKind,
                    newOperand,
                    null,
                    method,
                    LookupResultKind.Viable,
                    upconvertType);

                return(MakeConversionNode(newNode.Syntax, newNode, Conversion.ExplicitEnumeration, type, @checked: false));
            }

            if (kind == UnaryOperatorKind.DecimalUnaryMinus)
            {
                method = (MethodSymbol)_compilation.Assembly.GetSpecialTypeMember(SpecialMember.System_Decimal__op_UnaryNegation);
                if (!_inExpressionLambda)
                {
                    return(BoundCall.Synthesized(syntax, null, method, loweredOperand));
                }
            }

            return((oldNode != null) ?
                   oldNode.Update(kind, loweredOperand, oldNode.ConstantValueOpt, method, oldNode.ResultKind, type) :
                   new BoundUnaryOperator(syntax, kind, loweredOperand, null, method, LookupResultKind.Viable, type));
        }
Example #44
0
        private BoundBlock VisitAwaitExpression(
            BoundAwaitExpression node,
            BoundExpression resultPlace
            )
        {
            var expression           = (BoundExpression)Visit(node.Expression);
            var awaitablePlaceholder = node.AwaitableInfo.AwaitableInstancePlaceholder;

            if (awaitablePlaceholder != null)
            {
                _placeholderMap.Add(awaitablePlaceholder, expression);
            }

            var getAwaiter = node.AwaitableInfo.IsDynamic
                ? MakeCallMaybeDynamic(expression, null, WellKnownMemberNames.GetAwaiter)
                : (BoundExpression)Visit(node.AwaitableInfo.GetAwaiter);

            resultPlace = (BoundExpression)Visit(resultPlace);
            MethodSymbol getResult         = VisitMethodSymbol(node.AwaitableInfo.GetResult);
            MethodSymbol isCompletedMethod =
                ((object)node.AwaitableInfo.IsCompleted != null)
                    ? VisitMethodSymbol(node.AwaitableInfo.IsCompleted.GetMethod)
                    : null;
            TypeSymbol type = VisitType(node.Type);

            if (awaitablePlaceholder != null)
            {
                _placeholderMap.Remove(awaitablePlaceholder);
            }

            // The awaiter temp facilitates EnC method remapping and thus have to be long-lived.
            // It transfers the awaiter objects from the old version of the MoveNext method to the new one.
            Debug.Assert(
                node.Syntax.IsKind(SyntaxKind.AwaitExpression) || node.WasCompilerGenerated
                );

            var awaiterTemp = F.SynthesizedLocal(
                getAwaiter.Type,
                syntax: node.Syntax,
                kind: SynthesizedLocalKind.Awaiter
                );
            var awaitIfIncomplete = F.Block(
                // temp $awaiterTemp = <expr>.GetAwaiter();
                F.Assignment(F.Local(awaiterTemp), getAwaiter),
                // hidden sequence point facilitates EnC method remapping, see explanation on SynthesizedLocalKind.Awaiter:
                F.HiddenSequencePoint(),
                // if(!($awaiterTemp.IsCompleted)) { ... }
                F.If(
                    condition: F.Not(GenerateGetIsCompleted(awaiterTemp, isCompletedMethod)),
                    thenClause: GenerateAwaitForIncompleteTask(awaiterTemp)
                    )
                );
            BoundExpression getResultCall = MakeCallMaybeDynamic(
                F.Local(awaiterTemp),
                getResult,
                WellKnownMemberNames.GetResult,
                resultsDiscarded: resultPlace == null
                );

            // [$resultPlace = ] $awaiterTemp.GetResult();
            BoundStatement getResultStatement =
                resultPlace != null && !type.IsVoidType()
                    ? F.Assignment(resultPlace, getResultCall)
                    : F.ExpressionStatement(getResultCall);

            return(F.Block(
                       ImmutableArray.Create(awaiterTemp),
                       awaitIfIncomplete,
                       getResultStatement
                       ));
        }
        /// <summary>
        /// digs into known concat operators and unwraps their arguments
        /// otherwise returns the expression as-is
        ///
        /// Generally we only need to recognize same node patterns that we create as a result of concatenation rewrite.
        /// </summary>
        private void FlattenConcatArg(BoundExpression lowered, ArrayBuilder <BoundExpression> flattened)
        {
            switch (lowered.Kind)
            {
            case BoundKind.Call:
                var boundCall = (BoundCall)lowered;

                var method = boundCall.Method;
                if (method.IsStatic && method.ContainingType.SpecialType == SpecialType.System_String)
                {
                    if ((object)method == (object)this.compilation.GetSpecialTypeMember(SpecialMember.System_String__ConcatStringString) ||
                        (object)method == (object)this.compilation.GetSpecialTypeMember(SpecialMember.System_String__ConcatStringStringString) ||
                        (object)method == (object)this.compilation.GetSpecialTypeMember(SpecialMember.System_String__ConcatStringStringStringString) ||
                        (object)method == (object)this.compilation.GetSpecialTypeMember(SpecialMember.System_String__ConcatObject) ||
                        (object)method == (object)this.compilation.GetSpecialTypeMember(SpecialMember.System_String__ConcatObjectObject) ||
                        (object)method == (object)this.compilation.GetSpecialTypeMember(SpecialMember.System_String__ConcatObjectObjectObject))
                    {
                        flattened.AddRange(boundCall.Arguments);
                        return;
                    }

                    if ((object)method == (object)this.compilation.GetSpecialTypeMember(SpecialMember.System_String__ConcatStringArray) ||
                        (object)method == (object)this.compilation.GetSpecialTypeMember(SpecialMember.System_String__ConcatObjectArray))
                    {
                        var args = boundCall.Arguments[0] as BoundArrayCreation;
                        if (args != null)
                        {
                            var initializer = args.InitializerOpt;
                            if (initializer != null)
                            {
                                flattened.AddRange(initializer.Initializers);
                                return;
                            }
                        }
                    }
                }
                break;

            case BoundKind.NullCoalescingOperator:
                var boundCoalesce = (BoundNullCoalescingOperator)lowered;

                if (boundCoalesce.LeftConversion.IsIdentity)
                {
                    // The RHS may be a constant value with an identity conversion to string even
                    // if it is not a string: in particular, the null literal behaves this way.
                    // To be safe, check that the constant value is actually a string before
                    // attempting to access its value as a string.

                    var rightConstant = boundCoalesce.RightOperand.ConstantValue;
                    if (rightConstant != null && rightConstant.IsString && rightConstant.StringValue.Length == 0)
                    {
                        flattened.Add(boundCoalesce.LeftOperand);
                        return;
                    }
                }
                break;
            }

            // fallback - if nothing above worked, leave arg as-is
            flattened.Add(lowered);
            return;
        }
        /// <summary>
        /// Strangely enough there is such a thing as unary concatenation and it must be rewritten.
        /// </summary>
        private BoundExpression RewriteStringConcatenationOneExpr(CSharpSyntaxNode syntax, BoundExpression loweredOperand)
        {
            if (loweredOperand.Type.SpecialType == SpecialType.System_String)
            {
                // loweredOperand ?? ""
                return(factory.Coalesce(loweredOperand, factory.Literal("")));
            }

            var method = GetSpecialTypeMethod(syntax, SpecialMember.System_String__ConcatObject);

            Debug.Assert((object)method != null);

            return((BoundExpression)BoundCall.Synthesized(syntax, null, method, loweredOperand));
        }
Example #47
0
        private BoundExpression MakeUnaryOperator(
            BoundUnaryOperator oldNode,
            UnaryOperatorKind kind,
            CSharpSyntaxNode syntax,
            MethodSymbol method,
            BoundExpression loweredOperand,
            TypeSymbol type)
        {
            if (kind.IsDynamic())
            {
                Debug.Assert(kind == UnaryOperatorKind.DynamicTrue && type.SpecialType == SpecialType.System_Boolean || type.IsDynamic());
                Debug.Assert((object)method == null);

                // Logical operators on boxed Boolean constants:
                var constant = UnboxConstant(loweredOperand);
                if (constant == ConstantValue.True || constant == ConstantValue.False)
                {
                    if (kind == UnaryOperatorKind.DynamicTrue)
                    {
                        return(_factory.Literal(constant.BooleanValue));
                    }
                    else if (kind == UnaryOperatorKind.DynamicLogicalNegation)
                    {
                        return(MakeConversionNode(_factory.Literal(!constant.BooleanValue), type, @checked: false));
                    }
                }

                return(_dynamicFactory.MakeDynamicUnaryOperator(kind, loweredOperand, type).ToExpression());
            }
            else if (kind.IsLifted())
            {
                if (!_inExpressionLambda)
                {
                    return(LowerLiftedUnaryOperator(kind, syntax, method, loweredOperand, type));
                }
            }
            else if (kind.IsUserDefined())
            {
                Debug.Assert((object)method != null);
                Debug.Assert(type == method.ReturnType);
                if (!_inExpressionLambda || kind == UnaryOperatorKind.UserDefinedTrue || kind == UnaryOperatorKind.UserDefinedFalse)
                {
                    // @t-mawind
                    //   As usual, concept accesses need to be rewritten down to their
                    //   default() form.
                    // Is this correct?  It's mostly a copy over from the binary case,
                    // but the unary case is different enough to make me nervous.
                    if (method is SynthesizedWitnessMethodSymbol)
                    {
                        return(BoundCall.Synthesized(syntax, SynthesizeWitnessInvocationReceiver(syntax, ((SynthesizedWitnessMethodSymbol)method).Parent), method, loweredOperand));
                    }

                    return(BoundCall.Synthesized(syntax, null, method, loweredOperand));
                }
            }
            else if (kind.Operator() == UnaryOperatorKind.UnaryPlus)
            {
                // We do not call the operator even for decimal; we simply optimize it away entirely.
                return(loweredOperand);
            }

            if (kind == UnaryOperatorKind.EnumBitwiseComplement)
            {
                var underlyingType       = loweredOperand.Type.GetEnumUnderlyingType();
                var upconvertSpecialType = Binder.GetEnumPromotedType(underlyingType.SpecialType);
                var upconvertType        = upconvertSpecialType == underlyingType.SpecialType ?
                                           underlyingType :
                                           _compilation.GetSpecialType(upconvertSpecialType);


                var newOperand            = MakeConversionNode(loweredOperand, upconvertType, false);
                UnaryOperatorKind newKind = kind.Operator().WithType(upconvertSpecialType);

                var newNode = (oldNode != null) ?
                              oldNode.Update(
                    newKind,
                    newOperand,
                    oldNode.ConstantValueOpt,
                    method,
                    newOperand.ResultKind,
                    upconvertType) :
                              new BoundUnaryOperator(
                    syntax,
                    newKind,
                    newOperand,
                    null,
                    method,
                    LookupResultKind.Viable,
                    upconvertType);

                return(MakeConversionNode(newNode.Syntax, newNode, Conversion.ExplicitEnumeration, type, @checked: false));
            }

            if (kind == UnaryOperatorKind.DecimalUnaryMinus)
            {
                method = (MethodSymbol)_compilation.Assembly.GetSpecialTypeMember(SpecialMember.System_Decimal__op_UnaryNegation);
                if (!_inExpressionLambda)
                {
                    return(BoundCall.Synthesized(syntax, null, method, loweredOperand));
                }
            }

            return((oldNode != null) ?
                   oldNode.Update(kind, loweredOperand, oldNode.ConstantValueOpt, method, oldNode.ResultKind, type) :
                   new BoundUnaryOperator(syntax, kind, loweredOperand, null, method, LookupResultKind.Viable, type));
        }
Example #48
0
        public BoundDecisionDag SimplifyDecisionDagIfConstantInput(BoundExpression input)
        {
            if (input.ConstantValue == null)
            {
                return(this);
            }
            else
            {
                ConstantValue inputConstant = input.ConstantValue;
                return(Rewrite(makeReplacement));

                // Make a replacement for a given node, using the precomputed replacements for its successors.
                BoundDecisionDagNode makeReplacement(BoundDecisionDagNode dag, Func <BoundDecisionDagNode, BoundDecisionDagNode> replacement)
                {
                    if (dag is BoundTestDecisionDagNode p)
                    {
                        // This is the key to the optimization. The result of a top-level test might be known if the input is constant.
                        switch (knownResult(p.Test))
                        {
                        case true:
                            return(replacement(p.WhenTrue));

                        case false:
                            return(replacement(p.WhenFalse));
                        }
                    }

                    return(TrivialReplacement(dag, replacement));
                }

                // Is the decision's result known because the input is a constant?
                bool?knownResult(BoundDagTest choice)
                {
                    if (!choice.Input.IsOriginalInput)
                    {
                        // This is a test of something other than the main input; result unknown
                        return(null);
                    }

                    switch (choice)
                    {
                    case BoundDagExplicitNullTest d:
                        return(inputConstant.IsNull);

                    case BoundDagNonNullTest d:
                        return(!inputConstant.IsNull);

                    case BoundDagValueTest d:
                        return(d.Value == inputConstant);

                    case BoundDagTypeTest d:
                        return(inputConstant.IsNull ? (bool?)false : null);

                    case BoundDagRelationalTest d:
                        var f = ValueSetFactory.ForType(input.Type);
                        if (f is null)
                        {
                            return(null);
                        }
                        // TODO: When ValueSetFactory has a method for comparing two values, use it.
                        var set = f.Related(d.Relation.Operator(), d.Value);
                        return(set.Any(BinaryOperatorKind.Equal, inputConstant));

                    default:
                        throw ExceptionUtilities.UnexpectedValue(choice);
                    }
                }
            }
        }
Example #49
0
 public ExpressionAndDiagnostics(BoundExpression expression, ImmutableArray <Diagnostic> diagnostics)
 {
     this.Expression  = expression;
     this.Diagnostics = diagnostics;
 }
Example #50
0
        private BoundExpression MakeLiftedDecimalIncDecOperator(SyntaxNode syntax, BinaryOperatorKind oper, BoundExpression operand)
        {
            Debug.Assert(operand.Type.IsNullableType() && operand.Type.GetNullableUnderlyingType().SpecialType == SpecialType.System_Decimal);

            // This method assumes that operand is already a temporary and so there is no need to copy it again.
            MethodSymbol method            = GetDecimalIncDecOperator(oper);
            MethodSymbol getValueOrDefault = UnsafeGetNullableMethod(syntax, operand.Type, SpecialMember.System_Nullable_T_GetValueOrDefault);
            MethodSymbol ctor = UnsafeGetNullableMethod(syntax, operand.Type, SpecialMember.System_Nullable_T__ctor);

            // x.HasValue
            BoundExpression condition = MakeNullableHasValue(syntax, operand);
            // x.GetValueOrDefault()
            BoundExpression getValueCall = BoundCall.Synthesized(syntax, operand, getValueOrDefault);
            // op_Inc(x.GetValueOrDefault())
            BoundExpression methodCall = BoundCall.Synthesized(syntax, null, method, getValueCall);
            // new decimal?(op_Inc(x.GetValueOrDefault()))
            BoundExpression consequence = new BoundObjectCreationExpression(syntax, ctor, methodCall);
            // default(decimal?)
            BoundExpression alternative = new BoundDefaultExpression(syntax, null, operand.Type);

            // x.HasValue ? new decimal?(op_Inc(x.GetValueOrDefault())) : default(decimal?)
            return(RewriteConditionalOperator(syntax, condition, consequence, alternative, ConstantValue.NotAvailable, operand.Type));
        }
 public BoundSpillSequenceBuilder(BoundExpression value = null)
     : base(SpillSequenceBuilder, null, value?.Type)
 {
     Debug.Assert(value == null || value.Kind != SpillSequenceBuilder);
     this.Value = value;
 }
Example #52
0
        private BoundExpression GetLiftedUnaryOperatorConsequence(UnaryOperatorKind kind, SyntaxNode syntax, MethodSymbol method, TypeSymbol type, BoundExpression nonNullOperand)
        {
            MethodSymbol ctor = UnsafeGetNullableMethod(syntax, type, SpecialMember.System_Nullable_T__ctor);

            // OP(temp.GetValueOrDefault())
            BoundExpression unliftedOp = MakeUnaryOperator(
                oldNode: null,
                kind: kind.Unlifted(),
                syntax: syntax,
                method: method,
                loweredOperand: nonNullOperand,
                type: type.GetNullableUnderlyingType());

            // new R?(OP(temp.GetValueOrDefault()))
            BoundExpression consequence = new BoundObjectCreationExpression(
                syntax,
                ctor,
                unliftedOp);

            return(consequence);
        }
        private static BoundExpression UpdateExpression(BoundSpillSequenceBuilder builder, BoundExpression expression)
        {
            if (builder == null)
            {
                return(expression);
            }

            Debug.Assert(builder.Value == null);
            if (!builder.HasLocals && !builder.HasStatements)
            {
                builder.Free();
                return(expression);
            }

            return(builder.Update(expression));
        }
Example #54
0
        private BoundExpression OptimizeLiftedUnaryOperator(
            UnaryOperatorKind operatorKind,
            SyntaxNode syntax,
            MethodSymbol method,
            BoundExpression loweredOperand,
            TypeSymbol type)
        {
            if (NullableNeverHasValue(loweredOperand))
            {
                return(new BoundDefaultExpression(syntax, null, type));
            }

            // Second, another simple optimization. If we know that the operand is never null
            // then we can obtain the non-null value and skip generating the temporary. That is,
            // "~(new int?(M()))" is the same as "new int?(~M())".

            BoundExpression neverNull = NullableAlwaysHasValue(loweredOperand);

            if (neverNull != null)
            {
                return(GetLiftedUnaryOperatorConsequence(operatorKind, syntax, method, type, neverNull));
            }

            var conditionalLeft = loweredOperand as BoundLoweredConditionalAccess;

            // NOTE: we could in theory handle side-effecting loweredRight here too
            //       by including it as a part of whenNull, but there is a concern
            //       that it can lead to code duplication
            var optimize = conditionalLeft != null &&
                           (conditionalLeft.WhenNullOpt == null || conditionalLeft.WhenNullOpt.IsDefaultValue());

            if (optimize)
            {
                var result = LowerLiftedUnaryOperator(operatorKind, syntax, method, conditionalLeft.WhenNotNull, type);

                return(conditionalLeft.Update(
                           conditionalLeft.Receiver,
                           conditionalLeft.HasValueMethodOpt,
                           whenNotNull: result,
                           whenNullOpt: null,
                           id: conditionalLeft.Id,
                           type: result.Type
                           ));
            }

            // This optimization is analogous to DistributeLiftedConversionIntoLiftedOperand.

            // Suppose we have a lifted unary conversion whose operand is itself a lifted operation.
            // That is, we have something like:
            //
            // int? r = - (M() + N());
            //
            // where M() and N() return nullable ints. We would simply codegen this as first
            // creating the nullable int result of M() + N(), then checking it for nullity,
            // and then doing the unary minus. That is:
            //
            // int? m = M();
            // int? n = N();
            // int? t = m.HasValue && n.HasValue ? new int?(m.Value + n.Value) : new int?();
            // int? r = t.HasValue ? new int?(-t.Value) : new int?();
            //
            // However, we also observe that we can distribute the unary minus into both branches of
            // the conditional:
            //
            // int? m = M();
            // int? n = N();
            // int? r = m.HasValue && n.HasValue ? - (new int?(m.Value + n.Value))) : - new int?();
            //
            // And we already optimize those! So we could reduce this to:
            //
            // int? m = M();
            // int? n = N();
            // int? r = m.HasValue && n.HasValue ? new int?(- (m.Value + n.Value)) : new int?());
            //
            // which avoids entirely the creation of the unnecessary nullable int and the unnecessary
            // extra null check.

            if (loweredOperand.Kind == BoundKind.Sequence)
            {
                BoundSequence seq = (BoundSequence)loweredOperand;
                if (seq.Value.Kind == BoundKind.ConditionalOperator)
                {
                    BoundConditionalOperator conditional = (BoundConditionalOperator)seq.Value;
                    Debug.Assert(seq.Type == conditional.Type);
                    Debug.Assert(conditional.Type == conditional.Consequence.Type);
                    Debug.Assert(conditional.Type == conditional.Alternative.Type);

                    if (NullableAlwaysHasValue(conditional.Consequence) != null && NullableNeverHasValue(conditional.Alternative))
                    {
                        return(new BoundSequence(
                                   syntax,
                                   seq.Locals,
                                   seq.SideEffects,
                                   RewriteConditionalOperator(
                                       syntax,
                                       conditional.Condition,
                                       MakeUnaryOperator(operatorKind, syntax, method, conditional.Consequence, type),
                                       MakeUnaryOperator(operatorKind, syntax, method, conditional.Alternative, type),
                                       ConstantValue.NotAvailable,
                                       type),
                                   type));
                    }
                }
            }

            return(null);
        }
Example #55
0
        private BoundExpression MakeUserDefinedIncrementOperator(BoundIncrementOperator node, BoundExpression rewrittenValueToIncrement)
        {
            Debug.Assert((object)node.MethodOpt != null);
            Debug.Assert(node.MethodOpt.ParameterCount == 1);

            bool isLifted = node.OperatorKind.IsLifted();
            bool @checked = node.OperatorKind.IsChecked();

            BoundExpression rewrittenArgument = rewrittenValueToIncrement;
            SyntaxNode      syntax            = node.Syntax;

            TypeSymbol type = node.MethodOpt.ParameterTypes[0];

            if (isLifted)
            {
                type = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(type);
                Debug.Assert(node.MethodOpt.ParameterTypes[0] == node.MethodOpt.ReturnType);
            }

            if (!node.OperandConversion.IsIdentity)
            {
                rewrittenArgument = MakeConversionNode(
                    syntax: syntax,
                    rewrittenOperand: rewrittenValueToIncrement,
                    conversion: node.OperandConversion,
                    rewrittenType: type,
                    @checked: @checked);
            }

            if (!isLifted)
            {
                return(BoundCall.Synthesized(syntax, null, node.MethodOpt, rewrittenArgument));
            }

            // S? temp = operand;
            // S? r = temp.HasValue ?
            //        new S?(op_Increment(temp.GetValueOrDefault())) :
            //        default(S?);

            // Unlike the other unary operators, we do not attempt to optimize nullable user-defined
            // increment or decrement. The operand is a variable (or property), and so we do not know if
            // it is always null/never null.

            BoundAssignmentOperator tempAssignment;
            BoundLocal boundTemp = _factory.StoreToTemp(rewrittenArgument, out tempAssignment);

            MethodSymbol getValueOrDefault = UnsafeGetNullableMethod(syntax, type, SpecialMember.System_Nullable_T_GetValueOrDefault);
            MethodSymbol ctor = UnsafeGetNullableMethod(syntax, type, SpecialMember.System_Nullable_T__ctor);

            // temp.HasValue
            BoundExpression condition = MakeNullableHasValue(node.Syntax, boundTemp);

            // temp.GetValueOrDefault()
            BoundExpression call_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault);

            // op_Increment(temp.GetValueOrDefault())
            BoundExpression userDefinedCall = BoundCall.Synthesized(syntax, null, node.MethodOpt, call_GetValueOrDefault);

            // new S?(op_Increment(temp.GetValueOrDefault()))
            BoundExpression consequence = new BoundObjectCreationExpression(syntax, ctor, userDefinedCall);

            // default(S?)
            BoundExpression alternative = new BoundDefaultExpression(syntax, null, type);

            // temp.HasValue ?
            //          new S?(op_Increment(temp.GetValueOrDefault())) :
            //          default(S?);
            BoundExpression conditionalExpression = RewriteConditionalOperator(
                syntax: syntax,
                rewrittenCondition: condition,
                rewrittenConsequence: consequence,
                rewrittenAlternative: alternative,
                constantValueOpt: null,
                rewrittenType: type);

            // temp = operand;
            // temp.HasValue ?
            //          new S?(op_Increment(temp.GetValueOrDefault())) :
            //          default(S?);
            return(new BoundSequence(
                       syntax: syntax,
                       locals: ImmutableArray.Create <LocalSymbol>(boundTemp.LocalSymbol),
                       sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment),
                       value: conditionalExpression,
                       type: type));
        }
        /// <summary>
        /// folds two concat operands into one expression if possible
        /// otherwise returns null
        /// </summary>
        private BoundExpression TryFoldTwoConcatOperands(CSharpSyntaxNode syntax, BoundExpression loweredLeft, BoundExpression loweredRight)
        {
            // both left and right are constants
            var leftConst  = loweredLeft.ConstantValue;
            var rightConst = loweredRight.ConstantValue;

            if (leftConst != null && rightConst != null)
            {
                // const concat may fail to fold if strings are huge.
                // This would be unusual.
                ConstantValue concatenated = TryFoldTwoConcatConsts(leftConst, rightConst);
                if (concatenated != null)
                {
                    return(factory.StringLiteral(concatenated));
                }
            }

            // one or another is null.
            if (IsNullOrEmptyStringConstant(loweredLeft))
            {
                if (IsNullOrEmptyStringConstant(loweredRight))
                {
                    return(factory.Literal((string)null + (string)null));
                }

                return(RewriteStringConcatenationOneExpr(syntax, loweredRight));
            }
            else if (IsNullOrEmptyStringConstant(loweredRight))
            {
                return(RewriteStringConcatenationOneExpr(syntax, loweredLeft));
            }

            return(null);
        }
Example #57
0
        /// <summary>
        /// The rewrites are as follows: suppose the operand x is a variable of type X. The
        /// chosen increment/decrement operator is modelled as a static method on a type T,
        /// which takes a value of type T and returns the result of incrementing or decrementing
        /// that value.
        ///
        /// x++
        ///     X temp = x
        ///     x = (X)(T.Increment((T)temp))
        ///     return temp
        /// x--
        ///     X temp = x
        ///     x = (X)(T.Decrement((T)temp))
        ///     return temp
        /// ++x
        ///     X temp = (X)(T.Increment((T)x))
        ///     x = temp
        ///     return temp
        /// --x
        ///     X temp = (X)(T.Decrement((T)x))
        ///     x = temp
        ///     return temp
        ///
        /// Note:
        /// Dev11 implements dynamic prefix operators incorrectly.
        ///
        ///   result = ++x.P  is emitted as  result = SetMember{"P"}(t, UnaryOperation{Inc}(GetMember{"P"}(x)))
        ///
        /// The difference is that Dev11 relies on SetMember returning the same value as it was given as an argument.
        /// Failing to do so changes the semantics of ++/-- operator which is undesirable. We emit the same pattern for
        /// both dynamic and static operators.
        ///
        /// For example, we might have a class X with user-defined implicit conversions
        /// to and from short, but no user-defined increment or decrement operators. We
        /// would bind x++ as "X temp = x; x = (X)(short)((int)(short)temp + 1); return temp;"
        /// </summary>
        /// <param name="node">The unary operator expression representing the increment/decrement.</param>
        /// <returns>A bound sequence that uses a temp to achieve the correct side effects and return value.</returns>
        public override BoundNode VisitIncrementOperator(BoundIncrementOperator node)
        {
            bool isPrefix  = IsPrefix(node);
            bool isDynamic = node.OperatorKind.IsDynamic();
            bool isChecked = node.OperatorKind.IsChecked();

            ArrayBuilder <LocalSymbol> tempSymbols = ArrayBuilder <LocalSymbol> .GetInstance();

            ArrayBuilder <BoundExpression> tempInitializers = ArrayBuilder <BoundExpression> .GetInstance();

            SyntaxNode syntax = node.Syntax;

            // This will be filled in with the LHS that uses temporaries to prevent
            // double-evaluation of side effects.
            BoundExpression transformedLHS = TransformCompoundAssignmentLHS(node.Operand, tempInitializers, tempSymbols, isDynamic);
            TypeSymbol      operandType    = transformedLHS.Type; //type of the variable being incremented

            Debug.Assert(operandType == node.Type);

            LocalSymbol tempSymbol = _factory.SynthesizedLocal(operandType);

            tempSymbols.Add(tempSymbol);
            // Not adding an entry to tempInitializers because the initial value depends on the case.

            BoundExpression boundTemp = new BoundLocal(
                syntax: syntax,
                localSymbol: tempSymbol,
                constantValueOpt: null,
                type: operandType);

            // prefix:  (X)(T.Increment((T)operand)))
            // postfix: (X)(T.Increment((T)temp)))
            var newValue = MakeIncrementOperator(node, rewrittenValueToIncrement: (isPrefix ? MakeRValue(transformedLHS) : boundTemp));

            // there are two strategies for completing the rewrite.
            // The reason is that indirect assignments read the target of the assignment before evaluating
            // of the assignment value and that may cause reads of operand and boundTemp to cross which
            // in turn would require one of them to be a real temp (not a stack local)
            //
            // To avoid this issue, in a case of ByRef operand, we perform a "nested sequence" rewrite.
            //
            // Ex:
            //    Seq{..., operand = Seq{temp = operand + 1, temp}, ...}
            //  instead of
            //    Seq{.... temp = operand + 1, operand = temp, ...}
            //
            // Such rewrite will nest reads of boundTemp relative to reads of operand so both
            // operand and boundTemp could be optimizable (subject to all other conditions of course).
            //
            // In a case of the non-byref operand we use a single-sequence strategy as it results in shorter
            // overall life time of temps and as such more appropriate. (problem of crossed reads does not affect that case)
            //
            if (IsIndirectOrInstanceField(transformedLHS))
            {
                return(RewriteWithRefOperand(isPrefix, isChecked, tempSymbols, tempInitializers, syntax, transformedLHS, operandType, boundTemp, newValue));
            }
            else
            {
                return(RewriteWithNotRefOperand(isPrefix, isChecked, tempSymbols, tempInitializers, syntax, transformedLHS, operandType, boundTemp, newValue));
            }
        }
Example #58
0
        // Build Decimal.op_Increment((Decimal)operand) or Decimal.op_Decrement((Decimal)operand)
        private BoundExpression MakeDecimalIncDecOperator(SyntaxNode syntax, BinaryOperatorKind oper, BoundExpression operand)
        {
            Debug.Assert(operand.Type.SpecialType == SpecialType.System_Decimal);
            MethodSymbol method = GetDecimalIncDecOperator(oper);

            return(BoundCall.Synthesized(syntax, null, method, operand));
        }
        private BoundExpression RewriteStringConcatenationThreeExprs(CSharpSyntaxNode syntax, BoundExpression loweredFirst, BoundExpression loweredSecond, BoundExpression loweredThird)
        {
            SpecialMember member = (loweredFirst.Type.SpecialType == SpecialType.System_String &&
                                    loweredSecond.Type.SpecialType == SpecialType.System_String &&
                                    loweredThird.Type.SpecialType == SpecialType.System_String) ?
                                   SpecialMember.System_String__ConcatStringStringString :
                                   SpecialMember.System_String__ConcatObjectObjectObject;

            var method = GetSpecialTypeMethod(syntax, member);

            Debug.Assert((object)method != null);

            return(BoundCall.Synthesized(syntax, null, method, ImmutableArray.Create(loweredFirst, loweredSecond, loweredThird)));
        }
        /// <summary>
        /// Most of the above optimizations are not applicable in expression trees as the operator
        /// must stay a binary operator. We cannot do much beyond constant folding which is done in binder.
        /// </summary>
        private BoundExpression RewriteStringConcatInExpressionLambda(CSharpSyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type)
        {
            SpecialMember member = (operatorKind == BinaryOperatorKind.StringConcatenation) ?
                                   SpecialMember.System_String__ConcatStringString :
                                   SpecialMember.System_String__ConcatObjectObject;

            var method = GetSpecialTypeMethod(syntax, member);

            Debug.Assert((object)method != null);

            return(new BoundBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, default(ConstantValue), method, default(LookupResultKind), type));
        }