/// <summary>
        /// Applies the conversions.
        /// Adds any new locals to the temps and any new expressions to be evaluated to the stores.
        /// </summary>
        private void ApplyConversions(BoundDeconstructionAssignmentOperator node, ArrayBuilder<LocalSymbol> temps, ArrayBuilder<BoundExpression> stores, ArrayBuilder<BoundValuePlaceholderBase> placeholders)
        {
            int numConversions = node.ConversionSteps.Length;
            var conversionLocals = ArrayBuilder<BoundExpression>.GetInstance();

            foreach (var conversionInfo in node.ConversionSteps)
            {
                // lower the conversions and assignments to locals
                var localSymbol = new SynthesizedLocal(_factory.CurrentMethod, conversionInfo.OutputPlaceholder.Type, SynthesizedLocalKind.LoweringTemp);
                var localBound = new BoundLocal(node.Syntax,
                                               localSymbol,
                                               null,
                                               conversionInfo.OutputPlaceholder.Type)
                { WasCompilerGenerated = true };

                temps.Add(localSymbol);
                conversionLocals.Add(localBound);

                AddPlaceholderReplacement(conversionInfo.OutputPlaceholder, localBound);
                placeholders.Add(conversionInfo.OutputPlaceholder);

                var conversion = VisitExpression(conversionInfo.Assignment);

                stores.Add(conversion);
            }
        }
Exemple #2
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);
            }
        }
Exemple #3
0
        /// <summary>
        /// Takes an expression and returns the bound local expression "temp" 
        /// and the bound assignment expression "temp = expr".
        /// </summary>
        /// <param name="argument"></param>
        /// <param name="refKind"></param>
        /// <param name="containingMethod"></param>
        /// <returns></returns>
        public static Pair<BoundAssignmentOperator, BoundLocal> StoreToTemp(BoundExpression argument, RefKind refKind, MethodSymbol containingMethod)
        {
            var syntax = argument.Syntax;
            var type = argument.Type;
            var local = new BoundLocal(
                    syntax,
                    // TODO: the following temp local symbol should have its ContainingSymbol set.
                    new TempLocalSymbol(type, refKind, containingMethod),
                    null,
                    type);

            var store = new BoundAssignmentOperator(
                syntax,
                local,
                argument,
                refKind,
                type);

            return Pair.Make(store, local);
        }
        public override BoundNode VisitDeclarationExpression(BoundDeclarationExpression node)
        {
            var local = new BoundLocal(node.Syntax, node.LocalSymbol, null, node.LocalSymbol.Type);

            if (node.InitializerOpt == null)
            {
                return local;
            }

            return new BoundSequence(node.Syntax, 
                                     ImmutableArray<LocalSymbol>.Empty, 
                                     ImmutableArray.Create<BoundExpression>(new BoundAssignmentOperator(
                                                                                                        node.Syntax,
                                                                                                        new BoundLocal(
                                                                                                            local.Syntax,
                                                                                                            local.LocalSymbol,
                                                                                                            null,
                                                                                                            local.Type
                                                                                                        ),
                                                                                                        VisitExpression(node.InitializerOpt),
                                                                                                        local.Type)),
                                     local,
                                     local.Type);
        }
Exemple #5
0
        private DecisionTree AddByType(DecisionTree.ByType byType, TypeSymbol type, DecisionMaker makeDecision)
        {
            if (byType.Default != null)
            {
                try
                {
                    return AddByType(byType.Default, type, makeDecision);
                }
                finally
                {
                    if (byType.Default.MatchIsComplete)
                    {
                        byType.MatchIsComplete = true;
                    }
                }
            }
            foreach (var kvp in byType.TypeAndDecision)
            {
                var MatchedType = kvp.Key;
                var Decision = kvp.Value;
                // See if matching Type matches this value
                switch (ExpressionOfTypeMatchesPatternType(type, MatchedType, ref _useSiteDiagnostics))
                {
                    case true:
                        if (Decision.MatchIsComplete)
                        {
                            return null;
                        }

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

            var localSymbol = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatchingTemp, Syntax, false, RefKind.None);
            var expression = new BoundLocal(Syntax, localSymbol, null, type);
            var result = makeDecision(expression, type);
            Debug.Assert(result.Temp == null);
            result.Temp = localSymbol;
            byType.TypeAndDecision.Add(new KeyValuePair<TypeSymbol, DecisionTree>(type, result));
            if (ExpressionOfTypeMatchesPatternType(byType.Type, type, ref _useSiteDiagnostics) == true &&
                result.MatchIsComplete &&
                byType.WhenNull?.MatchIsComplete == true)
            {
                byType.MatchIsComplete = true;
            }

            return result;
        }
            public override BoundNode VisitLocal(BoundLocal node)
            {
                if (lambdaLevel != 0 && locals.ContainsKey(node.LocalSymbol))
                {
                    captured = true;
                }

                return null;
            }
 protected override void VisitLvalue(BoundLocal node)
 {
     VisitLocal(node);
 }
Exemple #8
0
        /// <summary>
        /// Lower a foreach loop that will enumerate a collection using an enumerator.
        ///
        /// E e = ((C)(x)).GetEnumerator()
        /// try {
        ///     while (e.MoveNext()) {
        ///         V v = (V)(T)e.Current;
        ///         // body
        ///     }
        /// }
        /// finally {
        ///     // clean up e
        /// }
        /// </summary>
        private BoundStatement RewriteEnumeratorForEachStatement(BoundForEachStatement node)
        {
            ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax;

            ForEachEnumeratorInfo enumeratorInfo = node.EnumeratorInfoOpt;

            Debug.Assert(enumeratorInfo != null);

            BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node);
            BoundExpression rewrittenExpression  = (BoundExpression)Visit(collectionExpression);
            BoundStatement  rewrittenBody        = (BoundStatement)Visit(node.Body);

            TypeSymbol enumeratorType = enumeratorInfo.GetEnumeratorMethod.ReturnType;
            TypeSymbol elementType    = enumeratorInfo.ElementType;

            // E e
            LocalSymbol enumeratorVar = factory.SynthesizedLocal(enumeratorType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachEnumerator);

            // Reference to e.
            BoundLocal boundEnumeratorVar = MakeBoundLocal(forEachSyntax, enumeratorVar, enumeratorType);

            // ((C)(x)).GetEnumerator() or (x).GetEnumerator();
            BoundExpression enumeratorVarInitValue = SynthesizeCall(forEachSyntax, rewrittenExpression, enumeratorInfo.GetEnumeratorMethod, enumeratorInfo.CollectionConversion, enumeratorInfo.CollectionType);

            // E e = ((C)(x)).GetEnumerator();
            BoundStatement enumeratorVarDecl = MakeLocalDeclaration(forEachSyntax, enumeratorVar, enumeratorVarInitValue);

            AddForEachExpressionSequencePoint(forEachSyntax, ref enumeratorVarDecl);

            // V v
            LocalSymbol iterationVar = node.IterationVariable;

            //(V)(T)e.Current
            BoundExpression iterationVarAssignValue = MakeConversion(
                syntax: forEachSyntax,
                rewrittenOperand: MakeConversion(
                    syntax: forEachSyntax,
                    rewrittenOperand: BoundCall.Synthesized(
                        syntax: forEachSyntax,
                        receiverOpt: boundEnumeratorVar,
                        method: enumeratorInfo.CurrentPropertyGetter),
                    conversion: enumeratorInfo.CurrentConversion,
                    rewrittenType: elementType,
                    @checked: node.Checked),
                conversion: node.ElementConversion,
                rewrittenType: iterationVar.Type,
                @checked: node.Checked);

            // V v = (V)(T)e.Current;
            BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarAssignValue);

            AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl);

            // while (e.MoveNext()) {
            //     V v = (V)(T)e.Current;
            //     /* node.Body */
            // }
            BoundStatement whileLoop = RewriteWhileStatement(
                syntax: forEachSyntax,
                rewrittenCondition: BoundCall.Synthesized(
                    syntax: forEachSyntax,
                    receiverOpt: boundEnumeratorVar,
                    method: enumeratorInfo.MoveNextMethod),
                conditionSequencePointSpan: forEachSyntax.InKeyword.Span,
                rewrittenBody: new BoundBlock(rewrittenBody.Syntax,
                                              statements: ImmutableArray.Create <BoundStatement>(iterationVarDecl, rewrittenBody),
                                              locals: ImmutableArray.Create <LocalSymbol>(iterationVar)),
                breakLabel: node.BreakLabel,
                continueLabel: node.ContinueLabel,
                hasErrors: false);

            BoundStatement result;

            MethodSymbol disposeMethod;

            if (enumeratorInfo.NeedsDisposeMethod && TryGetSpecialTypeMember(forEachSyntax, SpecialMember.System_IDisposable__Dispose, out disposeMethod))
            {
                Binder.ReportDiagnosticsIfObsolete(diagnostics, disposeMethod, forEachSyntax,
                                                   hasBaseReceiver: false,
                                                   containingMember: this.factory.CurrentMethod,
                                                   containingType: this.factory.CurrentClass,
                                                   location: enumeratorInfo.Location);

                BoundBlock finallyBlockOpt;
                var        idisposableTypeSymbol = disposeMethod.ContainingType;
                var        conversions           = new TypeConversions(this.factory.CurrentMethod.ContainingAssembly.CorLibrary);

                HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                var isImplicit = conversions.ClassifyImplicitConversion(enumeratorType, idisposableTypeSymbol, ref useSiteDiagnostics).IsImplicit;
                diagnostics.Add(forEachSyntax, useSiteDiagnostics);

                if (isImplicit)
                {
                    Debug.Assert(enumeratorInfo.NeedsDisposeMethod);

                    Conversion receiverConversion = enumeratorType.IsStructType() ?
                                                    Conversion.Boxing :
                                                    Conversion.ImplicitReference;

                    // ((IDisposable)e).Dispose(); or e.Dispose();
                    BoundStatement disposeCall = new BoundExpressionStatement(forEachSyntax,
                                                                              expression: SynthesizeCall(forEachSyntax, boundEnumeratorVar, disposeMethod, receiverConversion, idisposableTypeSymbol));

                    BoundStatement disposeStmt;
                    if (enumeratorType.IsValueType)
                    {
                        // No way for the struct to be nullable and disposable.
                        Debug.Assert(((TypeSymbol)enumeratorType.OriginalDefinition).SpecialType != SpecialType.System_Nullable_T);

                        // For non-nullable structs, no null check is required.
                        disposeStmt = disposeCall;
                    }
                    else
                    {
                        // NB: cast to object missing from spec.  Needed to ignore user-defined operators and box type parameters.
                        // if ((object)e != null) ((IDisposable)e).Dispose();
                        disposeStmt = RewriteIfStatement(
                            syntax: forEachSyntax,
                            rewrittenCondition: new BoundBinaryOperator(forEachSyntax,
                                                                        operatorKind: BinaryOperatorKind.NotEqual,
                                                                        left: MakeConversion(
                                                                            syntax: forEachSyntax,
                                                                            rewrittenOperand: boundEnumeratorVar,
                                                                            conversion: enumeratorInfo.EnumeratorConversion,
                                                                            rewrittenType: this.compilation.GetSpecialType(SpecialType.System_Object),
                                                                            @checked: false),
                                                                        right: MakeLiteral(forEachSyntax,
                                                                                           constantValue: ConstantValue.Null,
                                                                                           type: null),
                                                                        constantValueOpt: null,
                                                                        methodOpt: null,
                                                                        resultKind: LookupResultKind.Viable,
                                                                        type: this.compilation.GetSpecialType(SpecialType.System_Boolean)),
                            rewrittenConsequence: disposeCall,
                            rewrittenAlternativeOpt: null,
                            hasErrors: false);
                    }

                    finallyBlockOpt = new BoundBlock(forEachSyntax,
                                                     locals: ImmutableArray <LocalSymbol> .Empty,
                                                     statements: ImmutableArray.Create <BoundStatement>(disposeStmt));
                }
                else
                {
                    Debug.Assert(!enumeratorType.IsSealed);

                    // IDisposable d
                    LocalSymbol disposableVar = factory.SynthesizedLocal(idisposableTypeSymbol);

                    // Reference to d.
                    BoundLocal boundDisposableVar = MakeBoundLocal(forEachSyntax, disposableVar, idisposableTypeSymbol);

                    BoundTypeExpression boundIDisposableTypeExpr = new BoundTypeExpression(forEachSyntax,
                                                                                           aliasOpt: null,
                                                                                           type: idisposableTypeSymbol);

                    // e as IDisposable
                    BoundExpression disposableVarInitValue = new BoundAsOperator(forEachSyntax,
                                                                                 operand: boundEnumeratorVar,
                                                                                 targetType: boundIDisposableTypeExpr,
                                                                                 conversion: Conversion.ExplicitReference, // Explicit so the emitter won't optimize it away.
                                                                                 type: idisposableTypeSymbol);

                    // IDisposable d = e as IDisposable;
                    BoundStatement disposableVarDecl = MakeLocalDeclaration(forEachSyntax, disposableVar, disposableVarInitValue);

                    // if (d != null) d.Dispose();
                    BoundStatement ifStmt = RewriteIfStatement(
                        syntax: forEachSyntax,
                        rewrittenCondition: new BoundBinaryOperator(forEachSyntax,
                                                                    operatorKind: BinaryOperatorKind.NotEqual, // reference equality
                                                                    left: boundDisposableVar,
                                                                    right: MakeLiteral(forEachSyntax,
                                                                                       constantValue: ConstantValue.Null,
                                                                                       type: null),
                                                                    constantValueOpt: null,
                                                                    methodOpt: null,
                                                                    resultKind: LookupResultKind.Viable,
                                                                    type: this.compilation.GetSpecialType(SpecialType.System_Boolean)),
                        rewrittenConsequence: new BoundExpressionStatement(forEachSyntax,
                                                                           expression: BoundCall.Synthesized(
                                                                               syntax: forEachSyntax,
                                                                               receiverOpt: boundDisposableVar,
                                                                               method: disposeMethod)),
                        rewrittenAlternativeOpt: null,
                        hasErrors: false);

                    // IDisposable d = e as IDisposable;
                    // if (d != null) d.Dispose();
                    finallyBlockOpt = new BoundBlock(forEachSyntax,
                                                     locals: ImmutableArray.Create <LocalSymbol>(disposableVar),
                                                     statements: ImmutableArray.Create <BoundStatement>(disposableVarDecl, ifStmt));
                }

                // try {
                //     while (e.MoveNext()) {
                //         V v = (V)(T)e.Current;
                //         /* loop body */
                //     }
                // }
                // finally {
                //     /* dispose of e */
                // }
                BoundStatement tryFinally = new BoundTryStatement(forEachSyntax,
                                                                  tryBlock: new BoundBlock(forEachSyntax,
                                                                                           locals: ImmutableArray <LocalSymbol> .Empty,
                                                                                           statements: ImmutableArray.Create <BoundStatement>(whileLoop)),
                                                                  catchBlocks: ImmutableArray <BoundCatchBlock> .Empty,
                                                                  finallyBlockOpt: finallyBlockOpt);

                // E e = ((C)(x)).GetEnumerator();
                // try {
                //     /* as above */
                result = new BoundBlock(
                    syntax: forEachSyntax,
                    locals: ImmutableArray.Create(enumeratorVar),
                    statements: ImmutableArray.Create <BoundStatement>(enumeratorVarDecl, tryFinally));
            }
            else
            {
                // E e = ((C)(x)).GetEnumerator();
                // while (e.MoveNext()) {
                //     V v = (V)(T)e.Current;
                //     /* loop body */
                // }
                result = new BoundBlock(
                    syntax: forEachSyntax,
                    locals: ImmutableArray.Create(enumeratorVar),
                    statements: ImmutableArray.Create <BoundStatement>(enumeratorVarDecl, whileLoop));
            }

            AddForEachKeywordSequencePoint(forEachSyntax, ref result);

            return(result);
        }
        /// <summary>
        /// Produces a Try/Finally if frame has a handler (otherwise a regular block).
        /// Handler goes into the Finally.
        /// If there are nested frames, they are emitted into the try block.
        /// This way the handler for the current frame is guaranteed to run even if 
        /// nested handlers throw exceptions.
        /// 
        /// {
        ///     switch(state)
        ///     {
        ///         case state1:
        ///         case state2:
        ///         case state3:
        ///         case state4:
        ///             try
        ///             {
        ///                 switch(state)
        ///                 {
        ///                     case state3:
        ///                     case state4:
        ///                         try
        ///                         {
        ///                             ... more nested state dispatches if any ....
        ///                         }
        ///                         finally
        ///                         {
        ///                             // handler for a try where state3 and state4 can be observed
        ///                             handler_3_4()
        ///                         }
        ///                         break;
        ///                  }
        ///             }
        ///             finally
        ///             {
        ///                 // handler for a try where state1 and state2 can be observed
        ///                 handler_1_2()
        ///             }
        ///             break;
        ///             
        ///         case state5:
        ///             ... another dispatch of nested states to their finally blocks ...
        ///             break;
        ///     }
        /// }
        /// 
        /// </summary>
        private BoundStatement EmitFinallyFrame(IteratorFinallyFrame frame, BoundLocal state)
        {
            BoundStatement body = null;
            if (frame.knownStates != null)
            {
                var breakLabel = F.GenerateLabel("break");
                var sections = from ft in frame.knownStates
                               group ft.Key by ft.Value into g
                               select F.SwitchSection(
                                    new List<int>(g),
                                    EmitFinallyFrame(g.Key, state),
                                    F.Goto(breakLabel));

                body = F.Block(
                    F.Switch(state, sections),
                    F.Label(breakLabel));
            }

            if (!frame.IsRoot())
            {
                var tryBlock = body != null ? F.Block(body) : F.Block();
                body = F.Try(
                    tryBlock,
                    ImmutableArray<BoundCatchBlock>.Empty,
                    F.Block(F.ExpressionStatement(F.Call(F.This(), frame.handler))));
            }

            Debug.Assert(body != null, "we should have either sub-dispatch or a handler");
            return body;
        }
 // Used to increment integer index into an array or string.
 private BoundStatement MakePositionIncrement(CSharpSyntaxNode syntax, BoundLocal boundPositionVar, TypeSymbol intType)
 {
     // A normal for-loop would have a sequence point on the increment.  We don't want that since the code is synthesized,
     // but we add a hidden sequence point to avoid disrupting the stepping experience.
     return new BoundSequencePoint(null,
         statementOpt: new BoundExpressionStatement(syntax,
             expression: new BoundAssignmentOperator(syntax,
                 left: boundPositionVar,
                 right: new BoundBinaryOperator(syntax,
                     operatorKind: BinaryOperatorKind.IntAddition, // unchecked, never overflows since array/string index can't be >= Int32.MaxValue
                     left: boundPositionVar,
                     right: MakeLiteral(syntax,
                         constantValue: ConstantValue.Create(1),
                         type: intType),
                     constantValueOpt: null,
                     methodOpt: null,
                     resultKind: LookupResultKind.Viable,
                     type: intType),
                 type: intType)));
 }
        /// <summary>
        /// Generate a thread-safe accessor for a regular field-like event.
        /// 
        /// DelegateType tmp0 = _event; //backing field
        /// DelegateType tmp1;
        /// DelegateType tmp2;
        /// do {
        ///     tmp1 = tmp0;
        ///     tmp2 = (DelegateType)Delegate.Combine(tmp1, value); //Remove for -=
        ///     tmp0 = Interlocked.CompareExchange&lt;DelegateType&gt;(ref _event, tmp2, tmp1);
        /// } while ((object)tmp0 != (object)tmp1);
        /// </summary>
        internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, DiagnosticBag diagnostics)
        {
            CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode;

            TypeSymbol delegateType = eventSymbol.Type;
            MethodSymbol accessor = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod;
            ParameterSymbol thisParameter = accessor.ThisParameter;

            TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean);
            MethodSymbol updateMethod = (MethodSymbol)compilation.GetSpecialTypeMember(isAddMethod ? SpecialMember.System_Delegate__Combine : SpecialMember.System_Delegate__Remove);
            MethodSymbol compareExchangeMethod = GetConstructedCompareExchangeMethod(delegateType, compilation, accessor.Locations[0], diagnostics);

            if ((object)compareExchangeMethod == null)
            {
                return new BoundBlock(syntax,
                    locals: ImmutableArray<LocalSymbol>.Empty,
                    statements: ImmutableArray.Create<BoundStatement>(
                        new BoundReturnStatement(syntax,
                            expressionOpt: null)
                { WasCompilerGenerated = true }))
                { WasCompilerGenerated = true };
            }

            GeneratedLabelSymbol loopLabel = new GeneratedLabelSymbol("loop");

            const int numTemps = 3;

            LocalSymbol[] tmps = new LocalSymbol[numTemps];
            BoundLocal[] boundTmps = new BoundLocal[numTemps];

            for (int i = 0; i < numTemps; i++)
            {
                tmps[i] = new SynthesizedLocal(accessor, delegateType, SynthesizedLocalKind.LoweringTemp);
                boundTmps[i] = new BoundLocal(syntax, tmps[i], null, delegateType);
            }

            BoundThisReference fieldReceiver = eventSymbol.IsStatic ?
                null :
                new BoundThisReference(syntax, thisParameter.Type) { WasCompilerGenerated = true };

            BoundFieldAccess boundBackingField = new BoundFieldAccess(syntax,
                receiver: fieldReceiver,
                fieldSymbol: eventSymbol.AssociatedField,
                constantValueOpt: null)
            { WasCompilerGenerated = true };

            BoundParameter boundParameter = new BoundParameter(syntax,
                parameterSymbol: accessor.Parameters[0])
            { WasCompilerGenerated = true };

            // tmp0 = _event;
            BoundStatement tmp0Init = new BoundExpressionStatement(syntax,
                expression: new BoundAssignmentOperator(syntax,
                    left: boundTmps[0],
                    right: boundBackingField,
                    type: delegateType)
            { WasCompilerGenerated = true })
            { WasCompilerGenerated = true };

            // LOOP:
            BoundStatement loopStart = new BoundLabelStatement(syntax,
                label: loopLabel)
            { WasCompilerGenerated = true };

            // tmp1 = tmp0;
            BoundStatement tmp1Update = new BoundExpressionStatement(syntax,
                expression: new BoundAssignmentOperator(syntax,
                    left: boundTmps[1],
                    right: boundTmps[0],
                    type: delegateType)
            { WasCompilerGenerated = true })
            { WasCompilerGenerated = true };

            // (DelegateType)Delegate.Combine(tmp1, value)
            BoundExpression delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax,
                operand: BoundCall.Synthesized(syntax,
                    receiverOpt: null,
                    method: updateMethod,
                    arguments: ImmutableArray.Create<BoundExpression>(boundTmps[1], boundParameter)),
                kind: ConversionKind.ExplicitReference,
                type: delegateType);

            // tmp2 = (DelegateType)Delegate.Combine(tmp1, value);
            BoundStatement tmp2Update = new BoundExpressionStatement(syntax,
                expression: new BoundAssignmentOperator(syntax,
                    left: boundTmps[2],
                    right: delegateUpdate,
                    type: delegateType)
            { WasCompilerGenerated = true })
            { WasCompilerGenerated = true };

            // Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1)
            BoundExpression compareExchange = BoundCall.Synthesized(syntax,
                receiverOpt: null,
                method: compareExchangeMethod,
                arguments: ImmutableArray.Create<BoundExpression>(boundBackingField, boundTmps[2], boundTmps[1]));

            // tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1);
            BoundStatement tmp0Update = new BoundExpressionStatement(syntax,
                expression: new BoundAssignmentOperator(syntax,
                    left: boundTmps[0],
                    right: compareExchange,
                    type: delegateType)
            { WasCompilerGenerated = true })
            { WasCompilerGenerated = true };

            // tmp0 == tmp1 // i.e. exit when they are equal, jump to start otherwise
            BoundExpression loopExitCondition = new BoundBinaryOperator(syntax,
                operatorKind: BinaryOperatorKind.ObjectEqual,
                left: boundTmps[0],
                right: boundTmps[1],
                constantValueOpt: null,
                methodOpt: null,
                resultKind: LookupResultKind.Viable,
                type: boolType)
            { WasCompilerGenerated = true };
            
            // branchfalse (tmp0 == tmp1) LOOP
            BoundStatement loopEnd = new BoundConditionalGoto(syntax,
                condition: loopExitCondition,
                jumpIfTrue: false,
                label: loopLabel)
            { WasCompilerGenerated = true };

            BoundStatement @return = new BoundReturnStatement(syntax,
                expressionOpt: null)
            { WasCompilerGenerated = true };

            return new BoundBlock(syntax,
                locals: tmps.AsImmutable(),
                statements: ImmutableArray.Create<BoundStatement>(
                    tmp0Init,
                    loopStart,
                    tmp1Update,
                    tmp2Update,
                    tmp0Update,
                    loopEnd,
                    @return))
            { WasCompilerGenerated = true };

        }
 protected override void VisitLvalue(BoundLocal node)
 {
     VisitLocal(node);
 }
Exemple #13
0
        /// <summary>
        /// Generate a thread-safe accessor for a regular field-like event.
        ///
        /// DelegateType tmp0 = _event; //backing field
        /// DelegateType tmp1;
        /// DelegateType tmp2;
        /// do {
        ///     tmp1 = tmp0;
        ///     tmp2 = (DelegateType)Delegate.Combine(tmp1, value); //Remove for -=
        ///     tmp0 = Interlocked.CompareExchange&lt;DelegateType&gt;(ref _event, tmp2, tmp1);
        /// } while ((object)tmp0 != (object)tmp1);
        /// </summary>
        internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, DiagnosticBag diagnostics)
        {
            CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode;

            TypeSymbol      delegateType  = eventSymbol.Type;
            MethodSymbol    accessor      = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod;
            ParameterSymbol thisParameter = accessor.ThisParameter;

            TypeSymbol   boolType              = compilation.GetSpecialType(SpecialType.System_Boolean);
            MethodSymbol updateMethod          = (MethodSymbol)compilation.GetSpecialTypeMember(isAddMethod ? SpecialMember.System_Delegate__Combine : SpecialMember.System_Delegate__Remove);
            MethodSymbol compareExchangeMethod = GetConstructedCompareExchangeMethod(delegateType, compilation, accessor.Locations[0], diagnostics);

            if ((object)compareExchangeMethod == null)
            {
                return(new BoundBlock(syntax,
                                      locals: ImmutableArray <LocalSymbol> .Empty,
                                      statements: ImmutableArray.Create <BoundStatement>(
                                          new BoundReturnStatement(syntax,
                                                                   expressionOpt: null)
                {
                    WasCompilerGenerated = true
                }))
                {
                    WasCompilerGenerated = true
                });
            }

            GeneratedLabelSymbol loopLabel = new GeneratedLabelSymbol("loop");

            const int numTemps = 3;

            LocalSymbol[] tmps      = new LocalSymbol[numTemps];
            BoundLocal[]  boundTmps = new BoundLocal[numTemps];

            for (int i = 0; i < numTemps; i++)
            {
                tmps[i]      = new SynthesizedLocal(accessor, delegateType, SynthesizedLocalKind.LoweringTemp);
                boundTmps[i] = new BoundLocal(syntax, tmps[i], null, delegateType);
            }

            BoundThisReference fieldReceiver = eventSymbol.IsStatic ?
                                               null :
                                               new BoundThisReference(syntax, thisParameter.Type)
            {
                WasCompilerGenerated = true
            };

            BoundFieldAccess boundBackingField = new BoundFieldAccess(syntax,
                                                                      receiver: fieldReceiver,
                                                                      fieldSymbol: eventSymbol.AssociatedField,
                                                                      constantValueOpt: null)
            {
                WasCompilerGenerated = true
            };

            BoundParameter boundParameter = new BoundParameter(syntax,
                                                               parameterSymbol: accessor.Parameters[0])
            {
                WasCompilerGenerated = true
            };

            // tmp0 = _event;
            BoundStatement tmp0Init = new BoundExpressionStatement(syntax,
                                                                   expression: new BoundAssignmentOperator(syntax,
                                                                                                           left: boundTmps[0],
                                                                                                           right: boundBackingField,
                                                                                                           type: delegateType)
            {
                WasCompilerGenerated = true
            })
            {
                WasCompilerGenerated = true
            };

            // LOOP:
            BoundStatement loopStart = new BoundLabelStatement(syntax,
                                                               label: loopLabel)
            {
                WasCompilerGenerated = true
            };

            // tmp1 = tmp0;
            BoundStatement tmp1Update = new BoundExpressionStatement(syntax,
                                                                     expression: new BoundAssignmentOperator(syntax,
                                                                                                             left: boundTmps[1],
                                                                                                             right: boundTmps[0],
                                                                                                             type: delegateType)
            {
                WasCompilerGenerated = true
            })
            {
                WasCompilerGenerated = true
            };

            // (DelegateType)Delegate.Combine(tmp1, value)
            BoundExpression delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax,
                                                                                       operand: BoundCall.Synthesized(syntax,
                                                                                                                      receiverOpt: null,
                                                                                                                      method: updateMethod,
                                                                                                                      arguments: ImmutableArray.Create <BoundExpression>(boundTmps[1], boundParameter)),
                                                                                       kind: ConversionKind.ExplicitReference,
                                                                                       type: delegateType);

            // tmp2 = (DelegateType)Delegate.Combine(tmp1, value);
            BoundStatement tmp2Update = new BoundExpressionStatement(syntax,
                                                                     expression: new BoundAssignmentOperator(syntax,
                                                                                                             left: boundTmps[2],
                                                                                                             right: delegateUpdate,
                                                                                                             type: delegateType)
            {
                WasCompilerGenerated = true
            })
            {
                WasCompilerGenerated = true
            };

            // Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1)
            BoundExpression compareExchange = BoundCall.Synthesized(syntax,
                                                                    receiverOpt: null,
                                                                    method: compareExchangeMethod,
                                                                    arguments: ImmutableArray.Create <BoundExpression>(boundBackingField, boundTmps[2], boundTmps[1]));

            // tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1);
            BoundStatement tmp0Update = new BoundExpressionStatement(syntax,
                                                                     expression: new BoundAssignmentOperator(syntax,
                                                                                                             left: boundTmps[0],
                                                                                                             right: compareExchange,
                                                                                                             type: delegateType)
            {
                WasCompilerGenerated = true
            })
            {
                WasCompilerGenerated = true
            };

            // tmp0 == tmp1 // i.e. exit when they are equal, jump to start otherwise
            BoundExpression loopExitCondition = new BoundBinaryOperator(syntax,
                                                                        operatorKind: BinaryOperatorKind.ObjectEqual,
                                                                        left: boundTmps[0],
                                                                        right: boundTmps[1],
                                                                        constantValueOpt: null,
                                                                        methodOpt: null,
                                                                        resultKind: LookupResultKind.Viable,
                                                                        type: boolType)
            {
                WasCompilerGenerated = true
            };

            // branchfalse (tmp0 == tmp1) LOOP
            BoundStatement loopEnd = new BoundConditionalGoto(syntax,
                                                              condition: loopExitCondition,
                                                              jumpIfTrue: false,
                                                              label: loopLabel)
            {
                WasCompilerGenerated = true
            };

            BoundStatement @return = new BoundReturnStatement(syntax,
                                                              expressionOpt: null)
            {
                WasCompilerGenerated = true
            };

            return(new BoundBlock(syntax,
                                  locals: tmps.AsImmutable(),
                                  statements: ImmutableArray.Create <BoundStatement>(
                                      tmp0Init,
                                      loopStart,
                                      tmp1Update,
                                      tmp2Update,
                                      tmp0Update,
                                      loopEnd,
                                      @return))
            {
                WasCompilerGenerated = true
            });
        }
Exemple #14
0
        private BoundStatement RewriteUsingStatementTryFinally(CSharpSyntaxNode syntax, BoundBlock tryBlock, BoundLocal local)
        {
            // SPEC: When ResourceType is a non-nullable value type, the expansion is:
            // SPEC:
            // SPEC: {
            // SPEC:   ResourceType resource = expr;
            // SPEC:   try { statement; }
            // SPEC:   finally { ((IDisposable)resource).Dispose(); }
            // SPEC: }
            // SPEC:
            // SPEC: Otherwise, when Resource type is a nullable value type or
            // SPEC: a reference type other than dynamic, the expansion is:
            // SPEC:
            // SPEC: {
            // SPEC:   ResourceType resource = expr;
            // SPEC:   try { statement; }
            // SPEC:   finally { if (resource != null) ((IDisposable)resource).Dispose(); }
            // SPEC: }
            // SPEC:
            // SPEC: Otherwise, when ResourceType is dynamic, the expansion is:
            // SPEC: {
            // SPEC:   dynamic resource = expr;
            // SPEC:   IDisposable d = (IDisposable)resource;
            // SPEC:   try { statement; }
            // SPEC:   finally { if (d != null) d.Dispose(); }
            // SPEC: }
            // SPEC:
            // SPEC: An implementation is permitted to implement a given using statement
            // SPEC: differently -- for example, for performance reasons -- as long as the
            // SPEC: behavior is consistent with the above expansion.
            //
            // And we do in fact generate the code slightly differently than precisely how it is
            // described above.
            //
            // First: if the type is a non-nullable value type then we do not do the
            // *boxing conversion* from the resource to IDisposable. Rather, we do
            // a *constrained virtual call* that elides the boxing if possible.
            //
            // Now, you might wonder if that is legal; isn't skipping the boxing producing
            // an observable difference? Because if the value type is mutable and the Dispose
            // mutates it, then skipping the boxing means that we are now mutating the original,
            // not the boxed copy. But this is never observable. Either (1) we have "using(R r = x){}"
            // and r is out of scope after the finally, so it is not possible to observe the mutation,
            // or (2) we have "using(x) {}". But that has the semantics of "using(R temp = x){}",
            // so again, we are not mutating x to begin with; we're always mutating a copy. Therefore
            // it doesn't matter if we skip making *a copy of the copy*.
            //
            // This is what the dev10 compiler does, and we do so as well.
            //
            // Second: if the type is a nullable value type then we can similarly elide the boxing.
            // We can generate
            //
            // {
            //   ResourceType resource = expr;
            //   try { statement; }
            //   finally { if (resource.HasValue) resource.GetValueOrDefault().Dispose(); }
            // }
            //
            // Where again we do a constrained virtual call to Dispose, rather than boxing
            // the value to IDisposable.
            //
            // Note that this optimization is *not* what the native compiler does; in this case
            // the native compiler behavior is to test for HasValue, then *box* and convert
            // the boxed value to IDisposable. There's no need to do that.
            //
            // Third: if we have "using(x)" and x is dynamic then obviously we need not generate
            // "{ dynamic temp1 = x; IDisposable temp2 = (IDisposable) temp1; ... }". Rather, we elide
            // the completely unnecessary first temporary.

            BoundExpression disposedExpression;
            bool            isNullableValueType = local.Type.IsNullableType();

            if (isNullableValueType)
            {
                MethodSymbol getValueOrDefault = GetNullableMethod(syntax, local.Type, SpecialMember.System_Nullable_T_GetValueOrDefault);
                // local.GetValueOrDefault()
                disposedExpression = BoundCall.Synthesized(syntax, local, getValueOrDefault);
            }
            else
            {
                // local
                disposedExpression = local;
            }

            // local.Dispose()
            BoundExpression disposeCall;

            MethodSymbol disposeMethodSymbol;

            if (Binder.TryGetSpecialTypeMember(_compilation, SpecialMember.System_IDisposable__Dispose, syntax, _diagnostics, out disposeMethodSymbol))
            {
                disposeCall = BoundCall.Synthesized(syntax, disposedExpression, disposeMethodSymbol);
            }
            else
            {
                disposeCall = new BoundBadExpression(syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(disposedExpression), ErrorTypeSymbol.UnknownResultType);
            }

            // local.Dispose();
            BoundStatement disposeStatement = new BoundExpressionStatement(syntax, disposeCall);

            BoundExpression ifCondition;

            if (isNullableValueType)
            {
                // local.HasValue
                ifCondition = MakeNullableHasValue(syntax, local);
            }
            else if (local.Type.IsValueType)
            {
                ifCondition = null;
            }
            else
            {
                // local != null
                ifCondition = MakeNullCheck(syntax, local, BinaryOperatorKind.NotEqual);
            }

            BoundStatement finallyStatement;

            if (ifCondition == null)
            {
                // local.Dispose();
                finallyStatement = disposeStatement;
            }
            else
            {
                // if (local != null) local.Dispose();
                // or
                // if (local.HasValue) local.GetValueOrDefault().Dispose();
                finallyStatement = RewriteIfStatement(
                    syntax: syntax,
                    rewrittenCondition: ifCondition,
                    rewrittenConsequence: disposeStatement,
                    rewrittenAlternativeOpt: null,
                    hasErrors: false);
            }

            // try { ... } finally { if (local != null) local.Dispose(); }
            BoundStatement tryFinally = new BoundTryStatement(
                syntax: syntax,
                tryBlock: tryBlock,
                catchBlocks: ImmutableArray <BoundCatchBlock> .Empty,
                finallyBlockOpt: BoundBlock.SynthesizedNoLocals(syntax, finallyStatement));

            return(tryFinally);
        }
Exemple #15
0
 public override BoundNode VisitLocal(BoundLocal node)
 {
     CheckDeclared(node.LocalSymbol);
     base.VisitLocal(node);
     return(null);
 }
            /// <summary>
            /// Generates the `ValueTask&lt;bool> MoveNextAsync()` method.
            /// </summary>
            private void GenerateIAsyncEnumeratorImplementation_MoveNextAsync()
            {
                // Produce:
                //  if (State == StateMachineStates.FinishedStateMachine)
                //  {
                //      return default(ValueTask<bool>)
                //  }
                //  _valueOrEndPromise.Reset();
                //  var inst = this;
                //  _builder.Start(ref inst);
                //  return new ValueTask<bool>(this, _valueOrEndPromise.Version);

                NamedTypeSymbol IAsyncEnumeratorOfElementType =
                    F.WellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T)
                    .Construct(_currentField.Type.TypeSymbol);

                MethodSymbol IAsyncEnumerableOfElementType_MoveNextAsync =
                    F.WellKnownMethod(WellKnownMember.System_Collections_Generic_IAsyncEnumerator_T__MoveNextAsync)
                    .AsMember(IAsyncEnumeratorOfElementType);

                // The implementation doesn't depend on the method body of the iterator method.
                OpenMethodImplementation(IAsyncEnumerableOfElementType_MoveNextAsync, hasMethodBodyDependency: false);

                var ifFinished = F.If(
                    // if (State == StateMachineStates.FinishedStateMachine)
                    F.IntEqual(F.Field(F.This(), stateField), F.Literal(StateMachineStates.FinishedStateMachine)),
                    // return default(ValueTask<bool>)
                    thenClause: F.Return(F.Default(IAsyncEnumerableOfElementType_MoveNextAsync.ReturnType.TypeSymbol)));

                // _promiseOfValueOrEnd.Reset();
                BoundFieldAccess promiseField = F.Field(F.This(), _promiseOfValueOrEndField);
                var resetMethod = (MethodSymbol)F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_ManualResetValueTaskSourceLogic_T__Reset, isOptional: true)
                                  .SymbolAsMember((NamedTypeSymbol)_promiseOfValueOrEndField.Type.TypeSymbol);

                var callReset = F.ExpressionStatement(F.Call(promiseField, resetMethod));

                // _builder.Start(ref inst);
                Debug.Assert(!_asyncMethodBuilderMemberCollection.CheckGenericMethodConstraints);
                MethodSymbol             startMethod = _asyncMethodBuilderMemberCollection.Start.Construct(this.stateMachineType);
                LocalSymbol              instSymbol  = F.SynthesizedLocal(this.stateMachineType);
                BoundLocal               instLocal   = F.Local(instSymbol);
                BoundExpressionStatement startCall   = F.ExpressionStatement(
                    F.Call(
                        F.Field(F.This(), _builderField),
                        startMethod,
                        ImmutableArray.Create <BoundExpression>(instLocal)));

                MethodSymbol valueTask_ctor =
                    F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_ValueTask_T__ctor)
                    .AsMember((NamedTypeSymbol)IAsyncEnumerableOfElementType_MoveNextAsync.ReturnType.TypeSymbol);

                MethodSymbol promise_get_Version =
                    F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_ManualResetValueTaskSourceLogic_T__get_Version)
                    .AsMember((NamedTypeSymbol)_promiseOfValueOrEndField.Type.TypeSymbol);

                // return new ValueTask<bool>(this, _valueOrEndPromise.Version);
                var returnStatement = F.Return(F.New(valueTask_ctor, F.This(), F.Call(F.Field(F.This(), _promiseOfValueOrEndField), promise_get_Version)));

                F.CloseMethod(F.Block(
                                  ImmutableArray.Create(instSymbol),
                                  ifFinished,
                                  callReset,                         // _promiseOfValueOrEnd.Reset();
                                  F.Assignment(instLocal, F.This()), // var inst = this;
                                  startCall,                         // _builder.Start(ref inst);
                                  returnStatement));
            }
        private BoundStatement MakeSwitchStatementWithNullableExpression(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenExpression,
            ImmutableArray <BoundSwitchSection> rewrittenSections,
            LabelSymbol constantTargetOpt,
            ImmutableArray <LocalSymbol> locals,
            GeneratedLabelSymbol breakLabel,
            BoundSwitchStatement oldNode)
        {
            Debug.Assert(rewrittenExpression.Type.IsNullableType());

            var exprSyntax       = rewrittenExpression.Syntax;
            var exprNullableType = rewrittenExpression.Type;

            var statementBuilder = ArrayBuilder <BoundStatement> .GetInstance();

            // Rewrite the nullable expression to a temp as we might have a user defined conversion from source expression to switch governing type.
            // We can avoid generating the temp if the expression is a bound local.
            LocalSymbol tempLocal;

            if (rewrittenExpression.Kind != BoundKind.Local)
            {
                BoundAssignmentOperator assignmentToTemp;
                BoundLocal boundTemp      = _factory.StoreToTemp(rewrittenExpression, out assignmentToTemp);
                var        tempAssignment = new BoundExpressionStatement(exprSyntax, assignmentToTemp);
                statementBuilder.Add(tempAssignment);
                tempLocal           = boundTemp.LocalSymbol;
                rewrittenExpression = boundTemp;
            }
            else
            {
                tempLocal = null;
            }

            // Generate a BoundConditionalGoto with null check as the conditional expression and appropriate switch label as the target: null, default or exit label.
            BoundStatement condGotoNullValueTargetLabel = new BoundConditionalGoto(
                exprSyntax,
                condition: MakeNullCheck(exprSyntax, rewrittenExpression, BinaryOperatorKind.NullableNullEqual),
                jumpIfTrue: true,
                label: GetNullValueTargetSwitchLabel(rewrittenSections, breakLabel));

            // Rewrite the switch statement using nullable expression's underlying value as the switch expression.

            // rewrittenExpression.GetValueOrDefault()
            MethodSymbol getValueOrDefault     = GetNullableMethod(syntax, exprNullableType, SpecialMember.System_Nullable_T_GetValueOrDefault);
            BoundCall    callGetValueOrDefault = BoundCall.Synthesized(exprSyntax, rewrittenExpression, getValueOrDefault);

            rewrittenExpression = callGetValueOrDefault;

            // rewrite switch statement
            BoundStatement rewrittenSwitchStatement = MakeSwitchStatementWithNonNullableExpression(
                syntax,
                condGotoNullValueTargetLabel,
                rewrittenExpression,
                rewrittenSections,
                constantTargetOpt,
                locals,
                breakLabel,
                oldNode);

            statementBuilder.Add(rewrittenSwitchStatement);

            return(new BoundBlock(syntax, locals: (object)tempLocal == null ? ImmutableArray <LocalSymbol> .Empty : ImmutableArray.Create <LocalSymbol>(tempLocal), statements: statementBuilder.ToImmutableAndFree()));
        }
        private BoundExpression MakePropertyAssignment(
            SyntaxNode syntax,
            BoundExpression?rewrittenReceiver,
            PropertySymbol property,
            ImmutableArray <BoundExpression> rewrittenArguments,
            ImmutableArray <RefKind> argumentRefKindsOpt,
            bool expanded,
            ImmutableArray <int> argsToParamsOpt,
            BoundExpression rewrittenRight,
            TypeSymbol type,
            bool used)
        {
            // Rewrite property assignment into call to setter.
            var setMethod = property.GetOwnOrInheritedSetMethod();

            if ((object)setMethod == null)
            {
                var sourceProperty = (SourcePropertySymbol)property;
                Debug.Assert(sourceProperty.IsAutoProperty == true,
                             "only autoproperties can be assignable without having setters");

                var backingField = sourceProperty.BackingField;
                return(_factory.AssignmentExpression(
                           _factory.Field(rewrittenReceiver, backingField),
                           rewrittenRight));
            }

            // We have already lowered each argument, but we may need some additional rewriting for the arguments,
            // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc.
            ImmutableArray <LocalSymbol> argTemps;

            rewrittenArguments = MakeArguments(
                syntax,
                rewrittenArguments,
                property,
                setMethod,
                expanded,
                argsToParamsOpt,
                ref argumentRefKindsOpt,
                out argTemps,
                invokedAsExtensionMethod: false,
                enableCallerInfo: ThreeState.True);

            if (used)
            {
                // Save expression value to a temporary before calling the
                // setter, and restore the temporary after the setter, so the
                // assignment can be used as an embedded expression.
                TypeSymbol?exprType = rewrittenRight.Type;
                Debug.Assert(exprType is object);

                LocalSymbol rhsTemp = _factory.SynthesizedLocal(exprType);

                BoundExpression boundRhs = new BoundLocal(syntax, rhsTemp, null, exprType);

                BoundExpression rhsAssignment = new BoundAssignmentOperator(
                    syntax,
                    boundRhs,
                    rewrittenRight,
                    exprType);

                BoundExpression setterCall = BoundCall.Synthesized(
                    syntax,
                    rewrittenReceiver,
                    setMethod,
                    AppendToPossibleNull(rewrittenArguments, rhsAssignment));

                return(new BoundSequence(
                           syntax,
                           AppendToPossibleNull(argTemps, rhsTemp),
                           ImmutableArray.Create(setterCall),
                           boundRhs,
                           type));
            }
            else
            {
                BoundCall setterCall = BoundCall.Synthesized(
                    syntax,
                    rewrittenReceiver,
                    setMethod,
                    AppendToPossibleNull(rewrittenArguments, rewrittenRight));

                if (argTemps.IsDefaultOrEmpty)
                {
                    return(setterCall);
                }
                else
                {
                    return(new BoundSequence(
                               syntax,
                               argTemps,
                               ImmutableArray <BoundExpression> .Empty,
                               setterCall,
                               setMethod.ReturnType));
                }
            }
        }
 private void CheckOutVarDeclaration(BoundLocal node)
 {
     if (IsInside &&
         !node.WasCompilerGenerated && node.Syntax.Kind() == SyntaxKind.Argument &&
         ((ArgumentSyntax)node.Syntax).Identifier == node.LocalSymbol.IdentifierToken)
     {
         _variablesDeclared.Add(node.LocalSymbol);
     }
 }
        public override BoundNode VisitLocal(BoundLocal node)
        {
            var catchFrame = this.currentAwaitCatchFrame;
            if (catchFrame == null || !catchFrame.hoistedLocals.ContainsKey(node.LocalSymbol))
            {
                return base.VisitLocal(node);
            }

            var newLocal = catchFrame.hoistedLocals[node.LocalSymbol];
            return node.Update(newLocal, node.ConstantValueOpt, newLocal.Type);
        }
Exemple #21
0
        private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern value, DecisionMaker makeDecision)
        {
            if (byType.Default != null)
            {
                try
                {
                    return(AddByValue(byType.Default, value, makeDecision));
                }
                finally
                {
                    if (byType.Default.MatchIsComplete)
                    {
                        // This code may be unreachable due to https://github.com/dotnet/roslyn/issues/16878
                        byType.MatchIsComplete = true;
                    }
                }
            }

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

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

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

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

                    continue;

                case false:
                case null:
                    continue;
                }
            }

            DecisionTree forType = null;

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

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

            return(AddByValue(forType, value, makeDecision));
        }
Exemple #22
0
        /// <summary>
        /// Lower a foreach loop that will enumerate a single-dimensional array.
        ///
        /// A[] a = x;
        /// for (int p = 0; p &lt; a.Length; p = p + 1) {
        ///     V v = (V)a[p];
        ///     // body
        /// }
        /// </summary>
        /// <remarks>
        /// We will follow Dev10 in diverging from the C# 4 spec by ignoring Array's
        /// implementation of IEnumerable and just indexing into its elements.
        ///
        /// NOTE: We're assuming that sequence points have already been generated.
        /// Otherwise, lowering to for-loops would generated spurious ones.
        /// </remarks>
        private BoundStatement RewriteSingleDimensionalArrayForEachStatement(BoundForEachStatement node)
        {
            ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax;

            BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node);

            Debug.Assert(collectionExpression.Type.IsArray());

            ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type;

            Debug.Assert(arrayType.Rank == 1);

            TypeSymbol intType  = compilation.GetSpecialType(SpecialType.System_Int32);
            TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean);

            BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression);
            BoundStatement  rewrittenBody       = (BoundStatement)Visit(node.Body);

            // A[] a
            LocalSymbol arrayVar = factory.SynthesizedLocal(arrayType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArray);

            // A[] a = /*node.Expression*/;
            BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression);

            AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl);

            // Reference to a.
            BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType);

            // int p
            LocalSymbol positionVar = factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayIndex0);

            // Reference to p.
            BoundLocal boundPositionVar = MakeBoundLocal(forEachSyntax, positionVar, intType);

            // int p = 0;
            BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar,
                                                                  MakeLiteral(forEachSyntax, ConstantValue.Default(SpecialType.System_Int32), intType));

            // V v
            LocalSymbol iterationVar     = node.IterationVariable;
            TypeSymbol  iterationVarType = iterationVar.Type;

            // (V)a[p]
            BoundExpression iterationVarInitValue = MakeConversion(
                syntax: forEachSyntax,
                rewrittenOperand: new BoundArrayAccess(
                    syntax: forEachSyntax,
                    expression: boundArrayVar,
                    indices: ImmutableArray.Create <BoundExpression>(boundPositionVar),
                    type: arrayType.ElementType),
                conversion: node.ElementConversion,
                rewrittenType: iterationVarType,
                @checked: node.Checked);

            // V v = (V)a[p];
            BoundStatement iterationVariableDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue);

            AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVariableDecl);

            BoundStatement initializer = new BoundStatementList(forEachSyntax,
                                                                statements: ImmutableArray.Create <BoundStatement>(arrayVarDecl, positionVarDecl));

            // a.Length
            BoundExpression arrayLength = new BoundArrayLength(
                syntax: forEachSyntax,
                expression: boundArrayVar,
                type: intType);

            // p < a.Length
            BoundExpression exitCondition = new BoundBinaryOperator(
                syntax: forEachSyntax,
                operatorKind: BinaryOperatorKind.IntLessThan,
                left: boundPositionVar,
                right: arrayLength,
                constantValueOpt: null,
                methodOpt: null,
                resultKind: LookupResultKind.Viable,
                type: boolType);

            // p = p + 1;
            BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar, intType);

            // { V v = (V)a[p]; /* node.Body */ }
            BoundStatement loopBody = new BoundBlock(forEachSyntax,
                                                     locals: ImmutableArray.Create <LocalSymbol>(iterationVar),
                                                     statements: ImmutableArray.Create <BoundStatement>(iterationVariableDecl, rewrittenBody));

            // for (A[] a = /*node.Expression*/, int p = 0; p < a.Length; p = p + 1) {
            //     V v = (V)a[p];
            //     /*node.Body*/
            // }
            BoundStatement result = RewriteForStatement(
                syntax: node.Syntax,
                outerLocals: ImmutableArray.Create <LocalSymbol>(arrayVar, positionVar),
                rewrittenInitializer: initializer,
                rewrittenCondition: exitCondition,
                conditionSyntaxOpt: null,
                conditionSpanOpt: forEachSyntax.InKeyword.Span,
                rewrittenIncrement: positionIncrement,
                rewrittenBody: loopBody,
                breakLabel: node.BreakLabel,
                continueLabel: node.ContinueLabel,
                hasErrors: node.HasErrors);

            AddForEachKeywordSequencePoint(forEachSyntax, ref result);

            return(result);
        }
Exemple #23
0
 public override BoundNode VisitLocal(BoundLocal node)
 {
     ReferenceVariable(node.Syntax, node.LocalSymbol);
     return(base.VisitLocal(node));
 }
        /// <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();

            CSharpSyntaxNode 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);
            }
        }
Exemple #25
0
        private BoundExpression MakeNullCoalescingOperator(
            SyntaxNode syntax,
            BoundExpression rewrittenLeft,
            BoundExpression rewrittenRight,
            Conversion leftConversion,
            BoundNullCoalescingOperatorResultKind resultKind,
            TypeSymbol rewrittenResultType)
        {
            Debug.Assert(rewrittenLeft != null);
            Debug.Assert(rewrittenRight != null);
            Debug.Assert(leftConversion.IsValid);
            Debug.Assert((object)rewrittenResultType != null);
            Debug.Assert(rewrittenRight.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames));

            if (_inExpressionLambda)
            {
                TypeSymbol strippedLeftType    = rewrittenLeft.Type.StrippedType();
                Conversion rewrittenConversion = TryMakeConversion(syntax, leftConversion, strippedLeftType, rewrittenResultType);
                if (!rewrittenConversion.Exists)
                {
                    return(BadExpression(syntax, rewrittenResultType, rewrittenLeft, rewrittenRight));
                }

                return(new BoundNullCoalescingOperator(syntax, rewrittenLeft, rewrittenRight, rewrittenConversion, resultKind, rewrittenResultType));
            }

            var isUnconstrainedTypeParameter = rewrittenLeft.Type != null && !rewrittenLeft.Type.IsReferenceType && !rewrittenLeft.Type.IsValueType;

            // first we can make a small optimization:
            // If left is a constant then we already know whether it is null or not. If it is null then we
            // can simply generate "right". If it is not null then we can simply generate
            // MakeConversion(left). This does not hold when the left is an unconstrained type parameter: at runtime,
            // it can be either left or right depending on the runtime type of T
            if (!isUnconstrainedTypeParameter)
            {
                if (rewrittenLeft.IsDefaultValue())
                {
                    return(rewrittenRight);
                }

                if (rewrittenLeft.ConstantValue != null)
                {
                    Debug.Assert(!rewrittenLeft.ConstantValue.IsNull);

                    return(GetConvertedLeftForNullCoalescingOperator(rewrittenLeft, leftConversion, rewrittenResultType));
                }
            }

            // string concatenation is never null.
            // interpolated string lowering may introduce redundant null coalescing, which we have to remove.
            if (IsStringConcat(rewrittenLeft))
            {
                return(GetConvertedLeftForNullCoalescingOperator(rewrittenLeft, leftConversion, rewrittenResultType));
            }

            // if left conversion is intrinsic implicit (always succeeds) and results in a reference type
            // we can apply conversion before doing the null check that allows for a more efficient IL emit.
            if (rewrittenLeft.Type.IsReferenceType &&
                leftConversion.IsImplicit &&
                !leftConversion.IsUserDefined)
            {
                if (!leftConversion.IsIdentity)
                {
                    rewrittenLeft = MakeConversionNode(rewrittenLeft.Syntax, rewrittenLeft, leftConversion, rewrittenResultType, @checked: false);
                }
                return(new BoundNullCoalescingOperator(syntax, rewrittenLeft, rewrittenRight, Conversion.Identity, resultKind, rewrittenResultType));
            }

            if (leftConversion.IsIdentity || leftConversion.Kind == ConversionKind.ExplicitNullable)
            {
                var conditionalAccess = rewrittenLeft as BoundLoweredConditionalAccess;
                if (conditionalAccess != null &&
                    (conditionalAccess.WhenNullOpt == null || NullableNeverHasValue(conditionalAccess.WhenNullOpt)))
                {
                    var notNullAccess = NullableAlwaysHasValue(conditionalAccess.WhenNotNull);
                    if (notNullAccess != null)
                    {
                        var whenNullOpt = rewrittenRight;

                        if (whenNullOpt.Type.IsNullableType())
                        {
                            notNullAccess = conditionalAccess.WhenNotNull;
                        }

                        if (whenNullOpt.IsDefaultValue() && whenNullOpt.Type.SpecialType != SpecialType.System_Decimal)
                        {
                            whenNullOpt = null;
                        }

                        return(conditionalAccess.Update(
                                   conditionalAccess.Receiver,
                                   conditionalAccess.HasValueMethodOpt,
                                   whenNotNull: notNullAccess,
                                   whenNullOpt: whenNullOpt,
                                   id: conditionalAccess.Id,
                                   type: rewrittenResultType
                                   ));
                    }
                }
            }

            // Optimize left ?? right to left.GetValueOrDefault() when left is T? and right is the default value of T
            if (rewrittenLeft.Type.IsNullableType() &&
                RemoveIdentityConversions(rewrittenRight).IsDefaultValue() &&
                rewrittenRight.Type.Equals(rewrittenLeft.Type.GetNullableUnderlyingType(), TypeCompareKind.AllIgnoreOptions) &&
                TryGetNullableMethod(rewrittenLeft.Syntax, rewrittenLeft.Type, SpecialMember.System_Nullable_T_GetValueOrDefault, out MethodSymbol getValueOrDefault))
            {
                return(BoundCall.Synthesized(rewrittenLeft.Syntax, rewrittenLeft, getValueOrDefault));
            }

            // We lower left ?? right to
            //
            // var temp = left;
            // (temp != null) ? MakeConversion(temp) : right
            //

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

            // temp != null
            BoundExpression nullCheck = MakeNullCheck(syntax, boundTemp, BinaryOperatorKind.NotEqual);

            // MakeConversion(temp, rewrittenResultType)
            BoundExpression convertedLeft = GetConvertedLeftForNullCoalescingOperator(boundTemp, leftConversion, rewrittenResultType);

            Debug.Assert(convertedLeft.HasErrors || convertedLeft.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames));

            // (temp != null) ? MakeConversion(temp, LeftConversion) : RightOperand
            BoundExpression conditionalExpression = RewriteConditionalOperator(
                syntax: syntax,
                rewrittenCondition: nullCheck,
                rewrittenConsequence: convertedLeft,
                rewrittenAlternative: rewrittenRight,
                constantValueOpt: null,
                rewrittenType: rewrittenResultType,
                isRef: false);

            Debug.Assert(conditionalExpression.ConstantValue == null); // we shouldn't have hit this else case otherwise
            Debug.Assert(conditionalExpression.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames));

            return(new BoundSequence(
                       syntax: syntax,
                       locals: ImmutableArray.Create(boundTemp.LocalSymbol),
                       sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment),
                       value: conditionalExpression,
                       type: rewrittenResultType));
        }
        private BoundCatchBlock BindCatchBlock(CatchClauseSyntax node, ArrayBuilder<BoundCatchBlock> previousBlocks, DiagnosticBag diagnostics)
        {
            bool hasError = false;
            TypeSymbol type = null;
            BoundExpression boundFilter = null;
            var declaration = node.Declaration;
            if (declaration != null)
            {
                // Note: The type is being bound twice: here and in LocalSymbol.Type. Currently,
                // LocalSymbol.Type ignores diagnostics so it seems cleaner to bind the type here
                // as well. However, if LocalSymbol.Type is changed to report diagnostics, we'll
                // need to avoid binding here since that will result in duplicate diagnostics.
                type = this.BindType(declaration.Type, diagnostics);
                Debug.Assert((object)type != null);

                if (type.IsErrorType())
                {
                    hasError = true;
                }
                else
                {
                    HashSet<DiagnosticInfo> useSiteDiagnostics = null;
                    TypeSymbol effectiveType = type.EffectiveType(ref useSiteDiagnostics);
                    if (!Compilation.IsExceptionType(effectiveType, ref useSiteDiagnostics))
                    {
                        // "The type caught or thrown must be derived from System.Exception"
                        Error(diagnostics, ErrorCode.ERR_BadExceptionType, declaration.Type);
                        hasError = true;
                        diagnostics.Add(declaration.Type, useSiteDiagnostics);
                    }
                }
            }

            var filter = node.Filter;
            if (filter != null)
            {
                var filterBinder = this.GetBinder(filter);
                boundFilter = filterBinder.BindCatchFilter(filter, diagnostics);
                hasError |= boundFilter.HasAnyErrors;
            }

            if (!hasError)
            {
                // TODO: Loop is O(n), caller is O(n^2).  Perhaps we could iterate in reverse order (since it's easier to find
                // base types than to find derived types).
                Debug.Assert(((object)type == null) || !type.IsErrorType());
                foreach (var previousBlock in previousBlocks)
                {
                    var previousType = previousBlock.ExceptionTypeOpt;

                    // If the previous type is a generic parameter we don't know what exception types it's gonna catch exactly.
                    // If it is a class-type we know it's gonna catch all exception types of its type and types that are derived from it.
                    // So if the current type is a class-type (or an effective base type of a generic parameter) 
                    // that derives from the previous type the current catch is unreachable.

                    if (previousBlock.ExceptionFilterOpt == null && (object)previousType != null && !previousType.IsErrorType())
                    {
                        if ((object)type != null)
                        {
                            HashSet<DiagnosticInfo> useSiteDiagnostics = null;

                            if (Conversions.HasIdentityOrImplicitReferenceConversion(type, previousType, ref useSiteDiagnostics))
                            {
                                // "A previous catch clause already catches all exceptions of this or of a super type ('{0}')"
                                Error(diagnostics, ErrorCode.ERR_UnreachableCatch, declaration.Type, previousType);
                                diagnostics.Add(declaration.Type, useSiteDiagnostics);
                                hasError = true;
                                break;
                            }

                            diagnostics.Add(declaration.Type, useSiteDiagnostics);
                        }
                        else if (previousType == Compilation.GetWellKnownType(WellKnownType.System_Exception) &&
                                 Compilation.SourceAssembly.RuntimeCompatibilityWrapNonExceptionThrows)
                        {
                            // If the RuntimeCompatibility(WrapNonExceptionThrows = false) is applied on the source assembly or any referenced netmodule.
                            // an empty catch may catch exceptions that don't derive from System.Exception.

                            // "A previous catch clause already catches all exceptions..."
                            Error(diagnostics, ErrorCode.WRN_UnreachableGeneralCatch, node.CatchKeyword);
                            break;
                        }
                    }
                }
            }

            BoundExpression exceptionSource = null;
            LocalSymbol local = this.Locals.FirstOrDefault();
            if ((object)local != null)
            {
                Debug.Assert(this.Locals.Length == 1);

                // Check for local variable conflicts in the *enclosing* binder, not the *current* binder;
                // obviously we will find a local of the given name in the current binder.
                hasError |= this.ValidateDeclarationNameConflictsInScope(local, diagnostics);

                exceptionSource = new BoundLocal(declaration, local, ConstantValue.NotAvailable, local.Type);
            }

            var block = this.BindBlock(node.Block, diagnostics);
            Debug.Assert((object)local == null || local.DeclarationKind == LocalDeclarationKind.CatchVariable);
            Debug.Assert((object)local == null || local.Type.IsErrorType() || (local.Type == type));
            return new BoundCatchBlock(node, local, exceptionSource, type, boundFilter, block, hasError);
        }
        /// <summary>
        /// Lowers a lock statement to a try-finally block that calls Monitor.Enter and Monitor.Exit
        /// before and after the body, respectively.
        /// </summary>
        public override BoundNode VisitLockStatement(BoundLockStatement node)
        {
            LockStatementSyntax lockSyntax = (LockStatementSyntax)node.Syntax;

            BoundExpression rewrittenArgument = VisitExpression(node.Argument);
            BoundStatement  rewrittenBody     = (BoundStatement)Visit(node.Body);

            TypeSymbol argumentType = rewrittenArgument.Type;

            if ((object)argumentType == null)
            {
                // This isn't particularly elegant, but hopefully locking on null is
                // not very common.
                Debug.Assert(rewrittenArgument.ConstantValue == ConstantValue.Null);
                argumentType      = _compilation.GetSpecialType(SpecialType.System_Object);
                rewrittenArgument = MakeLiteral(
                    rewrittenArgument.Syntax,
                    rewrittenArgument.ConstantValue,
                    argumentType); //need to have a non-null type here for TempHelpers.StoreToTemp.
            }

            if (argumentType.Kind == SymbolKind.TypeParameter)
            {
                // If the argument has a type parameter type, then we'll box it right away
                // so that the same object is passed to both Monitor.Enter and Monitor.Exit.
                argumentType = _compilation.GetSpecialType(SpecialType.System_Object);

                rewrittenArgument = MakeConversionNode(
                    rewrittenArgument.Syntax,
                    rewrittenArgument,
                    Conversion.Boxing,
                    argumentType,
                    @checked: false,
                    constantValueOpt: rewrittenArgument.ConstantValue);
            }

            BoundAssignmentOperator assignmentToLockTemp;
            BoundLocal boundLockTemp = _factory.StoreToTemp(rewrittenArgument, out assignmentToLockTemp, syntaxOpt: lockSyntax, kind: SynthesizedLocalKind.Lock);

            BoundStatement  boundLockTempInit = new BoundExpressionStatement(lockSyntax, assignmentToLockTemp);
            BoundExpression exitCallExpr;

            MethodSymbol exitMethod;

            if (TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Exit, out exitMethod))
            {
                exitCallExpr = BoundCall.Synthesized(
                    lockSyntax,
                    null,
                    exitMethod,
                    boundLockTemp);
            }
            else
            {
                exitCallExpr = new BoundBadExpression(lockSyntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(boundLockTemp), ErrorTypeSymbol.UnknownResultType);
            }

            BoundStatement exitCall = new BoundExpressionStatement(lockSyntax, exitCallExpr);

            MethodSymbol enterMethod;

            if ((TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter2, out enterMethod, isOptional: true) ||
                 TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter, out enterMethod)) && // If we didn't find the overload introduced in .NET 4.0, then use the older one.
                enterMethod.ParameterCount == 2)
            {
                // C# 4.0+ version
                // L $lock = `argument`;                      // sequence point
                // bool $lockTaken = false;
                // try
                // {
                //     Monitor.Enter($lock, ref $lockTaken);
                //     `body`                                 // sequence point
                // }
                // finally
                // {                                          // hidden sequence point
                //     if ($lockTaken) Monitor.Exit($lock);
                // }

                TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean);
                BoundAssignmentOperator assignmentToLockTakenTemp;

                BoundLocal boundLockTakenTemp = _factory.StoreToTemp(
                    MakeLiteral(rewrittenArgument.Syntax, ConstantValue.False, boolType),
                    store: out assignmentToLockTakenTemp,
                    syntaxOpt: lockSyntax,
                    kind: SynthesizedLocalKind.LockTaken);

                BoundStatement boundLockTakenTempInit = new BoundExpressionStatement(lockSyntax, assignmentToLockTakenTemp);

                BoundStatement enterCall = new BoundExpressionStatement(
                    lockSyntax,
                    BoundCall.Synthesized(
                        lockSyntax,
                        null,
                        enterMethod,
                        boundLockTemp,
                        boundLockTakenTemp));

                exitCall = RewriteIfStatement(
                    lockSyntax,
                    boundLockTakenTemp,
                    exitCall,
                    null,
                    node.HasErrors);

                return(new BoundBlock(
                           lockSyntax,
                           ImmutableArray.Create(boundLockTemp.LocalSymbol, boundLockTakenTemp.LocalSymbol),
                           ImmutableArray.Create(
                               InstrumentLockTargetCapture(node, boundLockTempInit),
                               boundLockTakenTempInit,
                               new BoundTryStatement(
                                   lockSyntax,
                                   BoundBlock.SynthesizedNoLocals(lockSyntax, ImmutableArray.Create(
                                                                      enterCall,
                                                                      rewrittenBody)),
                                   ImmutableArray <BoundCatchBlock> .Empty,
                                   BoundBlock.SynthesizedNoLocals(lockSyntax,
                                                                  exitCall)))));
            }
            else
            {
                // Pre-4.0 version
                // L $lock = `argument`;           // sequence point
                // Monitor.Enter($lock);           // NB: before try-finally so we don't Exit if an exception prevents us from acquiring the lock.
                // try
                // {
                //     `body`                      // sequence point
                // }
                // finally
                // {
                //     Monitor.Exit($lock);        // hidden sequence point
                // }

                BoundExpression enterCallExpr;

                if ((object)enterMethod != null)
                {
                    Debug.Assert(enterMethod.ParameterCount == 1);

                    enterCallExpr = BoundCall.Synthesized(
                        lockSyntax,
                        null,
                        enterMethod,
                        boundLockTemp);
                }
                else
                {
                    enterCallExpr = new BoundBadExpression(lockSyntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(boundLockTemp), ErrorTypeSymbol.UnknownResultType);
                }

                BoundStatement enterCall = new BoundExpressionStatement(
                    lockSyntax,
                    enterCallExpr);

                return(new BoundBlock(
                           lockSyntax,
                           ImmutableArray.Create(boundLockTemp.LocalSymbol),
                           ImmutableArray.Create(
                               InstrumentLockTargetCapture(node, boundLockTempInit),
                               enterCall,
                               new BoundTryStatement(
                                   lockSyntax,
                                   BoundBlock.SynthesizedNoLocals(lockSyntax, rewrittenBody),
                                   ImmutableArray <BoundCatchBlock> .Empty,
                                   BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall)))));
            }
        }
Exemple #28
0
 public override BoundNode VisitLocal(BoundLocal node)
 {
     Capture(node.LocalSymbol, node.Syntax);
     return(base.VisitLocal(node));
 }
Exemple #29
0
        private BoundExpression MakeNullCoalescingOperator(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenLeft,
            BoundExpression rewrittenRight,
            Conversion leftConversion,
            TypeSymbol rewrittenResultType)
        {
            Debug.Assert(rewrittenLeft != null);
            Debug.Assert(rewrittenRight != null);
            Debug.Assert(leftConversion.IsValid);
            Debug.Assert((object)rewrittenResultType != null);
            Debug.Assert(rewrittenRight.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames));

            if (_inExpressionLambda)
            {
                TypeSymbol strippedLeftType    = rewrittenLeft.Type.StrippedType();
                Conversion rewrittenConversion = MakeConversion(syntax, leftConversion, strippedLeftType, rewrittenResultType);
                return(new BoundNullCoalescingOperator(syntax, rewrittenLeft, rewrittenRight, rewrittenConversion, rewrittenResultType));
            }

            // first we can make a small optimization:
            // If left is a constant then we already know whether it is null or not. If it is null then we
            // can simply generate "right". If it is not null then we can simply generate
            // MakeConversion(left).

            if (rewrittenLeft.IsDefaultValue())
            {
                return(rewrittenRight);
            }

            if (rewrittenLeft.ConstantValue != null)
            {
                Debug.Assert(!rewrittenLeft.ConstantValue.IsNull);

                return(GetConvertedLeftForNullCoalescingOperator(rewrittenLeft, leftConversion, rewrittenResultType));
            }

            // if left conversion is intrinsic implicit (always succeeds) and results in a reference type
            // we can apply conversion before doing the null check that allows for a more efficient IL emit.
            if (rewrittenLeft.Type.IsReferenceType &&
                leftConversion.IsImplicit &&
                !leftConversion.IsUserDefined)
            {
                if (!leftConversion.IsIdentity)
                {
                    rewrittenLeft = MakeConversionNode(rewrittenLeft.Syntax, rewrittenLeft, leftConversion, rewrittenResultType, @checked: false);
                }
                return(new BoundNullCoalescingOperator(syntax, rewrittenLeft, rewrittenRight, Conversion.Identity, rewrittenResultType));
            }

            if (leftConversion.IsIdentity || leftConversion.Kind == ConversionKind.ExplicitNullable)
            {
                var conditionalAccess = rewrittenLeft as BoundLoweredConditionalAccess;
                if (conditionalAccess != null &&
                    (conditionalAccess.WhenNullOpt == null || NullableNeverHasValue(conditionalAccess.WhenNullOpt)))
                {
                    var notNullAccess = NullableAlwaysHasValue(conditionalAccess.WhenNotNull);
                    if (notNullAccess != null)
                    {
                        var whenNullOpt = rewrittenRight;

                        if (whenNullOpt.Type.IsNullableType())
                        {
                            notNullAccess = conditionalAccess.WhenNotNull;
                        }

                        if (whenNullOpt.IsDefaultValue() && whenNullOpt.Type.SpecialType != SpecialType.System_Decimal)
                        {
                            whenNullOpt = null;
                        }

                        return(conditionalAccess.Update(
                                   conditionalAccess.Receiver,
                                   conditionalAccess.HasValueMethodOpt,
                                   whenNotNull: notNullAccess,
                                   whenNullOpt: whenNullOpt,
                                   id: conditionalAccess.Id,
                                   type: rewrittenResultType
                                   ));
                    }
                }
            }

            // We lower left ?? right to
            //
            // var temp = left;
            // (temp != null) ? MakeConversion(temp) : right
            //

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

            // temp != null
            BoundExpression nullCheck = MakeNullCheck(syntax, boundTemp, BinaryOperatorKind.NotEqual);

            // MakeConversion(temp, rewrittenResultType)
            BoundExpression convertedLeft = GetConvertedLeftForNullCoalescingOperator(boundTemp, leftConversion, rewrittenResultType);

            Debug.Assert(convertedLeft.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames));

            // (temp != null) ? MakeConversion(temp, LeftConversion) : RightOperand
            BoundExpression conditionalExpression = RewriteConditionalOperator(
                syntax: syntax,
                rewrittenCondition: nullCheck,
                rewrittenConsequence: convertedLeft,
                rewrittenAlternative: rewrittenRight,
                constantValueOpt: null,
                rewrittenType: rewrittenResultType);

            Debug.Assert(conditionalExpression.ConstantValue == null); // we shouldn't have hit this else case otherwise
            Debug.Assert(conditionalExpression.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames));

            return(new BoundSequence(
                       syntax: syntax,
                       locals: ImmutableArray.Create(boundTemp.LocalSymbol),
                       sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment),
                       value: conditionalExpression,
                       type: rewrittenResultType));
        }
        public override BoundNode VisitLocal(BoundLocal node)
        {
            if (IsInside && node.IsDeclaration)
            {
                _variablesDeclared.Add(node.LocalSymbol);
            }

            return null;
        }
Exemple #31
0
        internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(
            SourceEventSymbol eventSymbol,
            bool isAddMethod,
            CSharpCompilation compilation,
            BindingDiagnosticBag diagnostics
            )
        {
            CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode;

            TypeSymbol      delegateType  = eventSymbol.Type;
            MethodSymbol    accessor      = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod;
            ParameterSymbol thisParameter = accessor.ThisParameter;

            TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean);

            SpecialMember updateMethodId = isAddMethod
                ? SpecialMember.System_Delegate__Combine
                : SpecialMember.System_Delegate__Remove;
            MethodSymbol updateMethod = (MethodSymbol)compilation.GetSpecialTypeMember(
                updateMethodId
                );

            BoundStatement @return = new BoundReturnStatement(
                syntax,
                refKind: RefKind.None,
                expressionOpt: null
                )
            {
                WasCompilerGenerated = true
            };

            if (updateMethod == null)
            {
                MemberDescriptor memberDescriptor = SpecialMembers.GetDescriptor(updateMethodId);
                diagnostics.Add(
                    new CSDiagnostic(
                        new CSDiagnosticInfo(
                            ErrorCode.ERR_MissingPredefinedMember,
                            memberDescriptor.DeclaringTypeMetadataName,
                            memberDescriptor.Name
                            ),
                        syntax.Location
                        )
                    );

                return(BoundBlock.SynthesizedNoLocals(syntax, @return));
            }

            Binder.ReportUseSite(updateMethod, diagnostics, syntax);

            BoundThisReference fieldReceiver = eventSymbol.IsStatic
                ? null
                : new BoundThisReference(syntax, thisParameter.Type)
            {
                WasCompilerGenerated = true
            };

            BoundFieldAccess boundBackingField = new BoundFieldAccess(
                syntax,
                receiver: fieldReceiver,
                fieldSymbol: eventSymbol.AssociatedField,
                constantValueOpt: null
                )
            {
                WasCompilerGenerated = true
            };

            BoundParameter boundParameter = new BoundParameter(
                syntax,
                parameterSymbol: accessor.Parameters[0]
                )
            {
                WasCompilerGenerated = true
            };

            BoundExpression delegateUpdate;

            MethodSymbol compareExchangeMethod = (MethodSymbol)compilation.GetWellKnownTypeMember(
                WellKnownMember.System_Threading_Interlocked__CompareExchange_T
                );

            if ((object)compareExchangeMethod == null)
            {
                // (DelegateType)Delegate.Combine(_event, value)
                delegateUpdate = BoundConversion.SynthesizedNonUserDefined(
                    syntax,
                    operand: BoundCall.Synthesized(
                        syntax,
                        receiverOpt: null,
                        method: updateMethod,
                        arguments: ImmutableArray.Create <BoundExpression>(
                            boundBackingField,
                            boundParameter
                            )
                        ),
                    conversion: Conversion.ExplicitReference,
                    type: delegateType
                    );

                // _event = (DelegateType)Delegate.Combine(_event, value);
                BoundStatement eventUpdate = new BoundExpressionStatement(
                    syntax,
                    expression: new BoundAssignmentOperator(
                        syntax,
                        left: boundBackingField,
                        right: delegateUpdate,
                        type: delegateType
                        )
                {
                    WasCompilerGenerated = true
                }
                    )
                {
                    WasCompilerGenerated = true
                };

                return(BoundBlock.SynthesizedNoLocals(
                           syntax,
                           statements: ImmutableArray.Create <BoundStatement>(eventUpdate, @return)
                           ));
            }

            compareExchangeMethod = compareExchangeMethod.Construct(
                ImmutableArray.Create <TypeSymbol>(delegateType)
                );

            Binder.ReportUseSite(compareExchangeMethod, diagnostics, syntax);

            GeneratedLabelSymbol loopLabel = new GeneratedLabelSymbol("loop");

            const int numTemps = 3;

            LocalSymbol[] tmps      = new LocalSymbol[numTemps];
            BoundLocal[]  boundTmps = new BoundLocal[numTemps];

            for (int i = 0; i < numTemps; i++)
            {
                tmps[i] = new SynthesizedLocal(
                    accessor,
                    TypeWithAnnotations.Create(delegateType),
                    SynthesizedLocalKind.LoweringTemp
                    );
                boundTmps[i] = new BoundLocal(syntax, tmps[i], null, delegateType)
                {
                    WasCompilerGenerated = true
                };
            }

            // tmp0 = _event;
            BoundStatement tmp0Init = new BoundExpressionStatement(
                syntax,
                expression: new BoundAssignmentOperator(
                    syntax,
                    left: boundTmps[0],
                    right: boundBackingField,
                    type: delegateType
                    )
            {
                WasCompilerGenerated = true
            }
                )
            {
                WasCompilerGenerated = true
            };

            // LOOP:
            BoundStatement loopStart = new BoundLabelStatement(syntax, label: loopLabel)
            {
                WasCompilerGenerated = true
            };

            // tmp1 = tmp0;
            BoundStatement tmp1Update = new BoundExpressionStatement(
                syntax,
                expression: new BoundAssignmentOperator(
                    syntax,
                    left: boundTmps[1],
                    right: boundTmps[0],
                    type: delegateType
                    )
            {
                WasCompilerGenerated = true
            }
                )
            {
                WasCompilerGenerated = true
            };

            // (DelegateType)Delegate.Combine(tmp1, value)
            delegateUpdate = BoundConversion.SynthesizedNonUserDefined(
                syntax,
                operand: BoundCall.Synthesized(
                    syntax,
                    receiverOpt: null,
                    method: updateMethod,
                    arguments: ImmutableArray.Create <BoundExpression>(boundTmps[1], boundParameter)
                    ),
                conversion: Conversion.ExplicitReference,
                type: delegateType
                );

            // tmp2 = (DelegateType)Delegate.Combine(tmp1, value);
            BoundStatement tmp2Update = new BoundExpressionStatement(
                syntax,
                expression: new BoundAssignmentOperator(
                    syntax,
                    left: boundTmps[2],
                    right: delegateUpdate,
                    type: delegateType
                    )
            {
                WasCompilerGenerated = true
            }
                )
            {
                WasCompilerGenerated = true
            };

            // Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1)
            BoundExpression compareExchange = BoundCall.Synthesized(
                syntax,
                receiverOpt: null,
                method: compareExchangeMethod,
                arguments: ImmutableArray.Create <BoundExpression>(
                    boundBackingField,
                    boundTmps[2],
                    boundTmps[1]
                    )
                );

            // tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1);
            BoundStatement tmp0Update = new BoundExpressionStatement(
                syntax,
                expression: new BoundAssignmentOperator(
                    syntax,
                    left: boundTmps[0],
                    right: compareExchange,
                    type: delegateType
                    )
            {
                WasCompilerGenerated = true
            }
                )
            {
                WasCompilerGenerated = true
            };

            // tmp0 == tmp1 // i.e. exit when they are equal, jump to start otherwise
            BoundExpression loopExitCondition = new BoundBinaryOperator(
                syntax,
                operatorKind: BinaryOperatorKind.ObjectEqual,
                left: boundTmps[0],
                right: boundTmps[1],
                constantValueOpt: null,
                methodOpt: null,
                resultKind: LookupResultKind.Viable,
                type: boolType
                )
            {
                WasCompilerGenerated = true
            };

            // branchfalse (tmp0 == tmp1) LOOP
            BoundStatement loopEnd = new BoundConditionalGoto(
                syntax,
                condition: loopExitCondition,
                jumpIfTrue: false,
                label: loopLabel
                )
            {
                WasCompilerGenerated = true
            };

            return(new BoundBlock(
                       syntax,
                       locals: tmps.AsImmutable(),
                       statements: ImmutableArray.Create <BoundStatement>(
                           tmp0Init,
                           loopStart,
                           tmp1Update,
                           tmp2Update,
                           tmp0Update,
                           loopEnd,
                           @return
                           )
                       )
            {
                WasCompilerGenerated = true
            });
        }
Exemple #32
0
        private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern value, DecisionMaker makeDecision)
        {
            if (byType.Default != null)
            {
                try
                {
                    return AddByValue(byType.Default, value, makeDecision);
                }
                finally
                {
                    if (byType.Default.MatchIsComplete)
                    {
                        byType.MatchIsComplete = true;
                    }
                }
            }

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

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

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

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

            DecisionTree forType = null;

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

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

            return AddByValue(forType, value, makeDecision);
        }
 public override BoundNode VisitLocal(BoundLocal node)
 {
     CheckReferenceToVariable(node, node.LocalSymbol);
     return(base.VisitLocal(node));
 }
 protected override void VisitLvalue(BoundLocal node)
 {
     CheckOutVarDeclaration(node);
     base.VisitLvalue(node);
 }
        private BoundExpression LowerLiftedUnaryOperator(
            UnaryOperatorKind kind,
            CSharpSyntaxNode 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 = GetNullableMethod(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 BoundDefaultOperator(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));
        }
 public override BoundNode VisitLocal(BoundLocal node)
 {
     CheckOutVarDeclaration(node);
     return base.VisitLocal(node);
 }
        /// <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();

            CSharpSyntaxNode 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 realtive 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
            // overal 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));
            }
        }
 /// <summary>
 /// Takes an expression and returns the bound local expression "temp" 
 /// and the bound assignment expression "temp = expr".
 /// </summary>
 public BoundLocal StoreToTemp(BoundExpression argument, out BoundAssignmentOperator store, RefKind refKind = RefKind.None, TempKind tempKind = TempKind.None)
 {
     MethodSymbol containingMethod = this.CurrentMethod;
     var syntax = argument.Syntax;
     var type = argument.Type;
     var local = new BoundLocal(
         syntax,
         new SynthesizedLocal(containingMethod, type, syntax: (tempKind == TempKind.None) ? null : syntax, refKind: refKind, tempKind: tempKind),
         null,
         type);
     store = new BoundAssignmentOperator(
         syntax,
         local,
         argument,
         refKind,
         type);
     return local;
 }
        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;
            CSharpSyntaxNode 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 = MakeConversion(
                    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 = GetNullableMethod(syntax, type, SpecialMember.System_Nullable_T_GetValueOrDefault);
            MethodSymbol ctor = GetNullableMethod(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 BoundDefaultOperator(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));
        }
Exemple #40
0
        /// <summary>
        /// Takes an expression and returns the bound local expression "temp" 
        /// and the bound assignment expression "temp = expr".
        /// </summary>
        public BoundLocal StoreToTemp(
            BoundExpression argument,
            out BoundAssignmentOperator store,
            RefKind refKind = RefKind.None,
            SynthesizedLocalKind kind = SynthesizedLocalKind.LoweringTemp,
            CSharpSyntaxNode syntaxOpt = null
#if DEBUG
            , [CallerLineNumber]int callerLineNumber = 0
            , [CallerFilePath]string callerFilePath = null
#endif
            )
        {
            MethodSymbol containingMethod = this.CurrentMethod;
            var syntax = argument.Syntax;
            var type = argument.Type;

            var local = new BoundLocal(
                syntax,
                new SynthesizedLocal(
                    containingMethod,
                    type,
                    kind,
#if DEBUG
                    createdAtLineNumber: callerLineNumber,
                    createdAtFilePath: callerFilePath,
#endif
                    syntaxOpt: syntaxOpt ?? (kind.IsLongLived() ? syntax : null),
                    isPinned: false,
                    refKind: refKind),
                null,
                type);

            store = new BoundAssignmentOperator(
                syntax,
                local,
                argument,
                refKind,
                type);

            return local;
        }
        private BoundStatement UnpendBranches(
            AwaitFinallyFrame frame,
            SynthesizedLocal pendingBranchVar,
            SynthesizedLocal pendingException)
        {
            var parent = frame.ParentOpt;

            // handle proxy labels if have any
            var proxiedLabels = frame.proxiedLabels;
            var proxyLabels   = frame.proxyLabels;

            // skip 0 - it means we took no explicit branches
            int i     = 1;
            var cases = ArrayBuilder <BoundSwitchSection> .GetInstance();

            if (proxiedLabels != null)
            {
                for (int cnt = proxiedLabels.Count; i <= cnt; i++)
                {
                    var target        = proxiedLabels[i - 1];
                    var parentProxy   = parent.ProxyLabelIfNeeded(target);
                    var caseStatement = _F.SwitchSection(i, _F.Goto(parentProxy));
                    cases.Add(caseStatement);
                }
            }

            if (frame.returnProxyLabel != null)
            {
                BoundLocal pendingValue = null;
                if (frame.returnValue != null)
                {
                    pendingValue = _F.Local(frame.returnValue);
                }

                SynthesizedLocal returnValue;
                BoundStatement   unpendReturn;

                var returnLabel = parent.ProxyReturnIfNeeded(_F.CurrentMethod, pendingValue, out returnValue);

                if (returnLabel == null)
                {
                    unpendReturn = new BoundReturnStatement(_F.Syntax, pendingValue);
                }
                else
                {
                    if (pendingValue == null)
                    {
                        unpendReturn = _F.Goto(returnLabel);
                    }
                    else
                    {
                        unpendReturn = _F.Block(
                            _F.Assignment(
                                _F.Local(returnValue),
                                pendingValue),
                            _F.Goto(returnLabel));
                    }
                }

                var caseStatement = _F.SwitchSection(i, unpendReturn);
                cases.Add(caseStatement);
            }

            return(_F.Switch(_F.Local(pendingBranchVar), cases.ToImmutableAndFree()));
        }
        /// <summary>
        /// Lower a foreach loop that will enumerate a multi-dimensional array.
        /// 
        /// A[...] a = x;
        /// int q_0 = a.GetUpperBound(0), q_1 = a.GetUpperBound(1), ...;
        /// for (int p_0 = a.GetLowerBound(0); p_0 &lt;= q_0; p_0 = p_0 + 1)
        ///     for (int p_1 = a.GetLowerBound(1); p_1 &lt;= q_1; p_1 = p_1 + 1)
        ///         ...
        ///             { V v = (V)a[p_0, p_1, ...]; /* body */ }
        /// </summary>
        /// <remarks>
        /// We will follow Dev10 in diverging from the C# 4 spec by ignoring Array's 
        /// implementation of IEnumerable and just indexing into its elements.
        /// 
        /// NOTE: We're assuming that sequence points have already been generated.
        /// Otherwise, lowering to nested for-loops would generated spurious ones.
        /// </remarks>
        private BoundStatement RewriteMultiDimensionalArrayForEachStatement(BoundForEachStatement node)
        {
            ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax;

            BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node);
            Debug.Assert(collectionExpression.Type.IsArray());

            ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type;

            int rank = arrayType.Rank;
            Debug.Assert(!arrayType.IsSZArray);

            TypeSymbol intType = _compilation.GetSpecialType(SpecialType.System_Int32);
            TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean);

            // Values we'll use every iteration
            MethodSymbol getLowerBoundMethod = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_Array__GetLowerBound);
            MethodSymbol getUpperBoundMethod = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_Array__GetUpperBound);

            BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression);
            BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body);

            // A[...] a
            LocalSymbol arrayVar = _factory.SynthesizedLocal(arrayType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArray);
            BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType);

            // A[...] a = /*node.Expression*/;
            BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression);

            AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl);

            // NOTE: dev10 initializes all of the upper bound temps before entering the loop (as opposed to
            // initializing each one at the corresponding level of nesting).  Doing it at the same time as
            // the lower bound would make this code a bit simpler, but it would make it harder to compare
            // the roslyn and dev10 IL.

            // int q_0, q_1, ...
            LocalSymbol[] upperVar = new LocalSymbol[rank];
            BoundLocal[] boundUpperVar = new BoundLocal[rank];
            BoundStatement[] upperVarDecl = new BoundStatement[rank];
            for (int dimension = 0; dimension < rank; dimension++)
            {
                // int q_dimension
                upperVar[dimension] = _factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayLimit);
                boundUpperVar[dimension] = MakeBoundLocal(forEachSyntax, upperVar[dimension], intType);

                ImmutableArray<BoundExpression> dimensionArgument = ImmutableArray.Create(
                    MakeLiteral(forEachSyntax,
                        constantValue: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32),
                        type: intType));

                // a.GetUpperBound(dimension)
                BoundExpression currentDimensionUpperBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getUpperBoundMethod, dimensionArgument);

                // int q_dimension = a.GetUpperBound(dimension);
                upperVarDecl[dimension] = MakeLocalDeclaration(forEachSyntax, upperVar[dimension], currentDimensionUpperBound);
            }

            // int p_0, p_1, ...
            LocalSymbol[] positionVar = new LocalSymbol[rank];
            BoundLocal[] boundPositionVar = new BoundLocal[rank];
            for (int dimension = 0; dimension < rank; dimension++)
            {
                positionVar[dimension] = _factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayIndex);
                boundPositionVar[dimension] = MakeBoundLocal(forEachSyntax, positionVar[dimension], intType);
            }

            // V v
            LocalSymbol iterationVar = node.IterationVariable;
            TypeSymbol iterationVarType = iterationVar.Type;

            // (V)a[p_0, p_1, ...]
            BoundExpression iterationVarInitValue = MakeConversion(
                syntax: forEachSyntax,
                rewrittenOperand: new BoundArrayAccess(forEachSyntax,
                    expression: boundArrayVar,
                    indices: ImmutableArray.Create((BoundExpression[])boundPositionVar),
                    type: arrayType.ElementType),
                conversion: node.ElementConversion,
                rewrittenType: iterationVarType,
                @checked: node.Checked);

            // V v = (V)a[p_0, p_1, ...];
            BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue);

            AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl);

            // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ }

            BoundStatement innermostLoopBody = CreateBlockDeclaringIterationVariable(iterationVar, iterationVarDecl, rewrittenBody, forEachSyntax);

            // work from most-nested to least-nested
            // for (int p_0 = a.GetLowerBound(0); p_0 <= q_0; p_0 = p_0 + 1)
            //     for (int p_1 = a.GetLowerBound(0); p_1 <= q_1; p_1 = p_1 + 1)
            //         ...
            //             { V v = (V)a[p_0, p_1, ...]; /* node.Body */ }
            BoundStatement forLoop = null;
            for (int dimension = rank - 1; dimension >= 0; dimension--)
            {
                ImmutableArray<BoundExpression> dimensionArgument = ImmutableArray.Create(
                    MakeLiteral(forEachSyntax,
                        constantValue: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32),
                        type: intType));

                // a.GetLowerBound(dimension)
                BoundExpression currentDimensionLowerBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getLowerBoundMethod, dimensionArgument);

                // int p_dimension = a.GetLowerBound(dimension);
                BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar[dimension], currentDimensionLowerBound);

                GeneratedLabelSymbol breakLabel = dimension == 0 // outermost for-loop
                    ? node.BreakLabel // i.e. the one that break statements will jump to
                    : new GeneratedLabelSymbol("break"); // Should not affect emitted code since unused

                // p_dimension <= q_dimension  //NB: OrEqual
                BoundExpression exitCondition = new BoundBinaryOperator(
                    syntax: forEachSyntax,
                    operatorKind: BinaryOperatorKind.IntLessThanOrEqual,
                    left: boundPositionVar[dimension],
                    right: boundUpperVar[dimension],
                    constantValueOpt: null,
                    methodOpt: null,
                    resultKind: LookupResultKind.Viable,
                    type: boolType);

                // p_dimension = p_dimension + 1;
                BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar[dimension], intType);

                BoundStatement body;
                GeneratedLabelSymbol continueLabel;

                if (forLoop == null)
                {
                    // innermost for-loop
                    body = innermostLoopBody;
                    continueLabel = node.ContinueLabel; //i.e. the one continue statements will actually jump to
                }
                else
                {
                    body = forLoop;
                    continueLabel = new GeneratedLabelSymbol("continue"); // Should not affect emitted code since unused
                }

                forLoop = RewriteForStatement(
                    syntax: forEachSyntax,
                    outerLocals: ImmutableArray.Create(positionVar[dimension]),
                    rewrittenInitializer: positionVarDecl,
                    rewrittenCondition: exitCondition,
                    conditionSyntaxOpt: null,
                    conditionSpanOpt: forEachSyntax.InKeyword.Span,
                    rewrittenIncrement: positionIncrement,
                    rewrittenBody: body,
                    breakLabel: breakLabel,
                    continueLabel: continueLabel,
                    hasErrors: node.HasErrors);
            }

            Debug.Assert(forLoop != null);

            BoundStatement result = new BoundBlock(
                forEachSyntax,
                ImmutableArray.Create(arrayVar).Concat(upperVar.AsImmutableOrNull()),
                ImmutableArray<LocalFunctionSymbol>.Empty,
                ImmutableArray.Create(arrayVarDecl).Concat(upperVarDecl.AsImmutableOrNull()).Add(forLoop));

            AddForEachKeywordSequencePoint(forEachSyntax, ref result);

            return result;
        }
        /// <summary>
        /// Lowers a lock statement to a try-finally block that calls Monitor.Enter and Monitor.Exit
        /// before and after the body, respectively.
        ///
        /// C# 4.0 version
        ///
        /// L locked;
        /// bool flag = false;
        /// try {
        ///     locked = `argument`;
        ///     Monitor.Enter(locked, ref flag);
        ///     `body`
        /// } finally {
        ///     if (flag) Monitor.Exit(locked);
        /// }
        ///
        /// Pre-4.0 version
        ///
        /// L locked = `argument`;
        /// Monitor.Enter(locked, ref flag);
        /// try {
        ///     `body`
        /// } finally {
        ///     Monitor.Exit(locked);
        /// }
        /// </summary>
        public override BoundNode VisitLockStatement(BoundLockStatement node)
        {
            LockStatementSyntax lockSyntax = (LockStatementSyntax)node.Syntax;

            BoundExpression rewrittenArgument = VisitExpression(node.Argument);
            BoundStatement  rewrittenBody     = (BoundStatement)Visit(node.Body);

            TypeSymbol argumentType = rewrittenArgument.Type;

            if ((object)argumentType == null)
            {
                // This isn't particularly elegant, but hopefully locking on null is
                // not very common.
                Debug.Assert(rewrittenArgument.ConstantValue == ConstantValue.Null);
                argumentType      = this.compilation.GetSpecialType(SpecialType.System_Object);
                rewrittenArgument = MakeLiteral(
                    rewrittenArgument.Syntax,
                    rewrittenArgument.ConstantValue,
                    argumentType); //need to have a non-null type here for TempHelpers.StoreToTemp.
            }
            if (argumentType.Kind == SymbolKind.TypeParameter)
            {
                // If the argument has a type parameter type, then we'll box it right away
                // so that the same object is passed to both Monitor.Enter and Monitor.Exit.
                argumentType = this.compilation.GetSpecialType(SpecialType.System_Object);

                rewrittenArgument = MakeConversion(
                    rewrittenArgument.Syntax,
                    rewrittenArgument,
                    ConversionKind.Boxing,
                    argumentType,
                    @checked: false,
                    constantValueOpt: rewrittenArgument.ConstantValue);
            }

            BoundAssignmentOperator assignmentToLockTemp;
            BoundLocal boundLockTemp = this.factory.StoreToTemp(rewrittenArgument, tempKind: TempKind.Lock, store: out assignmentToLockTemp);

            BoundStatement boundLockTempInit = new BoundExpressionStatement(lockSyntax, assignmentToLockTemp);

            if (this.generateDebugInfo)
            {
                boundLockTempInit = new BoundSequencePointWithSpan( // NOTE: the lock temp is uninitialized at this sequence point.
                    lockSyntax,
                    boundLockTempInit,
                    TextSpan.FromBounds(lockSyntax.LockKeyword.SpanStart, lockSyntax.CloseParenToken.Span.End));
            }

            BoundExpression exitCallExpr;

            MethodSymbol exitMethod;

            if (TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Exit, out exitMethod))
            {
                exitCallExpr = BoundCall.Synthesized(
                    lockSyntax,
                    null,
                    exitMethod,
                    boundLockTemp);
            }
            else
            {
                exitCallExpr = new BoundBadExpression(lockSyntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(boundLockTemp), ErrorTypeSymbol.UnknownResultType);
            }

            BoundStatement exitCall = new BoundExpressionStatement(
                lockSyntax,
                exitCallExpr);

            MethodSymbol enterMethod;

            if ((TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter2, out enterMethod, isOptional: true) ||
                 TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter, out enterMethod)) && // If we didn't find the overload introduced in .NET 4.0, then use the older one.
                enterMethod.ParameterCount == 2)
            {
                // C# 4.0 version
                // L locked;
                // bool flag = false;
                // try {
                //     locked = `argument`;
                //     Monitor.Enter(locked, ref flag);
                //     `body`
                // } finally {
                //     if (flag) Monitor.Exit(locked);
                // }

                TypeSymbol boolType = this.compilation.GetSpecialType(SpecialType.System_Boolean);
                BoundAssignmentOperator assignmentToTemp;
                BoundLocal boundFlagTemp = this.factory.StoreToTemp(
                    MakeLiteral(rewrittenArgument.Syntax, ConstantValue.False, boolType),
                    tempKind: TempKind.LockTaken,
                    store: out assignmentToTemp);

                BoundStatement boundFlagTempInit = new BoundExpressionStatement(lockSyntax, assignmentToTemp);
                if (this.generateDebugInfo)
                {
                    // hide the preamble code, we should not stop until we get to " locked = `argument`; "
                    boundFlagTempInit = new BoundSequencePoint(null, boundFlagTempInit);
                }

                BoundStatement enterCall = new BoundExpressionStatement(
                    lockSyntax,
                    BoundCall.Synthesized(
                        lockSyntax,
                        null,
                        enterMethod,
                        boundLockTemp,
                        boundFlagTemp));

                exitCall = RewriteIfStatement(
                    lockSyntax,
                    ImmutableArray <LocalSymbol> .Empty,
                    boundFlagTemp,
                    exitCall,
                    null,
                    node.HasErrors);

                return(new BoundBlock(
                           lockSyntax,
                           ImmutableArray.Create <LocalSymbol>(boundLockTemp.LocalSymbol, boundFlagTemp.LocalSymbol),
                           ImmutableArray.Create <BoundStatement>(
                               boundFlagTempInit,
                               new BoundTryStatement(
                                   lockSyntax,
                                   BoundBlock.SynthesizedNoLocals(lockSyntax, boundLockTempInit, enterCall, rewrittenBody),
                                   ImmutableArray <BoundCatchBlock> .Empty,
                                   BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall)))));
            }
            else
            {
                BoundExpression enterCallExpr;

                if ((object)enterMethod != null)
                {
                    Debug.Assert(enterMethod.ParameterCount == 1);
                    // Pre-4.0 version
                    // L locked = `argument`;
                    // Monitor.Enter(locked, ref flag); //NB: before try-finally so we don't Exit if an exception prevents us from acquiring the lock.
                    // try {
                    //     `body`
                    // } finally {
                    //     Monitor.Exit(locked);
                    // }

                    enterCallExpr = BoundCall.Synthesized(
                        lockSyntax,
                        null,
                        enterMethod,
                        boundLockTemp);
                }
                else
                {
                    enterCallExpr = new BoundBadExpression(lockSyntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(boundLockTemp), ErrorTypeSymbol.UnknownResultType);
                }

                BoundStatement enterCall = new BoundExpressionStatement(
                    lockSyntax,
                    enterCallExpr);

                return(new BoundBlock(
                           lockSyntax,
                           ImmutableArray.Create <LocalSymbol>(boundLockTemp.LocalSymbol),
                           ImmutableArray.Create <BoundStatement>(
                               boundLockTempInit,
                               enterCall,
                               new BoundTryStatement(
                                   lockSyntax,
                                   BoundBlock.SynthesizedNoLocals(lockSyntax, rewrittenBody),
                                   ImmutableArray <BoundCatchBlock> .Empty,
                                   BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall)))));
            }
        }
        public override BoundNode VisitLocal(BoundLocal node)
        {
            var catchFrame = _currentAwaitCatchFrame;
            LocalSymbol hoistedLocal;
            if (catchFrame == null || !catchFrame.TryGetHoistedLocal(node.LocalSymbol, out hoistedLocal))
            {
                return base.VisitLocal(node);
            }

            return node.Update(hoistedLocal, node.ConstantValueOpt, hoistedLocal.Type);
        }
        /// <summary>
        /// Prepares local variables to be used in Deconstruct call
        /// Adds a invocation of Deconstruct with those as out parameters onto the 'stores' sequence
        /// Returns the expressions for those out parameters
        /// </summary>
        private void CallDeconstruct(BoundDeconstructionAssignmentOperator node, BoundDeconstructionDeconstructStep deconstruction, ArrayBuilder<LocalSymbol> temps, ArrayBuilder<BoundExpression> stores, ArrayBuilder<BoundValuePlaceholderBase> placeholders)
        {
            Debug.Assert((object)deconstruction.DeconstructInvocationOpt != null);

            CSharpSyntaxNode syntax = node.Syntax;

            // prepare out parameters for Deconstruct
            var deconstructParameters = deconstruction.OutputPlaceholders;
            var outParametersBuilder = ArrayBuilder<BoundExpression>.GetInstance(deconstructParameters.Length);

            for (var i = 0; i < deconstructParameters.Length; i++)
            {
                var deconstructParameter = deconstructParameters[i];
                var localSymbol = new SynthesizedLocal(_factory.CurrentMethod, deconstructParameter.Type, SynthesizedLocalKind.LoweringTemp);

                var localBound = new BoundLocal(syntax,
                                                localSymbol,
                                                null,
                                                deconstructParameter.Type
                                                )
                { WasCompilerGenerated = true };

                temps.Add(localSymbol);
                outParametersBuilder.Add(localBound);

                AddPlaceholderReplacement(deconstruction.OutputPlaceholders[i], localBound);
                placeholders.Add(deconstruction.OutputPlaceholders[i]);
            }

            var outParameters = outParametersBuilder.ToImmutableAndFree();

            // invoke Deconstruct with placeholders replaced by locals
            stores.Add(VisitExpression(deconstruction.DeconstructInvocationOpt));
        }
 public override BoundNode VisitLocal(BoundLocal node)
 {
     ReferenceVariable(node.Syntax, node.LocalSymbol);
     return base.VisitLocal(node);
 }
        private BoundStatement RewriteUsingStatementTryFinally(CSharpSyntaxNode syntax, BoundBlock tryBlock, BoundLocal local)
        {
            // SPEC: When ResourceType is a non-nullable value type, the expansion is:
            // SPEC: 
            // SPEC: { 
            // SPEC:   ResourceType resource = expr; 
            // SPEC:   try { statement; } 
            // SPEC:   finally { ((IDisposable)resource).Dispose(); }
            // SPEC: }
            // SPEC:
            // SPEC: Otherwise, when Resource type is a nullable value type or
            // SPEC: a reference type other than dynamic, the expansion is:
            // SPEC: 
            // SPEC: { 
            // SPEC:   ResourceType resource = expr; 
            // SPEC:   try { statement; } 
            // SPEC:   finally { if (resource != null) ((IDisposable)resource).Dispose(); }
            // SPEC: }
            // SPEC: 
            // SPEC: Otherwise, when ResourceType is dynamic, the expansion is:
            // SPEC: { 
            // SPEC:   dynamic resource = expr; 
            // SPEC:   IDisposable d = (IDisposable)resource;
            // SPEC:   try { statement; } 
            // SPEC:   finally { if (d != null) d.Dispose(); }
            // SPEC: }
            // SPEC: 
            // SPEC: An implementation is permitted to implement a given using statement 
            // SPEC: differently -- for example, for performance reasons -- as long as the 
            // SPEC: behavior is consistent with the above expansion.
            //
            // And we do in fact generate the code slightly differently than precisely how it is 
            // described above.
            //
            // First: if the type is a non-nullable value type then we do not do the 
            // *boxing conversion* from the resource to IDisposable. Rather, we do
            // a *constrained virtual call* that elides the boxing if possible. 
            //
            // Now, you might wonder if that is legal; isn't skipping the boxing producing
            // an observable difference? Because if the value type is mutable and the Dispose
            // mutates it, then skipping the boxing means that we are now mutating the original,
            // not the boxed copy. But this is never observable. Either (1) we have "using(R r = x){}"
            // and r is out of scope after the finally, so it is not possible to observe the mutation,
            // or (2) we have "using(x) {}". But that has the semantics of "using(R temp = x){}",
            // so again, we are not mutating x to begin with; we're always mutating a copy. Therefore
            // it doesn't matter if we skip making *a copy of the copy*.
            //
            // This is what the dev10 compiler does, and we do so as well.
            //
            // Second: if the type is a nullable value type then we can similarly elide the boxing.
            // We can generate
            //
            // { 
            //   ResourceType resource = expr; 
            //   try { statement; } 
            //   finally { if (resource.HasValue) resource.GetValueOrDefault().Dispose(); }
            // }
            //
            // Where again we do a constrained virtual call to Dispose, rather than boxing
            // the value to IDisposable.
            //
            // Note that this optimization is *not* what the native compiler does; in this case
            // the native compiler behavior is to test for HasValue, then *box* and convert
            // the boxed value to IDisposable. There's no need to do that.
            //
            // Third: if we have "using(x)" and x is dynamic then obviously we need not generate
            // "{ dynamic temp1 = x; IDisposable temp2 = (IDisposable) temp1; ... }". Rather, we elide
            // the completely unnecessary first temporary. 

            BoundExpression disposedExpression;
            bool isNullableValueType = local.Type.IsNullableType();

            if (isNullableValueType)
            {
                MethodSymbol getValueOrDefault = GetNullableMethod(syntax, local.Type, SpecialMember.System_Nullable_T_GetValueOrDefault);
                // local.GetValueOrDefault()
                disposedExpression = BoundCall.Synthesized(syntax, local, getValueOrDefault);
            }
            else
            {
                // local
                disposedExpression = local;
            }

            // local.Dispose()
            BoundExpression disposeCall;

            MethodSymbol disposeMethodSymbol;
            if (TryGetSpecialTypeMember(syntax, SpecialMember.System_IDisposable__Dispose, out disposeMethodSymbol))
            {
                disposeCall = BoundCall.Synthesized(syntax, disposedExpression, disposeMethodSymbol);
            }
            else
            {
                disposeCall = new BoundBadExpression(syntax, LookupResultKind.NotInvocable, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(disposedExpression), ErrorTypeSymbol.UnknownResultType);
            }

            // local.Dispose();
            BoundStatement disposeStatement = new BoundExpressionStatement(syntax, disposeCall);

            BoundExpression ifCondition;

            if (isNullableValueType)
            {
                MethodSymbol hasValue = GetNullableMethod(syntax, local.Type, SpecialMember.System_Nullable_T_get_HasValue);
                // local.HasValue
                ifCondition = BoundCall.Synthesized(syntax, local, hasValue);
            }
            else if (local.Type.IsValueType)
            {
                ifCondition = null;
            }
            else
            {
                // local != null
                ifCondition = MakeNullCheck(syntax, local, BinaryOperatorKind.NotEqual);
            }

            BoundStatement finallyStatement;

            if (ifCondition == null)
            {
                // local.Dispose();
                finallyStatement = disposeStatement;
            }
            else
            {
                // if (local != null) local.Dispose();
                // or
                // if (local.HasValue) local.GetValueOrDefault().Dispose();
                finallyStatement = RewriteIfStatement(
                    syntax: syntax,
                    locals: ImmutableArray<LocalSymbol>.Empty,
                    rewrittenCondition: ifCondition,
                    rewrittenConsequence: disposeStatement,
                    rewrittenAlternativeOpt: null,
                    hasErrors: false);
            }

            // try { ... } finally { if (local != null) local.Dispose(); }
            BoundStatement tryFinally = new BoundTryStatement(
                syntax: syntax,
                tryBlock: tryBlock,
                catchBlocks: ImmutableArray<BoundCatchBlock>.Empty,
                finallyBlockOpt: BoundBlock.SynthesizedNoLocals(syntax, finallyStatement));

            return tryFinally;
        }
        /// <summary>
        /// Takes an expression and returns the bound local expression "temp" 
        /// and the bound assignment expression "temp = expr".
        /// </summary>
        public BoundLocal StoreToTemp(BoundExpression argument, out BoundAssignmentOperator store, RefKind refKind = RefKind.None, SynthesizedLocalKind kind = SynthesizedLocalKind.LoweringTemp)
        {
            MethodSymbol containingMethod = this.CurrentMethod;
            var syntax = argument.Syntax;
            var type = argument.Type;

            var local = new BoundLocal(
                syntax,
                new SynthesizedLocal(containingMethod, type, kind, syntax: kind.IsLongLived() ? syntax : null, refKind: refKind),
                null,
                type);

            store = new BoundAssignmentOperator(
                syntax,
                local,
                argument,
                refKind,
                type);

            return local;
        }
Exemple #49
0
        /// <summary>
        /// Lower a foreach loop that will enumerate the characters of a string.
        ///
        /// string s = x;
        /// for (int p = 0; p &lt; s.Length; p = p + 1) {
        ///     V v = (V)s.Chars[p];
        ///     // body
        /// }
        /// </summary>
        /// <remarks>
        /// We will follow Dev10 in diverging from the C# 4 spec by ignoring string's
        /// implementation of IEnumerable and just indexing into its characters.
        ///
        /// NOTE: We're assuming that sequence points have already been generated.
        /// Otherwise, lowering to for-loops would generated spurious ones.
        /// </remarks>
        private BoundStatement RewriteStringForEachStatement(BoundForEachStatement node)
        {
            ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax;

            BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node);
            TypeSymbol      stringType           = collectionExpression.Type;

            Debug.Assert(stringType.SpecialType == SpecialType.System_String);

            TypeSymbol intType  = compilation.GetSpecialType(SpecialType.System_Int32);
            TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean);

            BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression);
            BoundStatement  rewrittenBody       = (BoundStatement)Visit(node.Body);

            // string s;
            LocalSymbol stringVar = factory.SynthesizedLocal(stringType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArray);
            // int p;
            LocalSymbol positionVar = factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayIndex0);

            // Reference to s.
            BoundLocal boundStringVar = MakeBoundLocal(forEachSyntax, stringVar, stringType);

            // Reference to p.
            BoundLocal boundPositionVar = MakeBoundLocal(forEachSyntax, positionVar, intType);

            // string s = /*expr*/;
            BoundStatement stringVarDecl = MakeLocalDeclaration(forEachSyntax, stringVar, rewrittenExpression);

            AddForEachExpressionSequencePoint(forEachSyntax, ref stringVarDecl);

            // int p = 0;
            BoundStatement positionVariableDecl = MakeLocalDeclaration(forEachSyntax, positionVar,
                                                                       MakeLiteral(forEachSyntax, ConstantValue.Default(SpecialType.System_Int32), intType));

            // string s = /*node.Expression*/; int p = 0;
            BoundStatement initializer = new BoundStatementList(forEachSyntax,
                                                                statements: ImmutableArray.Create <BoundStatement>(stringVarDecl, positionVariableDecl));

            MethodSymbol    method       = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_String__Length);
            BoundExpression stringLength = BoundCall.Synthesized(
                syntax: forEachSyntax,
                receiverOpt: boundStringVar,
                method: method,
                arguments: ImmutableArray <BoundExpression> .Empty);

            // p < s.Length
            BoundExpression exitCondition = new BoundBinaryOperator(
                syntax: forEachSyntax,
                operatorKind: BinaryOperatorKind.IntLessThan,
                left: boundPositionVar,
                right: stringLength,
                constantValueOpt: null,
                methodOpt: null,
                resultKind: LookupResultKind.Viable,
                type: boolType);

            // p = p + 1;
            BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar, intType);

            LocalSymbol iterationVar     = node.IterationVariable;
            TypeSymbol  iterationVarType = iterationVar.Type;

            Debug.Assert(node.ElementConversion.IsValid);

            // (V)s.Chars[p]
            MethodSymbol    chars = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_String__Chars);
            BoundExpression iterationVarInitValue = MakeConversion(
                syntax: forEachSyntax,
                rewrittenOperand: BoundCall.Synthesized(
                    syntax: forEachSyntax,
                    receiverOpt: boundStringVar,
                    method: chars,
                    arguments: ImmutableArray.Create <BoundExpression>(boundPositionVar)),
                conversion: node.ElementConversion,
                rewrittenType: iterationVarType,
                @checked: node.Checked);

            // V v = (V)s.Chars[p];
            BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue);

            AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl);

            // { V v = (V)s.Chars[p]; /*node.Body*/ }
            BoundStatement loopBody = new BoundBlock(forEachSyntax,
                                                     locals: ImmutableArray.Create <LocalSymbol>(iterationVar),
                                                     statements: ImmutableArray.Create <BoundStatement>(iterationVarDecl, rewrittenBody));

            // for (string s = /*node.Expression*/, int p = 0; p < s.Length; p = p + 1) {
            //     V v = (V)s.Chars[p];
            //     /*node.Body*/
            // }
            BoundStatement result = RewriteForStatement(
                syntax: forEachSyntax,
                outerLocals: ImmutableArray.Create(stringVar, positionVar),
                rewrittenInitializer: initializer,
                rewrittenCondition: exitCondition,
                conditionSyntaxOpt: null,
                conditionSpanOpt: forEachSyntax.InKeyword.Span,
                rewrittenIncrement: positionIncrement,
                rewrittenBody: loopBody,
                breakLabel: node.BreakLabel,
                continueLabel: node.ContinueLabel,
                hasErrors: node.HasErrors);

            AddForEachKeywordSequencePoint(forEachSyntax, ref result);

            return(result);
        }
 public override BoundNode VisitLocal(BoundLocal node)
 {
     Capture(node.LocalSymbol, node.Syntax);
     return base.VisitLocal(node);
 }
Exemple #51
0
        /// <summary>
        /// Lower a foreach loop that will enumerate a multi-dimensional array.
        ///
        /// A[...] a = x;
        /// int q_0 = a.GetUpperBound(0), q_1 = a.GetUpperBound(1), ...;
        /// for (int p_0 = a.GetLowerBound(0); p_0 &lt;= q_0; p_0 = p_0 + 1)
        ///     for (int p_1 = a.GetLowerBound(1); p_1 &lt;= q_1; p_1 = p_1 + 1)
        ///         ...
        ///             { V v = (V)a[p_0, p_1, ...]; /* body */ }
        /// </summary>
        /// <remarks>
        /// We will follow Dev10 in diverging from the C# 4 spec by ignoring Array's
        /// implementation of IEnumerable and just indexing into its elements.
        ///
        /// NOTE: We're assuming that sequence points have already been generated.
        /// Otherwise, lowering to nested for-loops would generated spurious ones.
        /// </remarks>
        private BoundStatement RewriteMultiDimensionalArrayForEachStatement(BoundForEachStatement node)
        {
            ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax;

            BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node);

            Debug.Assert(collectionExpression.Type.IsArray());

            ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type;

            int rank = arrayType.Rank;

            Debug.Assert(rank > 1);

            TypeSymbol intType  = compilation.GetSpecialType(SpecialType.System_Int32);
            TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean);

            // Values we'll use every iteration
            MethodSymbol getLowerBoundMethod = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_Array__GetLowerBound);
            MethodSymbol getUpperBoundMethod = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_Array__GetUpperBound);

            BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression);
            BoundStatement  rewrittenBody       = (BoundStatement)Visit(node.Body);

            // A[...] a
            LocalSymbol arrayVar      = factory.SynthesizedLocal(arrayType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArray);
            BoundLocal  boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType);

            // A[...] a = /*node.Expression*/;
            BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression);

            AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl);

            // NOTE: dev10 initializes all of the upper bound temps before entering the loop (as opposed to
            // initializing each one at the corresponding level of nesting).  Doing it at the same time as
            // the lower bound would make this code a bit simpler, but it would make it harder to compare
            // the roslyn and dev10 IL.

            // int q_0, q_1, ...
            LocalSymbol[]    upperVar      = new LocalSymbol[rank];
            BoundLocal[]     boundUpperVar = new BoundLocal[rank];
            BoundStatement[] upperVarDecl  = new BoundStatement[rank];
            for (int dimension = 0; dimension < rank; dimension++)
            {
                // int q_/*dimension*/
                upperVar[dimension] = factory.SynthesizedLocal(
                    intType,
                    syntax: forEachSyntax,
                    kind: (SynthesizedLocalKind)((int)SynthesizedLocalKind.ForEachArrayLimit0 + dimension));
                boundUpperVar[dimension] = MakeBoundLocal(forEachSyntax, upperVar[dimension], intType);

                ImmutableArray <BoundExpression> dimensionArgument = ImmutableArray.Create <BoundExpression>(
                    MakeLiteral(forEachSyntax,
                                constantValue: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32),
                                type: intType));

                // a.GetUpperBound(/*dimension*/)
                BoundExpression currentDimensionUpperBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getUpperBoundMethod, dimensionArgument);

                // int q_/*dimension*/ = a.GetUpperBound(/*dimension*/);
                upperVarDecl[dimension] = MakeLocalDeclaration(forEachSyntax, upperVar[dimension], currentDimensionUpperBound);
            }

            // int p_0, p_1, ...
            LocalSymbol[] positionVar      = new LocalSymbol[rank];
            BoundLocal[]  boundPositionVar = new BoundLocal[rank];
            for (int dimension = 0; dimension < rank; dimension++)
            {
                positionVar[dimension] = factory.SynthesizedLocal(
                    intType,
                    syntax: forEachSyntax,
                    kind: (SynthesizedLocalKind)((int)SynthesizedLocalKind.ForEachArrayIndex0 + dimension));
                boundPositionVar[dimension] = MakeBoundLocal(forEachSyntax, positionVar[dimension], intType);
            }

            // V v
            LocalSymbol iterationVar     = node.IterationVariable;
            TypeSymbol  iterationVarType = iterationVar.Type;

            // (V)a[p_0, p_1, ...]
            BoundExpression iterationVarInitValue = MakeConversion(
                syntax: forEachSyntax,
                rewrittenOperand: new BoundArrayAccess(forEachSyntax,
                                                       expression: boundArrayVar,
                                                       indices: ImmutableArray.Create <BoundExpression>((BoundExpression[])boundPositionVar),
                                                       type: arrayType.ElementType),
                conversion: node.ElementConversion,
                rewrittenType: iterationVarType,
                @checked: node.Checked);

            // V v = (V)a[p_0, p_1, ...];
            BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue);

            AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl);

            // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ }
            BoundStatement innermostLoopBody = new BoundBlock(forEachSyntax,
                                                              locals: ImmutableArray.Create <LocalSymbol>(iterationVar),
                                                              statements: ImmutableArray.Create <BoundStatement>(iterationVarDecl, rewrittenBody));

            // work from most-nested to least-nested
            // for (int p_0 = a.GetLowerBound(0); p_0 <= q_0; p_0 = p_0 + 1)
            //     for (int p_1 = a.GetLowerBound(0); p_1 <= q_1; p_1 = p_1 + 1)
            //         ...
            //             { V v = (V)a[p_0, p_1, ...]; /* node.Body */ }
            BoundStatement forLoop = null;

            for (int dimension = rank - 1; dimension >= 0; dimension--)
            {
                ImmutableArray <BoundExpression> dimensionArgument = ImmutableArray.Create <BoundExpression>(
                    MakeLiteral(forEachSyntax,
                                constantValue: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32),
                                type: intType));

                // a.GetLowerBound(/*dimension*/)
                BoundExpression currentDimensionLowerBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getLowerBoundMethod, dimensionArgument);

                // int p_/*dimension*/ = a.GetLowerBound(/*dimension*/);
                BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar[dimension], currentDimensionLowerBound);

                GeneratedLabelSymbol breakLabel = dimension == 0 // outermost for-loop
                    ? node.BreakLabel                            // i.e. the one that break statements will jump to
                    : new GeneratedLabelSymbol("break");         // Should not affect emitted code since unused

                // p_/*dimension*/ <= q_/*dimension*/  //NB: OrEqual
                BoundExpression exitCondition = new BoundBinaryOperator(
                    syntax: forEachSyntax,
                    operatorKind: BinaryOperatorKind.IntLessThanOrEqual,
                    left: boundPositionVar[dimension],
                    right: boundUpperVar[dimension],
                    constantValueOpt: null,
                    methodOpt: null,
                    resultKind: LookupResultKind.Viable,
                    type: boolType);

                // p_/*dimension*/ = p_/*dimension*/ + 1;
                BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar[dimension], intType);

                BoundStatement       body;
                GeneratedLabelSymbol continueLabel;

                if (forLoop == null)
                {
                    // innermost for-loop
                    body          = innermostLoopBody;
                    continueLabel = node.ContinueLabel; //i.e. the one continue statements will actually jump to
                }
                else
                {
                    body          = forLoop;
                    continueLabel = new GeneratedLabelSymbol("continue"); // Should not affect emitted code since unused
                }

                forLoop = RewriteForStatement(
                    syntax: forEachSyntax,
                    outerLocals: ImmutableArray.Create(positionVar[dimension]),
                    rewrittenInitializer: positionVarDecl,
                    rewrittenCondition: exitCondition,
                    conditionSyntaxOpt: null,
                    conditionSpanOpt: forEachSyntax.InKeyword.Span,
                    rewrittenIncrement: positionIncrement,
                    rewrittenBody: body,
                    breakLabel: breakLabel,
                    continueLabel: continueLabel,
                    hasErrors: node.HasErrors);
            }

            Debug.Assert(forLoop != null);

            BoundStatement result = new BoundBlock(
                forEachSyntax,
                ImmutableArray.Create <LocalSymbol>(arrayVar).Concat(upperVar.AsImmutableOrNull()),
                ImmutableArray.Create <BoundStatement>(arrayVarDecl).Concat(upperVarDecl.AsImmutableOrNull()).Add(forLoop));

            AddForEachKeywordSequencePoint(forEachSyntax, ref result);

            return(result);
        }
        /// <summary>
        /// Lower "using (ResourceType resource = expression) statement" to a try-finally block.
        /// </summary>
        /// <remarks>
        /// Assumes that the local symbol will be declared (i.e. in the LocalsOpt array) of an enclosing block.
        /// Assumes that using statements with multiple locals have already been split up into multiple using statements.
        /// </remarks>
        private BoundBlock RewriteDeclarationUsingStatement(CSharpSyntaxNode usingSyntax, BoundLocalDeclaration localDeclaration, BoundBlock tryBlock, Conversion idisposableConversion)
        {
            CSharpSyntaxNode declarationSyntax = localDeclaration.Syntax;

            LocalSymbol localSymbol = localDeclaration.LocalSymbol;
            TypeSymbol localType = localSymbol.Type;
            Debug.Assert((object)localType != null); //otherwise, there wouldn't be a conversion to IDisposable

            BoundLocal boundLocal = new BoundLocal(declarationSyntax, localSymbol, localDeclaration.InitializerOpt.ConstantValue, localType);

            BoundStatement rewrittenDeclaration = (BoundStatement)Visit(localDeclaration);

            // If we know that the expression is null, then we know that the null check in the finally block
            // will fail, and the Dispose call will never happen.  That is, the finally block will have no effect.
            // Consequently, we can simply skip the whole try-finally construct and just create a block containing
            // the new declaration.
            if (boundLocal.ConstantValue == ConstantValue.Null)
            {
                //localSymbol will be declared by an enclosing block
                return BoundBlock.SynthesizedNoLocals(usingSyntax, rewrittenDeclaration, tryBlock);
            }

            if (localType.IsDynamic())
            {
                BoundExpression tempInit = MakeConversion(
                    declarationSyntax,
                    boundLocal,
                    idisposableConversion,
                    compilation.GetSpecialType(SpecialType.System_IDisposable),
                    @checked: false);

                BoundAssignmentOperator tempAssignment;
                BoundLocal boundTemp = this.factory.StoreToTemp(tempInit, out tempAssignment);

                BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundTemp);

                return new BoundBlock(
                    syntax: usingSyntax,
                    locals: ImmutableArray.Create<LocalSymbol>(boundTemp.LocalSymbol), //localSymbol will be declared by an enclosing block
                    statements: ImmutableArray.Create<BoundStatement>(
                        rewrittenDeclaration,
                        new BoundExpressionStatement(declarationSyntax, tempAssignment),
                        tryFinally));
            }
            else
            {
                BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundLocal);

                // localSymbol will be declared by an enclosing block
                return BoundBlock.SynthesizedNoLocals(usingSyntax, rewrittenDeclaration, tryFinally);
            }
        }
 private void CheckOutVarDeclaration(BoundLocal node)
 {
     if (IsInside &&
         !node.WasCompilerGenerated && node.Syntax.Kind() == SyntaxKind.DeclarationExpression)
     {
         var declaration = (DeclarationExpressionSyntax)node.Syntax;
         if (declaration.Designation.Kind() == SyntaxKind.SingleVariableDesignation &&
             ((SingleVariableDesignationSyntax)declaration.Designation).Identifier == node.LocalSymbol.IdentifierToken &&
             declaration.Parent != null &&
             declaration.Parent.Kind() == SyntaxKind.Argument &&
             ((ArgumentSyntax)declaration.Parent).RefOrOutKeyword.Kind() == SyntaxKind.OutKeyword)
         {
             _variablesDeclared.Add(node.LocalSymbol);
         }
     }
 }
 /// <summary>
 /// Called when a local represents an out variable declaration. Its syntax is of type DeclarationExpressionSyntax.
 /// </summary>
 private void CheckOutDeclaration(BoundLocal local, Symbol method)
 {
     if (_inExpressionLambda)
     {
         Error(ErrorCode.ERR_ExpressionTreeContainsOutVariable, local);
     }
 }
Exemple #55
0
        private BoundExpression MakeNullCoalescingOperator(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenLeft,
            BoundExpression rewrittenRight,
            Conversion leftConversion,
            TypeSymbol rewrittenResultType)
        {
            Debug.Assert(rewrittenLeft != null);
            Debug.Assert(rewrittenRight != null);
            Debug.Assert(leftConversion.IsValid);
            Debug.Assert((object)rewrittenResultType != null);
            Debug.Assert(rewrittenRight.Type.Equals(rewrittenResultType, ignoreDynamic: true));

            if (inExpressionLambda)
            {
                TypeSymbol strippedLeftType    = rewrittenLeft.Type.StrippedType();
                Conversion rewrittenConversion = MakeConversion(syntax, leftConversion, strippedLeftType, rewrittenResultType);
                return(new BoundNullCoalescingOperator(syntax, rewrittenLeft, rewrittenRight, rewrittenConversion, rewrittenResultType));
            }

            // first we can make a small optimization:

            ConstantValue leftConstantValue = rewrittenLeft.ConstantValue;

            if (leftConstantValue != null)
            {
                // If left is a constant then we already know whether it is null or not. If it is null then we
                // can simply generate "right". If it is not null then we can simply generate
                // MakeConversion(left).

                return(leftConstantValue.IsNull ?
                       rewrittenRight :
                       GetConvertedLeftForNullCoalescingOperator(rewrittenLeft, leftConversion, rewrittenResultType));
            }

            // if left conversion is intrinsic implicit (always succeeds) and results in a reference type
            // we can apply conversion before doing the null check that allows for a more efficient IL emit.
            if (rewrittenLeft.Type.IsReferenceType &&
                leftConversion.IsImplicit &&
                !leftConversion.IsUserDefined)
            {
                if (!leftConversion.IsIdentity)
                {
                    rewrittenLeft = MakeConversion(rewrittenLeft.Syntax, rewrittenLeft, leftConversion, rewrittenResultType, @checked: false);
                }
                return(new BoundNullCoalescingOperator(syntax, rewrittenLeft, rewrittenRight, Conversion.Identity, rewrittenResultType));
            }

            // We lower left ?? right to
            //
            // var temp = left;
            // (temp != null) ? MakeConversion(temp) : right
            //

            BoundAssignmentOperator tempAssignment;
            BoundLocal boundTemp = factory.StoreToTemp(rewrittenLeft, out tempAssignment);

            // temp != null
            BoundExpression nullCheck = MakeNullCheck(syntax, boundTemp, BinaryOperatorKind.NotEqual);

            // MakeConversion(temp, rewrittenResultType)
            BoundExpression convertedLeft = GetConvertedLeftForNullCoalescingOperator(boundTemp, leftConversion, rewrittenResultType);

            Debug.Assert(convertedLeft.Type.Equals(rewrittenResultType, ignoreDynamic: true));

            // (temp != null) ? MakeConversion(temp, LeftConversion) : RightOperand
            BoundExpression conditionalExpression = RewriteConditionalOperator(
                syntax: syntax,
                rewrittenCondition: nullCheck,
                rewrittenConsequence: convertedLeft,
                rewrittenAlternative: rewrittenRight,
                constantValueOpt: null,
                rewrittenType: rewrittenResultType);

            Debug.Assert(conditionalExpression.ConstantValue == null); // we shouldn't have hit this else case otherwise
            Debug.Assert(conditionalExpression.Type.Equals(rewrittenResultType, ignoreDynamic: true));

            return(new BoundSequence(
                       syntax: syntax,
                       locals: ImmutableArray.Create(boundTemp.LocalSymbol),
                       sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment),
                       value: conditionalExpression,
                       type: rewrittenResultType));
        }