未验证 提交 9388cc88 编写于 作者: J Julien Couvreur 提交者: GitHub

Use boxed `TypeWithAnnotations` instead of builder (#35459)

上级 afc40768
......@@ -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<FieldSymbol> fieldsBeingBound)
{
EnsureSignatureIsLoaded();
return _lazyType.ToType();
return _lazyType.Value;
}
public override bool IsFixedSizeBuffer
......
// 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
/// </summary>
internal class GlobalExpressionVariable : SourceMemberFieldSymbol
{
private TypeWithAnnotations.Builder _lazyType;
private TypeWithAnnotations.Boxed _lazyType;
/// <summary>
/// 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<FieldSymbol> 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<FieldSymbol> 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<FieldSymbol> fieldsB
}
diagnostics.Free();
return _lazyType.ToType();
return _lazyType.Value;
}
/// <summary>
......@@ -121,7 +120,7 @@ internal override TypeWithAnnotations GetFieldType(ConsList<FieldSymbol> fieldsB
/// </summary>
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;
}
/// <summary>
......
......@@ -26,7 +26,7 @@ internal sealed class LocalFunctionSymbol : SourceMethodSymbol
// Initialized in two steps. Hold a copy if accessing during initialization.
private ImmutableArray<TypeParameterConstraintClause> _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);
}
}
......
......@@ -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;
/// <summary>
/// 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} <var> ${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;
}
}
}
......
......@@ -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<FieldSymbol>
{
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<FieldSymbol>
}
// 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<FieldSymbol>
diagnostics.Free();
diagnosticsForFirstDeclarator.Free();
return _lazyType.ToType();
return _lazyType.Value;
}
internal bool FieldTypeInferred(ConsList<FieldSymbol> fieldsBeingBound)
......
......@@ -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<CSharpAttributeData> _lazyCustomAttributesBag;
private CustomAttributesBag<CSharpAttributeData> _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);
}
}
......
......@@ -37,7 +37,7 @@ internal sealed class SourcePropertySymbol : PropertySymbol, IAttributeTargetSym
private SymbolCompletionState _state;
private ImmutableArray<ParameterSymbol> _lazyParameters;
private TypeWithAnnotations.Builder _lazyType;
private TypeWithAnnotations.Boxed _lazyType;
/// <summary>
/// Set in constructor, might be changed while decoding <see cref="IndexerNameAttribute"/>.
......@@ -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();
......
......@@ -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;
}
}
......
......@@ -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<FieldSymbol> 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
......
// 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<ParameterSymbol> _lazyParameters;
private TypeMap _lazyMap;
private ImmutableArray<TypeParameterSymbol> _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;
}
}
......
......@@ -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<ParameterSymbol> _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;
}
}
......
......@@ -18,82 +18,15 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
internal readonly struct TypeWithAnnotations : IFormattable
{
/// <summary>
/// A builder for lazy instances of TypeWithAnnotations.
/// </summary>
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
internal struct Builder
{
private TypeSymbol _defaultType;
private int _nullableAnnotation;
private Extensions _extensions;
/// <summary>
/// The underlying type, unless overridden by _extensions.
/// </summary>
internal TypeSymbol DefaultType => Volatile.Read(ref _defaultType);
/// <summary>
/// True if the fields of the builder are unset.
/// </summary>
internal bool IsDefault => Volatile.Read(ref _extensions) == null;
/// <summary>
/// Set the fields of the builder.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
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;
}
/// <summary>
/// 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
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
internal void InterlockedDangerousReset()
internal sealed class Boxed
{
Interlocked.Exchange(ref _nullableAnnotation, 0);
Interlocked.Exchange(ref _defaultType, null);
Interlocked.Exchange(ref _extensions, null);
}
/// <summary>
/// Create immutable TypeWithAnnotations instance.
/// </summary>
internal TypeWithAnnotations ToType()
internal readonly TypeWithAnnotations Value;
internal Boxed(TypeWithAnnotations value)
{
return IsDefault ?
default :
new TypeWithAnnotations(Volatile.Read(ref _defaultType), (NullableAnnotation)Volatile.Read(ref _nullableAnnotation), Volatile.Read(ref _extensions));
Value = value;
}
internal string GetDebuggerDisplay() => ToType().GetDebuggerDisplay();
internal string GetDebuggerDisplay() => Value.GetDebuggerDisplay();
}
/// <summary>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册