| File: Symbols\Synthesized\Records\SynthesizedRecordCopyCtor.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.Symbols { internal sealed class SynthesizedRecordCopyCtor : SynthesizedInstanceConstructor { private readonly int _memberOffset; public SynthesizedRecordCopyCtor( SourceMemberContainerTypeSymbol containingType, int memberOffset) : base(containingType) { _memberOffset = memberOffset; Parameters = ImmutableArray.Create(SynthesizedParameterSymbol.Create( this, TypeWithAnnotations.Create( isNullableEnabled: true, ContainingType), ordinal: 0, RefKind.None, "original")); } public override ImmutableArray<ParameterSymbol> Parameters { get; } public override Accessibility DeclaredAccessibility => ContainingType.IsSealed ? Accessibility.Private : Accessibility.Protected; internal override LexicalSortKey GetLexicalSortKey() => LexicalSortKey.GetSynthesizedMemberKey(_memberOffset); internal override void GenerateMethodBodyStatements(SyntheticBoundNodeFactory F, ArrayBuilder<BoundStatement> statements, BindingDiagnosticBag diagnostics) { // Write assignments to fields // .ctor(DerivedRecordType original) : base((BaseRecordType)original) // { // this.field1 = parameter.field1 // ... // this.fieldN = parameter.fieldN // } var param = F.Parameter(Parameters[0]); foreach (var field in ContainingType.GetFieldsToEmit()) { if (!field.IsStatic) { statements.Add(F.Assignment(F.Field(F.This(), field), F.Field(param, field))); } } // Add a sequence point at the end of the copy-constructor, so that a breakpoint placed on the record constructor // can be hit whenever a new instance of the record is created (either via a primary constructor or via a copy constructor). // // If the record doesn't have base initializer the span overlaps the span of the sequence point generated for primary // constructor implicit base initializer: // // record [|C<T>(int P, int Q)|] // implicit base constructor initializer span // record C<T>(int P, int Q) : [|B(...)|] // explicit base constructor initializer span // record [|C<T>|](int P, int Q) // copy-constructor span // // The copy-constructor span does not include the parameter list since the parameters are not in scope. var recordDeclaration = (RecordDeclarationSyntax)F.Syntax; statements.Add(new BoundSequencePointWithSpan(recordDeclaration, statementOpt: null, (recordDeclaration.TypeParameterList == null) ? recordDeclaration.Identifier.Span : TextSpan.FromBounds(recordDeclaration.Identifier.Span.Start, recordDeclaration.TypeParameterList.Span.End))); } internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder<CSharpAttributeData> attributes) { base.AddSynthesizedAttributes(moduleBuilder, ref attributes); Debug.Assert(IsImplicitlyDeclared); var compilation = this.DeclaringCompilation; AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor)); Debug.Assert(WellKnownMembers.IsSynthesizedAttributeOptional(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor)); if (HasSetsRequiredMembersImpl) { AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Diagnostics_CodeAnalysis_SetsRequiredMembersAttribute__ctor)); } } internal static MethodSymbol? FindCopyConstructor(NamedTypeSymbol containingType, NamedTypeSymbol within, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo) { MethodSymbol? bestCandidate = null; int bestModifierCountSoFar = -1; // stays as -1 unless we hit an ambiguity foreach (var member in containingType.InstanceConstructors) { if (HasCopyConstructorSignature(member) && !member.HasUnsupportedMetadata && AccessCheck.IsSymbolAccessible(member, within, ref useSiteInfo)) { // If one has fewer custom modifiers, that is better // (see OverloadResolution.BetterFunctionMember) if (bestCandidate is null && bestModifierCountSoFar < 0) { bestCandidate = member; continue; } if (bestModifierCountSoFar < 0) { bestModifierCountSoFar = bestCandidate.CustomModifierCount(); } var memberModCount = member.CustomModifierCount(); if (memberModCount > bestModifierCountSoFar) { continue; } if (memberModCount == bestModifierCountSoFar) { bestCandidate = null; continue; } bestCandidate = member; bestModifierCountSoFar = memberModCount; } } return bestCandidate; } internal static bool IsCopyConstructor(Symbol member) { if (member is MethodSymbol { ContainingType.IsRecord: true, MethodKind: MethodKind.Constructor } method) { return HasCopyConstructorSignature(method); } return false; } internal static bool HasCopyConstructorSignature(MethodSymbol member) { NamedTypeSymbol containingType = member.ContainingType; return member is MethodSymbol { IsStatic: false, ParameterCount: 1, Arity: 0 } method && method.Parameters[0].Type.Equals(containingType, TypeCompareKind.AllIgnoreOptions) && method.Parameters[0].RefKind == RefKind.None; } protected sealed override bool HasSetsRequiredMembersImpl // If the record type has a required members error, then it does have required members of some kind, we emit the SetsRequiredMembers attribute. => ContainingType.HasAnyRequiredMembers || ContainingType.HasRequiredMembersError; } }