diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorEasyOut.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorEasyOut.cs index 0ec693b8156ff..a54c72b957ac3 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorEasyOut.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorEasyOut.cs @@ -309,7 +309,7 @@ private void BinaryOperatorEasyOut(BinaryOperatorKind kind, BoundExpression left return; } - BinaryOperatorSignature signature = this.Compilation.builtInOperators.GetSignature(easyOut); + BinaryOperatorSignature signature = this.Compilation.BuiltInOperators.GetSignature(easyOut); Conversion leftConversion = Conversions.FastClassifyConversion(leftType, signature.LeftType); Conversion rightConversion = Conversions.FastClassifyConversion(rightType, signature.RightType); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs index 8895af98195d4..4f85925b77db2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs @@ -719,7 +719,7 @@ private void GetAllBuiltInOperators(BinaryOperatorKind kind, bool isChecked, Bou } else { - this.Compilation.builtInOperators.GetSimpleBuiltInOperators(kind, operators, skipNativeIntegerOperators: !left.Type.IsNativeIntegerOrNullableThereof() && !right.Type.IsNativeIntegerOrNullableThereof()); + this.Compilation.BuiltInOperators.GetSimpleBuiltInOperators(kind, operators, skipNativeIntegerOperators: !left.Type.IsNativeIntegerOrNullableThereof() && !right.Type.IsNativeIntegerOrNullableThereof()); // SPEC 7.3.4: For predefined enum and delegate operators, the only operators // considered are those defined by an enum or delegate type that is the binding @@ -734,7 +734,7 @@ private void GetAllBuiltInOperators(BinaryOperatorKind kind, bool isChecked, Bou isUtf8ByteRepresentation(left) && isUtf8ByteRepresentation(right)) { - this.Compilation.builtInOperators.GetUtf8ConcatenationBuiltInOperator(left.Type, operators); + this.Compilation.BuiltInOperators.GetUtf8ConcatenationBuiltInOperator(left.Type, operators); } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorEasyOut.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorEasyOut.cs index 3fb09576b41a7..60dcb37adad9a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorEasyOut.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorEasyOut.cs @@ -109,7 +109,7 @@ private void UnaryOperatorEasyOut(UnaryOperatorKind kind, BoundExpression operan return; } - UnaryOperatorSignature signature = this.Compilation.builtInOperators.GetSignature(easyOut); + UnaryOperatorSignature signature = this.Compilation.BuiltInOperators.GetSignature(easyOut); Conversion? conversion = Conversions.FastClassifyConversion(operandType, signature.OperandType); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs index a45f5438b9a81..1d7e91ec1f7e0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs @@ -252,7 +252,7 @@ private void GetAllBuiltInOperators(UnaryOperatorKind kind, bool isChecked, Boun // specification to match the previous implementation. var operators = ArrayBuilder.GetInstance(); - this.Compilation.builtInOperators.GetSimpleBuiltInOperators(kind, operators, skipNativeIntegerOperators: !operand.Type.IsNativeIntegerOrNullableThereof()); + this.Compilation.BuiltInOperators.GetSimpleBuiltInOperators(kind, operators, skipNativeIntegerOperators: !operand.Type.IsNativeIntegerOrNullableThereof()); GetEnumOperations(kind, operand, operators); diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 3bac2a10db5aa..18c0cf52a71d2 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -50,11 +50,12 @@ public sealed partial class CSharpCompilation : Compilation // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! private readonly CSharpCompilationOptions _options; - private readonly Lazy _usingsFromOptions; - private readonly Lazy> _globalImports; - private readonly Lazy _previousSubmissionImports; - private readonly Lazy _globalNamespaceAlias; // alias symbol used to resolve "global::". - private readonly Lazy _scriptClass; + private UsingsFromOptionsAndDiagnostics? _lazyUsingsFromOptions; + private ImmutableArray _lazyGlobalImports; + private Imports? _lazyPreviousSubmissionImports; + private AliasSymbol? _lazyGlobalNamespaceAlias; // alias symbol used to resolve "global::". + + private NamedTypeSymbol? _lazyScriptClass = ErrorTypeSymbol.UnknownResultType; // The type of host object model if available. private TypeSymbol? _lazyHostObjectTypeSymbol; @@ -92,11 +93,11 @@ internal Conversions Conversions /// /// Manages anonymous types declared in this compilation. Unifies types that are structurally equivalent. /// - private readonly AnonymousTypeManager _anonymousTypeManager; + private AnonymousTypeManager? _lazyAnonymousTypeManager; private NamespaceSymbol? _lazyGlobalNamespace; - internal readonly BuiltInOperators builtInOperators; + private BuiltInOperators? _lazyBuiltInOperators; /// /// The for this compilation. Do not access directly, use Assembly property @@ -152,7 +153,7 @@ internal Conversions Conversions /// /// Cache of T to Nullable<T>. /// - private readonly ConcurrentCache _typeToNullableVersion = new ConcurrentCache(size: 100); + private ConcurrentCache? _lazyTypeToNullableVersion; /// Lazily caches SyntaxTrees by their mapped path. Used to look up the syntax tree referenced by an interceptor (temporary compat behavior). /// Must be removed prior to interceptors stable release. @@ -188,11 +189,19 @@ public override bool IsCaseSensitive } } + internal BuiltInOperators BuiltInOperators + { + get + { + return InterlockedOperations.Initialize(ref _lazyBuiltInOperators, static self => new BuiltInOperators(self), this); + } + } + internal AnonymousTypeManager AnonymousTypeManager { get { - return _anonymousTypeManager; + return InterlockedOperations.Initialize(ref _lazyAnonymousTypeManager, static self => new AnonymousTypeManager(self), this); } } @@ -469,16 +478,8 @@ private CSharpCompilation( AsyncQueue? eventQueue = null) : base(assemblyName, references, features, isSubmission, semanticModelProvider, eventQueue) { - WellKnownMemberSignatureComparer = new WellKnownMembersSignatureComparer(this); _options = options; - this.builtInOperators = new BuiltInOperators(this); - _scriptClass = new Lazy(BindScriptClass); - _globalImports = new Lazy>(BindGlobalImports); - _usingsFromOptions = new Lazy(BindUsingsFromOptions); - _previousSubmissionImports = new Lazy(ExpandPreviousSubmissionImports); - _globalNamespaceAlias = new Lazy(CreateGlobalNamespaceAlias); - _anonymousTypeManager = new AnonymousTypeManager(this); this.LanguageVersion = CommonLanguageVersion(syntaxAndDeclarations.ExternalSyntaxTrees); if (isSubmission) @@ -1503,7 +1504,15 @@ internal bool GetExternAliasTarget(string aliasName, out NamespaceSymbol @namesp /// internal new NamedTypeSymbol? ScriptClass { - get { return _scriptClass.Value; } + get + { + if (ReferenceEquals(_lazyScriptClass, ErrorTypeSymbol.UnknownResultType)) + { + Interlocked.CompareExchange(ref _lazyScriptClass, BindScriptClass()!, ErrorTypeSymbol.UnknownResultType); + } + + return _lazyScriptClass; + } } /// @@ -1526,7 +1535,8 @@ internal bool IsSubmissionSyntaxTree(SyntaxTree tree) /// /// Global imports (including those from previous submissions, if there are any). /// - internal ImmutableArray GlobalImports => _globalImports.Value; + internal ImmutableArray GlobalImports + => InterlockedOperations.Initialize(ref _lazyGlobalImports, static self => self.BindGlobalImports(), arg: this); private ImmutableArray BindGlobalImports() { @@ -1565,7 +1575,8 @@ private ImmutableArray BindGlobalImports() /// /// Global imports not including those from previous submissions. /// - private UsingsFromOptionsAndDiagnostics UsingsFromOptions => _usingsFromOptions.Value; + private UsingsFromOptionsAndDiagnostics UsingsFromOptions + => InterlockedOperations.Initialize(ref _lazyUsingsFromOptions, static self => self.BindUsingsFromOptions(), this); private UsingsFromOptionsAndDiagnostics BindUsingsFromOptions() => UsingsFromOptionsAndDiagnostics.FromOptions(this); @@ -1590,7 +1601,8 @@ internal Imports GetSubmissionImports() /// /// Imports from all previous submissions. /// - internal Imports GetPreviousSubmissionImports() => _previousSubmissionImports.Value; + internal Imports GetPreviousSubmissionImports() + => InterlockedOperations.Initialize(ref _lazyPreviousSubmissionImports, static self => self.ExpandPreviousSubmissionImports(), this); private Imports ExpandPreviousSubmissionImports() { @@ -1610,7 +1622,7 @@ internal AliasSymbol GlobalNamespaceAlias { get { - return _globalNamespaceAlias.Value; + return InterlockedOperations.Initialize(ref _lazyGlobalNamespaceAlias, static self => self.CreateGlobalNamespaceAlias(), this); } } @@ -1639,6 +1651,14 @@ internal AliasSymbol GlobalNamespaceAlias return result; } + private ConcurrentCache TypeToNullableVersion + { + get + { + return InterlockedOperations.Initialize(ref _lazyTypeToNullableVersion, static () => new ConcurrentCache(size: 100)); + } + } + /// /// Given a provided , gives back constructed with that /// argument. This function is only intended to be used for very common instantiations produced heavily during @@ -1653,10 +1673,11 @@ internal NamedTypeSymbol GetOrCreateNullableType(TypeSymbol typeArgument) Debug.Fail($"Unsupported type argument: {typeArgument.ToDisplayString()}"); #endif - if (!_typeToNullableVersion.TryGetValue(typeArgument, out var constructedNullableInstance)) + var typeToNullableVersion = TypeToNullableVersion; + if (!typeToNullableVersion.TryGetValue(typeArgument, out var constructedNullableInstance)) { constructedNullableInstance = this.GetSpecialType(SpecialType.System_Nullable_T).Construct(typeArgument); - _typeToNullableVersion.TryAdd(typeArgument, constructedNullableInstance); + typeToNullableVersion.TryAdd(typeArgument, constructedNullableInstance); } return constructedNullableInstance; @@ -4198,7 +4219,7 @@ csharpLeftType.TypeKind is TypeKind.Dynamic || if (easyOutBinaryKind != BinaryOperatorKind.Error) { - var signature = this.builtInOperators.GetSignature(easyOutBinaryKind); + var signature = this.BuiltInOperators.GetSignature(easyOutBinaryKind); if (csharpReturnType.SpecialType == signature.ReturnType.SpecialType && csharpLeftType.SpecialType == signature.LeftType.SpecialType && csharpRightType.SpecialType == signature.RightType.SpecialType) @@ -4421,7 +4442,7 @@ void validateSignature() if (easyOutUnaryKind != UnaryOperatorKind.Error) { - var signature = this.builtInOperators.GetSignature(easyOutUnaryKind); + var signature = this.BuiltInOperators.GetSignature(easyOutUnaryKind); if (csharpReturnType.SpecialType == signature.ReturnType.SpecialType && csharpOperandType.SpecialType == signature.OperandType.SpecialType) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs b/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs index 8689b03637478..a2f9383db8641 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.CSharp { public partial class CSharpCompilation { - internal readonly WellKnownMembersSignatureComparer WellKnownMemberSignatureComparer; + private WellKnownMembersSignatureComparer? _lazyWellKnownMemberSignatureComparer; /// /// An array of cached well known types available for use in this Compilation. @@ -35,6 +35,9 @@ public partial class CSharpCompilation private int _needsGeneratedAttributes; private bool _needsGeneratedAttributes_IsFrozen; + internal WellKnownMembersSignatureComparer WellKnownMemberSignatureComparer + => InterlockedOperations.Initialize(ref _lazyWellKnownMemberSignatureComparer, static self => new WellKnownMembersSignatureComparer(self), this); + /// /// Returns a value indicating which embedded attributes should be generated during emit phase. /// The value is set during binding the symbols that need those attributes, and is frozen on first trial to get it. diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs index 8e1c7fe46abf2..4760ca1c2c254 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs @@ -1537,7 +1537,7 @@ static void verifyOperators(CSharpCompilation comp) static void verifyUnaryOperators(CSharpCompilation comp, UnaryOperatorKind operatorKind, bool skipNativeIntegerOperators) { var builder = ArrayBuilder.GetInstance(); - comp.builtInOperators.GetSimpleBuiltInOperators(operatorKind, builder, skipNativeIntegerOperators); + comp.BuiltInOperators.GetSimpleBuiltInOperators(operatorKind, builder, skipNativeIntegerOperators); var operators = builder.ToImmutableAndFree(); int expectedSigned = skipNativeIntegerOperators ? 0 : 1; int expectedUnsigned = skipNativeIntegerOperators ? 0 : (operatorKind == UnaryOperatorKind.UnaryMinus) ? 0 : 1; @@ -1548,7 +1548,7 @@ static void verifyUnaryOperators(CSharpCompilation comp, UnaryOperatorKind opera static void verifyBinaryOperators(CSharpCompilation comp, BinaryOperatorKind operatorKind, bool skipNativeIntegerOperators) { var builder = ArrayBuilder.GetInstance(); - comp.builtInOperators.GetSimpleBuiltInOperators(operatorKind, builder, skipNativeIntegerOperators); + comp.BuiltInOperators.GetSimpleBuiltInOperators(operatorKind, builder, skipNativeIntegerOperators); var operators = builder.ToImmutableAndFree(); int expected = skipNativeIntegerOperators ? 0 : 1; verifyOperators(operators, (op, signed) => isNativeInt(op.LeftType, signed), expected, expected); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntegerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntegerTests.cs index 4c74fefb3c406..320f13951a7f9 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntegerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntegerTests.cs @@ -4789,7 +4789,7 @@ static void verifyOperators(CSharpCompilation comp) static void verifyUnaryOperators(CSharpCompilation comp, UnaryOperatorKind operatorKind, bool skipNativeIntegerOperators) { var builder = ArrayBuilder.GetInstance(); - comp.builtInOperators.GetSimpleBuiltInOperators(operatorKind, builder, skipNativeIntegerOperators); + comp.BuiltInOperators.GetSimpleBuiltInOperators(operatorKind, builder, skipNativeIntegerOperators); var operators = builder.ToImmutableAndFree(); int expectedSigned = skipNativeIntegerOperators ? 0 : 1; int expectedUnsigned = skipNativeIntegerOperators ? 0 : (operatorKind == UnaryOperatorKind.UnaryMinus) ? 0 : 1; @@ -4800,7 +4800,7 @@ static void verifyUnaryOperators(CSharpCompilation comp, UnaryOperatorKind opera static void verifyBinaryOperators(CSharpCompilation comp, BinaryOperatorKind operatorKind, bool skipNativeIntegerOperators) { var builder = ArrayBuilder.GetInstance(); - comp.builtInOperators.GetSimpleBuiltInOperators(operatorKind, builder, skipNativeIntegerOperators); + comp.BuiltInOperators.GetSimpleBuiltInOperators(operatorKind, builder, skipNativeIntegerOperators); var operators = builder.ToImmutableAndFree(); int expected = skipNativeIntegerOperators ? 0 : 1; verifyOperators(operators, (op, signed) => isNativeInt(op.LeftType, signed), expected, expected); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs index f05146a2ca750..b31d4f8291d41 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs @@ -7593,7 +7593,7 @@ ExpressionSyntax node4 } else { - signature = compilation.builtInOperators.GetSignature(result); + signature = compilation.BuiltInOperators.GetSignature(result); if ((object)underlying != (object)type) { @@ -8232,14 +8232,14 @@ ExpressionSyntax node8 else if ((op == BinaryOperatorKind.Addition || op == BinaryOperatorKind.Subtraction) && leftType.IsEnumType() && (rightType.IsIntegralType() || rightType.IsCharType()) && (result = OverloadResolution.BinopEasyOut.OpKind(op, leftType.EnumUnderlyingTypeOrSelf(), rightType)) != BinaryOperatorKind.Error && - TypeSymbol.Equals((signature = compilation.builtInOperators.GetSignature(result)).RightType, leftType.EnumUnderlyingTypeOrSelf(), TypeCompareKind.ConsiderEverything2)) + TypeSymbol.Equals((signature = compilation.BuiltInOperators.GetSignature(result)).RightType, leftType.EnumUnderlyingTypeOrSelf(), TypeCompareKind.ConsiderEverything2)) { signature = new BinaryOperatorSignature(signature.Kind | BinaryOperatorKind.EnumAndUnderlying, leftType, signature.RightType, leftType); } else if ((op == BinaryOperatorKind.Addition || op == BinaryOperatorKind.Subtraction) && rightType.IsEnumType() && (leftType.IsIntegralType() || leftType.IsCharType()) && (result = OverloadResolution.BinopEasyOut.OpKind(op, leftType, rightType.EnumUnderlyingTypeOrSelf())) != BinaryOperatorKind.Error && - TypeSymbol.Equals((signature = compilation.builtInOperators.GetSignature(result)).LeftType, rightType.EnumUnderlyingTypeOrSelf(), TypeCompareKind.ConsiderEverything2)) + TypeSymbol.Equals((signature = compilation.BuiltInOperators.GetSignature(result)).LeftType, rightType.EnumUnderlyingTypeOrSelf(), TypeCompareKind.ConsiderEverything2)) { signature = new BinaryOperatorSignature(signature.Kind | BinaryOperatorKind.EnumAndUnderlying, signature.LeftType, rightType, rightType); } @@ -8355,7 +8355,7 @@ ExpressionSyntax node8 } else { - signature = compilation.builtInOperators.GetSignature(result); + signature = compilation.BuiltInOperators.GetSignature(result); } Assert.NotNull(symbol1);