MethodCompiler.cs 88.3 KB
Newer Older
1
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
P
Pilchie 已提交
2 3 4

using System;
using System.Collections.Concurrent;
5
using System.Collections.Generic;
P
Pilchie 已提交
6 7 8 9 10 11 12 13 14
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
15
using Microsoft.CodeAnalysis.Diagnostics;
16 17
using Microsoft.CodeAnalysis.Emit;
using Roslyn.Utilities;
P
Pilchie 已提交
18 19 20

namespace Microsoft.CodeAnalysis.CSharp
{
T
TomasMatousek 已提交
21
    internal sealed class MethodCompiler : CSharpSymbolVisitor<TypeCompilationState, object>
P
Pilchie 已提交
22
    {
23
        private readonly CSharpCompilation _compilation;
24
        private readonly bool _emittingPdb;
25 26 27 28 29 30
        private readonly CancellationToken _cancellationToken;
        private readonly DiagnosticBag _diagnostics;
        private readonly bool _hasDeclarationErrors;
        private readonly PEModuleBuilder _moduleBeingBuiltOpt; // Null if compiling for diagnostics
        private readonly Predicate<Symbol> _filterOpt;         // If not null, limit analysis to specific symbols
        private readonly DebugDocumentProvider _debugDocumentProvider;
P
Pilchie 已提交
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46

        //
        // MethodCompiler employs concurrency by following flattened fork/join pattern.
        //
        // For every item that we want to compile in parallel a new task is forked.
        // compileTaskQueue is used to track and observe all the tasks. 
        // Once compileTaskQueue is empty, we know that there are no more tasks (and no more can be created)
        // and that means we are done compiling. WaitForWorkers ensures this condition.
        //
        // Note that while tasks may fork more tasks (nested types, lambdas, whatever else that may introduce more types),
        // we do not want any child/parent relationship between spawned tasks and their creators. 
        // Creator has no real dependencies on the completion of its children and should finish and release any resources
        // as soon as it can regardless of the tasks it may have spawned.
        //
        // Stack is used so that the wait would observe the most recently added task and have 
        // more chances to do inlined execution.
47
        private ConcurrentStack<Task> _compilerTasks;
P
Pilchie 已提交
48 49 50 51 52 53 54

        // This field tracks whether any bound method body had hasErrors set or whether any constant field had a bad value.
        // We track it so that we can abort emission in the event that an error occurs without a corresponding diagnostic
        // (e.g. if this module depends on a bad type or constant from another module).
        // CONSIDER: instead of storing a flag, we could track the first member symbol with an error (to improve the diagnostic).

        // NOTE: once the flag is set to true, it should never go back to false!!!
55
        // Do not use this as a short-circuiting for stages that might produce diagnostics.
P
Pilchie 已提交
56
        // That would make diagnostics to depend on the random order in which methods are compiled.
57
        private bool _globalHasErrors;
P
Pilchie 已提交
58 59 60 61 62 63 64 65 66 67 68 69 70

        private void SetGlobalErrorIfTrue(bool arg)
        {
            //NOTE: this is not a volatile write
            //      for correctness we need only single threaded consistency.
            //      Within a single task - if we have got an error it may not be safe to continue with some lowerings.
            //      It is ok if other tasks will see the change after some delay or does not observe at all.
            //      Such races are unavoidable and will just result in performing some work that is safe to do
            //      but may no longer be needed.
            //      The final Join of compiling tasks cannot happen without interlocked operations and that 
            //      will ensure that any write of the flag is globally visible.
            if (arg)
            {
71
                _globalHasErrors = true;
P
Pilchie 已提交
72 73 74
            }
        }

T
TomasMatousek 已提交
75
        // Internal for testing only.
76
        internal MethodCompiler(CSharpCompilation compilation, PEModuleBuilder moduleBeingBuiltOpt, bool emittingPdb, bool hasDeclarationErrors,
T
TomasMatousek 已提交
77 78 79 80 81
            DiagnosticBag diagnostics, Predicate<Symbol> filterOpt, CancellationToken cancellationToken)
        {
            Debug.Assert(compilation != null);
            Debug.Assert(diagnostics != null);

82 83
            _compilation = compilation;
            _moduleBeingBuiltOpt = moduleBeingBuiltOpt;
84
            _emittingPdb = emittingPdb;
85 86 87
            _cancellationToken = cancellationToken;
            _diagnostics = diagnostics;
            _filterOpt = filterOpt;
T
TomasMatousek 已提交
88

89
            _hasDeclarationErrors = hasDeclarationErrors;
T
TomasMatousek 已提交
90 91
            SetGlobalErrorIfTrue(hasDeclarationErrors);

92
            if (emittingPdb || moduleBeingBuiltOpt?.EmitOptions.EmitTestCoverageData == true)
T
TomasMatousek 已提交
93
            {
94
                _debugDocumentProvider = (path, basePath) => moduleBeingBuiltOpt.DebugDocumentsBuilder.GetOrAddDebugDocument(path, basePath, CreateDebugDocumentForFile);
T
TomasMatousek 已提交
95 96 97
            }
        }

P
Pilchie 已提交
98 99
        public static void CompileMethodBodies(
            CSharpCompilation compilation,
T
TomasMatousek 已提交
100
            PEModuleBuilder moduleBeingBuiltOpt,
P
Pilchie 已提交
101 102 103
            bool generateDebugInfo,
            bool hasDeclarationErrors,
            DiagnosticBag diagnostics,
T
TomasMatousek 已提交
104
            Predicate<Symbol> filterOpt,
P
Pilchie 已提交
105 106
            CancellationToken cancellationToken)
        {
T
TomasMatousek 已提交
107 108
            Debug.Assert(compilation != null);
            Debug.Assert(diagnostics != null);
P
Pilchie 已提交
109

110
            if (compilation.PreviousSubmission != null)
P
Pilchie 已提交
111
            {
112 113
                // In case there is a previous submission, we should ensure 
                // it has already created anonymous type/delegates templates
P
Pilchie 已提交
114

115 116
                // NOTE: if there are any errors, we will pick up what was created anyway
                compilation.PreviousSubmission.EnsureAnonymousTypeTemplates(cancellationToken);
P
Pilchie 已提交
117

118 119
                // TODO: revise to use a loop instead of a recursion
            }
T
TomasMatousek 已提交
120

121 122 123 124 125 126 127 128 129 130 131 132 133
            MethodCompiler methodCompiler = new MethodCompiler(
                compilation,
                moduleBeingBuiltOpt,
                generateDebugInfo,
                hasDeclarationErrors,
                diagnostics,
                filterOpt,
                cancellationToken);

            if (compilation.Options.ConcurrentBuild)
            {
                methodCompiler._compilerTasks = new ConcurrentStack<Task>();
            }
T
TomasMatousek 已提交
134

135 136 137 138 139 140 141 142 143
            // directly traverse global namespace (no point to defer this to async)
            methodCompiler.CompileNamespace(compilation.SourceModule.GlobalNamespace);
            methodCompiler.WaitForWorkers();

            // compile additional and anonymous types if any
            if (moduleBeingBuiltOpt != null)
            {
                var additionalTypes = moduleBeingBuiltOpt.GetAdditionalTopLevelTypes();
                if (!additionalTypes.IsEmpty)
T
TomasMatousek 已提交
144
                {
145
                    methodCompiler.CompileSynthesizedMethods(additionalTypes, diagnostics);
T
TomasMatousek 已提交
146 147
                }

148 149
                // By this time we have processed all types reachable from module's global namespace
                compilation.AnonymousTypeManager.AssignTemplatesNamesAndCompile(methodCompiler, moduleBeingBuiltOpt, diagnostics);
T
TomasMatousek 已提交
150 151
                methodCompiler.WaitForWorkers();

152 153
                var privateImplClass = moduleBeingBuiltOpt.PrivateImplClass;
                if (privateImplClass != null)
T
TomasMatousek 已提交
154
                {
155 156
                    // all threads that were adding methods must be finished now, we can freeze the class:
                    privateImplClass.Freeze();
T
TomasMatousek 已提交
157

158
                    methodCompiler.CompileSynthesizedMethods(privateImplClass, diagnostics);
P
Pilchie 已提交
159
                }
160
            }
P
Pilchie 已提交
161

162 163 164 165
            // If we are trying to emit and there's an error without a corresponding diagnostic (e.g. because
            // we depend on an invalid type or constant from another module), then explicitly add a diagnostic.
            // This diagnostic is not very helpful to the user, but it will prevent us from emitting an invalid
            // module or crashing.
166
            if (moduleBeingBuiltOpt != null && (methodCompiler._globalHasErrors || moduleBeingBuiltOpt.SourceModule.HasBadAttributes) && !diagnostics.HasAnyErrors() && !hasDeclarationErrors)
167 168 169
            {
                diagnostics.Add(ErrorCode.ERR_ModuleEmitFailure, NoLocation.Singleton, ((Cci.INamedEntity)moduleBeingBuiltOpt).Name);
            }
P
Pilchie 已提交
170

171
            diagnostics.AddRange(compilation.AdditionalCodegenWarnings);
P
Pilchie 已提交
172

173 174 175 176 177
            // we can get unused field warnings only if compiling whole compilation.
            if (filterOpt == null)
            {
                WarnUnusedFields(compilation, diagnostics, cancellationToken);

178 179 180 181 182
                MethodSymbol entryPoint = GetEntryPoint(compilation, moduleBeingBuiltOpt, hasDeclarationErrors, diagnostics, cancellationToken);
                if (moduleBeingBuiltOpt != null && entryPoint != null && compilation.Options.OutputKind.IsApplication())
                {
                    moduleBeingBuiltOpt.SetPEEntryPoint(entryPoint, diagnostics);
                }
P
Pilchie 已提交
183
            }
T
TomasMatousek 已提交
184
        }
P
Pilchie 已提交
185

186
        private static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModuleBuilder moduleBeingBuilt, bool hasDeclarationErrors, DiagnosticBag diagnostics, CancellationToken cancellationToken)
T
TomasMatousek 已提交
187
        {
C
Charles Stoner 已提交
188 189
            var entryPointAndDiagnostics = compilation.GetEntryPointAndDiagnostics(cancellationToken);
            if (entryPointAndDiagnostics == null)
P
Pilchie 已提交
190
            {
C
Charles Stoner 已提交
191
                return null;
P
Pilchie 已提交
192
            }
T
TomasMatousek 已提交
193

C
Charles Stoner 已提交
194 195
            Debug.Assert(!entryPointAndDiagnostics.Diagnostics.IsDefault);
            diagnostics.AddRange(entryPointAndDiagnostics.Diagnostics);
T
TomasMatousek 已提交
196

C
Charles Stoner 已提交
197 198 199 200 201 202
            var entryPoint = entryPointAndDiagnostics.MethodSymbol;
            var synthesizedEntryPoint = entryPoint as SynthesizedEntryPointSymbol;
            if (((object)synthesizedEntryPoint != null) &&
                (moduleBeingBuilt != null) &&
                !hasDeclarationErrors &&
                !diagnostics.HasAnyErrors())
T
TomasMatousek 已提交
203
            {
C
Charles Stoner 已提交
204 205 206 207 208 209 210 211 212 213 214 215 216 217
                var body = synthesizedEntryPoint.CreateBody();
                const int methodOrdinal = -1;
                var emittedBody = GenerateMethodBody(
                    moduleBeingBuilt,
                    synthesizedEntryPoint,
                    methodOrdinal,
                    body,
                    ImmutableArray<LambdaDebugInfo>.Empty,
                    ImmutableArray<ClosureDebugInfo>.Empty,
                    stateMachineTypeOpt: null,
                    variableSlotAllocatorOpt: null,
                    diagnostics: diagnostics,
                    debugDocumentProvider: null,
                    importChainOpt: null,
J
More  
John Hamby 已提交
218 219
                    emittingPdb: false,
                    dynamicAnalysisSpans: ImmutableArray<SourceSpan>.Empty);
C
Charles Stoner 已提交
220
                moduleBeingBuilt.SetMethodBody(synthesizedEntryPoint, emittedBody);
C
Charles Stoner 已提交
221
            }
222

C
Charles Stoner 已提交
223 224
            Debug.Assert((object)entryPoint != null || entryPointAndDiagnostics.Diagnostics.HasAnyErrors() || !compilation.Options.Errors.IsDefaultOrEmpty);
            return entryPoint;
P
Pilchie 已提交
225 226 227 228
        }

        private void WaitForWorkers()
        {
229
            var tasks = _compilerTasks;
P
Pilchie 已提交
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
            if (tasks == null)
            {
                return;
            }

            Task curTask;
            while (tasks.TryPop(out curTask))
            {
                curTask.GetAwaiter().GetResult();
            }
        }

        private static void WarnUnusedFields(CSharpCompilation compilation, DiagnosticBag diagnostics, CancellationToken cancellationToken)
        {
            SourceAssemblySymbol assembly = (SourceAssemblySymbol)compilation.Assembly;
            diagnostics.AddRange(assembly.GetUnusedFieldWarnings(cancellationToken));
        }

        public override object VisitNamespace(NamespaceSymbol symbol, TypeCompilationState arg)
        {
250
            if (!PassesFilter(_filterOpt, symbol))
P
Pilchie 已提交
251 252 253 254 255
            {
                return null;
            }

            arg = null; // do not use compilation state of outer type.
256
            _cancellationToken.ThrowIfCancellationRequested();
P
Pilchie 已提交
257

258
            if (_compilation.Options.ConcurrentBuild)
P
Pilchie 已提交
259 260
            {
                Task worker = CompileNamespaceAsTask(symbol);
261
                _compilerTasks.Push(worker);
P
Pilchie 已提交
262 263 264 265 266 267 268 269 270 271 272
            }
            else
            {
                CompileNamespace(symbol);
            }

            return null;
        }

        private Task CompileNamespaceAsTask(NamespaceSymbol symbol)
        {
273 274
            return Task.Run(UICultureUtilities.WithCurrentUICulture(() =>
                {
T
TomasMatousek 已提交
275 276 277 278
                    try
                    {
                        CompileNamespace(symbol);
                    }
B
beep boop 已提交
279
                    catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
T
TomasMatousek 已提交
280 281 282
                    {
                        throw ExceptionUtilities.Unreachable;
                    }
B
beep boop 已提交
283
                }), _cancellationToken);
P
Pilchie 已提交
284 285 286 287 288 289 290 291 292 293 294 295
        }

        private void CompileNamespace(NamespaceSymbol symbol)
        {
            foreach (var s in symbol.GetMembersUnordered())
            {
                s.Accept(this, null);
            }
        }

        public override object VisitNamedType(NamedTypeSymbol symbol, TypeCompilationState arg)
        {
296
            if (!PassesFilter(_filterOpt, symbol))
P
Pilchie 已提交
297 298 299 300 301
            {
                return null;
            }

            arg = null; // do not use compilation state of outer type.
302
            _cancellationToken.ThrowIfCancellationRequested();
P
Pilchie 已提交
303

304
            if (_compilation.Options.ConcurrentBuild)
P
Pilchie 已提交
305 306
            {
                Task worker = CompileNamedTypeAsTask(symbol);
307
                _compilerTasks.Push(worker);
P
Pilchie 已提交
308 309 310 311 312 313 314 315 316 317 318
            }
            else
            {
                CompileNamedType(symbol);
            }

            return null;
        }

        private Task CompileNamedTypeAsTask(NamedTypeSymbol symbol)
        {
319 320
            return Task.Run(UICultureUtilities.WithCurrentUICulture(() =>
                {
T
TomasMatousek 已提交
321 322 323 324
                    try
                    {
                        CompileNamedType(symbol);
                    }
325
                    catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
T
TomasMatousek 已提交
326 327 328
                    {
                        throw ExceptionUtilities.Unreachable;
                    }
B
beep boop 已提交
329
                }), _cancellationToken);
P
Pilchie 已提交
330 331
        }

332
        private void CompileNamedType(NamedTypeSymbol containingType)
P
Pilchie 已提交
333
        {
334
            var compilationState = new TypeCompilationState(containingType, _compilation, _moduleBeingBuiltOpt);
P
Pilchie 已提交
335

336
            _cancellationToken.ThrowIfCancellationRequested();
P
Pilchie 已提交
337 338

            // Find the constructor of a script class.
339 340
            SynthesizedInstanceConstructor scriptCtor = null;
            SynthesizedInteractiveInitializerMethod scriptInitializer = null;
C
Charles Stoner 已提交
341
            SynthesizedEntryPointSymbol scriptEntryPoint = null;
342 343
            int scriptCtorOrdinal = -1;
            if (containingType.IsScriptClass)
P
Pilchie 已提交
344 345 346 347 348 349
            {
                // The field initializers of a script class could be arbitrary statements,
                // including blocks.  Field initializers containing blocks need to
                // use a MethodBodySemanticModel to build up the appropriate tree of binders, and
                // MethodBodySemanticModel requires an "owning" method.  That's why we're digging out
                // the constructor - it will own the field initializers.
350 351
                scriptCtor = containingType.GetScriptConstructor();
                scriptInitializer = containingType.GetScriptInitializer();
C
Charles Stoner 已提交
352
                scriptEntryPoint = containingType.GetScriptEntryPoint();
P
Pilchie 已提交
353
                Debug.Assert((object)scriptCtor != null);
354
                Debug.Assert((object)scriptInitializer != null);
P
Pilchie 已提交
355 356
            }

357
            var synthesizedSubmissionFields = containingType.IsSubmissionClass ? new SynthesizedSubmissionFields(_compilation, containingType) : null;
T
TomasMatousek 已提交
358 359
            var processedStaticInitializers = new Binder.ProcessedFieldInitializers();
            var processedInstanceInitializers = new Binder.ProcessedFieldInitializers();
P
Pilchie 已提交
360

361
            var sourceTypeSymbol = containingType as SourceMemberContainerTypeSymbol;
362

P
Pilchie 已提交
363 364
            if ((object)sourceTypeSymbol != null)
            {
365
                _cancellationToken.ThrowIfCancellationRequested();
C
Charles Stoner 已提交
366
                Binder.BindFieldInitializers(_compilation, scriptInitializer, sourceTypeSymbol.StaticInitializers, _diagnostics, ref processedStaticInitializers);
T
TomasMatousek 已提交
367

368
                _cancellationToken.ThrowIfCancellationRequested();
C
Charles Stoner 已提交
369
                Binder.BindFieldInitializers(_compilation, scriptInitializer, sourceTypeSymbol.InstanceInitializers, _diagnostics, ref processedInstanceInitializers);
P
Pilchie 已提交
370 371 372 373 374 375 376 377 378 379 380

                if (compilationState.Emitting)
                {
                    CompileSynthesizedExplicitImplementations(sourceTypeSymbol, compilationState);
                }
            }

            // Indicates if a static constructor is in the member,
            // so we can decide to synthesize a static constructor.
            bool hasStaticConstructor = false;

381
            var members = containingType.GetMembers();
382
            for (int memberOrdinal = 0; memberOrdinal < members.Length; memberOrdinal++)
P
Pilchie 已提交
383
            {
384 385
                var member = members[memberOrdinal];

P
Pilchie 已提交
386
                //When a filter is supplied, limit the compilation of members passing the filter.
387
                if (!PassesFilter(_filterOpt, member))
P
Pilchie 已提交
388 389 390 391 392 393 394 395 396 397 398 399 400
                {
                    continue;
                }

                switch (member.Kind)
                {
                    case SymbolKind.NamedType:
                        member.Accept(this, compilationState);
                        break;

                    case SymbolKind.Method:
                        {
                            MethodSymbol method = (MethodSymbol)member;
401
                            if (method.IsScriptConstructor)
402
                            {
403
                                Debug.Assert(scriptCtorOrdinal == -1);
C
Charles Stoner 已提交
404
                                Debug.Assert((object)scriptCtor == method);
405
                                scriptCtorOrdinal = memberOrdinal;
406 407
                                continue;
                            }
B
beep boop 已提交
408

C
Charles Stoner 已提交
409 410 411 412 413
                            if ((object)method == scriptEntryPoint)
                            {
                                continue;
                            }

414
                            if (IsFieldLikeEventAccessor(method))
P
Pilchie 已提交
415 416 417 418
                            {
                                continue;
                            }

419 420
                            if (method.IsPartialDefinition())
                            {
421
                                method = method.PartialImplementationPart;
P
Pilchie 已提交
422 423 424 425 426 427
                                if ((object)method == null)
                                {
                                    continue;
                                }
                            }

T
TomasMatousek 已提交
428
                            Binder.ProcessedFieldInitializers processedInitializers =
J
Jared Parsons 已提交
429
                                (method.MethodKind == MethodKind.Constructor || method.IsScriptInitializer) ? processedInstanceInitializers :
P
Pilchie 已提交
430
                                method.MethodKind == MethodKind.StaticConstructor ? processedStaticInitializers :
T
TomasMatousek 已提交
431
                                default(Binder.ProcessedFieldInitializers);
P
Pilchie 已提交
432

433
                            CompileMethod(method, memberOrdinal, ref processedInitializers, synthesizedSubmissionFields, compilationState);
P
Pilchie 已提交
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457

                            // Set a flag to indicate that a static constructor is created.
                            if (method.MethodKind == MethodKind.StaticConstructor)
                            {
                                hasStaticConstructor = true;
                            }
                            break;
                        }

                    case SymbolKind.Property:
                        {
                            SourcePropertySymbol sourceProperty = member as SourcePropertySymbol;
                            if ((object)sourceProperty != null && sourceProperty.IsSealed && compilationState.Emitting)
                            {
                                CompileSynthesizedSealedAccessors(sourceProperty, compilationState);
                            }
                            break;
                        }

                    case SymbolKind.Event:
                        {
                            SourceEventSymbol eventSymbol = member as SourceEventSymbol;
                            if ((object)eventSymbol != null && eventSymbol.HasAssociatedField && !eventSymbol.IsAbstract && compilationState.Emitting)
                            {
458 459
                                CompileFieldLikeEventAccessor(eventSymbol, isAddMethod: true);
                                CompileFieldLikeEventAccessor(eventSymbol, isAddMethod: false);
P
Pilchie 已提交
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
                            }
                            break;
                        }

                    case SymbolKind.Field:
                        {
                            SourceMemberFieldSymbol fieldSymbol = member as SourceMemberFieldSymbol;
                            if ((object)fieldSymbol != null)
                            {
                                if (fieldSymbol.IsConst)
                                {
                                    // We check specifically for constant fields with bad values because they never result
                                    // in bound nodes being inserted into method bodies (in which case, they would be covered
                                    // by the method-level check).
                                    ConstantValue constantValue = fieldSymbol.GetConstantValue(ConstantFieldsInProgress.Empty, earlyDecodingWellKnownAttributes: false);
                                    SetGlobalErrorIfTrue(constantValue == null || constantValue.IsBad);
                                }

                                if (fieldSymbol.IsFixed && compilationState.Emitting)
                                {
                                    // force the generation of implementation types for fixed-size buffers
T
TomasMatousek 已提交
481
                                    TypeSymbol discarded = fieldSymbol.FixedImplementationType(compilationState.ModuleBuilderOpt);
P
Pilchie 已提交
482 483 484 485 486 487 488
                                }
                            }
                            break;
                        }
                }
            }

489
            Debug.Assert(containingType.IsScriptClass == (scriptCtorOrdinal >= 0));
P
Pilchie 已提交
490

491
            // process additional anonymous type members
492
            if (AnonymousTypeManager.IsAnonymousTypeTemplate(containingType))
P
Pilchie 已提交
493
            {
T
TomasMatousek 已提交
494
                var processedInitializers = default(Binder.ProcessedFieldInitializers);
495
                foreach (var method in AnonymousTypeManager.GetAnonymousTypeHiddenMethods(containingType))
P
Pilchie 已提交
496
                {
497
                    CompileMethod(method, -1, ref processedInitializers, synthesizedSubmissionFields, compilationState);
P
Pilchie 已提交
498 499 500 501 502 503
                }
            }

            // In the case there are field initializers but we haven't created an implicit static constructor (.cctor) for it,
            // (since we may not add .cctor implicitly created for decimals into the symbol table)
            // it is necessary for the compiler to generate the static constructor here if we are emitting.
504
            if (_moduleBeingBuiltOpt != null && !hasStaticConstructor && !processedStaticInitializers.BoundInitializers.IsDefaultOrEmpty)
P
Pilchie 已提交
505 506 507 508 509
            {
                Debug.Assert(processedStaticInitializers.BoundInitializers.All((init) =>
                    (init.Kind == BoundKind.FieldInitializer) && !((BoundFieldInitializer)init).Field.IsMetadataConstant));

                MethodSymbol method = new SynthesizedStaticConstructor(sourceTypeSymbol);
510
                if (PassesFilter(_filterOpt, method))
P
Pilchie 已提交
511
                {
512 513
                    CompileMethod(method, -1, ref processedStaticInitializers, synthesizedSubmissionFields, compilationState);

P
Pilchie 已提交
514
                    // If this method has been successfully built, we emit it.
515
                    if (_moduleBeingBuiltOpt.GetMethodBody(method) != null)
516
                    {
517
                        _moduleBeingBuiltOpt.AddSynthesizedDefinition(sourceTypeSymbol, method);
518
                    }
P
Pilchie 已提交
519 520 521 522
                }
            }

            // compile submission constructor last so that synthesized submission fields are collected from all script methods:
523
            if (scriptCtor != null && compilationState.Emitting)
P
Pilchie 已提交
524
            {
525 526 527 528 529 530 531
                Debug.Assert(scriptCtorOrdinal >= 0);
                var processedInitializers = new Binder.ProcessedFieldInitializers() { BoundInitializers = ImmutableArray<BoundInitializer>.Empty };
                CompileMethod(scriptCtor, scriptCtorOrdinal, ref processedInitializers, synthesizedSubmissionFields, compilationState);
                if (synthesizedSubmissionFields != null)
                {
                    synthesizedSubmissionFields.AddToType(containingType, compilationState.ModuleBuilderOpt);
                }
P
Pilchie 已提交
532 533
            }

534
            // Emit synthesized methods produced during lowering if any
535
            if (_moduleBeingBuiltOpt != null)
536 537 538 539
            {
                CompileSynthesizedMethods(compilationState);
            }

P
Pilchie 已提交
540 541 542
            compilationState.Free();
        }

T
TomasMatousek 已提交
543
        private void CompileSynthesizedMethods(PrivateImplementationDetails privateImplClass, DiagnosticBag diagnostics)
P
Pilchie 已提交
544
        {
545
            Debug.Assert(_moduleBeingBuiltOpt != null);
546

547
            var compilationState = new TypeCompilationState(null, _compilation, _moduleBeingBuiltOpt);
548
            foreach (MethodSymbol method in privateImplClass.GetMethods(new EmitContext(_moduleBeingBuiltOpt, null, diagnostics)))
P
Pilchie 已提交
549 550 551 552 553
            {
                Debug.Assert(method.SynthesizesLoweredBoundBody);
                method.GenerateMethodBody(compilationState, diagnostics);
            }

T
TomasMatousek 已提交
554
            CompileSynthesizedMethods(compilationState);
P
Pilchie 已提交
555 556 557
            compilationState.Free();
        }

T
TomasMatousek 已提交
558
        private void CompileSynthesizedMethods(ImmutableArray<NamedTypeSymbol> additionalTypes, DiagnosticBag diagnostics)
P
Pilchie 已提交
559
        {
560
            foreach (var additionalType in additionalTypes)
P
Pilchie 已提交
561
            {
562
                var compilationState = new TypeCompilationState(additionalType, _compilation, _moduleBeingBuiltOpt);
563
                foreach (var method in additionalType.GetMethodsToEmit())
P
Pilchie 已提交
564
                {
565
                    method.GenerateMethodBody(compilationState, diagnostics);
P
Pilchie 已提交
566 567
                }

568 569 570 571
                if (!diagnostics.HasAnyErrors())
                {
                    CompileSynthesizedMethods(compilationState);
                }
T
TomasMatousek 已提交
572

573 574
                compilationState.Free();
            }
P
Pilchie 已提交
575 576
        }

T
TomasMatousek 已提交
577
        private void CompileSynthesizedMethods(TypeCompilationState compilationState)
P
Pilchie 已提交
578
        {
579
            Debug.Assert(_moduleBeingBuiltOpt != null);
580
            Debug.Assert(compilationState.ModuleBuilderOpt == _moduleBeingBuiltOpt);
581

582 583
            var synthesizedMethods = compilationState.SynthesizedMethods;
            if (synthesizedMethods == null)
P
Pilchie 已提交
584
            {
T
TomasMatousek 已提交
585 586
                return;
            }
P
Pilchie 已提交
587

588 589
            var oldImportChain = compilationState.CurrentImportChain;
            try
T
TomasMatousek 已提交
590
            {
591 592 593 594
                foreach (var methodWithBody in synthesizedMethods)
                {
                    var importChain = methodWithBody.ImportChainOpt;
                    compilationState.CurrentImportChain = importChain;
595

T
TomasMatousek 已提交
596 597 598 599
                // We make sure that an asynchronous mutation to the diagnostic bag does not 
                // confuse the method body generator by making a fresh bag and then loading
                // any diagnostics emitted into it back into the main diagnostic bag.
                var diagnosticsThisMethod = DiagnosticBag.GetInstance();
P
Pilchie 已提交
600

601
                    var method = methodWithBody.Method;
602 603 604 605 606
                var lambda = method as SynthesizedLambdaMethod;
                var variableSlotAllocatorOpt = ((object)lambda != null) ?
                    _moduleBeingBuiltOpt.TryCreateVariableSlotAllocator(lambda, lambda.TopLevelMethod, diagnosticsThisMethod) :
                    _moduleBeingBuiltOpt.TryCreateVariableSlotAllocator(method, method, diagnosticsThisMethod);

607 608 609
                // Synthesized methods have no ordinal stored in custom debug information (only user-defined methods have ordinals).
                // In case of async lambdas, which synthesize a state machine type during the following rewrite, the containing method has already been uniquely named, 
                // so there is no need to produce a unique method ordinal for the corresponding state machine type, whose name includes the (unique) containing method name.
610
                const int methodOrdinal = -1;
T
TomasMatousek 已提交
611
                MethodBody emittedBody = null;
612

613
                try
614
                {
615 616 617 618
                    // Local functions can be iterators as well as be async (lambdas can only be async), so we need to lower both iterators and async
                    IteratorStateMachine iteratorStateMachine;
                    BoundStatement loweredBody = IteratorRewriter.Rewrite(methodWithBody.Body, method, methodOrdinal, variableSlotAllocatorOpt, compilationState, diagnosticsThisMethod, out iteratorStateMachine);
                    StateMachineTypeSymbol stateMachine = iteratorStateMachine;
619

620
                    if (!loweredBody.HasErrors)
621
                    {
622 623
                        AsyncStateMachine asyncStateMachine;
                        loweredBody = AsyncRewriter.Rewrite(loweredBody, method, methodOrdinal, variableSlotAllocatorOpt, compilationState, diagnosticsThisMethod, out asyncStateMachine);
624

625 626
                        Debug.Assert(iteratorStateMachine == null || asyncStateMachine == null);
                        stateMachine = stateMachine ?? asyncStateMachine;
627
                    }
P
Pilchie 已提交
628

629
                    if (!diagnosticsThisMethod.HasAnyErrors() && !_globalHasErrors)
630
                    {
631 632 633 634
                        emittedBody = GenerateMethodBody(
                            _moduleBeingBuiltOpt,
                            method,
                            methodOrdinal,
635
                            loweredBody,
636 637
                            ImmutableArray<LambdaDebugInfo>.Empty,
                            ImmutableArray<ClosureDebugInfo>.Empty,
638
                            stateMachine,
639 640 641
                            variableSlotAllocatorOpt,
                            diagnosticsThisMethod,
                            _debugDocumentProvider,
642
                                method.GenerateDebugInfo ? importChain : null,
J
More  
John Hamby 已提交
643 644
                            emittingPdb: _emittingPdb,
                            dynamicAnalysisSpans: ImmutableArray<SourceSpan>.Empty);
645
                    }
646 647
                }
                catch (BoundTreeVisitor.CancelledByStackGuardException ex)
T
TomasMatousek 已提交
648
                {
649
                    ex.AddAnError(_diagnostics);
T
TomasMatousek 已提交
650
                }
P
Pilchie 已提交
651

652
                _diagnostics.AddRange(diagnosticsThisMethod);
T
TomasMatousek 已提交
653 654 655 656 657 658
                diagnosticsThisMethod.Free();

                // error while generating IL
                if (emittedBody == null)
                {
                    break;
659
                }
T
TomasMatousek 已提交
660

661
                _moduleBeingBuiltOpt.SetMethodBody(method, emittedBody);
662
            }
P
Pilchie 已提交
663
        }
664 665 666
            finally
            {
                compilationState.CurrentImportChain = oldImportChain;
P
Pilchie 已提交
667 668 669 670 671
            }
        }

        private static bool IsFieldLikeEventAccessor(MethodSymbol method)
        {
672
            Symbol associatedPropertyOrEvent = method.AssociatedSymbol;
P
Pilchie 已提交
673 674 675 676 677 678 679 680 681 682 683 684 685
            return (object)associatedPropertyOrEvent != null &&
                associatedPropertyOrEvent.Kind == SymbolKind.Event &&
                ((EventSymbol)associatedPropertyOrEvent).HasAssociatedField;
        }

        /// <summary>
        /// In some circumstances (e.g. implicit implementation of an interface method by a non-virtual method in a 
        /// base type from another assembly) it is necessary for the compiler to generate explicit implementations for
        /// some interface methods.  They don't go in the symbol table, but if we are emitting, then we should
        /// generate code for them.
        /// </summary>
        private void CompileSynthesizedExplicitImplementations(SourceMemberContainerTypeSymbol sourceTypeSymbol, TypeCompilationState compilationState)
        {
686
            // we are not generating any observable diagnostics here so it is ok to short-circuit on global errors.
687
            if (!_globalHasErrors)
P
Pilchie 已提交
688
            {
689
                foreach (var synthesizedExplicitImpl in sourceTypeSymbol.GetSynthesizedExplicitImplementations(_cancellationToken))
P
Pilchie 已提交
690 691 692 693 694 695
                {
                    Debug.Assert(synthesizedExplicitImpl.SynthesizesLoweredBoundBody);
                    var discardedDiagnostics = DiagnosticBag.GetInstance();
                    synthesizedExplicitImpl.GenerateMethodBody(compilationState, discardedDiagnostics);
                    Debug.Assert(!discardedDiagnostics.HasAnyErrors());
                    discardedDiagnostics.Free();
696
                    _moduleBeingBuiltOpt.AddSynthesizedDefinition(sourceTypeSymbol, synthesizedExplicitImpl);
P
Pilchie 已提交
697 698 699 700 701 702 703 704
                }
            }
        }

        private void CompileSynthesizedSealedAccessors(SourcePropertySymbol sourceProperty, TypeCompilationState compilationState)
        {
            SynthesizedSealedPropertyAccessor synthesizedAccessor = sourceProperty.SynthesizedSealedAccessorOpt;

705
            // we are not generating any observable diagnostics here so it is ok to short-circuit on global errors.
706
            if ((object)synthesizedAccessor != null && !_globalHasErrors)
P
Pilchie 已提交
707 708 709 710 711 712 713
            {
                Debug.Assert(synthesizedAccessor.SynthesizesLoweredBoundBody);
                var discardedDiagnostics = DiagnosticBag.GetInstance();
                synthesizedAccessor.GenerateMethodBody(compilationState, discardedDiagnostics);
                Debug.Assert(!discardedDiagnostics.HasAnyErrors());
                discardedDiagnostics.Free();

714
                _moduleBeingBuiltOpt.AddSynthesizedDefinition(sourceProperty.ContainingType, synthesizedAccessor);
P
Pilchie 已提交
715 716 717
            }
        }

718
        private void CompileFieldLikeEventAccessor(SourceEventSymbol eventSymbol, bool isAddMethod)
P
Pilchie 已提交
719 720 721 722 723 724
        {
            MethodSymbol accessor = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod;

            var diagnosticsThisMethod = DiagnosticBag.GetInstance();
            try
            {
725
                BoundBlock boundBody = MethodBodySynthesizer.ConstructFieldLikeEventAccessorBody(eventSymbol, isAddMethod, _compilation, diagnosticsThisMethod);
P
Pilchie 已提交
726 727 728 729 730 731
                var hasErrors = diagnosticsThisMethod.HasAnyErrors();
                SetGlobalErrorIfTrue(hasErrors);

                // we cannot rely on GlobalHasErrors since that can be changed concurrently by other methods compiling
                // we however do not want to continue with generating method body if we have errors in this particular method - generating may crash
                // or if had declaration errors - we will fail anyways, but if some types are bad enough, generating may produce duplicate errors about that.
732
                if (!hasErrors && !_hasDeclarationErrors)
P
Pilchie 已提交
733
                {
734 735
                    const int accessorOrdinal = -1;

T
TomasMatousek 已提交
736
                    MethodBody emittedBody = GenerateMethodBody(
737
                        _moduleBeingBuiltOpt,
P
Pilchie 已提交
738
                        accessor,
739
                        accessorOrdinal,
P
Pilchie 已提交
740
                        boundBody,
741 742
                        ImmutableArray<LambdaDebugInfo>.Empty,
                        ImmutableArray<ClosureDebugInfo>.Empty,
743 744 745
                        stateMachineTypeOpt: null,
                        variableSlotAllocatorOpt: null,
                        diagnostics: diagnosticsThisMethod,
746
                        debugDocumentProvider: _debugDocumentProvider,
T
TomasMatousek 已提交
747
                        importChainOpt: null,
J
More  
John Hamby 已提交
748 749
                        emittingPdb: false,
                        dynamicAnalysisSpans: ImmutableArray<SourceSpan>.Empty);
P
Pilchie 已提交
750

751
                    _moduleBeingBuiltOpt.SetMethodBody(accessor, emittedBody);
P
Pilchie 已提交
752 753 754 755 756
                    // Definition is already in the symbol table, so don't call moduleBeingBuilt.AddCompilerGeneratedDefinition
                }
            }
            finally
            {
757
                _diagnostics.AddRange(diagnosticsThisMethod);
P
Pilchie 已提交
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
                diagnosticsThisMethod.Free();
            }
        }

        public override object VisitMethod(MethodSymbol symbol, TypeCompilationState arg)
        {
            throw ExceptionUtilities.Unreachable;
        }

        public override object VisitProperty(PropertySymbol symbol, TypeCompilationState argument)
        {
            throw ExceptionUtilities.Unreachable;
        }

        public override object VisitEvent(EventSymbol symbol, TypeCompilationState argument)
        {
            throw ExceptionUtilities.Unreachable;
        }

        public override object VisitField(FieldSymbol symbol, TypeCompilationState argument)
        {
            throw ExceptionUtilities.Unreachable;
        }

        private void CompileMethod(
            MethodSymbol methodSymbol,
784
            int methodOrdinal,
T
TomasMatousek 已提交
785
            ref Binder.ProcessedFieldInitializers processedInitializers,
P
Pilchie 已提交
786 787 788
            SynthesizedSubmissionFields previousSubmissionFields,
            TypeCompilationState compilationState)
        {
789
            _cancellationToken.ThrowIfCancellationRequested();
790
            SourceMethodSymbol sourceMethod = methodSymbol as SourceMethodSymbol;
P
Pilchie 已提交
791 792 793

            if (methodSymbol.IsAbstract)
            {
794
                if ((object)sourceMethod != null)
795
                {
796 797
                    bool diagsWritten;
                    sourceMethod.SetDiagnostics(ImmutableArray<Diagnostic>.Empty, out diagsWritten);
798
                    if (diagsWritten && !methodSymbol.IsImplicitlyDeclared && _compilation.EventQueue != null)
799
                    {
800
                        _compilation.SymbolDeclaredEvent(methodSymbol);
801
                    }
802
                }
803

P
Pilchie 已提交
804 805 806
                return;
            }

807
            // get cached diagnostics if not building and we have 'em
808
            if (_moduleBeingBuiltOpt == null && (object)sourceMethod != null)
P
Pilchie 已提交
809 810 811 812 813
            {
                var cachedDiagnostics = sourceMethod.Diagnostics;

                if (!cachedDiagnostics.IsDefault)
                {
814
                    _diagnostics.AddRange(cachedDiagnostics);
P
Pilchie 已提交
815 816 817 818
                    return;
                }
            }

T
TomasMatousek 已提交
819
            ImportChain oldImportChain = compilationState.CurrentImportChain;
820
            bool instrumentForDynamicAnalysis = _moduleBeingBuiltOpt?.EmitOptions.EmitTestCoverageData == true;
P
Pilchie 已提交
821 822 823 824 825 826 827 828 829

            // In order to avoid generating code for methods with errors, we create a diagnostic bag just for this method.
            DiagnosticBag diagsForCurrentMethod = DiagnosticBag.GetInstance();

            try
            {
                // if synthesized method returns its body in lowered form
                if (methodSymbol.SynthesizesLoweredBoundBody)
                {
830
                    if (_moduleBeingBuiltOpt != null)
P
Pilchie 已提交
831 832
                    {
                        methodSymbol.GenerateMethodBody(compilationState, diagsForCurrentMethod);
833
                        _diagnostics.AddRange(diagsForCurrentMethod);
P
Pilchie 已提交
834 835 836 837 838
                    }

                    return;
                }

839 840 841 842 843 844
                // no need to emit the default ctor, we are not emitting those
                if (methodSymbol.IsDefaultValueTypeConstructor())
                {
                    return;
                }

845 846 847 848
                bool includeInitializersInBody = false;
                BoundBlock body;
                bool originalBodyNested = false;

T
TomasMatousek 已提交
849
                // initializers that have been analyzed but not yet lowered.
P
Pilchie 已提交
850 851
                BoundStatementList analyzedInitializers = null;

852
                ImportChain importChain = null;
853
                var hasTrailingExpression = false;
P
Pilchie 已提交
854 855

                if (methodSymbol.IsScriptConstructor)
856
                {
857
                    body = new BoundBlock(methodSymbol.GetNonNullSyntaxNode(), ImmutableArray<LocalSymbol>.Empty, ImmutableArray<BoundStatement>.Empty) { WasCompilerGenerated = true };
858 859
                }
                else if (methodSymbol.IsScriptInitializer)
P
Pilchie 已提交
860 861
                {
                    // rewrite top-level statements and script variable declarations to a list of statements and assignments, respectively:
862
                    var initializerStatements = InitializerRewriter.RewriteScriptInitializer(processedInitializers.BoundInitializers, (SynthesizedInteractiveInitializerMethod)methodSymbol, out hasTrailingExpression);
P
Pilchie 已提交
863 864

                    // the lowered script initializers should not be treated as initializers anymore but as a method body:
E
Evan Hauck 已提交
865
                    body = BoundBlock.SynthesizedNoLocals(initializerStatements.Syntax, initializerStatements.Statements);
P
Pilchie 已提交
866

C
Charles Stoner 已提交
867 868 869 870
                    var unusedDiagnostics = DiagnosticBag.GetInstance();
                    DataFlowPass.Analyze(_compilation, methodSymbol, initializerStatements, unusedDiagnostics, requireOutParamsAssigned: false);
                    DiagnosticsPass.IssueDiagnostics(_compilation, initializerStatements, unusedDiagnostics, methodSymbol);
                    unusedDiagnostics.Free();
P
Pilchie 已提交
871 872 873
                }
                else
                {
874
                    // Do not emit initializers if we are invoking another constructor of this class.
B
beep boop 已提交
875
                    includeInitializersInBody = !processedInitializers.BoundInitializers.IsDefaultOrEmpty &&
876
                                                !HasThisConstructorInitializer(methodSymbol);
877

878
                    body = BindMethodBody(methodSymbol, compilationState, diagsForCurrentMethod, out importChain, out originalBodyNested);
P
Pilchie 已提交
879 880 881 882 883 884 885

                    // lower initializers just once. the lowered tree will be reused when emitting all constructors 
                    // with field initializers. Once lowered, these initializers will be stashed in processedInitializers.LoweredInitializers
                    // (see later in this method). Don't bother lowering _now_ if this particular ctor won't have the initializers 
                    // appended to its body.
                    if (includeInitializersInBody && processedInitializers.LoweredInitializers == null)
                    {
C
Charles Stoner 已提交
886
                        analyzedInitializers = InitializerRewriter.RewriteConstructor(processedInitializers.BoundInitializers, methodSymbol);
P
Pilchie 已提交
887 888
                        processedInitializers.HasErrors = processedInitializers.HasErrors || analyzedInitializers.HasAnyErrors;

889
                        if (body != null && ((methodSymbol.ContainingType.IsStructType() && !methodSymbol.IsImplicitConstructor) || instrumentForDynamicAnalysis))
890
                        {
891 892 893 894 895 896 897 898
                            if (instrumentForDynamicAnalysis && methodSymbol.IsImplicitConstructor)
                            {
                                // Flow analysis over the initializers is necessary in order to find assignments to fields.
                                // Bodies of implicit constructors do not get flow analysis later, so the initializers
                                // are analyzed here.
                                DataFlowPass.Analyze(_compilation, methodSymbol, analyzedInitializers, diagsForCurrentMethod, requireOutParamsAssigned: false);
                            }

899
                            // In order to get correct diagnostics, we need to analyze initializers and the body together.
E
Evan Hauck 已提交
900
                            body = body.Update(body.Locals, body.LocalFunctions, body.Statements.Insert(0, analyzedInitializers));
901 902 903
                            includeInitializersInBody = false;
                            analyzedInitializers = null;
                        }
904 905 906 907
                        else
                        {
                            // These analyses check for diagnostics in lambdas.
                            // Control flow analysis and implicit return insertion are unnecessary.
908 909
                            DataFlowPass.Analyze(_compilation, methodSymbol, analyzedInitializers, diagsForCurrentMethod, requireOutParamsAssigned: false);
                            DiagnosticsPass.IssueDiagnostics(_compilation, analyzedInitializers, diagsForCurrentMethod, methodSymbol);
910
                        }
911
                    }
P
Pilchie 已提交
912 913 914 915 916
                }

#if DEBUG
                // If the method is a synthesized static or instance constructor, then debugImports will be null and we will use the value
                // from the first field initializer.
917 918
                if ((methodSymbol.MethodKind == MethodKind.Constructor || methodSymbol.MethodKind == MethodKind.StaticConstructor) &&
                    methodSymbol.IsImplicitlyDeclared && body == null)
P
Pilchie 已提交
919
                {
920 921
                    // There was no body to bind, so we didn't get anything from BindMethodBody.
                    Debug.Assert(importChain == null);
P
Pilchie 已提交
922
                }
923 924 925

                // Either there were no field initializers or we grabbed debug imports from the first one.
                Debug.Assert(processedInitializers.BoundInitializers.IsDefaultOrEmpty || processedInitializers.FirstImportChain != null);
P
Pilchie 已提交
926 927
#endif

T
TomasMatousek 已提交
928
                importChain = importChain ?? processedInitializers.FirstImportChain;
P
Pilchie 已提交
929 930

                // Associate these debug imports with all methods generated from this one.
T
TomasMatousek 已提交
931
                compilationState.CurrentImportChain = importChain;
P
Pilchie 已提交
932

933
                if (body != null)
P
Pilchie 已提交
934
                {
935
                    DiagnosticsPass.IssueDiagnostics(_compilation, body, diagsForCurrentMethod, methodSymbol);
P
Pilchie 已提交
936 937
                }

938
                BoundBlock flowAnalyzedBody = null;
939
                if (body != null)
940
                {
941
                    flowAnalyzedBody = FlowAnalysisPass.Rewrite(methodSymbol, body, diagsForCurrentMethod, hasTrailingExpression: hasTrailingExpression, originalBodyNested: originalBodyNested);
942
                }
943

944
                bool hasErrors = _hasDeclarationErrors || diagsForCurrentMethod.HasAnyErrors() || processedInitializers.HasErrors;
P
Pilchie 已提交
945 946 947 948 949

                // Record whether or not the bound tree for the lowered method body (including any initializers) contained any
                // errors (note: errors, not diagnostics).
                SetGlobalErrorIfTrue(hasErrors);

950 951
                bool diagsWritten = false;
                var actualDiagnostics = diagsForCurrentMethod.ToReadOnly();
T
TomasMatousek 已提交
952 953 954 955 956
                if (sourceMethod != null)
                {
                    actualDiagnostics = sourceMethod.SetDiagnostics(actualDiagnostics, out diagsWritten);
                }

957
                if (diagsWritten && !methodSymbol.IsImplicitlyDeclared && _compilation.EventQueue != null)
958 959 960 961
                {
                    var lazySemanticModel = body == null ? null : new Lazy<SemanticModel>(() =>
                    {
                        var syntax = body.Syntax;
962
                        var semanticModel = (CSharpSemanticModel)_compilation.GetSemanticModel(syntax.SyntaxTree);
963 964 965
                        var memberModel = semanticModel.GetMemberModel(syntax);
                        if (memberModel != null)
                        {
966
                            memberModel.UnguardedAddBoundTreeForStandaloneSyntax(syntax, body);
967 968 969
                        }
                        return semanticModel;
                    });
T
TomasMatousek 已提交
970

971
                    MethodSymbol symbolToProduce = methodSymbol.PartialDefinitionPart ?? methodSymbol;
N
Neal Gafter 已提交
972
                    _compilation.EventQueue.TryEnqueue(new SymbolDeclaredCompilationEvent(_compilation, symbolToProduce, lazySemanticModel));
973 974
                }

P
Pilchie 已提交
975 976
                // Don't lower if we're not emitting or if there were errors. 
                // Methods that had binding errors are considered too broken to be lowered reliably.
977
                if (_moduleBeingBuiltOpt == null || hasErrors)
P
Pilchie 已提交
978
                {
979
                    _diagnostics.AddRange(actualDiagnostics);
P
Pilchie 已提交
980 981 982 983 984 985 986 987
                    return;
                }

                // ############################
                // LOWERING AND EMIT
                // Any errors generated below here are considered Emit diagnostics 
                // and will not be reported to callers Compilation.GetDiagnostics()

J
More  
John Hamby 已提交
988
                ImmutableArray<SourceSpan> dynamicAnalysisSpans = ImmutableArray<SourceSpan>.Empty;
989
                bool hasBody = flowAnalyzedBody != null;
990
                VariableSlotAllocator lazyVariableSlotAllocator = null;
991
                StateMachineTypeSymbol stateMachineTypeOpt = null;
992 993
                var lambdaDebugInfoBuilder = ArrayBuilder<LambdaDebugInfo>.GetInstance();
                var closureDebugInfoBuilder = ArrayBuilder<ClosureDebugInfo>.GetInstance();
994
                BoundStatement loweredBodyOpt = null;
P
Pilchie 已提交
995

996
                try
P
Pilchie 已提交
997
                {
998
                    if (hasBody)
P
Pilchie 已提交
999
                    {
1000 1001 1002 1003 1004 1005
                        loweredBodyOpt = LowerBodyOrInitializer(
                            methodSymbol,
                            methodOrdinal,
                            flowAnalyzedBody,
                            previousSubmissionFields,
                            compilationState,
1006
                            instrumentForDynamicAnalysis,
1007
                            _debugDocumentProvider,
J
John Hamby 已提交
1008
                            ref dynamicAnalysisSpans,
1009 1010 1011 1012 1013 1014 1015
                            diagsForCurrentMethod,
                            ref lazyVariableSlotAllocator,
                            lambdaDebugInfoBuilder,
                            closureDebugInfoBuilder,
                            out stateMachineTypeOpt);

                        Debug.Assert(loweredBodyOpt != null);
P
Pilchie 已提交
1016 1017 1018
                    }
                    else
                    {
1019 1020 1021 1022 1023 1024 1025 1026 1027
                        loweredBodyOpt = null;
                    }

                    hasErrors = hasErrors || (hasBody && loweredBodyOpt.HasErrors) || diagsForCurrentMethod.HasAnyErrors();
                    SetGlobalErrorIfTrue(hasErrors);

                    // don't emit if the resulting method would contain initializers with errors
                    if (!hasErrors && (hasBody || includeInitializersInBody))
                    {
A
AlekseyTs 已提交
1028 1029
                        Debug.Assert(!methodSymbol.IsImplicitInstanceConstructor || !methodSymbol.ContainingType.IsStructType());

1030 1031 1032
                        // Fields must be initialized before constructor initializer (which is the first statement of the analyzed body, if specified),
                        // so that the initialization occurs before any method overridden by the declaring class can be invoked from the base constructor
                        // and access the fields.
P
Pilchie 已提交
1033

1034 1035 1036
                        ImmutableArray<BoundStatement> boundStatements;

                        if (methodSymbol.IsScriptConstructor)
P
Pilchie 已提交
1037
                        {
1038
                            boundStatements = MethodBodySynthesizer.ConstructScriptConstructorBody(loweredBodyOpt, methodSymbol, previousSubmissionFields, _compilation);
1039 1040 1041 1042
                        }
                        else
                        {
                            boundStatements = ImmutableArray<BoundStatement>.Empty;
1043

1044 1045
                            if (analyzedInitializers != null)
                            {
J
John Hamby 已提交
1046
                                // For dynamic analysis, field initializers are instrumented as part of constructors,
1047 1048
                                // and so are never instrumented here.
                                Debug.Assert(!instrumentForDynamicAnalysis);
1049
                                StateMachineTypeSymbol initializerStateMachineTypeOpt;
1050

T
Ty Overby 已提交
1051
                                var lowered = LowerBodyOrInitializer(
1052 1053 1054 1055 1056
                                    methodSymbol,
                                    methodOrdinal,
                                    analyzedInitializers,
                                    previousSubmissionFields,
                                    compilationState,
1057
                                    instrumentForDynamicAnalysis,
J
John Hamby 已提交
1058 1059
                                    _debugDocumentProvider,
                                    ref dynamicAnalysisSpans,
1060 1061 1062 1063 1064 1065 1066 1067 1068
                                    diagsForCurrentMethod,
                                    ref lazyVariableSlotAllocator,
                                    lambdaDebugInfoBuilder,
                                    closureDebugInfoBuilder,
                                    out initializerStateMachineTypeOpt);

                                // initializers can't produce state machines
                                Debug.Assert((object)initializerStateMachineTypeOpt == null);
                                Debug.Assert(!hasErrors);
T
Ty Overby 已提交
1069
                                hasErrors = lowered.HasAnyErrors || diagsForCurrentMethod.HasAnyErrors();
1070 1071 1072 1073
                                SetGlobalErrorIfTrue(hasErrors);

                                if (hasErrors)
                                {
1074
                                    _diagnostics.AddRange(diagsForCurrentMethod);
1075 1076
                                    return;
                                }
T
Ty Overby 已提交
1077 1078 1079 1080

                                // Only do the cast if we haven't returned with some error diagnostics.
                                // Otherwise, `lowered` might have been a BoundBadStatement.
                                processedInitializers.LoweredInitializers = (BoundStatementList)lowered;
1081
                            }
P
Pilchie 已提交
1082

1083 1084 1085 1086 1087
                            // initializers for global code have already been included in the body
                            if (includeInitializersInBody)
                            {
                                boundStatements = boundStatements.Concat(processedInitializers.LoweredInitializers.Statements);
                            }
P
Pilchie 已提交
1088

1089
                            if (hasBody)
P
Pilchie 已提交
1090
                            {
1091
                                boundStatements = boundStatements.Concat(ImmutableArray.Create(loweredBodyOpt));
P
Pilchie 已提交
1092 1093 1094
                            }
                        }

1095 1096 1097 1098 1099
                        CSharpSyntaxNode syntax = methodSymbol.GetNonNullSyntaxNode();

                        var boundBody = BoundStatementList.Synthesized(syntax, boundStatements);

                        var emittedBody = GenerateMethodBody(
1100
                            _moduleBeingBuiltOpt,
1101 1102
                            methodSymbol,
                            methodOrdinal,
1103 1104 1105 1106 1107
                            boundBody,
                            lambdaDebugInfoBuilder.ToImmutable(),
                            closureDebugInfoBuilder.ToImmutable(),
                            stateMachineTypeOpt,
                            lazyVariableSlotAllocator,
1108
                            diagsForCurrentMethod,
1109
                            _debugDocumentProvider,
T
TomasMatousek 已提交
1110
                            importChain,
J
More  
John Hamby 已提交
1111 1112
                            _emittingPdb,
                            dynamicAnalysisSpans);
1113

1114
                        _moduleBeingBuiltOpt.SetMethodBody(methodSymbol.PartialDefinitionPart ?? methodSymbol, emittedBody);
1115 1116
                    }

1117
                    _diagnostics.AddRange(diagsForCurrentMethod);
1118 1119 1120 1121 1122
                }
                finally
                {
                    lambdaDebugInfoBuilder.Free();
                    closureDebugInfoBuilder.Free();
P
Pilchie 已提交
1123 1124 1125 1126 1127
                }
            }
            finally
            {
                diagsForCurrentMethod.Free();
T
TomasMatousek 已提交
1128
                compilationState.CurrentImportChain = oldImportChain;
P
Pilchie 已提交
1129 1130 1131
            }
        }

T
TomasMatousek 已提交
1132 1133 1134
        // internal for testing
        internal static BoundStatement LowerBodyOrInitializer(
            MethodSymbol method,
1135
            int methodOrdinal,
T
TomasMatousek 已提交
1136 1137 1138
            BoundStatement body,
            SynthesizedSubmissionFields previousSubmissionFields,
            TypeCompilationState compilationState,
1139 1140
            bool instrumentForDynamicAnalysis,
            DebugDocumentProvider debugDocumentProvider,
J
John Hamby 已提交
1141
            ref ImmutableArray<SourceSpan> dynamicAnalysisSpans,
1142
            DiagnosticBag diagnostics,
1143 1144 1145 1146
            ref VariableSlotAllocator lazyVariableSlotAllocator,
            ArrayBuilder<LambdaDebugInfo> lambdaDebugInfoBuilder,
            ArrayBuilder<ClosureDebugInfo> closureDebugInfoBuilder,
            out StateMachineTypeSymbol stateMachineTypeOpt)
T
TomasMatousek 已提交
1147 1148
        {
            Debug.Assert(compilationState.ModuleBuilderOpt != null);
1149
            stateMachineTypeOpt = null;
T
TomasMatousek 已提交
1150 1151 1152 1153 1154 1155

            if (body.HasErrors)
            {
                return body;
            }

1156
            try
T
TomasMatousek 已提交
1157
            {
1158
                bool sawLambdas;
1159
                bool sawLocalFunctions;
1160 1161 1162
                bool sawAwaitInExceptionHandler;
                var loweredBody = LocalRewriter.Rewrite(
                    method.DeclaringCompilation,
T
TomasMatousek 已提交
1163
                    method,
1164
                    methodOrdinal,
T
TomasMatousek 已提交
1165
                    method.ContainingType,
1166
                    body,
T
TomasMatousek 已提交
1167
                    compilationState,
1168 1169
                    previousSubmissionFields: previousSubmissionFields,
                    allowOmissionOfConditionalCalls: true,
1170 1171
                    instrumentForDynamicAnalysis: instrumentForDynamicAnalysis,
                    debugDocumentProvider:debugDocumentProvider,
J
John Hamby 已提交
1172
                    dynamicAnalysisSpans: ref dynamicAnalysisSpans,
1173 1174
                    diagnostics: diagnostics,
                    sawLambdas: out sawLambdas,
1175
                    sawLocalFunctions: out sawLocalFunctions,
1176
                    sawAwaitInExceptionHandler: out sawAwaitInExceptionHandler);
T
TomasMatousek 已提交
1177

1178 1179 1180 1181
                if (loweredBody.HasErrors)
                {
                    return loweredBody;
                }
T
TomasMatousek 已提交
1182

1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197
                if (sawAwaitInExceptionHandler)
                {
                    // If we have awaits in handlers, we need to 
                    // replace handlers with synthetic ones which can be consumed by async rewriter.
                    // The reason why this rewrite happens before the lambda rewrite 
                    // is that we may need access to exception locals and it would be fairly hard to do
                    // if these locals are captured into closures (possibly nested ones).
                    Debug.Assert(!method.IsIterator);
                    loweredBody = AsyncExceptionHandlerRewriter.Rewrite(
                        method,
                        method.ContainingType,
                        loweredBody,
                        compilationState,
                        diagnostics);
                }
1198

1199 1200 1201 1202
                if (loweredBody.HasErrors)
                {
                    return loweredBody;
                }
T
TomasMatousek 已提交
1203

1204 1205
                if (lazyVariableSlotAllocator == null)
                {
1206
                    lazyVariableSlotAllocator = compilationState.ModuleBuilderOpt.TryCreateVariableSlotAllocator(method, method, diagnostics);
1207
                }
T
TomasMatousek 已提交
1208

1209
                BoundStatement bodyWithoutLambdas = loweredBody;
1210
                if (sawLambdas || sawLocalFunctions)
1211 1212 1213 1214 1215 1216 1217
                {
                    bodyWithoutLambdas = LambdaRewriter.Rewrite(
                        loweredBody,
                        method.ContainingType,
                        method.ThisParameter,
                        method,
                        methodOrdinal,
1218
                        null,
1219 1220 1221 1222 1223 1224 1225
                        lambdaDebugInfoBuilder,
                        closureDebugInfoBuilder,
                        lazyVariableSlotAllocator,
                        compilationState,
                        diagnostics,
                        assignLocals: false);
                }
T
TomasMatousek 已提交
1226

1227 1228 1229 1230 1231 1232 1233
                if (bodyWithoutLambdas.HasErrors)
                {
                    return bodyWithoutLambdas;
                }

                IteratorStateMachine iteratorStateMachine;
                BoundStatement bodyWithoutIterators = IteratorRewriter.Rewrite(bodyWithoutLambdas, method, methodOrdinal, lazyVariableSlotAllocator, compilationState, diagnostics, out iteratorStateMachine);
T
TomasMatousek 已提交
1234

1235 1236 1237 1238
                if (bodyWithoutIterators.HasErrors)
                {
                    return bodyWithoutIterators;
                }
1239

1240 1241
                AsyncStateMachine asyncStateMachine;
                BoundStatement bodyWithoutAsync = AsyncRewriter.Rewrite(bodyWithoutIterators, method, methodOrdinal, lazyVariableSlotAllocator, compilationState, diagnostics, out asyncStateMachine);
T
TomasMatousek 已提交
1242

1243 1244 1245 1246 1247 1248 1249 1250 1251 1252
                Debug.Assert(iteratorStateMachine == null || asyncStateMachine == null);
                stateMachineTypeOpt = (StateMachineTypeSymbol)iteratorStateMachine ?? asyncStateMachine;

                return bodyWithoutAsync;
            }
            catch (BoundTreeVisitor.CancelledByStackGuardException ex)
            {
                ex.AddAnError(diagnostics);
                return new BoundBadStatement(body.Syntax, ImmutableArray.Create<BoundNode>(body), hasErrors: true);
            }
T
TomasMatousek 已提交
1253 1254
        }

1255
        private static MethodBody GenerateMethodBody(
1256
            PEModuleBuilder moduleBuilder,
1257
            MethodSymbol method,
1258
            int methodOrdinal,
B
beep boop 已提交
1259
            BoundStatement block,
1260 1261
            ImmutableArray<LambdaDebugInfo> lambdaDebugInfo,
            ImmutableArray<ClosureDebugInfo> closureDebugInfo,
1262
            StateMachineTypeSymbol stateMachineTypeOpt,
1263
            VariableSlotAllocator variableSlotAllocatorOpt,
1264 1265
            DiagnosticBag diagnostics,
            DebugDocumentProvider debugDocumentProvider,
T
TomasMatousek 已提交
1266
            ImportChain importChainOpt,
J
More  
John Hamby 已提交
1267 1268
            bool emittingPdb,
            ImmutableArray<SourceSpan> dynamicAnalysisSpans)
T
TomasMatousek 已提交
1269 1270 1271 1272
        {
            // Note: don't call diagnostics.HasAnyErrors() in release; could be expensive if compilation has many warnings.
            Debug.Assert(!diagnostics.HasAnyErrors(), "Running code generator when errors exist might be dangerous; code generator not expecting errors");

1273
            var compilation = moduleBuilder.Compilation;
1274
            var localSlotManager = new LocalSlotManager(variableSlotAllocatorOpt);
1275
            var optimizations = compilation.Options.OptimizationLevel;
T
TomasMatousek 已提交
1276

1277
            ILBuilder builder = new ILBuilder(moduleBuilder, localSlotManager, optimizations);
T
TomasMatousek 已提交
1278 1279 1280 1281
            DiagnosticBag diagnosticsForThisMethod = DiagnosticBag.GetInstance();
            try
            {
                Cci.AsyncMethodBodyDebugInfo asyncDebugInfo = null;
1282

1283
                var codeGen = new CodeGen.CodeGenerator(method, block, builder, moduleBuilder, diagnosticsForThisMethod, optimizations, emittingPdb);
1284

1285 1286 1287 1288 1289 1290
                if (diagnosticsForThisMethod.HasAnyErrors())
                {
                    // we are done here. Since there were errors we should not emit anything.
                    return null;
                }

1291 1292
                // We need to save additional debugging information for MoveNext of an async state machine.
                var stateMachineMethod = method as SynthesizedStateMachineMethod;
1293 1294 1295
                bool isStateMachineMoveNextMethod = stateMachineMethod != null && method.Name == WellKnownMemberNames.MoveNextMethodName;

                if (isStateMachineMoveNextMethod && stateMachineMethod.StateMachineType.KickoffMethod.IsAsync)
T
TomasMatousek 已提交
1296 1297 1298 1299
                {
                    int asyncCatchHandlerOffset;
                    ImmutableArray<int> asyncYieldPoints;
                    ImmutableArray<int> asyncResumePoints;
1300
                    codeGen.Generate(out asyncCatchHandlerOffset, out asyncYieldPoints, out asyncResumePoints);
1301

1302 1303 1304 1305 1306 1307 1308 1309
                    var kickoffMethod = stateMachineMethod.StateMachineType.KickoffMethod;

                    // The exception handler IL offset is used by the debugger to treat exceptions caught by the marked catch block as "user unhandled".
                    // This is important for async void because async void exceptions generally result in the process being terminated,
                    // but without anything useful on the call stack. Async Task methods on the other hand return exceptions as the result of the Task.
                    // So it is undesirable to consider these exceptions "user unhandled" since there may well be user code that is awaiting the task.
                    // This is a heuristic since it's possible that there is no user code awaiting the task.
                    asyncDebugInfo = new Cci.AsyncMethodBodyDebugInfo(kickoffMethod, kickoffMethod.ReturnsVoid ? asyncCatchHandlerOffset : -1, asyncYieldPoints, asyncResumePoints);
1310 1311 1312 1313
                }
                else
                {
                    codeGen.Generate();
T
TomasMatousek 已提交
1314 1315
                }

1316 1317 1318 1319 1320
                // Translate the imports even if we are not writing PDBs. The translation has an impact on generated metadata 
                // and we don't want to emit different metadata depending on whether or we emit with PDB stream.
                // TODO (https://github.com/dotnet/roslyn/issues/2846): This will need to change for member initializers in partial class.
                var importScopeOpt = importChainOpt?.Translate(moduleBuilder, diagnosticsForThisMethod);

T
TomasMatousek 已提交
1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334
                var localVariables = builder.LocalSlotManager.LocalsInOrder();

                if (localVariables.Length > 0xFFFE)
                {
                    diagnosticsForThisMethod.Add(ErrorCode.ERR_TooManyLocals, method.Locations.First());
                }

                if (diagnosticsForThisMethod.HasAnyErrors())
                {
                    // we are done here. Since there were errors we should not emit anything.
                    return null;
                }

                // We will only save the IL builders when running tests.
1335
                if (moduleBuilder.SaveTestData)
T
TomasMatousek 已提交
1336
                {
1337
                    moduleBuilder.SetMethodTestData(method, builder.GetSnapshot());
T
TomasMatousek 已提交
1338 1339 1340
                }

                // Only compiler-generated MoveNext methods have iterator scopes.  See if this is one.
1341 1342 1343
                var stateMachineHoistedLocalScopes = default(ImmutableArray<Cci.StateMachineHoistedLocalScope>);
                if (isStateMachineMoveNextMethod)
                {
1344
                    stateMachineHoistedLocalScopes = builder.GetHoistedLocalScopes();
1345 1346
                }

1347 1348
                var stateMachineHoistedLocalSlots = default(ImmutableArray<EncHoistedLocalInfo>);
                var stateMachineAwaiterSlots = default(ImmutableArray<Cci.ITypeReference>);
1349 1350 1351
                if (optimizations == OptimizationLevel.Debug && stateMachineTypeOpt != null)
                {
                    Debug.Assert(method.IsAsync || method.IsIterator);
1352 1353
                    GetStateMachineSlotDebugInfo(moduleBuilder, moduleBuilder.GetSynthesizedFields(stateMachineTypeOpt), variableSlotAllocatorOpt, diagnosticsForThisMethod, out stateMachineHoistedLocalSlots, out stateMachineAwaiterSlots);
                    Debug.Assert(!diagnostics.HasAnyErrors());
1354
                }
T
TomasMatousek 已提交
1355

1356
                DynamicAnalysisMethodBodyData dynamicAnalysisDataOpt = null;
1357
                if (moduleBuilder.EmitOptions.EmitTestCoverageData)
1358 1359
                {
                    Debug.Assert(debugDocumentProvider != null);
J
More  
John Hamby 已提交
1360
                    dynamicAnalysisDataOpt = new DynamicAnalysisMethodBodyData(dynamicAnalysisSpans);
1361 1362
                }

T
TomasMatousek 已提交
1363 1364 1365
                return new MethodBody(
                    builder.RealizedIL,
                    builder.MaxStack,
1366
                    method.PartialDefinitionPart ?? method,
1367
                    variableSlotAllocatorOpt?.MethodId ?? new DebugId(methodOrdinal, moduleBuilder.CurrentGenerationOrdinal),
T
TomasMatousek 已提交
1368 1369 1370 1371 1372 1373
                    localVariables,
                    builder.RealizedSequencePoints,
                    debugDocumentProvider,
                    builder.RealizedExceptionHandlers,
                    builder.GetAllScopes(),
                    builder.HasDynamicLocal,
1374
                    importScopeOpt,
1375 1376
                    lambdaDebugInfo,
                    closureDebugInfo,
1377
                    stateMachineTypeOpt?.Name,
1378 1379
                    stateMachineHoistedLocalScopes,
                    stateMachineHoistedLocalSlots,
1380
                    stateMachineAwaiterSlots,
1381 1382
                    asyncDebugInfo,
                    dynamicAnalysisDataOpt);
T
TomasMatousek 已提交
1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395
            }
            finally
            {
                // Basic blocks contain poolable builders for IL and sequence points. Free those back
                // to their pools.
                builder.FreeBasicBlocks();

                // Remember diagnostics.
                diagnostics.AddRange(diagnosticsForThisMethod);
                diagnosticsForThisMethod.Free();
            }
        }

1396
        private static void GetStateMachineSlotDebugInfo(
1397
            PEModuleBuilder moduleBuilder,
B
beep boop 已提交
1398
            IEnumerable<Cci.IFieldDefinition> fieldDefs,
1399 1400
            VariableSlotAllocator variableSlotAllocatorOpt,
            DiagnosticBag diagnostics,
1401 1402
            out ImmutableArray<EncHoistedLocalInfo> hoistedVariableSlots,
            out ImmutableArray<Cci.ITypeReference> awaiterSlots)
1403
        {
1404 1405 1406
            var hoistedVariables = ArrayBuilder<EncHoistedLocalInfo>.GetInstance();
            var awaiters = ArrayBuilder<Cci.ITypeReference>.GetInstance();

1407 1408
            foreach (StateMachineFieldSymbol field in fieldDefs)
            {
1409
                int index = field.SlotIndex;
1410

1411
                if (field.SlotDebugInfo.SynthesizedKind == SynthesizedLocalKind.AwaiterField)
1412
                {
1413 1414 1415 1416 1417 1418 1419
                    Debug.Assert(index >= 0);

                    while (index >= awaiters.Count)
                    {
                        awaiters.Add(null);
                    }

1420
                    awaiters[index] = moduleBuilder.EncTranslateLocalVariableType(field.Type, diagnostics);
1421
                }
1422 1423 1424
                else if (!field.SlotDebugInfo.Id.IsNone)
                {
                    Debug.Assert(index >= 0 && field.SlotDebugInfo.SynthesizedKind.IsLongLived());
1425

1426 1427 1428
                    while (index >= hoistedVariables.Count)
                    {
                        // Empty slots may be present if variables were deleted during EnC.
1429
                        hoistedVariables.Add(new EncHoistedLocalInfo(true));
1430 1431
                    }

1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448
                    hoistedVariables[index] = new EncHoistedLocalInfo(field.SlotDebugInfo, moduleBuilder.EncTranslateLocalVariableType(field.Type, diagnostics));
                }
            }

            // Fill in empty slots for variables deleted during EnC that are not followed by an existing variable:
            if (variableSlotAllocatorOpt != null)
            {
                int previousAwaiterCount = variableSlotAllocatorOpt.PreviousAwaiterSlotCount;
                while (awaiters.Count < previousAwaiterCount)
                {
                    awaiters.Add(null);
                }

                int previousAwaiterSlotCount = variableSlotAllocatorOpt.PreviousHoistedLocalSlotCount;
                while (hoistedVariables.Count < previousAwaiterSlotCount)
                {
                    hoistedVariables.Add(new EncHoistedLocalInfo(true));
1449
                }
1450 1451
            }

1452 1453
            hoistedVariableSlots = hoistedVariables.ToImmutableAndFree();
            awaiterSlots = awaiters.ToImmutableAndFree();
1454 1455
        }

T
TomasMatousek 已提交
1456
        // NOTE: can return null if the method has no body.
1457
        internal static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationState compilationState, DiagnosticBag diagnostics)
T
TomasMatousek 已提交
1458
        {
1459 1460 1461
            ImportChain importChain;
            bool originalBodyNested;
            return BindMethodBody(method, compilationState, diagnostics, out importChain, out originalBodyNested);
T
TomasMatousek 已提交
1462 1463 1464
        }

        // NOTE: can return null if the method has no body.
1465
        private static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationState compilationState, DiagnosticBag diagnostics, out ImportChain importChain, out bool originalBodyNested)
T
TomasMatousek 已提交
1466
        {
1467
            originalBodyNested = false;
T
TomasMatousek 已提交
1468
            importChain = null;
T
TomasMatousek 已提交
1469

1470
            BoundBlock body;
T
TomasMatousek 已提交
1471 1472 1473 1474 1475 1476

            var sourceMethod = method as SourceMethodSymbol;
            if ((object)sourceMethod != null)
            {
                if (sourceMethod.IsExtern)
                {
1477
                    if (sourceMethod.BodySyntax == null && (sourceMethod.SyntaxNode as ConstructorDeclarationSyntax)?.Initializer == null)
T
TomasMatousek 已提交
1478
                    {
1479
                        // Generate warnings only if we are not generating ERR_ExternHasBody or ERR_ExternHasConstructorInitializer errors
T
TomasMatousek 已提交
1480 1481
                        GenerateExternalMethodWarnings(sourceMethod, diagnostics);
                    }
1482

T
TomasMatousek 已提交
1483 1484
                    return null;
                }
1485 1486

                if (sourceMethod.IsDefaultValueTypeConstructor())
T
TomasMatousek 已提交
1487 1488 1489 1490 1491
                {
                    // No body for default struct constructor.
                    return null;
                }

1492
                var compilation = method.DeclaringCompilation;
1493 1494
                var factory = compilation.GetBinderFactory(sourceMethod.SyntaxTree);

1495
                var bodySyntax = sourceMethod.BodySyntax;
1496

1497
                if (bodySyntax != null)
T
TomasMatousek 已提交
1498
                {
1499 1500
                    var inMethodBinder = factory.GetBinder(bodySyntax);
                    var binder = new ExecutableCodeBinder(bodySyntax, sourceMethod, inMethodBinder);
1501
                    importChain = binder.ImportChain;
1502

1503
                    binder.ValidateIteratorMethods(diagnostics);
T
TomasMatousek 已提交
1504

1505 1506 1507 1508
                    body = bodySyntax.Kind() == SyntaxKind.Block
                        ? binder.BindEmbeddedBlock((BlockSyntax)bodySyntax, diagnostics)
                        : binder.BindExpressionBodyAsBlock(
                            (ArrowExpressionClauseSyntax)bodySyntax, diagnostics);
1509 1510
                }
                else
1511 1512
                {
                    var property = sourceMethod.AssociatedSymbol as SourcePropertySymbol;
1513
                    if ((object)property != null && property.IsAutoProperty)
1514
                    {
1515 1516
                        return MethodBodySynthesizer.ConstructAutoPropertyAccessorBody(sourceMethod);
                    }
1517

1518
                    return null;
1519
                }
T
TomasMatousek 已提交
1520 1521 1522
            }
            else
            {
1523
                // synthesized methods should return their bound bodies
T
TomasMatousek 已提交
1524 1525 1526
                body = null;
            }

1527 1528 1529 1530 1531
            if (method.MethodKind == MethodKind.Destructor && body != null)
            {
                return MethodBodySynthesizer.ConstructDestructorBody(method, body);
            }

1532
            var constructorInitializer = BindConstructorInitializerIfAny(method, compilationState, diagnostics);
1533
            ImmutableArray<BoundStatement> statements;
T
TomasMatousek 已提交
1534

1535
            if (constructorInitializer == null)
T
TomasMatousek 已提交
1536
            {
1537 1538
                if (body != null)
                {
1539 1540 1541
                    return body;
                }

1542
                statements = ImmutableArray<BoundStatement>.Empty;
T
TomasMatousek 已提交
1543
            }
1544
            else if (body == null)
T
TomasMatousek 已提交
1545
            {
1546
                statements = ImmutableArray.Create(constructorInitializer);
T
TomasMatousek 已提交
1547 1548 1549
            }
            else
            {
1550
                statements = ImmutableArray.Create(constructorInitializer, body);
1551
                originalBodyNested = true;
T
TomasMatousek 已提交
1552 1553
            }

E
Evan Hauck 已提交
1554
            return BoundBlock.SynthesizedNoLocals(method.GetNonNullSyntaxNode(), statements);
T
TomasMatousek 已提交
1555 1556
        }

1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573
        private static BoundStatement BindConstructorInitializerIfAny(MethodSymbol method, TypeCompilationState compilationState, DiagnosticBag diagnostics)
        {
            // delegates have constructors but not constructor initializers
            if (method.MethodKind == MethodKind.Constructor && !method.ContainingType.IsDelegateType() && !method.IsExtern)
            {
                var compilation = method.DeclaringCompilation;
                var initializerInvocation = BindConstructorInitializer(method, diagnostics, compilation);

                if (initializerInvocation != null)
                {
                    var ctorCall = initializerInvocation as BoundCall;
                    if (ctorCall != null && !ctorCall.HasAnyErrors && ctorCall.Method != method && ctorCall.Method.ContainingType == method.ContainingType)
                    {
                        // Detect and report indirect cycles in the ctor-initializer call graph.
                        compilationState.ReportCtorInitializerCycles(method, ctorCall.Method, ctorCall.Syntax, diagnostics);
                    }

1574 1575
                    //  Base WasCompilerGenerated state off of whether constructor is implicitly declared, this will ensure proper instrumentation.
                    var constructorInitializer = new BoundExpressionStatement(initializerInvocation.Syntax, initializerInvocation) { WasCompilerGenerated = method.IsImplicitlyDeclared };
1576 1577 1578 1579 1580 1581 1582 1583
                    Debug.Assert(initializerInvocation.HasAnyErrors || constructorInitializer.IsConstructorInitializer(), "Please keep this bound node in sync with BoundNodeExtensions.IsConstructorInitializer.");
                    return constructorInitializer;
                }
            }

            return null;
        }

T
TomasMatousek 已提交
1584 1585 1586 1587 1588 1589 1590
        /// <summary>
        /// Bind the (implicit or explicit) constructor initializer of a constructor symbol.
        /// </summary>
        /// <param name="constructor">Constructor method.</param>
        /// <param name="diagnostics">Accumulates errors (e.g. access "this" in constructor initializer).</param>
        /// <param name="compilation">Used to retrieve binder.</param>
        /// <returns>A bound expression for the constructor initializer call.</returns>
1591
        internal static BoundExpression BindConstructorInitializer(MethodSymbol constructor, DiagnosticBag diagnostics, CSharpCompilation compilation)
T
TomasMatousek 已提交
1592 1593 1594 1595 1596
        {
            // Note that the base type can be null if we're compiling System.Object in source.
            NamedTypeSymbol baseType = constructor.ContainingType.BaseTypeNoUseSiteDiagnostics;

            SourceMethodSymbol sourceConstructor = constructor as SourceMethodSymbol;
1597
            ConstructorDeclarationSyntax constructorSyntax = null;
1598
            ArgumentListSyntax initializerArgumentListOpt = null;
T
TomasMatousek 已提交
1599 1600
            if ((object)sourceConstructor != null)
            {
1601 1602
                constructorSyntax = (ConstructorDeclarationSyntax)sourceConstructor.SyntaxNode;
                if (constructorSyntax.Initializer != null)
1603
                {
1604
                    initializerArgumentListOpt = constructorSyntax.Initializer.ArgumentList;
1605
                }
T
TomasMatousek 已提交
1606 1607 1608 1609 1610 1611 1612 1613
            }

            // The common case is that we have no constructor initializer and the type inherits directly from object.
            // Also, we might be trying to generate a constructor for an entirely compiler-generated class such
            // as a closure class; in that case it is vexing to try to find a suitable binder for the non-existing
            // constructor syntax so that we can do unnecessary overload resolution on the non-existing initializer!
            // Simply take the early out: bind directly to the parameterless object ctor rather than attempting
            // overload resolution.
1614
            if (initializerArgumentListOpt == null && (object)baseType != null)
T
TomasMatousek 已提交
1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673
            {
                if (baseType.SpecialType == SpecialType.System_Object)
                {
                    return GenerateObjectConstructorInitializer(constructor, diagnostics);
                }
                else if (baseType.IsErrorType() || baseType.IsStatic)
                {
                    // If the base type is bad and there is no initializer then we can just bail.
                    // We have no expressions we need to analyze to report errors on.
                    return null;
                }
            }

            // Either our base type is not object, or we have an initializer syntax, or both. We're going to
            // need to do overload resolution on the set of constructors of the base type, either on
            // the provided initializer syntax, or on an implicit ": base()" syntax.

            // SPEC ERROR: The specification states that if you have the situation 
            // SPEC ERROR: class B { ... } class D1 : B {} then the default constructor
            // SPEC ERROR: generated for D1 must call an accessible *parameterless* constructor
            // SPEC ERROR: in B. However, it also states that if you have 
            // SPEC ERROR: class B { ... } class D2 : B { D2() {} }  or
            // SPEC ERROR: class B { ... } class D3 : B { D3() : base() {} }  then
            // SPEC ERROR: the compiler performs *overload resolution* to determine
            // SPEC ERROR: which accessible constructor of B is called. Since B might have
            // SPEC ERROR: a ctor with all optional parameters, overload resolution might
            // SPEC ERROR: succeed even if there is no parameterless constructor. This
            // SPEC ERROR: is unintentionally inconsistent, and the native compiler does not
            // SPEC ERROR: implement this behavior. Rather, we should say in the spec that
            // SPEC ERROR: if there is no ctor in D1, then a ctor is created for you exactly
            // SPEC ERROR: as though you'd said "D1() : base() {}". 
            // SPEC ERROR: This is what we now do in Roslyn.

            // Now, in order to do overload resolution, we're going to need a binder. There are
            // three possible situations:
            //
            // class D1 : B { }
            // class D2 : B { D2(int x) { } }
            // class D3 : B { D3(int x) : base(x) { } }
            //
            // In the first case the binder needs to be the binder associated with
            // the *body* of D1 because if the base class ctor is protected, we need
            // to be inside the body of a derived class in order for it to be in the
            // accessibility domain of the protected base class ctor.
            //
            // In the second case the binder could be the binder associated with 
            // the body of D2; since the implicit call to base() will have no arguments
            // there is no need to look up "x".
            // 
            // In the third case the binder must be the binder that knows about "x" 
            // because x is in scope.

            Binder outerBinder;

            if ((object)sourceConstructor == null)
            {
                // The constructor is implicit. We need to get the binder for the body
                // of the enclosing class. 
                CSharpSyntaxNode containerNode = constructor.GetNonNullSyntaxNode();
1674
                SyntaxToken bodyToken = GetImplicitConstructorBodyToken(containerNode);
T
TomasMatousek 已提交
1675 1676
                outerBinder = compilation.GetBinderFactory(containerNode.SyntaxTree).GetBinder(containerNode, bodyToken.Position);
            }
1677
            else if (initializerArgumentListOpt == null)
T
TomasMatousek 已提交
1678 1679 1680 1681 1682
            {
                // We have a ctor in source but no explicit constructor initializer.  We can't just use the binder for the
                // type containing the ctor because the ctor might be marked unsafe.  Use the binder for the parameter list
                // as an approximation - the extra symbols won't matter because there are no identifiers to bind.

1683
                outerBinder = compilation.GetBinderFactory(sourceConstructor.SyntaxTree).GetBinder(constructorSyntax.ParameterList);
T
TomasMatousek 已提交
1684 1685 1686
            }
            else
            {
1687
                outerBinder = compilation.GetBinderFactory(sourceConstructor.SyntaxTree).GetBinder(initializerArgumentListOpt);
1688
            }
1689

1690
            // wrap in ConstructorInitializerBinder for appropriate errors
1691
            // Handle scoping for possible pattern variables declared in the initializer
1692 1693 1694 1695 1696 1697
            Binder initializerBinder = outerBinder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.ConstructorInitializer, constructor);

            if (initializerArgumentListOpt != null)
            {
                initializerBinder = new ExecutableCodeBinder(initializerArgumentListOpt, constructor, initializerBinder);
            }
1698

1699
            return initializerBinder.BindConstructorInitializer(initializerArgumentListOpt, constructor, diagnostics);
T
TomasMatousek 已提交
1700 1701
        }

1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718
        private static SyntaxToken GetImplicitConstructorBodyToken(CSharpSyntaxNode containerNode)
        {
            var kind = containerNode.Kind();
            switch (kind)
            {
                case SyntaxKind.ClassDeclaration:
                    return ((ClassDeclarationSyntax)containerNode).OpenBraceToken;
                case SyntaxKind.StructDeclaration:
                    return ((StructDeclarationSyntax)containerNode).OpenBraceToken;
                case SyntaxKind.EnumDeclaration:
                    // We're not going to find any non-default ctors, but we'll look anyway.
                    return ((EnumDeclarationSyntax)containerNode).OpenBraceToken;
                default:
                    throw ExceptionUtilities.UnexpectedValue(kind);
            }
        }

T
TomasMatousek 已提交
1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788
        internal static BoundCall GenerateObjectConstructorInitializer(MethodSymbol constructor, DiagnosticBag diagnostics)
        {
            NamedTypeSymbol objectType = constructor.ContainingType.BaseTypeNoUseSiteDiagnostics;
            Debug.Assert(objectType.SpecialType == SpecialType.System_Object);
            MethodSymbol objectConstructor = null;
            LookupResultKind resultKind = LookupResultKind.Viable;

            foreach (MethodSymbol objectCtor in objectType.InstanceConstructors)
            {
                if (objectCtor.ParameterCount == 0)
                {
                    objectConstructor = objectCtor;
                    break;
                }
            }

            // UNDONE: If this happens then something is deeply wrong. Should we give a better error?
            if ((object)objectConstructor == null)
            {
                diagnostics.Add(ErrorCode.ERR_BadCtorArgCount, constructor.Locations[0], objectType, /*desired param count*/ 0);
                return null;
            }

            // UNDONE: If this happens then something is deeply wrong. Should we give a better error?
            bool hasErrors = false;
            HashSet<DiagnosticInfo> useSiteDiagnostics = null;
            if (!AccessCheck.IsSymbolAccessible(objectConstructor, constructor.ContainingType, ref useSiteDiagnostics))
            {
                diagnostics.Add(ErrorCode.ERR_BadAccess, constructor.Locations[0], objectConstructor);
                resultKind = LookupResultKind.Inaccessible;
                hasErrors = true;
            }

            if (!useSiteDiagnostics.IsNullOrEmpty())
            {
                diagnostics.Add(constructor.Locations.IsEmpty ? NoLocation.Singleton : constructor.Locations[0], useSiteDiagnostics);
            }

            CSharpSyntaxNode syntax = constructor.GetNonNullSyntaxNode();

            BoundExpression receiver = new BoundThisReference(syntax, constructor.ContainingType) { WasCompilerGenerated = true };
            return new BoundCall(
                syntax: syntax,
                receiverOpt: receiver,
                method: objectConstructor,
                arguments: ImmutableArray<BoundExpression>.Empty,
                argumentNamesOpt: ImmutableArray<string>.Empty,
                argumentRefKindsOpt: ImmutableArray<RefKind>.Empty,
                isDelegateCall: false,
                expanded: false,
                invokedAsExtensionMethod: false,
                argsToParamsOpt: ImmutableArray<int>.Empty,
                resultKind: resultKind,
                type: objectType,
                hasErrors: hasErrors)
            { WasCompilerGenerated = true };
        }

        private static void GenerateExternalMethodWarnings(SourceMethodSymbol methodSymbol, DiagnosticBag diagnostics)
        {
            if (methodSymbol.GetAttributes().IsEmpty && !methodSymbol.ContainingType.IsComImport)
            {
                // external method with no attributes
                var errorCode = (methodSymbol.MethodKind == MethodKind.Constructor || methodSymbol.MethodKind == MethodKind.StaticConstructor) ?
                    ErrorCode.WRN_ExternCtorNoImplementation :
                    ErrorCode.WRN_ExternMethodNoImplementation;
                diagnostics.Add(errorCode, methodSymbol.Locations[0], methodSymbol);
            }
        }

P
Pilchie 已提交
1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804
        /// <summary>
        /// Returns true if the method is a constructor and has a this() constructor initializer.
        /// </summary>
        private static bool HasThisConstructorInitializer(MethodSymbol method)
        {
            if ((object)method != null && method.MethodKind == MethodKind.Constructor)
            {
                SourceMethodSymbol sourceMethod = method as SourceMethodSymbol;
                if ((object)sourceMethod != null)
                {
                    ConstructorDeclarationSyntax constructorSyntax = sourceMethod.SyntaxNode as ConstructorDeclarationSyntax;
                    if (constructorSyntax != null)
                    {
                        ConstructorInitializerSyntax initializerSyntax = constructorSyntax.Initializer;
                        if (initializerSyntax != null)
                        {
1805
                            return initializerSyntax.Kind() == SyntaxKind.ThisConstructorInitializer;
P
Pilchie 已提交
1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817
                        }
                    }
                }
            }

            return false;
        }

        private static Cci.DebugSourceDocument CreateDebugDocumentForFile(string normalizedPath)
        {
            return new Cci.DebugSourceDocument(normalizedPath, Cci.DebugSourceDocument.CorSymLanguageTypeCSharp);
        }
1818 1819 1820 1821 1822

        private static bool PassesFilter(Predicate<Symbol> filterOpt, Symbol symbol)
        {
            return (filterOpt == null) || filterOpt(symbol);
        }
P
Pilchie 已提交
1823 1824
    }
}