diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs index baf4cd9bf3d13065ea4bfdfb500513859e469a3f..7d78f0e7bd55e47b04e3a30202bfcd0edaad9bdc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.CSharp.DocumentationComments; using Microsoft.CodeAnalysis.CSharp.Emit; using Roslyn.Utilities; -using System.Linq; namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE { @@ -33,7 +32,7 @@ internal sealed class PEFieldSymbol : FieldSymbol private ObsoleteAttributeData _lazyObsoleteAttributeData = ObsoleteAttributeData.Uninitialized; - private TypeWithAnnotations.Builder _lazyType; + private TypeWithAnnotations.Boxed _lazyType; private int _lazyFixedSize; private NamedTypeSymbol _lazyFixedImplementationType; private PEEventSymbol _associatedEventOpt; @@ -203,7 +202,7 @@ internal void SetAssociatedEvent(PEEventSymbol eventSymbol) private void EnsureSignatureIsLoaded() { - if (_lazyType.IsDefault) + if (_lazyType == null) { var moduleSymbol = _containingType.ContainingPEModule; bool isVolatile; @@ -232,7 +231,7 @@ private void EnsureSignatureIsLoaded() type = TypeWithAnnotations.Create(new PointerTypeSymbol(TypeWithAnnotations.Create(fixedElementType))); } - _lazyType.InterlockedInitialize(type); + Interlocked.CompareExchange(ref _lazyType, new TypeWithAnnotations.Boxed(type), null); } } @@ -270,7 +269,7 @@ private PEModuleSymbol ContainingPEModule internal override TypeWithAnnotations GetFieldType(ConsList fieldsBeingBound) { EnsureSignatureIsLoaded(); - return _lazyType.ToType(); + return _lazyType.Value; } public override bool IsFixedSizeBuffer diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs b/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs index 606e659282c481bad1649caf63031e76c0186d2d..49ab681df086667f4b322d655c7e6723938885b7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; @@ -14,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols /// internal class GlobalExpressionVariable : SourceMemberFieldSymbol { - private TypeWithAnnotations.Builder _lazyType; + private TypeWithAnnotations.Boxed _lazyType; /// /// The type syntax, if any, from source. Optional for patterns that can omit an explicit type. @@ -66,9 +65,9 @@ internal override TypeWithAnnotations GetFieldType(ConsList fieldsB { Debug.Assert(fieldsBeingBound != null); - if (!_lazyType.IsDefault) + if (_lazyType != null) { - return _lazyType.ToType(); + return _lazyType.Value; } var typeSyntax = TypeSyntax; @@ -98,7 +97,7 @@ internal override TypeWithAnnotations GetFieldType(ConsList fieldsB if (isVar && !fieldsBeingBound.ContainsReference(this)) { InferFieldType(fieldsBeingBound, binder); - Debug.Assert(!_lazyType.IsDefault); + Debug.Assert(_lazyType != null); } else { @@ -112,7 +111,7 @@ internal override TypeWithAnnotations GetFieldType(ConsList fieldsB } diagnostics.Free(); - return _lazyType.ToType(); + return _lazyType.Value; } /// @@ -121,7 +120,7 @@ internal override TypeWithAnnotations GetFieldType(ConsList fieldsB /// private TypeWithAnnotations SetType(CSharpCompilation compilation, DiagnosticBag diagnostics, TypeWithAnnotations type) { - var originalType = _lazyType.DefaultType; + var originalType = _lazyType?.Value.DefaultType; // In the event that we race to set the type of a field, we should // always deduce the same type, unless the cached type is an error. @@ -130,14 +129,14 @@ private TypeWithAnnotations SetType(CSharpCompilation compilation, DiagnosticBag originalType.IsErrorType() || TypeSymbol.Equals(originalType, type.Type, TypeCompareKind.ConsiderEverything2)); - if (_lazyType.InterlockedInitialize(type)) + if (Interlocked.CompareExchange(ref _lazyType, new TypeWithAnnotations.Boxed(type), null) == null) { TypeChecks(type.Type, diagnostics); compilation.DeclarationDiagnostics.AddRange(diagnostics); state.NotePartComplete(CompletionPart.Type); } - return _lazyType.ToType(); + return _lazyType.Value; } /// diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index a23435ba276472f238abb57f5379872dd0fa91f3..48151eca251e6ffbc904499dd3893bc60f768762 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -26,7 +26,7 @@ internal sealed class LocalFunctionSymbol : SourceMethodSymbol // Initialized in two steps. Hold a copy if accessing during initialization. private ImmutableArray _lazyTypeParameterConstraints; private TypeWithAnnotations _lazyReturnType; - private TypeWithAnnotations.Builder _lazyIteratorElementType; + private TypeWithAnnotations.Boxed _lazyIteratorElementType; // Lock for initializing lazy fields and registering their diagnostics // Acquire this lock when initializing lazy objects to guarantee their declaration @@ -293,12 +293,12 @@ internal override TypeWithAnnotations IteratorElementTypeWithAnnotations { get { - return _lazyIteratorElementType.ToType(); + return _lazyIteratorElementType?.Value ?? default; } set { - Debug.Assert(_lazyIteratorElementType.IsDefault || TypeSymbol.Equals(_lazyIteratorElementType.ToType().Type, value.Type, TypeCompareKind.ConsiderEverything2)); - _lazyIteratorElementType.InterlockedInitialize(value); + Debug.Assert(_lazyIteratorElementType == null || TypeSymbol.Equals(_lazyIteratorElementType.Value.Type, value.Type, TypeCompareKind.ConsiderEverything2)); + Interlocked.CompareExchange(ref _lazyIteratorElementType, new TypeWithAnnotations.Boxed(value), null); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs index e9fa122e196911414c4fdaa0871bb81f4747f9c8..f2d697f0954062c264feb054d8d8fc59c418990c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs @@ -27,7 +27,7 @@ internal class SourceLocalSymbol : LocalSymbol private readonly RefKind _refKind; private readonly TypeSyntax _typeSyntax; private readonly LocalDeclarationKind _declarationKind; - private TypeWithAnnotations.Builder _type; + private TypeWithAnnotations.Boxed _type; /// /// Scope to which the local can "escape" via aliasing/ref assignment. @@ -100,7 +100,7 @@ internal Binder TypeSyntaxBinder // don't let the debugger force inference. internal override string GetDebuggerDisplay() { - return !_type.IsDefault + return _type != null ? base.GetDebuggerDisplay() : $"{this.Kind} ${this.Name}"; } @@ -287,7 +287,7 @@ public override TypeWithAnnotations TypeWithAnnotations { get { - if (_type.IsDefault) + if (_type == null) { #if DEBUG concurrentTypeResolutions++; @@ -297,7 +297,7 @@ public override TypeWithAnnotations TypeWithAnnotations SetTypeWithAnnotations(localType); } - return _type.ToType(); + return _type.Value; } } @@ -376,13 +376,13 @@ protected virtual TypeWithAnnotations InferTypeOfVarVariable(DiagnosticBag diagn // TODO: this method must be overridden for pattern variables to bind the // expression or statement that is the nearest enclosing to the pattern variable's // declaration. That will cause the type of the pattern variable to be set as a side-effect. - return _type.ToType(); + return _type?.Value ?? default; } internal void SetTypeWithAnnotations(TypeWithAnnotations newType) { Debug.Assert(!(newType.Type is null)); - TypeSymbol originalType = _type.DefaultType; + TypeSymbol originalType = _type?.Value.DefaultType; // In the event that we race to set the type of a local, we should // always deduce the same type, or deduce that the type is an error. @@ -393,7 +393,7 @@ internal void SetTypeWithAnnotations(TypeWithAnnotations newType) if ((object)originalType == null) { - _type.InterlockedInitialize(newType); + Interlocked.CompareExchange(ref _type, new TypeWithAnnotations.Boxed(newType), null); } } @@ -686,8 +686,7 @@ protected override TypeWithAnnotations InferTypeOfVarVariable(DiagnosticBag diag throw ExceptionUtilities.UnexpectedValue(_deconstruction.Kind()); } - Debug.Assert(!this._type.IsDefault); - return _type.ToType(); + return _type.Value; } internal override SyntaxNode ForbiddenZone @@ -781,13 +780,13 @@ protected override TypeWithAnnotations InferTypeOfVarVariable(DiagnosticBag diag break; } - if (this._type.IsDefault) + if (this._type == null) { Debug.Assert(this.DeclarationKind == LocalDeclarationKind.DeclarationExpressionVariable); SetTypeWithAnnotations(TypeWithAnnotations.Create(_nodeBinder.CreateErrorType("var"))); } - return _type.ToType(); + return _type.Value; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs index 8ce063aca66c6377a7e3c1997132026515983898..414b1d2d0a1c0a254ef8e4dc517d2bdc61127c5e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs @@ -10,6 +10,7 @@ using System.Diagnostics; using System.Threading; using Microsoft.CodeAnalysis.Text; +using System.Runtime.CompilerServices; namespace Microsoft.CodeAnalysis.CSharp.Symbols { @@ -276,7 +277,7 @@ internal class SourceMemberFieldSymbolFromDeclarator : SourceMemberFieldSymbol { private readonly bool _hasInitializer; - private TypeWithAnnotations.Builder _lazyType; + private TypeWithAnnotations.Boxed _lazyType; // Non-zero if the type of the field has been inferred from the type of its initializer expression // and the errors of binding the initializer have been or are being reported to compilation diagnostics. @@ -368,12 +369,12 @@ internal override bool HasPointerType { get { - if (!_lazyType.IsDefault) + if (_lazyType != null) { - Debug.Assert(_lazyType.DefaultType.IsPointerType() == + Debug.Assert(_lazyType.Value.DefaultType.IsPointerType() == IsPointerFieldSyntactically()); - return _lazyType.DefaultType.IsPointerType(); + return _lazyType.Value.DefaultType.IsPointerType(); } return IsPointerFieldSyntactically(); @@ -406,9 +407,9 @@ internal sealed override TypeWithAnnotations GetFieldType(ConsList { Debug.Assert(fieldsBeingBound != null); - if (!_lazyType.IsDefault) + if (_lazyType != null) { - return _lazyType.ToType(); + return _lazyType.Value; } var declarator = VariableDeclaratorNode; @@ -529,7 +530,7 @@ internal sealed override TypeWithAnnotations GetFieldType(ConsList } // update the lazyType only if it contains value last seen by the current thread: - if (_lazyType.InterlockedInitialize(type.WithModifiers(this.RequiredCustomModifiers))) + if (Interlocked.CompareExchange(ref _lazyType, new TypeWithAnnotations.Boxed(type.WithModifiers(this.RequiredCustomModifiers)), null) == null) { TypeChecks(type.Type, diagnostics); @@ -547,7 +548,7 @@ internal sealed override TypeWithAnnotations GetFieldType(ConsList diagnostics.Free(); diagnosticsForFirstDeclarator.Free(); - return _lazyType.ToType(); + return _lazyType.Value; } internal bool FieldTypeInferred(ConsList fieldsBeingBound) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs index 80ad859a23845633cda380099e5820e766e13ee8..dbc4f1e749d3ee9a62866af3b99bdad80978d306 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs @@ -9,7 +9,6 @@ using System.Runtime.InteropServices; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Emit; -using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -159,7 +158,7 @@ public void EnsureMetadataVirtual() private readonly NamedTypeSymbol _containingType; private ParameterSymbol _lazyThisParameter; - private TypeWithAnnotations.Builder _lazyIteratorElementType; + private TypeWithAnnotations.Boxed _lazyIteratorElementType; private CustomAttributesBag _lazyCustomAttributesBag; private CustomAttributesBag _lazyReturnTypeCustomAttributesBag; @@ -724,12 +723,12 @@ internal override TypeWithAnnotations IteratorElementTypeWithAnnotations { get { - return _lazyIteratorElementType.ToType(); + return _lazyIteratorElementType?.Value ?? default; } set { - Debug.Assert(_lazyIteratorElementType.IsDefault || TypeSymbol.Equals(_lazyIteratorElementType.ToType().Type, value.Type, TypeCompareKind.ConsiderEverything2)); - _lazyIteratorElementType.InterlockedInitialize(value); + Debug.Assert(_lazyIteratorElementType == null || TypeSymbol.Equals(_lazyIteratorElementType.Value.Type, value.Type, TypeCompareKind.ConsiderEverything2)); + Interlocked.CompareExchange(ref _lazyIteratorElementType, new TypeWithAnnotations.Boxed(value), null); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index 4faaeb7e67c5bdaf404195ff1b209f3cf2525c30..5170b0d92448fb6b1934fbbc365037804432a74d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -37,7 +37,7 @@ internal sealed class SourcePropertySymbol : PropertySymbol, IAttributeTargetSym private SymbolCompletionState _state; private ImmutableArray _lazyParameters; - private TypeWithAnnotations.Builder _lazyType; + private TypeWithAnnotations.Boxed _lazyType; /// /// Set in constructor, might be changed while decoding . @@ -211,7 +211,7 @@ internal sealed class SourcePropertySymbol : PropertySymbol, IAttributeTargetSym // and the property name is required to add the property to the containing type, and // the type and parameters are required to determine the override or implementation. var type = this.ComputeType(bodyBinder, syntax, diagnostics); - _lazyType.InterlockedInitialize(type); + _lazyType = new TypeWithAnnotations.Boxed(type); _lazyParameters = this.ComputeParameters(bodyBinder, syntax, diagnostics); bool isOverride = false; @@ -249,10 +249,7 @@ internal sealed class SourcePropertySymbol : PropertySymbol, IAttributeTargetSym CustomModifierUtils.CopyTypeCustomModifiers(overriddenPropertyType.Type, type.Type, this.ContainingAssembly), overriddenPropertyType.CustomModifiers); - // Although we only do this in error scenarios, it is undesirable to mutate the symbol by setting its type twice. - // Tracked by https://github.com/dotnet/roslyn/issues/35381 - _lazyType.InterlockedDangerousReset(); - _lazyType.InterlockedInitialize(type); + _lazyType = new TypeWithAnnotations.Boxed(type); } _lazyParameters = CustomModifierUtils.CopyParameterCustomModifiers(overriddenOrImplementedProperty.Parameters, _lazyParameters, alsoCopyParamsModifier: isOverride); @@ -519,20 +516,20 @@ public override TypeWithAnnotations TypeWithAnnotations { get { - if (_lazyType.IsDefault) + if (_lazyType == null) { var diagnostics = DiagnosticBag.GetInstance(); var binder = this.CreateBinderForTypeAndParameters(); var syntax = (BasePropertyDeclarationSyntax)_syntaxRef.GetSyntax(); var result = this.ComputeType(binder, syntax, diagnostics); - if (_lazyType.InterlockedInitialize(result)) + if (Interlocked.CompareExchange(ref _lazyType, new TypeWithAnnotations.Boxed(result), null) == null) { this.AddDeclarationDiagnostics(diagnostics); } diagnostics.Free(); } - return _lazyType.ToType(); + return _lazyType.Value; } } @@ -540,9 +537,9 @@ internal bool HasPointerType { get { - if (!_lazyType.IsDefault) + if (_lazyType != null) { - return _lazyType.DefaultType.IsPointerType(); + return _lazyType.Value.DefaultType.IsPointerType(); } var syntax = (BasePropertyDeclarationSyntax)_syntaxRef.GetSyntax(); diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedEventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedEventSymbol.cs index 81701f1727f4fbf26fc3380d00698276d8c3606c..e31e0fd352e509dd59bfc6e764466875172dbd12 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedEventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedEventSymbol.cs @@ -10,7 +10,7 @@ internal sealed class SubstitutedEventSymbol : WrappedEventSymbol { private readonly SubstitutedNamedTypeSymbol _containingType; - private TypeWithAnnotations.Builder _lazyType; + private TypeWithAnnotations.Boxed _lazyType; internal SubstitutedEventSymbol(SubstitutedNamedTypeSymbol containingType, EventSymbol originalDefinition) : base(originalDefinition) @@ -23,12 +23,13 @@ public override TypeWithAnnotations TypeWithAnnotations { get { - if (_lazyType.IsDefault) + if (_lazyType == null) { - _lazyType.InterlockedInitialize(_containingType.TypeSubstitution.SubstituteTypeWithTupleUnification(OriginalDefinition.TypeWithAnnotations)); + var type = _containingType.TypeSubstitution.SubstituteTypeWithTupleUnification(OriginalDefinition.TypeWithAnnotations); + Interlocked.CompareExchange(ref _lazyType, new TypeWithAnnotations.Boxed(type), null); } - return _lazyType.ToType(); + return _lazyType.Value; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedFieldSymbol.cs index ca968e690e04417560a01bcfa793830648e31254..ffd3e9d0507a8b70512e8df54cfabfe49b3ed634 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedFieldSymbol.cs @@ -12,7 +12,7 @@ internal sealed class SubstitutedFieldSymbol : WrappedFieldSymbol { private readonly SubstitutedNamedTypeSymbol _containingType; - private TypeWithAnnotations.Builder _lazyType; + private TypeWithAnnotations.Boxed _lazyType; internal SubstitutedFieldSymbol(SubstitutedNamedTypeSymbol containingType, FieldSymbol substitutedFrom) : base((FieldSymbol)substitutedFrom.OriginalDefinition) @@ -22,12 +22,13 @@ internal SubstitutedFieldSymbol(SubstitutedNamedTypeSymbol containingType, Field internal override TypeWithAnnotations GetFieldType(ConsList fieldsBeingBound) { - if (_lazyType.IsDefault) + if (_lazyType == null) { - _lazyType.InterlockedInitialize(_containingType.TypeSubstitution.SubstituteTypeWithTupleUnification(OriginalDefinition.GetFieldType(fieldsBeingBound))); + var type = _containingType.TypeSubstitution.SubstituteTypeWithTupleUnification(OriginalDefinition.GetFieldType(fieldsBeingBound)); + Interlocked.CompareExchange(ref _lazyType, new TypeWithAnnotations.Boxed(type), null); } - return _lazyType.ToType(); + return _lazyType.Value; } public override Symbol ContainingSymbol diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs index 84eb222c463c5b68955d5dd960037e7d9aef9681..43c609e51ba8a7185a2f99da69d15ceec1ed20d8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Collections.Immutable; using System.Diagnostics; using System.Threading; @@ -22,7 +21,7 @@ internal class SubstitutedMethodSymbol : WrappedMethodSymbol private readonly TypeMap _inputMap; private readonly MethodSymbol _constructedFrom; - private TypeWithAnnotations.Builder _lazyReturnType; + private TypeWithAnnotations.Boxed _lazyReturnType; private ImmutableArray _lazyParameters; private TypeMap _lazyMap; private ImmutableArray _lazyTypeParameters; @@ -230,12 +229,12 @@ public sealed override TypeWithAnnotations ReturnTypeWithAnnotations { get { - if (_lazyReturnType.IsDefault) + if (_lazyReturnType == null) { var returnType = Map.SubstituteTypeWithTupleUnification(OriginalDefinition.ReturnTypeWithAnnotations); - _lazyReturnType.InterlockedInitialize(returnType); + Interlocked.CompareExchange(ref _lazyReturnType, new TypeWithAnnotations.Boxed(returnType), null); } - return _lazyReturnType.ToType(); + return _lazyReturnType.Value; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedPropertySymbol.cs index 4f874658cca7797a324ceb47b6de3604a1b110cb..16d90d6c4bc5a8e259ec9a55347ace02174f516e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedPropertySymbol.cs @@ -2,7 +2,6 @@ using System.Collections.Immutable; using System.Threading; -using Microsoft.CodeAnalysis.CSharp.Symbols; namespace Microsoft.CodeAnalysis.CSharp.Symbols { @@ -10,7 +9,7 @@ internal sealed class SubstitutedPropertySymbol : WrappedPropertySymbol { private readonly SubstitutedNamedTypeSymbol _containingType; - private TypeWithAnnotations.Builder _lazyType; + private TypeWithAnnotations.Boxed _lazyType; private ImmutableArray _lazyParameters; internal SubstitutedPropertySymbol(SubstitutedNamedTypeSymbol containingType, PropertySymbol originalDefinition) @@ -23,12 +22,13 @@ public override TypeWithAnnotations TypeWithAnnotations { get { - if (_lazyType.IsDefault) + if (_lazyType == null) { - _lazyType.InterlockedInitialize(_containingType.TypeSubstitution.SubstituteTypeWithTupleUnification(OriginalDefinition.TypeWithAnnotations)); + var type = _containingType.TypeSubstitution.SubstituteTypeWithTupleUnification(OriginalDefinition.TypeWithAnnotations); + Interlocked.CompareExchange(ref _lazyType, new TypeWithAnnotations.Boxed(type), null); } - return _lazyType.ToType(); + return _lazyType.Value; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs b/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs index b9b2b8e845ed26b920de5b2dbba531f83761220b..7fd9b06abe4f189878da6dc4b2bbc89cb41cbcbc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs @@ -18,82 +18,15 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols [DebuggerDisplay("{GetDebuggerDisplay(), nq}")] internal readonly struct TypeWithAnnotations : IFormattable { - /// - /// A builder for lazy instances of TypeWithAnnotations. - /// [DebuggerDisplay("{GetDebuggerDisplay(), nq}")] - internal struct Builder + internal sealed class Boxed { - private TypeSymbol _defaultType; - private int _nullableAnnotation; - private Extensions _extensions; - - /// - /// The underlying type, unless overridden by _extensions. - /// - internal TypeSymbol DefaultType => Volatile.Read(ref _defaultType); - - /// - /// True if the fields of the builder are unset. - /// - internal bool IsDefault => Volatile.Read(ref _extensions) == null; - - /// - /// Set the fields of the builder. - /// - /// - /// This method guarantees: fields will be set once; exactly one caller is - /// returned true; and IsDefault will return true until all fields are initialized. - /// This method does not guarantee that all fields will be set by the same - /// caller. Instead, the expectation is that all callers will attempt to initialize - /// the builder with equivalent TypeWithAnnotations instances where - /// different fields of the builder may be assigned from different instances. - /// - internal bool InterlockedInitialize(TypeWithAnnotations type) - { - if (!IsDefault) - { - return false; - } - - Interlocked.CompareExchange(ref _nullableAnnotation, (int)type.NullableAnnotation, 0); - Interlocked.CompareExchange(ref _defaultType, type.DefaultType, null); - - // Because _extensions always gets a non-null value when the struct is initialized, we use it - // as a flag to signal that the initialization is complete. This means _extensions should be - // initialized last. - bool wasFirst = Interlocked.CompareExchange(ref _extensions, type._extensions ?? Extensions.Default, null) is null; - - Debug.Assert(_extensions == (type._extensions ?? Extensions.Default)); - Debug.Assert(_nullableAnnotation == (int)type.NullableAnnotation); - Debug.Assert(_defaultType.Equals(type.DefaultType)); - return wasFirst; - } - - /// - /// We should not be adding any new usages of this method. The only one is currently in SourcePropertySymbol, - /// which currently sets the property's type twice in error scenarios. - /// We should be able to remove this method by fixing https://github.com/dotnet/roslyn/issues/35381 - /// - [EditorBrowsable(EditorBrowsableState.Never)] - internal void InterlockedDangerousReset() + internal readonly TypeWithAnnotations Value; + internal Boxed(TypeWithAnnotations value) { - Interlocked.Exchange(ref _nullableAnnotation, 0); - Interlocked.Exchange(ref _defaultType, null); - Interlocked.Exchange(ref _extensions, null); + Value = value; } - - /// - /// Create immutable TypeWithAnnotations instance. - /// - internal TypeWithAnnotations ToType() - { - return IsDefault ? - default : - new TypeWithAnnotations(Volatile.Read(ref _defaultType), (NullableAnnotation)Volatile.Read(ref _nullableAnnotation), Volatile.Read(ref _extensions)); - } - - internal string GetDebuggerDisplay() => ToType().GetDebuggerDisplay(); + internal string GetDebuggerDisplay() => Value.GetDebuggerDisplay(); } ///