File: Compiler\MethodBodySynthesizer.cs
Web Access
Project: src\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.RuntimeMembers;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    /// <summary>
    /// Contains methods related to synthesizing bound nodes in initial binding
    /// form that needs lowering, primarily method bodies for compiler-generated methods.
    /// </summary>
    internal static class MethodBodySynthesizer
    {
        internal static ImmutableArray<BoundStatement> ConstructScriptConstructorBody(
            BoundStatement loweredBody,
            MethodSymbol constructor,
            SynthesizedSubmissionFields previousSubmissionFields,
            CSharpCompilation compilation)
        {
            // Script field initializers have to be emitted after the call to the base constructor because they can refer to "this" instance.
            //
            // Unlike regular field initializers, initializers of global script variables can access "this" instance.
            // If the base class had a constructor that initializes its state a global variable would access partially initialized object.
            // For this reason Script class must always derive directly from a class that has no state (System.Object).
 
            SyntaxNode syntax = loweredBody.Syntax;
 
            // base constructor call:
            Debug.Assert((object)constructor.ContainingType.BaseTypeNoUseSiteDiagnostics == null || constructor.ContainingType.BaseTypeNoUseSiteDiagnostics.SpecialType == SpecialType.System_Object);
            var objectType = constructor.ContainingAssembly.GetSpecialType(SpecialType.System_Object);
 
            BoundExpression receiver = new BoundThisReference(syntax, constructor.ContainingType) { WasCompilerGenerated = true };
 
            BoundStatement baseConstructorCall =
                new BoundExpressionStatement(syntax,
                    new BoundCall(syntax,
                        receiverOpt: receiver,
                        initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown,
                        method: objectType.InstanceConstructors[0],
                        arguments: ImmutableArray<BoundExpression>.Empty,
                        argumentNamesOpt: ImmutableArray<string>.Empty,
                        argumentRefKindsOpt: ImmutableArray<RefKind>.Empty,
                        isDelegateCall: false,
                        expanded: false,
                        invokedAsExtensionMethod: false,
                        argsToParamsOpt: ImmutableArray<int>.Empty,
                        defaultArguments: BitVector.Empty,
                        resultKind: LookupResultKind.Viable,
                        type: objectType)
                    { WasCompilerGenerated = true })
                { WasCompilerGenerated = true };
 
            var statements = ArrayBuilder<BoundStatement>.GetInstance();
            statements.Add(baseConstructorCall);
 
            if (constructor.IsSubmissionConstructor)
            {
                // submission initialization:
                MakeSubmissionInitialization(statements, syntax, constructor, previousSubmissionFields, compilation);
            }
 
            statements.Add(loweredBody);
            return statements.ToImmutableAndFree();
        }
 
        /// <summary>
        /// Generates a submission initialization part of a Script type constructor that represents an interactive submission.
        /// </summary>
        /// <remarks>
        /// The constructor takes a parameter of type Microsoft.CodeAnalysis.Scripting.Session - the session reference.
        /// It adds the object being constructed into the session by calling Microsoft.CSharp.RuntimeHelpers.SessionHelpers.SetSubmission,
        /// and retrieves strongly typed references on all previous submission script classes whose members are referenced by this submission.
        /// The references are stored to fields of the submission (<paramref name="synthesizedFields"/>).
        /// </remarks>
        private static void MakeSubmissionInitialization(
            ArrayBuilder<BoundStatement> statements,
            SyntaxNode syntax,
            MethodSymbol submissionConstructor,
            SynthesizedSubmissionFields synthesizedFields,
            CSharpCompilation compilation)
        {
            Debug.Assert(submissionConstructor.ParameterCount == 1);
 
            var submissionArrayReference = new BoundParameter(syntax, submissionConstructor.Parameters[0]) { WasCompilerGenerated = true };
 
            var intType = compilation.GetSpecialType(SpecialType.System_Int32);
            var objectType = compilation.GetSpecialType(SpecialType.System_Object);
            var thisReference = new BoundThisReference(syntax, submissionConstructor.ContainingType) { WasCompilerGenerated = true };
 
            var slotIndex = compilation.GetSubmissionSlotIndex();
            Debug.Assert(slotIndex >= 0);
 
            // <submission_array>[<slot_index] = this;
            statements.Add(new BoundExpressionStatement(syntax,
                new BoundAssignmentOperator(syntax,
                    new BoundArrayAccess(syntax,
                        submissionArrayReference,
                        ImmutableArray.Create<BoundExpression>(new BoundLiteral(syntax, ConstantValue.Create(slotIndex), intType) { WasCompilerGenerated = true }),
                        objectType)
                    { WasCompilerGenerated = true },
                    thisReference,
                    false,
                    thisReference.Type)
                { WasCompilerGenerated = true })
            { WasCompilerGenerated = true });
 
            var hostObjectField = synthesizedFields.GetHostObjectField();
            if ((object)hostObjectField != null)
            {
                // <host_object> = (<host_object_type>)<submission_array>[0]
                statements.Add(
                    new BoundExpressionStatement(syntax,
                        new BoundAssignmentOperator(syntax,
                            new BoundFieldAccess(syntax, thisReference, hostObjectField, ConstantValue.NotAvailable) { WasCompilerGenerated = true },
                            BoundConversion.Synthesized(syntax,
                                new BoundArrayAccess(syntax,
                                    submissionArrayReference,
                                    ImmutableArray.Create<BoundExpression>(new BoundLiteral(syntax, ConstantValue.Create(0), intType) { WasCompilerGenerated = true }),
                                    objectType),
                                Conversion.ExplicitReference,
                                false,
                                explicitCastInCode: true,
                                conversionGroupOpt: null,
                                ConstantValue.NotAvailable,
                                hostObjectField.Type
                            ),
                            hostObjectField.Type)
                        { WasCompilerGenerated = true })
                    { WasCompilerGenerated = true });
            }
 
            foreach (var field in synthesizedFields.FieldSymbols)
            {
                var targetScriptType = (ImplicitNamedTypeSymbol)field.Type;
                var targetSubmissionIndex = targetScriptType.DeclaringCompilation.GetSubmissionSlotIndex();
                Debug.Assert(targetSubmissionIndex >= 0);
 
                // this.<field> = (<target_script_type>)<submission_array>[<target_submission_index>];
                statements.Add(
                    new BoundExpressionStatement(syntax,
                        new BoundAssignmentOperator(syntax,
                            new BoundFieldAccess(syntax, thisReference, field, ConstantValue.NotAvailable) { WasCompilerGenerated = true },
                            BoundConversion.Synthesized(syntax,
                                new BoundArrayAccess(syntax,
                                    submissionArrayReference,
                                    ImmutableArray.Create<BoundExpression>(new BoundLiteral(syntax, ConstantValue.Create(targetSubmissionIndex), intType) { WasCompilerGenerated = true }),
                                    objectType)
                                { WasCompilerGenerated = true },
                                Conversion.ExplicitReference,
                                false,
                                explicitCastInCode: true,
                                conversionGroupOpt: null,
                                ConstantValue.NotAvailable,
                                targetScriptType
                            ),
                            targetScriptType
                        )
                        { WasCompilerGenerated = true })
                    { WasCompilerGenerated = true });
            }
        }
 
        /// <summary>
        /// Construct a body for an auto-property accessor (updating or returning the backing field).
        /// </summary>
        internal static BoundBlock ConstructAutoPropertyAccessorBody(SourceMemberMethodSymbol accessor)
        {
            Debug.Assert(accessor.MethodKind == MethodKind.PropertyGet || accessor.MethodKind == MethodKind.PropertySet);
 
            var property = (SourcePropertySymbolBase)accessor.AssociatedSymbol;
            CSharpSyntaxNode syntax = property.CSharpSyntaxNode;
            BoundExpression thisReference = null;
            if (!accessor.IsStatic)
            {
                var thisSymbol = accessor.ThisParameter;
                thisReference = new BoundThisReference(syntax, thisSymbol.Type) { WasCompilerGenerated = true };
            }
 
            var field = property.BackingField;
            var fieldAccess = new BoundFieldAccess(syntax, thisReference, field, ConstantValue.NotAvailable) { WasCompilerGenerated = true };
            BoundStatement statement;
 
            if (accessor.MethodKind == MethodKind.PropertyGet)
            {
                statement = new BoundReturnStatement(accessor.SyntaxNode, RefKind.None, fieldAccess, @checked: false);
            }
            else
            {
                Debug.Assert(accessor.MethodKind == MethodKind.PropertySet);
                var parameter = accessor.Parameters[0];
                statement = new BoundExpressionStatement(
                    accessor.SyntaxNode,
                    new BoundAssignmentOperator(
                        syntax,
                        fieldAccess,
                        new BoundParameter(syntax, parameter) { WasCompilerGenerated = true },
                        property.Type)
                    { WasCompilerGenerated = true });
            }
 
            return BoundBlock.SynthesizedNoLocals(syntax, statement);
        }
 
        /// <summary>
        /// Generate an accessor for a field-like event.
        /// </summary>
        internal static BoundBlock ConstructFieldLikeEventAccessorBody(SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, BindingDiagnosticBag diagnostics)
        {
            Debug.Assert(eventSymbol.HasAssociatedField);
            return eventSymbol.IsWindowsRuntimeEvent
                ? ConstructFieldLikeEventAccessorBody_WinRT(eventSymbol, isAddMethod, compilation, diagnostics)
                : ConstructFieldLikeEventAccessorBody_Regular(eventSymbol, isAddMethod, compilation, diagnostics);
        }
 
        /// <summary>
        /// Generate a thread-safe accessor for a WinRT field-like event.
        ///
        /// Add:
        ///   return EventRegistrationTokenTable&lt;Event&gt;.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).AddEventHandler(value);
        ///
        /// Remove:
        ///   EventRegistrationTokenTable&lt;Event&gt;.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).RemoveEventHandler(value);
        /// </summary>
        internal static BoundBlock ConstructFieldLikeEventAccessorBody_WinRT(SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, BindingDiagnosticBag diagnostics)
        {
            CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode;
 
            MethodSymbol accessor = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod;
            Debug.Assert((object)accessor != null);
 
            FieldSymbol field = eventSymbol.AssociatedField;
            Debug.Assert((object)field != null);
 
            NamedTypeSymbol fieldType = (NamedTypeSymbol)field.Type;
            Debug.Assert(fieldType.Name == "EventRegistrationTokenTable");
 
            MethodSymbol getOrCreateMethod = (MethodSymbol)Binder.GetWellKnownTypeMember(
                compilation,
                WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationTokenTable_T__GetOrCreateEventRegistrationTokenTable,
                diagnostics,
                syntax: syntax);
 
            if ((object)getOrCreateMethod == null)
            {
                Debug.Assert(diagnostics.DiagnosticBag is null || diagnostics.HasAnyErrors());
                return null;
            }
 
            getOrCreateMethod = getOrCreateMethod.AsMember(fieldType);
 
            WellKnownMember processHandlerMember = isAddMethod
                ? WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationTokenTable_T__AddEventHandler
                : WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationTokenTable_T__RemoveEventHandler;
 
            MethodSymbol processHandlerMethod = (MethodSymbol)Binder.GetWellKnownTypeMember(
                compilation,
                processHandlerMember,
                diagnostics,
                syntax: syntax);
 
            if ((object)processHandlerMethod == null)
            {
                Debug.Assert(diagnostics.DiagnosticBag is null || diagnostics.HasAnyErrors());
                return null;
            }
 
            processHandlerMethod = processHandlerMethod.AsMember(fieldType);
 
            // _tokenTable
            BoundFieldAccess fieldAccess = new BoundFieldAccess(
                syntax,
                field.IsStatic ? null : new BoundThisReference(syntax, accessor.ThisParameter.Type),
                field,
                constantValueOpt: null)
            { WasCompilerGenerated = true };
 
            // EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable)
            BoundCall getOrCreateCall = BoundCall.Synthesized(
                syntax,
                receiverOpt: null,
                initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown,
                method: getOrCreateMethod,
                arg0: fieldAccess);
 
            // value
            BoundParameter parameterAccess = new BoundParameter(
                syntax,
                accessor.Parameters[0]);
 
            // EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).AddHandler(value) // or RemoveHandler
            BoundCall processHandlerCall = BoundCall.Synthesized(
                syntax,
                receiverOpt: getOrCreateCall,
                initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown,
                method: processHandlerMethod,
                arg0: parameterAccess);
 
            if (isAddMethod)
            {
                // {
                //     return EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).AddHandler(value);
                // }
                BoundStatement returnStatement = BoundReturnStatement.Synthesized(syntax, RefKind.None, processHandlerCall);
                return BoundBlock.SynthesizedNoLocals(syntax, returnStatement);
            }
            else
            {
                // {
                //     EventRegistrationTokenTable<Event>.GetOrCreateEventRegistrationTokenTable(ref _tokenTable).RemoveHandler(value);
                //     return;
                // }
                BoundStatement callStatement = new BoundExpressionStatement(syntax, processHandlerCall);
                BoundStatement returnStatement = new BoundReturnStatement(syntax, RefKind.None, expressionOpt: null, @checked: false);
                return BoundBlock.SynthesizedNoLocals(syntax, callStatement, returnStatement);
            }
        }
 
        /// <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);
        ///
        /// Note, if System.Threading.Interlocked.CompareExchange&lt;T&gt; is not available,
        /// we emit the following code and mark the method Synchronized (unless it is a struct).
        ///
        /// _event = (DelegateType)Delegate.Combine(_event, value); //Remove for -=
        ///
        /// </summary>
        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,
                @checked: false)
            { 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,
                        initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown,
                        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,
                    initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown,
                    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,
                initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown,
                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,
                constrainedToTypeOpt: 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 };
        }
 
        internal static BoundBlock ConstructDestructorBody(MethodSymbol method, BoundBlock block)
        {
            var syntax = block.Syntax;
 
            Debug.Assert(method.MethodKind == MethodKind.Destructor);
            Debug.Assert(syntax.Kind() == SyntaxKind.Block || syntax.Kind() == SyntaxKind.ArrowExpressionClause);
 
            // If this is a destructor and a base type has a Finalize method (see GetBaseTypeFinalizeMethod for exact
            // requirements), then we need to call that method in a finally block.  Otherwise, just return block as-is.
            // NOTE: the Finalize method need not be a destructor or be overridden by the current method.
            MethodSymbol baseTypeFinalize = GetBaseTypeFinalizeMethod(method);
 
            if ((object)baseTypeFinalize != null)
            {
                BoundStatement baseFinalizeCall = new BoundExpressionStatement(
                    syntax,
                    BoundCall.Synthesized(
                        syntax,
                        new BoundBaseReference(
                            syntax,
                            method.ContainingType)
                        { WasCompilerGenerated = true },
                        initialBindingReceiverIsSubjectToCloning: ThreeState.False,
                        baseTypeFinalize))
                { WasCompilerGenerated = true };
 
                if (syntax.Kind() == SyntaxKind.Block)
                {
                    //sequence point to mimic Dev10
                    baseFinalizeCall = new BoundSequencePointWithSpan(
                        syntax,
                        baseFinalizeCall,
                        ((BlockSyntax)syntax).CloseBraceToken.Span);
                }
 
                return new BoundBlock(
                    syntax,
                    ImmutableArray<LocalSymbol>.Empty,
                    ImmutableArray.Create<BoundStatement>(
                        new BoundTryStatement(
                            syntax,
                            block,
                            ImmutableArray<BoundCatchBlock>.Empty,
                            new BoundBlock(
                                syntax,
                                ImmutableArray<LocalSymbol>.Empty,
                                ImmutableArray.Create<BoundStatement>(
                                    baseFinalizeCall)
                            )
                            { WasCompilerGenerated = true }
                        )
                        { WasCompilerGenerated = true }));
            }
 
            return block;
        }
 
        /// <summary>
        /// Look for a base type method named "Finalize" that is protected (or protected internal), has no parameters,
        /// and returns void.  It doesn't need to be virtual or a destructor.
        /// </summary>
        /// <remarks>
        /// You may assume that this would share code and logic with PEMethodSymbol.OverridesRuntimeFinalizer,
        /// but FUNCBRECCS::bindDestructor has its own loop that performs these checks (differently).
        /// </remarks>
        private static MethodSymbol GetBaseTypeFinalizeMethod(MethodSymbol method)
        {
            NamedTypeSymbol baseType = method.ContainingType.BaseTypeNoUseSiteDiagnostics;
            while ((object)baseType != null)
            {
                foreach (Symbol member in baseType.GetMembers(WellKnownMemberNames.DestructorName))
                {
                    if (member.Kind == SymbolKind.Method)
                    {
                        MethodSymbol baseTypeMethod = (MethodSymbol)member;
                        Accessibility accessibility = baseTypeMethod.DeclaredAccessibility;
                        if ((accessibility == Accessibility.ProtectedOrInternal || accessibility == Accessibility.Protected) &&
                            baseTypeMethod.ParameterCount == 0 &&
                            baseTypeMethod.Arity == 0 && // NOTE: the native compiler doesn't check this, so it broken IL.
                            baseTypeMethod.ReturnsVoid) // NOTE: not checking for virtual
                        {
                            return baseTypeMethod;
                        }
                    }
                }
 
                baseType = baseType.BaseTypeNoUseSiteDiagnostics;
            }
            return null;
        }
    }
}