From bc5d41e656abac62e51bf6ef2d44491edcec62a5 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Tue, 21 Jul 2020 14:55:51 -0700 Subject: [PATCH 01/11] Handle cycles in IsValueType and IsReferenceType binding constraints --- .../CSharp/Portable/Binder/Binder.cs | 4 + .../CSharp/Portable/Binder/BinderFlags.cs | 2 + .../Portable/Binder/Binder_Constraints.cs | 9 +- .../AnonymousType.TypeParameterSymbol.cs | 4 +- .../Portable/Symbols/ConstraintsHelper.cs | 2 +- ...rrorTypeSymbol.ErrorTypeParameterSymbol.cs | 4 +- .../Metadata/PE/PETypeParameterSymbol.cs | 11 +-- .../RetargetingTypeParameterSymbol.cs | 4 +- .../Symbols/Source/CrefTypeParameterSymbol.cs | 4 +- .../Source/IndexedTypeParameterSymbol.cs | 4 +- .../Symbols/Source/LocalFunctionSymbol.cs | 1 - .../Symbols/Source/SourceNamedTypeSymbol.cs | 20 +++-- .../Source/SourceOrdinaryMethodSymbol.cs | 2 +- .../Source/SourceTypeParameterSymbol.cs | 82 +++++++++++-------- .../Source/TypeParameterConstraintClause.cs | 27 ++++-- .../Symbols/SubstitutedTypeParameterSymbol.cs | 6 +- .../Portable/Symbols/TypeParameterBounds.cs | 24 +++++- .../Portable/Symbols/TypeParameterSymbol.cs | 32 ++++---- .../Wrapped/WrappedTypeParameterSymbol.cs | 4 +- .../Semantics/NullableReferenceTypesTests.cs | 41 +++++++++- .../Symbol/DocumentationComments/CrefTests.cs | 2 +- .../Symbols/EETypeParameterSymbol.cs | 8 +- .../Symbols/SimpleTypeParameterSymbol.cs | 4 +- 23 files changed, 201 insertions(+), 100 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.cs b/src/Compilers/CSharp/Portable/Binder/Binder.cs index 9543e244c75ac..7da5d1fbe027e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.cs @@ -253,6 +253,10 @@ internal bool AreNullableAnnotationsEnabled(SyntaxTree syntaxTree, int position) internal bool AreNullableAnnotationsEnabled(SyntaxToken token) { RoslynDebug.Assert(token.SyntaxTree is object); + if ((Flags & BinderFlags.IgnoreNullableAnnotationsEnabled) != 0) + { + return false; + } return AreNullableAnnotationsEnabled(token.SyntaxTree, token.SpanStart); } diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs b/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs index a88c4b5bfda8e..5a1e3e10371eb 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs @@ -110,6 +110,8 @@ internal enum BinderFlags : uint /// InEEMethodBinder = 1 << 30, + IgnoreNullableAnnotationsEnabled = 1u << 31, + // Groups AllClearedAtExecutableCodeBoundary = InLockBody | InCatchBlock | InCatchFilter | InFinallyBlock | InTryBlockOfTryCatch | InNestedFinallyBlock, diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs index bdc300d5bf45c..05caf6740360b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs @@ -26,6 +26,7 @@ internal ImmutableArray BindTypeParameterConstrai ImmutableArray typeParameters, TypeParameterListSyntax typeParameterList, SyntaxList clauses, + bool canIgnoreNullableContext, ref IReadOnlyDictionary isValueTypeOverride, DiagnosticBag diagnostics, bool isForOverride = false) @@ -66,7 +67,7 @@ internal ImmutableArray BindTypeParameterConstrai Debug.Assert(ordinal >= 0); Debug.Assert(ordinal < n); - (TypeParameterConstraintClause constraintClause, ArrayBuilder? typeConstraintNodes) = this.BindTypeParameterConstraints(typeParameterList.Parameters[ordinal], clause, isForOverride, diagnostics); + (TypeParameterConstraintClause constraintClause, ArrayBuilder? typeConstraintNodes) = this.BindTypeParameterConstraints(typeParameterList.Parameters[ordinal], clause, isForOverride, canIgnoreNullableContext, diagnostics); if (results[ordinal] == null) { results[ordinal] = constraintClause; @@ -117,7 +118,7 @@ internal ImmutableArray BindTypeParameterConstrai /// /// Bind and return a single type parameter constraint clause along with syntax nodes corresponding to type constraints. /// - private (TypeParameterConstraintClause, ArrayBuilder?) BindTypeParameterConstraints(TypeParameterSyntax typeParameterSyntax, TypeParameterConstraintClauseSyntax constraintClauseSyntax, bool isForOverride, DiagnosticBag diagnostics) + private (TypeParameterConstraintClause, ArrayBuilder?) BindTypeParameterConstraints(TypeParameterSyntax typeParameterSyntax, TypeParameterConstraintClauseSyntax constraintClauseSyntax, bool isForOverride, bool isEarly, DiagnosticBag diagnostics) { var constraints = TypeParameterConstraintKind.None; ArrayBuilder? constraintTypes = null; @@ -307,7 +308,7 @@ internal ImmutableArray BindTypeParameterConstrai Debug.Assert(!isForOverride || (constraints & (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.ValueType)) != (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.ValueType)); - return (TypeParameterConstraintClause.Create(constraints, constraintTypes?.ToImmutableAndFree() ?? ImmutableArray.Empty), syntaxBuilder); + return (TypeParameterConstraintClause.Create(constraints, constraintTypes?.ToImmutableAndFree() ?? ImmutableArray.Empty, isEarly), syntaxBuilder); static void reportOverrideWithConstraints(ref bool reportedOverrideWithConstraints, TypeParameterConstraintSyntax syntax, DiagnosticBag diagnostics) { @@ -390,7 +391,7 @@ private static TypeParameterConstraintClause RemoveInvalidConstraints( if (constraintTypeBuilder.Count < n) { - return TypeParameterConstraintClause.Create(constraintClause.Constraints, constraintTypeBuilder.ToImmutableAndFree()); + return TypeParameterConstraintClause.Create(constraintClause.Constraints, constraintTypeBuilder.ToImmutableAndFree(), constraintClause.IsEarly); } constraintTypeBuilder.Free(); diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeParameterSymbol.cs index 6538aba37cb86..f9714229446ba 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeParameterSymbol.cs @@ -99,11 +99,11 @@ public override VarianceKind Variance get { return VarianceKind.None; } } - internal override void EnsureAllConstraintsAreResolved() + internal override void EnsureAllConstraintsAreResolved(bool canIgnoreNullableContext) { } - internal override ImmutableArray GetConstraintTypes(ConsList inProgress) + internal override ImmutableArray GetConstraintTypes(ConsList inProgress, bool canIgnoreNullableContext) { return ImmutableArray.Empty; } diff --git a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs index 025a172012e6e..2b39f65a55bf9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs @@ -312,7 +312,6 @@ internal static ImmutableArray MakeTypeParameterC ImmutableArray typeParameters, TypeParameterListSyntax typeParameterList, SyntaxList constraintClauses, - Location location, DiagnosticBag diagnostics) { if (typeParameters.Length == 0) @@ -334,6 +333,7 @@ internal static ImmutableArray MakeTypeParameterC IReadOnlyDictionary isValueTypeOverride = null; return binder.BindTypeParameterConstraintClauses(containingSymbol, typeParameters, typeParameterList, constraintClauses, + canIgnoreNullableContext: false, ref isValueTypeOverride, diagnostics); } diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.ErrorTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.ErrorTypeParameterSymbol.cs index 0e134ff4d550d..d77bced1bc4ef 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.ErrorTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.ErrorTypeParameterSymbol.cs @@ -136,11 +136,11 @@ public override bool IsImplicitlyDeclared } } - internal override void EnsureAllConstraintsAreResolved() + internal override void EnsureAllConstraintsAreResolved(bool canIgnoreNullableContext) { } - internal override ImmutableArray GetConstraintTypes(ConsList inProgress) + internal override ImmutableArray GetConstraintTypes(ConsList inProgress, bool canIgnoreNullableContext) { return ImmutableArray.Empty; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs index 774cebab0119a..3d9eda4dcb70e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs @@ -568,18 +568,19 @@ public override VarianceKind Variance } } - internal override void EnsureAllConstraintsAreResolved() + internal override void EnsureAllConstraintsAreResolved(bool canIgnoreNullableContext) { - if (!_lazyBounds.IsSet()) + canIgnoreNullableContext = false; // Resolve bounds eagerly. + if (!_lazyBounds.IsSet(canIgnoreNullableContext)) { var typeParameters = (_containingSymbol.Kind == SymbolKind.Method) ? ((PEMethodSymbol)_containingSymbol).TypeParameters : ((PENamedTypeSymbol)_containingSymbol).TypeParameters; - EnsureAllConstraintsAreResolved(typeParameters); + EnsureAllConstraintsAreResolved(typeParameters, canIgnoreNullableContext); } } - internal override ImmutableArray GetConstraintTypes(ConsList inProgress) + internal override ImmutableArray GetConstraintTypes(ConsList inProgress, bool canIgnoreNullableContext) { var bounds = this.GetBounds(inProgress); return (bounds != null) ? bounds.ConstraintTypes : ImmutableArray.Empty; @@ -670,7 +671,7 @@ private TypeParameterBounds GetBounds(ConsList inProgress) internal override DiagnosticInfo GetConstraintsUseSiteErrorInfo() { - EnsureAllConstraintsAreResolved(); + EnsureAllConstraintsAreResolved(canIgnoreNullableContext: false); Debug.Assert(!ReferenceEquals(_lazyConstraintsUseSiteErrorInfo, CSDiagnosticInfo.EmptyErrorInfo)); return _lazyConstraintsUseSiteErrorInfo; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingTypeParameterSymbol.cs index 5939a5e325b4a..92f41e5e4b138 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingTypeParameterSymbol.cs @@ -79,9 +79,9 @@ internal override ModuleSymbol ContainingModule } } - internal override ImmutableArray GetConstraintTypes(ConsList inProgress) + internal override ImmutableArray GetConstraintTypes(ConsList inProgress, bool canIgnoreNullableContext) { - return this.RetargetingTranslator.Retarget(_underlyingTypeParameter.GetConstraintTypes(inProgress)); + return this.RetargetingTranslator.Retarget(_underlyingTypeParameter.GetConstraintTypes(inProgress, canIgnoreNullableContext)); } internal override bool? IsNotNullable diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/CrefTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/CrefTypeParameterSymbol.cs index c459a04ec6222..1f10cd9880fdf 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/CrefTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/CrefTypeParameterSymbol.cs @@ -175,11 +175,11 @@ public override ImmutableArray DeclaringSyntaxReferences } } - internal override void EnsureAllConstraintsAreResolved() + internal override void EnsureAllConstraintsAreResolved(bool canIgnoreNullableContext) { } - internal override ImmutableArray GetConstraintTypes(ConsList inProgress) + internal override ImmutableArray GetConstraintTypes(ConsList inProgress, bool canIgnoreNullableContext) { return ImmutableArray.Empty; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/IndexedTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/IndexedTypeParameterSymbol.cs index b6495fd68222c..894350b41dac1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/IndexedTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/IndexedTypeParameterSymbol.cs @@ -171,11 +171,11 @@ public override ImmutableArray DeclaringSyntaxReferences } } - internal override void EnsureAllConstraintsAreResolved() + internal override void EnsureAllConstraintsAreResolved(bool canIgnoreNullableContext) { } - internal override ImmutableArray GetConstraintTypes(ConsList inProgress) + internal override ImmutableArray GetConstraintTypes(ConsList inProgress, bool canIgnoreNullableContext) { return ImmutableArray.Empty; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index 11fa26452ec70..85bb56da41f32 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -472,7 +472,6 @@ public override ImmutableArray GetTypeParameterCo TypeParameters, syntax.TypeParameterList, syntax.ConstraintClauses, - syntax.Identifier.GetLocation(), diagnostics); lock (_declarationDiagnostics) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index 9a75d0a0d7baa..f4213ca69f5c7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -251,15 +251,18 @@ private ImmutableArray MakeTypeParameters(DiagnosticBag dia /// /// Returns the constraint clause for the given type parameter. /// - internal TypeParameterConstraintClause GetTypeParameterConstraintClause(int ordinal) + internal TypeParameterConstraintClause GetTypeParameterConstraintClause(bool canIgnoreNullableContext, int ordinal) { var clauses = _lazyTypeParameterConstraints; - if (clauses.IsDefault) + if (!clauses.IsSet(canIgnoreNullableContext)) { var diagnostics = DiagnosticBag.GetInstance(); - if (ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameterConstraints, MakeTypeParameterConstraints(diagnostics))) + if (ImmutableInterlocked.InterlockedCompareExchange(ref _lazyTypeParameterConstraints, MakeTypeParameterConstraints(canIgnoreNullableContext, diagnostics), clauses) == clauses) { - this.AddDeclarationDiagnostics(diagnostics); + if (!canIgnoreNullableContext) + { + this.AddDeclarationDiagnostics(diagnostics); + } } diagnostics.Free(); clauses = _lazyTypeParameterConstraints; @@ -268,7 +271,7 @@ internal TypeParameterConstraintClause GetTypeParameterConstraintClause(int ordi return (clauses.Length > 0) ? clauses[ordinal] : TypeParameterConstraintClause.Empty; } - private ImmutableArray MakeTypeParameterConstraints(DiagnosticBag diagnostics) + private ImmutableArray MakeTypeParameterConstraints(bool canIgnoreNullableContext, DiagnosticBag diagnostics) { var typeParameters = this.TypeParameters; var results = ImmutableArray.Empty; @@ -317,9 +320,10 @@ private ImmutableArray MakeTypeParameterConstrain // Wrap binder from factory in a generic constraints specific binder // to avoid checking constraints when binding type names. Debug.Assert(!binder.Flags.Includes(BinderFlags.GenericConstraintsClause)); - binder = binder.WithContainingMemberOrLambda(this).WithAdditionalFlags(BinderFlags.GenericConstraintsClause | BinderFlags.SuppressConstraintChecks); + binder = binder.WithContainingMemberOrLambda(this).WithAdditionalFlags( + BinderFlags.GenericConstraintsClause | BinderFlags.SuppressConstraintChecks | (canIgnoreNullableContext ? BinderFlags.IgnoreNullableAnnotationsEnabled : 0)); - constraints = binder.BindTypeParameterConstraintClauses(this, typeParameters, typeParameterList, constraintClauses, ref isValueTypeOverride, diagnostics); + constraints = binder.BindTypeParameterConstraintClauses(this, typeParameters, typeParameterList, constraintClauses, canIgnoreNullableContext, ref isValueTypeOverride, diagnostics); } Debug.Assert(constraints.Length == arity); @@ -438,7 +442,7 @@ private ImmutableArray MergeConstraintsForPartial } builder[i] = TypeParameterConstraintClause.Create(mergedKind, - mergedConstraintTypes?.ToImmutableAndFree() ?? originalConstraintTypes); + mergedConstraintTypes?.ToImmutableAndFree() ?? originalConstraintTypes, isEarly: constraint.IsEarly); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs index a1742e452436b..ac798b8150b9e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs @@ -164,6 +164,7 @@ protected override (TypeWithAnnotations ReturnType, ImmutableArray isValueTypeOverride = null; declaredConstraints = signatureBinder.WithAdditionalFlags(BinderFlags.GenericConstraintsClause | BinderFlags.SuppressConstraintChecks). BindTypeParameterConstraintClauses(this, TypeParameters, syntax.TypeParameterList, syntax.ConstraintClauses, + canIgnoreNullableContext: false, ref isValueTypeOverride, diagnostics, isForOverride: true); } @@ -295,7 +296,6 @@ public override ImmutableArray GetTypeParameterCo TypeParameters, syntax.TypeParameterList, syntax.ConstraintClauses, - syntax.Identifier.GetLocation(), diagnostics); if (ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameterConstraints, constraints)) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs index d85cf2cd54cba..9e595959c78e9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs @@ -86,27 +86,27 @@ public override string Name } } - internal override ImmutableArray GetConstraintTypes(ConsList inProgress) + internal override ImmutableArray GetConstraintTypes(ConsList inProgress, bool canIgnoreNullableContext) { - var bounds = this.GetBounds(inProgress); + var bounds = this.GetBounds(inProgress, canIgnoreNullableContext); return (bounds != null) ? bounds.ConstraintTypes : ImmutableArray.Empty; } internal override ImmutableArray GetInterfaces(ConsList inProgress) { - var bounds = this.GetBounds(inProgress); + var bounds = this.GetBounds(inProgress, canIgnoreNullableContext: false); return (bounds != null) ? bounds.Interfaces : ImmutableArray.Empty; } internal override NamedTypeSymbol GetEffectiveBaseClass(ConsList inProgress) { - var bounds = this.GetBounds(inProgress); + var bounds = this.GetBounds(inProgress, canIgnoreNullableContext: false); return (bounds != null) ? bounds.EffectiveBaseClass : this.GetDefaultBaseType(); } internal override TypeSymbol GetDeducedBaseType(ConsList inProgress) { - var bounds = this.GetBounds(inProgress); + var bounds = this.GetBounds(inProgress, canIgnoreNullableContext: false); return (bounds != null) ? bounds.DeducedBaseType : this.GetDefaultBaseType(); } @@ -202,11 +202,11 @@ internal virtual CustomAttributesBag GetAttributesBag() return _lazyCustomAttributesBag; } - internal override void EnsureAllConstraintsAreResolved() + internal override void EnsureAllConstraintsAreResolved(bool canIgnoreNullableContext) { - if (!_lazyBounds.IsSet()) + if (!_lazyBounds.IsSet(canIgnoreNullableContext)) { - EnsureAllConstraintsAreResolved(this.ContainerTypeParameters); + EnsureAllConstraintsAreResolved(this.ContainerTypeParameters, canIgnoreNullableContext); } } @@ -215,23 +215,27 @@ protected abstract ImmutableArray ContainerTypeParameters get; } - private TypeParameterBounds GetBounds(ConsList inProgress) + private TypeParameterBounds GetBounds(ConsList inProgress, bool canIgnoreNullableContext) { Debug.Assert(!inProgress.ContainsReference(this)); Debug.Assert(!inProgress.Any() || ReferenceEquals(inProgress.Head.ContainingSymbol, this.ContainingSymbol)); - if (!_lazyBounds.IsSet()) + var currentBounds = _lazyBounds; + if (!currentBounds.IsSet(canIgnoreNullableContext)) { var diagnostics = DiagnosticBag.GetInstance(); - var bounds = this.ResolveBounds(inProgress, diagnostics); + var bounds = this.ResolveBounds(inProgress, canIgnoreNullableContext, diagnostics); - if (ReferenceEquals(Interlocked.CompareExchange(ref _lazyBounds, bounds, TypeParameterBounds.Unset), TypeParameterBounds.Unset)) + if (ReferenceEquals(Interlocked.CompareExchange(ref _lazyBounds, bounds, currentBounds), currentBounds)) { - this.CheckConstraintTypeConstraints(diagnostics); - this.CheckUnmanagedConstraint(diagnostics); - this.EnsureAttributesFromConstraints(diagnostics); - this.AddDeclarationDiagnostics(diagnostics); - _state.NotePartComplete(CompletionPart.TypeParameterConstraints); + if (_lazyBounds?.IsEarly != true) + { + this.CheckConstraintTypeConstraints(diagnostics); + this.CheckUnmanagedConstraint(diagnostics); + this.EnsureAttributesFromConstraints(diagnostics); + this.AddDeclarationDiagnostics(diagnostics); + _state.NotePartComplete(CompletionPart.TypeParameterConstraints); + } } diagnostics.Free(); @@ -240,7 +244,7 @@ private TypeParameterBounds GetBounds(ConsList inProgress) return _lazyBounds; } - protected abstract TypeParameterBounds ResolveBounds(ConsList inProgress, DiagnosticBag diagnostics); + protected abstract TypeParameterBounds ResolveBounds(ConsList inProgress, bool canIgnoreNullableContext, DiagnosticBag diagnostics); /// /// Check constraints of generic types referenced in constraint types. For instance, @@ -486,7 +490,7 @@ public override bool HasConstructorConstraint { get { - var constraints = this.GetDeclaredConstraints(); + var constraints = this.GetDeclaredConstraints(canIgnoreNullableContext: true); return (constraints & TypeParameterConstraintKind.Constructor) != 0; } } @@ -495,7 +499,7 @@ public override bool HasValueTypeConstraint { get { - var constraints = this.GetDeclaredConstraints(); + var constraints = this.GetDeclaredConstraints(canIgnoreNullableContext: true); return (constraints & TypeParameterConstraintKind.AllValueTypeKinds) != 0; } } @@ -504,7 +508,7 @@ public override bool HasReferenceTypeConstraint { get { - var constraints = this.GetDeclaredConstraints(); + var constraints = this.GetDeclaredConstraints(canIgnoreNullableContext: true); return (constraints & TypeParameterConstraintKind.ReferenceType) != 0; } } @@ -513,7 +517,7 @@ internal override bool? ReferenceTypeConstraintIsNullable { get { - return CalculateReferenceTypeConstraintIsNullable(this.GetDeclaredConstraints()); + return CalculateReferenceTypeConstraintIsNullable(this.GetDeclaredConstraints(canIgnoreNullableContext: true)); } } @@ -521,7 +525,7 @@ public override bool HasNotNullConstraint { get { - var constraints = this.GetDeclaredConstraints(); + var constraints = this.GetDeclaredConstraints(canIgnoreNullableContext: false); return (constraints & TypeParameterConstraintKind.NotNull) != 0; } } @@ -530,7 +534,7 @@ internal override bool? IsNotNullable { get { - if ((this.GetDeclaredConstraints() & TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType) != 0) + if ((this.GetDeclaredConstraints(canIgnoreNullableContext: false) & TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType) != 0) { return null; } @@ -543,7 +547,7 @@ public override bool HasUnmanagedTypeConstraint { get { - var constraints = this.GetDeclaredConstraints(); + var constraints = this.GetDeclaredConstraints(canIgnoreNullableContext: true); return (constraints & TypeParameterConstraintKind.Unmanaged) != 0; } } @@ -553,26 +557,30 @@ protected override ImmutableArray ContainerTypeParameters get { return _owner.TypeParameters; } } - private TypeParameterConstraintClause GetTypeParameterConstraintClause() + private TypeParameterConstraintClause GetTypeParameterConstraintClause(bool canIgnoreNullableContext) { - return _owner.GetTypeParameterConstraintClause(this.Ordinal); + return _owner.GetTypeParameterConstraintClause(canIgnoreNullableContext, this.Ordinal); } - protected override TypeParameterBounds ResolveBounds(ConsList inProgress, DiagnosticBag diagnostics) + protected override TypeParameterBounds ResolveBounds(ConsList inProgress, bool canIgnoreNullableContext, DiagnosticBag diagnostics) { - var constraintClause = GetTypeParameterConstraintClause(); + var constraintClause = GetTypeParameterConstraintClause(canIgnoreNullableContext); if (constraintClause.IsEmpty) { return null; } var constraintTypes = constraintClause.ConstraintTypes; + if (canIgnoreNullableContext) + { + return new TypeParameterBounds(constraintTypes); + } return this.ResolveBounds(this.ContainingAssembly.CorLibrary, inProgress.Prepend(this), constraintTypes, inherited: false, this.DeclaringCompilation, diagnostics); } - private TypeParameterConstraintKind GetDeclaredConstraints() + private TypeParameterConstraintKind GetDeclaredConstraints(bool canIgnoreNullableContext) { - var constraintClause = GetTypeParameterConstraintClause(); + var constraintClause = GetTypeParameterConstraintClause(canIgnoreNullableContext); return constraintClause.Constraints; } } @@ -680,7 +688,7 @@ private TypeParameterConstraintClause GetTypeParameterConstraintClause() return constraintClauses.IsEmpty ? TypeParameterConstraintClause.Empty : constraintClauses[Ordinal]; } - protected override TypeParameterBounds ResolveBounds(ConsList inProgress, DiagnosticBag diagnostics) + protected override TypeParameterBounds ResolveBounds(ConsList inProgress, bool canIgnoreNullableContext, DiagnosticBag diagnostics) { var constraintClause = GetTypeParameterConstraintClause(); if (constraintClause.IsEmpty) @@ -689,6 +697,10 @@ protected override TypeParameterBounds ResolveBounds(ConsList ContainerTypeParameters get { return this.Owner.TypeParameters; } } - protected override TypeParameterBounds ResolveBounds(ConsList inProgress, DiagnosticBag diagnostics) + protected override TypeParameterBounds ResolveBounds(ConsList inProgress, bool canIgnoreNullableContext, DiagnosticBag diagnostics) { var typeParameter = this.OverriddenTypeParameter; if ((object)typeParameter == null) @@ -920,6 +932,10 @@ protected override TypeParameterBounds ResolveBounds(ConsList.Empty); + ImmutableArray.Empty, + isEarly: false); internal static readonly TypeParameterConstraintClause ObliviousNullabilityIfReferenceType = new TypeParameterConstraintClause( TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType, - ImmutableArray.Empty); + ImmutableArray.Empty, + isEarly: false); internal static TypeParameterConstraintClause Create( TypeParameterConstraintKind constraints, - ImmutableArray constraintTypes) + ImmutableArray constraintTypes, + bool isEarly) { Debug.Assert(!constraintTypes.IsDefault); - if (constraintTypes.IsEmpty) + if (!isEarly && constraintTypes.IsEmpty) { switch (constraints) { @@ -84,12 +87,13 @@ internal static TypeParameterConstraintClause Create( } } - return new TypeParameterConstraintClause(constraints, constraintTypes); + return new TypeParameterConstraintClause(constraints, constraintTypes, isEarly); } private TypeParameterConstraintClause( TypeParameterConstraintKind constraints, - ImmutableArray constraintTypes) + ImmutableArray constraintTypes, + bool isEarly) { #if DEBUG switch (constraints & TypeParameterConstraintKind.AllReferenceTypeKinds) @@ -109,10 +113,12 @@ private TypeParameterConstraintClause( #endif this.Constraints = constraints; this.ConstraintTypes = constraintTypes; + this.IsEarly = isEarly; } public readonly TypeParameterConstraintKind Constraints; public readonly ImmutableArray ConstraintTypes; + public readonly bool IsEarly; internal bool IsEmpty => Constraints == TypeParameterConstraintKind.None && ConstraintTypes.IsEmpty; @@ -212,6 +218,15 @@ static void adjustConstraintTypes(Symbol container, ImmutableArray constraintClauses, bool canIgnoreNullableContext) + { + if (constraintClauses.IsDefault) + { + return false; + } + return canIgnoreNullableContext || !constraintClauses.Any(clause => clause.IsEarly); + } + internal static bool ContainsOnlyEmptyConstraintClauses(this ImmutableArray constraintClauses) { return constraintClauses.All(clause => clause.IsEmpty); diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedTypeParameterSymbol.cs index e73d2ecb7d53f..54dd85af834f9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedTypeParameterSymbol.cs @@ -94,10 +94,10 @@ public override string Name } } - internal override ImmutableArray GetConstraintTypes(ConsList inProgress) + internal override ImmutableArray GetConstraintTypes(ConsList inProgress, bool canIgnoreNullableContext) { var constraintTypes = ArrayBuilder.GetInstance(); - _map.SubstituteConstraintTypesDistinctWithoutModifiers(_underlyingTypeParameter, _underlyingTypeParameter.GetConstraintTypes(inProgress), constraintTypes, null); + _map.SubstituteConstraintTypesDistinctWithoutModifiers(_underlyingTypeParameter, _underlyingTypeParameter.GetConstraintTypes(inProgress, canIgnoreNullableContext), constraintTypes, null); TypeWithAnnotations bestObjectConstraint = default; @@ -157,7 +157,7 @@ internal override bool? IsNotNullable else if (!HasNotNullConstraint && !HasValueTypeConstraint && !HasReferenceTypeConstraint) { var constraintTypes = ArrayBuilder.GetInstance(); - _map.SubstituteConstraintTypesDistinctWithoutModifiers(_underlyingTypeParameter, _underlyingTypeParameter.GetConstraintTypes(ConsList.Empty), constraintTypes, null); + _map.SubstituteConstraintTypesDistinctWithoutModifiers(_underlyingTypeParameter, _underlyingTypeParameter.GetConstraintTypes(ConsList.Empty, canIgnoreNullableContext: false), constraintTypes, null); return IsNotNullableFromConstraintTypes(constraintTypes.ToImmutableAndFree()); } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeParameterBounds.cs b/src/Compilers/CSharp/Portable/Symbols/TypeParameterBounds.cs index 86e2283eb6ff9..ca989d71d4814 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeParameterBounds.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeParameterBounds.cs @@ -17,6 +17,16 @@ internal sealed class TypeParameterBounds { public static readonly TypeParameterBounds Unset = new TypeParameterBounds(); + /// + /// Creates a "early" bound instance that has constraint types set + /// but no other fields. + /// + public TypeParameterBounds(ImmutableArray constraintTypes) + { + Debug.Assert(!constraintTypes.IsDefault); + this.ConstraintTypes = constraintTypes; + } + /// /// Creates a "late" bound instance with all fields set. /// @@ -41,6 +51,8 @@ private TypeParameterBounds() { } + public bool IsEarly => EffectiveBaseClass is null; + /// /// The type parameters, classes, and interfaces explicitly declared as /// constraint types on the containing type parameter, with cycles removed. @@ -80,9 +92,17 @@ private TypeParameterBounds() internal static class TypeParameterBoundsExtensions { - internal static bool IsSet(this TypeParameterBounds boundsOpt) + internal static bool IsSet(this TypeParameterBounds boundsOpt, bool canIgnoreNullableContext) { - return boundsOpt != TypeParameterBounds.Unset; + if (boundsOpt == TypeParameterBounds.Unset) + { + return false; + } + if (boundsOpt == null) + { + return true; + } + return canIgnoreNullableContext || !boundsOpt.IsEarly; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs index 7de3ee4e3610e..7d0c1a02c3510 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs @@ -77,15 +77,15 @@ internal virtual DiagnosticInfo GetConstraintsUseSiteErrorInfo() /// Duplicates and cycles are removed, although the collection may include /// redundant constraints where one constraint is a base type of another. /// - internal ImmutableArray ConstraintTypesNoUseSiteDiagnostics + internal ImmutableArray GetConstraintTypesNoUseSiteDiagnostics(bool canIgnoreNullableContext) { - get - { - this.EnsureAllConstraintsAreResolved(); - return this.GetConstraintTypes(ConsList.Empty); - } + this.EnsureAllConstraintsAreResolved(canIgnoreNullableContext); + return this.GetConstraintTypes(ConsList.Empty, canIgnoreNullableContext); } + internal ImmutableArray ConstraintTypesNoUseSiteDiagnostics => + GetConstraintTypesNoUseSiteDiagnostics(canIgnoreNullableContext: false); + internal ImmutableArray ConstraintTypesWithDefinitionUseSiteDiagnostics(ref HashSet useSiteDiagnostics) { var result = ConstraintTypesNoUseSiteDiagnostics; @@ -268,7 +268,7 @@ internal NamedTypeSymbol EffectiveBaseClassNoUseSiteDiagnostics { get { - this.EnsureAllConstraintsAreResolved(); + this.EnsureAllConstraintsAreResolved(canIgnoreNullableContext: false); return this.GetEffectiveBaseClass(ConsList.Empty); } } @@ -293,7 +293,7 @@ internal ImmutableArray EffectiveInterfacesNoUseSiteDiagnostics { get { - this.EnsureAllConstraintsAreResolved(); + this.EnsureAllConstraintsAreResolved(canIgnoreNullableContext: false); return this.GetInterfaces(ConsList.Empty); } } @@ -305,7 +305,7 @@ internal TypeSymbol DeducedBaseTypeNoUseSiteDiagnostics { get { - this.EnsureAllConstraintsAreResolved(); + this.EnsureAllConstraintsAreResolved(canIgnoreNullableContext: false); return this.GetDeducedBaseType(ConsList.Empty); } } @@ -362,21 +362,21 @@ internal ImmutableArray AllEffectiveInterfacesWithDefinitionUse /// type or method are resolved in a consistent order, regardless of the /// order the callers query individual type parameters. /// - internal abstract void EnsureAllConstraintsAreResolved(); + internal abstract void EnsureAllConstraintsAreResolved(bool canIgnoreNullableContext); /// /// Helper method to force type parameter constraints to be resolved. /// - protected static void EnsureAllConstraintsAreResolved(ImmutableArray typeParameters) + protected static void EnsureAllConstraintsAreResolved(ImmutableArray typeParameters, bool canIgnoreNullableContext) { foreach (var typeParameter in typeParameters) { // Invoke any method that forces constraints to be resolved. - var unused = typeParameter.GetConstraintTypes(ConsList.Empty); + var unused = typeParameter.GetConstraintTypes(ConsList.Empty, canIgnoreNullableContext); } } - internal abstract ImmutableArray GetConstraintTypes(ConsList inProgress); + internal abstract ImmutableArray GetConstraintTypes(ConsList inProgress, bool canIgnoreNullableContext); internal abstract ImmutableArray GetInterfaces(ConsList inProgress); @@ -388,7 +388,7 @@ private static bool ConstraintImpliesReferenceType(TypeSymbol constraint) { if (constraint.TypeKind == TypeKind.TypeParameter) { - return IsReferenceTypeFromConstraintTypes(((TypeParameterSymbol)constraint).ConstraintTypesNoUseSiteDiagnostics); + return IsReferenceTypeFromConstraintTypes(((TypeParameterSymbol)constraint).GetConstraintTypesNoUseSiteDiagnostics(canIgnoreNullableContext: true)); } else if (!constraint.IsReferenceType) { @@ -512,7 +512,7 @@ public sealed override bool IsReferenceType return true; } - return IsReferenceTypeFromConstraintTypes(this.ConstraintTypesNoUseSiteDiagnostics); + return IsReferenceTypeFromConstraintTypes(this.GetConstraintTypesNoUseSiteDiagnostics(canIgnoreNullableContext: true)); } } @@ -570,7 +570,7 @@ public sealed override bool IsValueType return true; } - return IsValueTypeFromConstraintTypes(this.ConstraintTypesNoUseSiteDiagnostics); + return IsValueTypeFromConstraintTypes(this.GetConstraintTypesNoUseSiteDiagnostics(canIgnoreNullableContext: true)); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedTypeParameterSymbol.cs index d2d129f6a1fa4..c9720ec35eac6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedTypeParameterSymbol.cs @@ -144,9 +144,9 @@ public override string Name return _underlyingTypeParameter.GetDocumentationCommentXml(preferredCulture, expandIncludes, cancellationToken); } - internal override void EnsureAllConstraintsAreResolved() + internal override void EnsureAllConstraintsAreResolved(bool canIgnoreNullableContext) { - _underlyingTypeParameter.EnsureAllConstraintsAreResolved(); + _underlyingTypeParameter.EnsureAllConstraintsAreResolved(canIgnoreNullableContext); } public override ImmutableArray GetAttributes() diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 73c74492fdd75..77b1f90ff9789 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -138851,7 +138851,7 @@ public static C SelectMany(this C source, Fu [Fact] [WorkItem(44348, "https://github.com/dotnet/roslyn/issues/44348")] - public void Constraints() + public void NestedTypeConstraints_01() { var source = @"class A @@ -138870,6 +138870,45 @@ internal interface IB : IA { } comp.VerifyEmitDiagnostics(); } + [Fact] + [WorkItem(45713, "https://github.com/dotnet/roslyn/issues/45713")] + public void NestedTypeConstraints_02() + { + var source = +@"class A +{ + internal interface IA { } +} +#nullable enable +class B : A + where T : B.IA +{ +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + [WorkItem(45863, "https://github.com/dotnet/roslyn/issues/45863")] + public void NestedTypeConstraints_03() + { + var source = +@"#nullable enable +interface IA +{ +} +interface IB : IA + where U : IB.IC +{ + interface IC { } +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } + + // PROTOTYPE: Test NestedTypeConstraints_* with PE symbols. + // PROTOTYPE: Test NestedTypeConstraints_* with T? and T~. + [Fact] [WorkItem(46342, "https://github.com/dotnet/roslyn/issues/46342")] public void LambdaNullReturn_01() diff --git a/src/Compilers/CSharp/Test/Symbol/DocumentationComments/CrefTests.cs b/src/Compilers/CSharp/Test/Symbol/DocumentationComments/CrefTests.cs index 15ab5d268a750..6a91e67f275a3 100644 --- a/src/Compilers/CSharp/Test/Symbol/DocumentationComments/CrefTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/DocumentationComments/CrefTests.cs @@ -1562,7 +1562,7 @@ class C AssertEx.None(actualTypeParameters, p => p.HasReferenceTypeConstraint); AssertEx.None(actualTypeParameters, p => p.HasConstructorConstraint); AssertEx.All(actualTypeParameters, p => p.ContainingSymbol == null); - AssertEx.All(actualTypeParameters, p => p.GetConstraintTypes(null).Length == 0); + AssertEx.All(actualTypeParameters, p => p.GetConstraintTypes(null, canIgnoreNullableContext: false).Length == 0); AssertEx.All(actualTypeParameters, p => p.GetInterfaces(null).Length == 0); foreach (var p in actualTypeParameters) diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EETypeParameterSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EETypeParameterSymbol.cs index 48ff758237dc3..459f575ce8c76 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EETypeParameterSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EETypeParameterSymbol.cs @@ -115,14 +115,14 @@ public override bool IsImplicitlyDeclared get { return true; } } - internal override void EnsureAllConstraintsAreResolved() + internal override void EnsureAllConstraintsAreResolved(bool canIgnoreNullableContext) { - _sourceTypeParameter.EnsureAllConstraintsAreResolved(); + _sourceTypeParameter.EnsureAllConstraintsAreResolved(canIgnoreNullableContext); } - internal override ImmutableArray GetConstraintTypes(ConsList inProgress) + internal override ImmutableArray GetConstraintTypes(ConsList inProgress, bool canIgnoreNullableContext) { - var constraintTypes = _sourceTypeParameter.GetConstraintTypes(inProgress); + var constraintTypes = _sourceTypeParameter.GetConstraintTypes(inProgress, canIgnoreNullableContext); if (constraintTypes.IsEmpty) { diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SimpleTypeParameterSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SimpleTypeParameterSymbol.cs index 6683febb0eae7..79ece729adb33 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SimpleTypeParameterSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SimpleTypeParameterSymbol.cs @@ -89,11 +89,11 @@ public override ImmutableArray DeclaringSyntaxReferences get { throw ExceptionUtilities.Unreachable; } } - internal override void EnsureAllConstraintsAreResolved() + internal override void EnsureAllConstraintsAreResolved(bool canIgnoreNullableContext) { } - internal override ImmutableArray GetConstraintTypes(ConsList inProgress) + internal override ImmutableArray GetConstraintTypes(ConsList inProgress, bool canIgnoreNullableContext) { return ImmutableArray.Empty; } From cbbf3b8fe55674b135b0594273b9e6e7ca2b8ca0 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Tue, 22 Sep 2020 22:49:22 -0700 Subject: [PATCH 02/11] Misc. --- src/Compilers/CSharp/Portable/Binder/Binder.cs | 2 +- src/Compilers/CSharp/Portable/Binder/BinderFlags.cs | 5 ++++- .../CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.cs b/src/Compilers/CSharp/Portable/Binder/Binder.cs index 7da5d1fbe027e..b4e785d2b3051 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.cs @@ -253,7 +253,7 @@ internal bool AreNullableAnnotationsEnabled(SyntaxTree syntaxTree, int position) internal bool AreNullableAnnotationsEnabled(SyntaxToken token) { RoslynDebug.Assert(token.SyntaxTree is object); - if ((Flags & BinderFlags.IgnoreNullableAnnotationsEnabled) != 0) + if ((Flags & BinderFlags.IgnoreNullableContext) != 0) { return false; } diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs b/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs index 5a1e3e10371eb..eae61f63d8fe2 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs @@ -110,7 +110,10 @@ internal enum BinderFlags : uint /// InEEMethodBinder = 1 << 30, - IgnoreNullableAnnotationsEnabled = 1u << 31, + /// + /// Assume '#nullable disabled'. + /// + IgnoreNullableContext = 1u << 31, // Groups diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index f4213ca69f5c7..7548b6762e939 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -259,7 +259,7 @@ internal TypeParameterConstraintClause GetTypeParameterConstraintClause(bool can var diagnostics = DiagnosticBag.GetInstance(); if (ImmutableInterlocked.InterlockedCompareExchange(ref _lazyTypeParameterConstraints, MakeTypeParameterConstraints(canIgnoreNullableContext, diagnostics), clauses) == clauses) { - if (!canIgnoreNullableContext) + if (_lazyTypeParameterConstraints.IsSet(false)) { this.AddDeclarationDiagnostics(diagnostics); } @@ -321,7 +321,7 @@ private ImmutableArray MakeTypeParameterConstrain // to avoid checking constraints when binding type names. Debug.Assert(!binder.Flags.Includes(BinderFlags.GenericConstraintsClause)); binder = binder.WithContainingMemberOrLambda(this).WithAdditionalFlags( - BinderFlags.GenericConstraintsClause | BinderFlags.SuppressConstraintChecks | (canIgnoreNullableContext ? BinderFlags.IgnoreNullableAnnotationsEnabled : 0)); + BinderFlags.GenericConstraintsClause | BinderFlags.SuppressConstraintChecks | (canIgnoreNullableContext ? BinderFlags.IgnoreNullableContext : 0)); constraints = binder.BindTypeParameterConstraintClauses(this, typeParameters, typeParameterList, constraintClauses, canIgnoreNullableContext, ref isValueTypeOverride, diagnostics); } From 3c76be2d75a2601dc36b2c49e416f1cf3f9516e6 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Wed, 23 Sep 2020 09:50:23 -0700 Subject: [PATCH 03/11] Fix tests --- .../CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs | 2 +- .../CSharp/Portable/Symbols/SubstitutedTypeParameterSymbol.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs index 9e595959c78e9..c8077898f8822 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs @@ -517,7 +517,7 @@ internal override bool? ReferenceTypeConstraintIsNullable { get { - return CalculateReferenceTypeConstraintIsNullable(this.GetDeclaredConstraints(canIgnoreNullableContext: true)); + return CalculateReferenceTypeConstraintIsNullable(this.GetDeclaredConstraints(canIgnoreNullableContext: false)); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedTypeParameterSymbol.cs index 54dd85af834f9..2f97f842566cc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedTypeParameterSymbol.cs @@ -114,7 +114,7 @@ internal override ImmutableArray GetConstraintTypes(ConsLis if (bestObjectConstraint.HasType) { // See if we need to put Object! or Object~ back in order to preserve nullability information for the type parameter. - if (ConstraintsHelper.IsObjectConstraintSignificant(CalculateIsNotNullableFromNonTypeConstraints(), bestObjectConstraint)) + if (!canIgnoreNullableContext && ConstraintsHelper.IsObjectConstraintSignificant(CalculateIsNotNullableFromNonTypeConstraints(), bestObjectConstraint)) { Debug.Assert(!HasNotNullConstraint && !HasValueTypeConstraint); if (constraintTypes.Count == 0) From 417f48be845f55ff23eadf22fb98bc28200f5d47 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Wed, 23 Sep 2020 10:13:00 -0700 Subject: [PATCH 04/11] PR feedback --- .../Metadata/PE/PETypeParameterSymbol.cs | 2 +- .../Symbols/Source/SourceNamedTypeSymbol.cs | 4 +-- .../Source/SourceTypeParameterSymbol.cs | 12 ++++----- .../Source/TypeParameterConstraintClause.cs | 2 +- .../Portable/Symbols/TypeParameterBounds.cs | 25 ++++++++++++------- 5 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs index 3d9eda4dcb70e..97ef0bc787f01 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs @@ -571,7 +571,7 @@ public override VarianceKind Variance internal override void EnsureAllConstraintsAreResolved(bool canIgnoreNullableContext) { canIgnoreNullableContext = false; // Resolve bounds eagerly. - if (!_lazyBounds.IsSet(canIgnoreNullableContext)) + if (!_lazyBounds.HasValue(canIgnoreNullableContext)) { var typeParameters = (_containingSymbol.Kind == SymbolKind.Method) ? ((PEMethodSymbol)_containingSymbol).TypeParameters : diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index 7548b6762e939..f1456e40f384a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -254,12 +254,12 @@ private ImmutableArray MakeTypeParameters(DiagnosticBag dia internal TypeParameterConstraintClause GetTypeParameterConstraintClause(bool canIgnoreNullableContext, int ordinal) { var clauses = _lazyTypeParameterConstraints; - if (!clauses.IsSet(canIgnoreNullableContext)) + if (!clauses.HasValue(canIgnoreNullableContext)) { var diagnostics = DiagnosticBag.GetInstance(); if (ImmutableInterlocked.InterlockedCompareExchange(ref _lazyTypeParameterConstraints, MakeTypeParameterConstraints(canIgnoreNullableContext, diagnostics), clauses) == clauses) { - if (_lazyTypeParameterConstraints.IsSet(false)) + if (_lazyTypeParameterConstraints.HasValue(canIgnoreNullableContext: false)) { this.AddDeclarationDiagnostics(diagnostics); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs index c8077898f8822..7fb8c9b1f18ab 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs @@ -204,7 +204,7 @@ internal virtual CustomAttributesBag GetAttributesBag() internal override void EnsureAllConstraintsAreResolved(bool canIgnoreNullableContext) { - if (!_lazyBounds.IsSet(canIgnoreNullableContext)) + if (!_lazyBounds.HasValue(canIgnoreNullableContext)) { EnsureAllConstraintsAreResolved(this.ContainerTypeParameters, canIgnoreNullableContext); } @@ -221,14 +221,14 @@ private TypeParameterBounds GetBounds(ConsList inProgress, Debug.Assert(!inProgress.Any() || ReferenceEquals(inProgress.Head.ContainingSymbol, this.ContainingSymbol)); var currentBounds = _lazyBounds; - if (!currentBounds.IsSet(canIgnoreNullableContext)) + if (!currentBounds.HasValue(canIgnoreNullableContext)) { var diagnostics = DiagnosticBag.GetInstance(); var bounds = this.ResolveBounds(inProgress, canIgnoreNullableContext, diagnostics); if (ReferenceEquals(Interlocked.CompareExchange(ref _lazyBounds, bounds, currentBounds), currentBounds)) { - if (_lazyBounds?.IsEarly != true) + if (_lazyBounds?.IgnoresNullableContext != true) { this.CheckConstraintTypeConstraints(diagnostics); this.CheckUnmanagedConstraint(diagnostics); @@ -573,7 +573,7 @@ protected override TypeParameterBounds ResolveBounds(ConsList constraintClauses, bool canIgnoreNullableContext) + internal static bool HasValue(this ImmutableArray constraintClauses, bool canIgnoreNullableContext) { if (constraintClauses.IsDefault) { diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeParameterBounds.cs b/src/Compilers/CSharp/Portable/Symbols/TypeParameterBounds.cs index ca989d71d4814..84f0315b81568 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeParameterBounds.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeParameterBounds.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System.Collections.Immutable; using System.Diagnostics; @@ -18,17 +20,22 @@ internal sealed class TypeParameterBounds public static readonly TypeParameterBounds Unset = new TypeParameterBounds(); /// - /// Creates a "early" bound instance that has constraint types set - /// but no other fields. + /// Creates an instance that has constraint types set but no other fields. + /// Used specifically when binding constraints ignoring #nullable context. /// - public TypeParameterBounds(ImmutableArray constraintTypes) + public static TypeParameterBounds ConstraintTypesOnlyNoNullableContext(ImmutableArray constraintTypes) + { + return new TypeParameterBounds(constraintTypes); + } + + private TypeParameterBounds(ImmutableArray constraintTypes) { Debug.Assert(!constraintTypes.IsDefault); this.ConstraintTypes = constraintTypes; } /// - /// Creates a "late" bound instance with all fields set. + /// Creates a fully bound instance with all fields set. /// public TypeParameterBounds( ImmutableArray constraintTypes, @@ -51,7 +58,7 @@ private TypeParameterBounds() { } - public bool IsEarly => EffectiveBaseClass is null; + public bool IgnoresNullableContext => EffectiveBaseClass is null; /// /// The type parameters, classes, and interfaces explicitly declared as @@ -69,7 +76,7 @@ private TypeParameterBounds() /// /// As defined in 10.1.5 of the specification. /// - public readonly NamedTypeSymbol EffectiveBaseClass; + public readonly NamedTypeSymbol? EffectiveBaseClass; /// /// The "exact" effective base type. @@ -87,12 +94,12 @@ private TypeParameterBounds() /// When computing the deduced type we don't perform this abstraction. We keep the original constraint T. /// Deduced base type is used to check that consistency rules are satisfied. /// - public readonly TypeSymbol DeducedBaseType; + public readonly TypeSymbol? DeducedBaseType; } internal static class TypeParameterBoundsExtensions { - internal static bool IsSet(this TypeParameterBounds boundsOpt, bool canIgnoreNullableContext) + internal static bool HasValue(this TypeParameterBounds? boundsOpt, bool canIgnoreNullableContext) { if (boundsOpt == TypeParameterBounds.Unset) { @@ -102,7 +109,7 @@ internal static bool IsSet(this TypeParameterBounds boundsOpt, bool canIgnoreNul { return true; } - return canIgnoreNullableContext || !boundsOpt.IsEarly; + return canIgnoreNullableContext || !boundsOpt.IgnoresNullableContext; } } } From 9367f0a887035c9acce2ef61878e29dabfbe06c2 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Wed, 23 Sep 2020 10:23:10 -0700 Subject: [PATCH 05/11] Misc. --- .../Portable/Binder/Binder_Constraints.cs | 10 ++++++---- .../Symbols/Source/SourceNamedTypeSymbol.cs | 2 +- .../Source/SourceTypeParameterSymbol.cs | 2 +- .../Source/TypeParameterConstraintClause.cs | 18 +++++++++--------- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs index 05caf6740360b..6bc4816682a80 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs @@ -67,7 +67,8 @@ internal ImmutableArray BindTypeParameterConstrai Debug.Assert(ordinal >= 0); Debug.Assert(ordinal < n); - (TypeParameterConstraintClause constraintClause, ArrayBuilder? typeConstraintNodes) = this.BindTypeParameterConstraints(typeParameterList.Parameters[ordinal], clause, isForOverride, canIgnoreNullableContext, diagnostics); + (TypeParameterConstraintClause constraintClause, ArrayBuilder? typeConstraintNodes) = + this.BindTypeParameterConstraints(typeParameterList.Parameters[ordinal], clause, isForOverride, canIgnoreNullableContext: canIgnoreNullableContext, diagnostics); if (results[ordinal] == null) { results[ordinal] = constraintClause; @@ -118,7 +119,8 @@ internal ImmutableArray BindTypeParameterConstrai /// /// Bind and return a single type parameter constraint clause along with syntax nodes corresponding to type constraints. /// - private (TypeParameterConstraintClause, ArrayBuilder?) BindTypeParameterConstraints(TypeParameterSyntax typeParameterSyntax, TypeParameterConstraintClauseSyntax constraintClauseSyntax, bool isForOverride, bool isEarly, DiagnosticBag diagnostics) + private (TypeParameterConstraintClause, ArrayBuilder?) BindTypeParameterConstraints( + TypeParameterSyntax typeParameterSyntax, TypeParameterConstraintClauseSyntax constraintClauseSyntax, bool isForOverride, bool canIgnoreNullableContext, DiagnosticBag diagnostics) { var constraints = TypeParameterConstraintKind.None; ArrayBuilder? constraintTypes = null; @@ -308,7 +310,7 @@ internal ImmutableArray BindTypeParameterConstrai Debug.Assert(!isForOverride || (constraints & (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.ValueType)) != (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.ValueType)); - return (TypeParameterConstraintClause.Create(constraints, constraintTypes?.ToImmutableAndFree() ?? ImmutableArray.Empty, isEarly), syntaxBuilder); + return (TypeParameterConstraintClause.Create(constraints, constraintTypes?.ToImmutableAndFree() ?? ImmutableArray.Empty, canIgnoreNullableContext), syntaxBuilder); static void reportOverrideWithConstraints(ref bool reportedOverrideWithConstraints, TypeParameterConstraintSyntax syntax, DiagnosticBag diagnostics) { @@ -391,7 +393,7 @@ private static TypeParameterConstraintClause RemoveInvalidConstraints( if (constraintTypeBuilder.Count < n) { - return TypeParameterConstraintClause.Create(constraintClause.Constraints, constraintTypeBuilder.ToImmutableAndFree(), constraintClause.IsEarly); + return TypeParameterConstraintClause.Create(constraintClause.Constraints, constraintTypeBuilder.ToImmutableAndFree(), constraintClause.IgnoresNullableContext); } constraintTypeBuilder.Free(); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index f1456e40f384a..3e04528b97945 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -442,7 +442,7 @@ private ImmutableArray MergeConstraintsForPartial } builder[i] = TypeParameterConstraintClause.Create(mergedKind, - mergedConstraintTypes?.ToImmutableAndFree() ?? originalConstraintTypes, isEarly: constraint.IsEarly); + mergedConstraintTypes?.ToImmutableAndFree() ?? originalConstraintTypes, ignoresNullableContext: constraint.IgnoresNullableContext); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs index 7fb8c9b1f18ab..1fe18aa101927 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs @@ -228,7 +228,7 @@ private TypeParameterBounds GetBounds(ConsList inProgress, if (ReferenceEquals(Interlocked.CompareExchange(ref _lazyBounds, bounds, currentBounds), currentBounds)) { - if (_lazyBounds?.IgnoresNullableContext != true) + if (_lazyBounds.HasValue(canIgnoreNullableContext: false)) { this.CheckConstraintTypeConstraints(diagnostics); this.CheckUnmanagedConstraint(diagnostics); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterConstraintClause.cs b/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterConstraintClause.cs index f5e43bb966286..8506fdf5bc666 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterConstraintClause.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterConstraintClause.cs @@ -62,20 +62,20 @@ internal sealed class TypeParameterConstraintClause internal static readonly TypeParameterConstraintClause Empty = new TypeParameterConstraintClause( TypeParameterConstraintKind.None, ImmutableArray.Empty, - isEarly: false); + ignoresNullableContext: false); internal static readonly TypeParameterConstraintClause ObliviousNullabilityIfReferenceType = new TypeParameterConstraintClause( TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType, ImmutableArray.Empty, - isEarly: false); + ignoresNullableContext: false); internal static TypeParameterConstraintClause Create( TypeParameterConstraintKind constraints, ImmutableArray constraintTypes, - bool isEarly) + bool ignoresNullableContext) { Debug.Assert(!constraintTypes.IsDefault); - if (!isEarly && constraintTypes.IsEmpty) + if (!ignoresNullableContext && constraintTypes.IsEmpty) { switch (constraints) { @@ -87,13 +87,13 @@ internal static TypeParameterConstraintClause Create( } } - return new TypeParameterConstraintClause(constraints, constraintTypes, isEarly); + return new TypeParameterConstraintClause(constraints, constraintTypes, ignoresNullableContext); } private TypeParameterConstraintClause( TypeParameterConstraintKind constraints, ImmutableArray constraintTypes, - bool isEarly) + bool ignoresNullableContext) { #if DEBUG switch (constraints & TypeParameterConstraintKind.AllReferenceTypeKinds) @@ -113,12 +113,12 @@ private TypeParameterConstraintClause( #endif this.Constraints = constraints; this.ConstraintTypes = constraintTypes; - this.IsEarly = isEarly; + this.IgnoresNullableContext = ignoresNullableContext; } public readonly TypeParameterConstraintKind Constraints; public readonly ImmutableArray ConstraintTypes; - public readonly bool IsEarly; + public readonly bool IgnoresNullableContext; internal bool IsEmpty => Constraints == TypeParameterConstraintKind.None && ConstraintTypes.IsEmpty; @@ -224,7 +224,7 @@ internal static bool HasValue(this ImmutableArray { return false; } - return canIgnoreNullableContext || !constraintClauses.Any(clause => clause.IsEarly); + return canIgnoreNullableContext || !constraintClauses.Any(clause => clause.IgnoresNullableContext); } internal static bool ContainsOnlyEmptyConstraintClauses(this ImmutableArray constraintClauses) From d88c2fd7e80d575dbe5398ac11d22ae984a10929 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Wed, 23 Sep 2020 10:52:41 -0700 Subject: [PATCH 06/11] More tests --- .../Symbols/Source/SourceNamedTypeSymbol.cs | 2 +- .../Semantics/NullableReferenceTypesTests.cs | 114 +++++++++++++++++- 2 files changed, 109 insertions(+), 7 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index 3e04528b97945..5f0c82c722abf 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -259,7 +259,7 @@ internal TypeParameterConstraintClause GetTypeParameterConstraintClause(bool can var diagnostics = DiagnosticBag.GetInstance(); if (ImmutableInterlocked.InterlockedCompareExchange(ref _lazyTypeParameterConstraints, MakeTypeParameterConstraints(canIgnoreNullableContext, diagnostics), clauses) == clauses) { - if (_lazyTypeParameterConstraints.HasValue(canIgnoreNullableContext: false)) + if (_lazyTypeParameterConstraints.HasValue(canIgnoreNullableContext: false)) { this.AddDeclarationDiagnostics(diagnostics); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 77b1f90ff9789..bd0f0b761431d 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -138874,7 +138874,7 @@ internal interface IB : IA { } [WorkItem(45713, "https://github.com/dotnet/roslyn/issues/45713")] public void NestedTypeConstraints_02() { - var source = + var source1 = @"class A { internal interface IA { } @@ -138884,13 +138884,86 @@ class B : A where T : B.IA { }"; - var comp = CreateCompilation(source); + var comp = CreateCompilation(source1); + comp.VerifyEmitDiagnostics(); + + var source2 = +@"class A +{ + internal interface IA { } +} +#nullable enable +class B : A + where T : B.IA +{ +}"; + comp = CreateCompilation(source2); + // PROTOTYPE: StackOverflowException + //comp.VerifyEmitDiagnostics(); + + var source3 = +@"class A +{ + internal interface IA { } +} +#nullable enable +class B : A +#nullable disable + where T : B.IA +{ +}"; + comp = CreateCompilation(source3); comp.VerifyEmitDiagnostics(); + + var source4 = +@"class A +{ + internal interface IA { } +} +#nullable enable +class B : A +#nullable disable + where T : B.IA +{ +}"; + comp = CreateCompilation(source4); + // PROTOTYPE: StackOverflowException + //comp.VerifyEmitDiagnostics(); } [Fact] - [WorkItem(45863, "https://github.com/dotnet/roslyn/issues/45863")] + [WorkItem(45713, "https://github.com/dotnet/roslyn/issues/45713")] public void NestedTypeConstraints_03() + { + var source0 = +@"public class A +{ + public interface IA { } +} +#nullable enable +public class B : A + where T : B.IA +{ +}"; + var comp = CreateCompilation(source0); + var ref0 = comp.EmitToImageReference(); + + var source1 = +@"class A : A.IA { } +class Program +{ + static B F() => new(); + static void Main() + { + System.Console.WriteLine(F()); + } +}"; + CompileAndVerify(source1, references: new[] { ref0 }, expectedOutput: "B`1[A]"); + } + + [Fact] + [WorkItem(45863, "https://github.com/dotnet/roslyn/issues/45863")] + public void NestedTypeConstraints_04() { var source = @"#nullable enable @@ -138900,14 +138973,43 @@ interface IA interface IB : IA where U : IB.IC { - interface IC { } + public interface IC { } }"; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); } - // PROTOTYPE: Test NestedTypeConstraints_* with PE symbols. - // PROTOTYPE: Test NestedTypeConstraints_* with T? and T~. + [Fact] + [WorkItem(45863, "https://github.com/dotnet/roslyn/issues/45863")] + public void NestedTypeConstraints_05() + { + var source0 = +@"#nullable enable +public interface IA +{ +} +public interface IB : IA + where U : IB.IC +{ + public interface IC { } +}"; + var comp = CreateCompilation(source0); + var ref0 = comp.EmitToImageReference(); + + var source1 = +@"class C : IB.IC +{ +} +class Program +{ + static C F() => new(); + static void Main() + { + System.Console.WriteLine(F()); + } +}"; + CompileAndVerify(source1, references: new[] { ref0 }, expectedOutput: "C"); + } [Fact] [WorkItem(46342, "https://github.com/dotnet/roslyn/issues/46342")] From 255803424c62a35fa1866d4867da84c359b28d1f Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Wed, 23 Sep 2020 11:49:58 -0700 Subject: [PATCH 07/11] Revert change --- .../Test/Semantic/Semantics/NullableReferenceTypesTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index bd0f0b761431d..6739cf70f70bc 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -138973,7 +138973,7 @@ interface IA interface IB : IA where U : IB.IC { - public interface IC { } + interface IC { } }"; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); From f9061da0a0960cf3ce3d74599937753a3e43fbd3 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Wed, 23 Sep 2020 11:59:23 -0700 Subject: [PATCH 08/11] More cycles --- .../Portable/Binder/Semantics/AccessCheck.cs | 2 +- .../Portable/Symbols/NamedTypeSymbol.cs | 4 +- .../CSharp/Portable/Symbols/Symbol.cs | 4 ++ .../Portable/Symbols/TypeWithAnnotations.cs | 18 +++++++- .../Semantics/NullableReferenceTypesTests.cs | 46 +++++++++++++------ 5 files changed, 54 insertions(+), 20 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs index abbfbf12932e6..62d1ad84ddd3f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs @@ -234,7 +234,7 @@ private static bool IsNamedTypeAccessible(NamedTypeSymbol type, Symbol within, r { // type parameters are always accessible, so don't check those (so common it's // worth optimizing this). - if (typeArg.Type.Kind != SymbolKind.TypeParameter && !IsSymbolAccessibleCore(typeArg.Type, within, null, out unused, compilation, ref useSiteDiagnostics, basesBeingResolved)) + if (!typeArg.IsAnnotatedOrUnannotatedTypeParameter && !IsSymbolAccessibleCore(typeArg.Type, within, null, out unused, compilation, ref useSiteDiagnostics, basesBeingResolved)) { return false; } diff --git a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs index 34fa1e371a575..0e71c036e19eb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs @@ -63,7 +63,7 @@ internal ImmutableArray TypeArgumentsWithDefinitionUseSiteD foreach (var typeArgument in result) { - typeArgument.Type.OriginalDefinition.AddUseSiteDiagnostics(ref useSiteDiagnostics); + typeArgument.AddUseSiteDiagnostics(ref useSiteDiagnostics); } return result; @@ -72,7 +72,7 @@ internal ImmutableArray TypeArgumentsWithDefinitionUseSiteD internal TypeWithAnnotations TypeArgumentWithDefinitionUseSiteDiagnostics(int index, ref HashSet useSiteDiagnostics) { var result = TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[index]; - result.Type.OriginalDefinition.AddUseSiteDiagnostics(ref useSiteDiagnostics); + result.AddUseSiteDiagnostics(ref useSiteDiagnostics); return result; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs index 7a610aaa4a553..79bf69c26cdf9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs @@ -1018,6 +1018,10 @@ private DiagnosticInfo GetSymbolSpecificUnsupportedMetadataUseSiteErrorInfo() internal bool DeriveUseSiteDiagnosticFromType(ref DiagnosticInfo result, TypeWithAnnotations type, AllowedRequiredModifierType allowedRequiredModifierType) { + if (type.IsAnnotatedOrUnannotatedTypeParameter) + { + return false; + } return DeriveUseSiteDiagnosticFromType(ref result, type.Type) || DeriveUseSiteDiagnosticFromCustomModifiers(ref result, type.CustomModifiers, allowedRequiredModifierType); } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs b/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs index c9ad96daa10a3..ae07f06535101 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs @@ -262,6 +262,8 @@ public TypeWithAnnotations WithModifiers(ImmutableArray customMo public ImmutableArray CustomModifiers => _extensions.CustomModifiers; public TypeKind TypeKind => Type.TypeKind; + public bool IsAnnotatedOrUnannotatedTypeParameter => DefaultType.TypeKind == TypeKind.TypeParameter; + public SpecialType SpecialType => _extensions.GetSpecialType(DefaultType); public Cci.PrimitiveTypeCode PrimitiveTypeCode => Type.PrimitiveTypeCode; @@ -428,6 +430,9 @@ public bool IsAtLeastAsVisibleAs(Symbol sym, ref HashSet useSite return NullableUnderlyingTypeOrSelf.IsAtLeastAsVisibleAs(sym, ref useSiteDiagnostics); } + public void AddUseSiteDiagnostics(ref HashSet useSiteDiagnostics) => + DefaultType.OriginalDefinition.AddUseSiteDiagnostics(ref useSiteDiagnostics); + public TypeWithAnnotations SubstituteType(AbstractTypeMap typeMap) => _extensions.SubstituteType(this, typeMap); @@ -468,9 +473,18 @@ internal TypeWithAnnotations SubstituteTypeCore(AbstractTypeMap typeMap) NullableAnnotation newAnnotation; - Debug.Assert(!IsIndexedTypeParameter(newTypeWithModifiers.Type) || newTypeWithModifiers.NullableAnnotation.IsOblivious()); + //Debug.Assert(!IsIndexedTypeParameter(newTypeWithModifiers.Type) || newTypeWithModifiers.NullableAnnotation.IsOblivious()); - if (NullableAnnotation.IsAnnotated() || newTypeWithModifiers.NullableAnnotation.IsAnnotated()) + if (newTypeWithModifiers.NullableAnnotation.IsAnnotated()) + { + if (newTypeWithModifiers._extensions is LazyNullableTypeParameter) + { + Debug.Assert(newCustomModifiers.IsEmpty); + return newTypeWithModifiers; + } + newAnnotation = NullableAnnotation.Annotated; + } + else if (NullableAnnotation.IsAnnotated()) { newAnnotation = NullableAnnotation.Annotated; } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 6739cf70f70bc..329ae1ada58ae 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -138874,7 +138874,7 @@ internal interface IB : IA { } [WorkItem(45713, "https://github.com/dotnet/roslyn/issues/45713")] public void NestedTypeConstraints_02() { - var source1 = + var source = @"class A { internal interface IA { } @@ -138884,10 +138884,15 @@ class B : A where T : B.IA { }"; - var comp = CreateCompilation(source1); + var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); + } - var source2 = + [Fact] + [WorkItem(45713, "https://github.com/dotnet/roslyn/issues/45713")] + public void NestedTypeConstraints_03() + { + var source = @"class A { internal interface IA { } @@ -138897,11 +138902,15 @@ class B : A where T : B.IA { }"; - comp = CreateCompilation(source2); - // PROTOTYPE: StackOverflowException - //comp.VerifyEmitDiagnostics(); + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } - var source3 = + [Fact] + [WorkItem(45713, "https://github.com/dotnet/roslyn/issues/45713")] + public void NestedTypeConstraints_04() + { + var source = @"class A { internal interface IA { } @@ -138912,10 +138921,15 @@ class B : A where T : B.IA { }"; - comp = CreateCompilation(source3); + var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); + } - var source4 = + [Fact] + [WorkItem(45713, "https://github.com/dotnet/roslyn/issues/45713")] + public void NestedTypeConstraints_05() + { + var source = @"class A { internal interface IA { } @@ -138926,14 +138940,16 @@ class B : A where T : B.IA { }"; - comp = CreateCompilation(source4); - // PROTOTYPE: StackOverflowException - //comp.VerifyEmitDiagnostics(); + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (8,18): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. + // where T : B.IA + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(8, 18)); } [Fact] [WorkItem(45713, "https://github.com/dotnet/roslyn/issues/45713")] - public void NestedTypeConstraints_03() + public void NestedTypeConstraints_06() { var source0 = @"public class A @@ -138963,7 +138979,7 @@ static void Main() [Fact] [WorkItem(45863, "https://github.com/dotnet/roslyn/issues/45863")] - public void NestedTypeConstraints_04() + public void NestedTypeConstraints_07() { var source = @"#nullable enable @@ -138981,7 +138997,7 @@ interface IC { } [Fact] [WorkItem(45863, "https://github.com/dotnet/roslyn/issues/45863")] - public void NestedTypeConstraints_05() + public void NestedTypeConstraints_08() { var source0 = @"#nullable enable From 89eeb4cad402a899893ea50be3e94b24dd3bed01 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Wed, 23 Sep 2020 21:32:03 -0700 Subject: [PATCH 09/11] Calculate TypeParameterBounds ignoring nullable context if necessary --- .../Portable/Symbols/ConstraintsHelper.cs | 6 +++-- .../Metadata/PE/PETypeParameterSymbol.cs | 2 +- .../Source/SourceTypeParameterSymbol.cs | 18 +++----------- .../CSharp/Portable/Symbols/Symbol.cs | 6 +---- .../Portable/Symbols/TypeParameterBounds.cs | 24 ++++--------------- 5 files changed, 13 insertions(+), 43 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs index 2b39f65a55bf9..5ba55d81b0ef5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs @@ -69,12 +69,13 @@ public static TypeParameterBounds ResolveBounds( ConsList inProgress, ImmutableArray constraintTypes, bool inherited, + bool ignoresNullableContext, CSharpCompilation currentCompilation, DiagnosticBag diagnostics) { var diagnosticsBuilder = ArrayBuilder.GetInstance(); ArrayBuilder useSiteDiagnosticsBuilder = null; - var bounds = typeParameter.ResolveBounds(corLibrary, inProgress, constraintTypes, inherited, currentCompilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder); + var bounds = typeParameter.ResolveBounds(corLibrary, inProgress, constraintTypes, inherited, ignoresNullableContext: ignoresNullableContext, currentCompilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder); if (useSiteDiagnosticsBuilder != null) { @@ -97,6 +98,7 @@ public static TypeParameterBounds ResolveBounds( ConsList inProgress, ImmutableArray constraintTypes, bool inherited, + bool ignoresNullableContext, CSharpCompilation currentCompilation, ArrayBuilder diagnosticsBuilder, ref ArrayBuilder useSiteDiagnosticsBuilder) @@ -295,7 +297,7 @@ public static TypeParameterBounds ResolveBounds( return null; } - var bounds = new TypeParameterBounds(constraintTypes, interfaces, effectiveBaseClass, deducedBaseType); + var bounds = new TypeParameterBounds(constraintTypes, interfaces, effectiveBaseClass, deducedBaseType, ignoresNullableContext); // Additional constraint checks for overrides. if (inherited) diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs index 97ef0bc787f01..2a0a6a96c82ea 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs @@ -635,7 +635,7 @@ private TypeParameterBounds GetBounds(ConsList inProgress) var diagnostics = ArrayBuilder.GetInstance(); ArrayBuilder useSiteDiagnosticsBuilder = null; bool inherited = (_containingSymbol.Kind == SymbolKind.Method) && ((MethodSymbol)_containingSymbol).IsOverride; - var bounds = this.ResolveBounds(this.ContainingAssembly.CorLibrary, inProgress.Prepend(this), constraintTypes, inherited, currentCompilation: null, + var bounds = this.ResolveBounds(this.ContainingAssembly.CorLibrary, inProgress.Prepend(this), constraintTypes, inherited, ignoresNullableContext: false, currentCompilation: null, diagnosticsBuilder: diagnostics, useSiteDiagnosticsBuilder: ref useSiteDiagnosticsBuilder); DiagnosticInfo errorInfo = null; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs index 1fe18aa101927..2a44a0e10b6da 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs @@ -571,11 +571,7 @@ protected override TypeParameterBounds ResolveBounds(ConsList diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs index 79bf69c26cdf9..7a7cf5e84bccf 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs @@ -1018,11 +1018,7 @@ private DiagnosticInfo GetSymbolSpecificUnsupportedMetadataUseSiteErrorInfo() internal bool DeriveUseSiteDiagnosticFromType(ref DiagnosticInfo result, TypeWithAnnotations type, AllowedRequiredModifierType allowedRequiredModifierType) { - if (type.IsAnnotatedOrUnannotatedTypeParameter) - { - return false; - } - return DeriveUseSiteDiagnosticFromType(ref result, type.Type) || + return (!type.IsAnnotatedOrUnannotatedTypeParameter && DeriveUseSiteDiagnosticFromType(ref result, type.Type)) || DeriveUseSiteDiagnosticFromCustomModifiers(ref result, type.CustomModifiers, allowedRequiredModifierType); } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeParameterBounds.cs b/src/Compilers/CSharp/Portable/Symbols/TypeParameterBounds.cs index 84f0315b81568..a1168974554f5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeParameterBounds.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeParameterBounds.cs @@ -19,29 +19,12 @@ internal sealed class TypeParameterBounds { public static readonly TypeParameterBounds Unset = new TypeParameterBounds(); - /// - /// Creates an instance that has constraint types set but no other fields. - /// Used specifically when binding constraints ignoring #nullable context. - /// - public static TypeParameterBounds ConstraintTypesOnlyNoNullableContext(ImmutableArray constraintTypes) - { - return new TypeParameterBounds(constraintTypes); - } - - private TypeParameterBounds(ImmutableArray constraintTypes) - { - Debug.Assert(!constraintTypes.IsDefault); - this.ConstraintTypes = constraintTypes; - } - - /// - /// Creates a fully bound instance with all fields set. - /// public TypeParameterBounds( ImmutableArray constraintTypes, ImmutableArray interfaces, NamedTypeSymbol effectiveBaseClass, - TypeSymbol deducedBaseType) + TypeSymbol deducedBaseType, + bool ignoresNullableContext) { Debug.Assert(!constraintTypes.IsDefault); Debug.Assert(!interfaces.IsDefault); @@ -52,13 +35,14 @@ public TypeParameterBounds( this.Interfaces = interfaces; this.EffectiveBaseClass = effectiveBaseClass; this.DeducedBaseType = deducedBaseType; + this.IgnoresNullableContext = ignoresNullableContext; } private TypeParameterBounds() { } - public bool IgnoresNullableContext => EffectiveBaseClass is null; + public readonly bool IgnoresNullableContext; /// /// The type parameters, classes, and interfaces explicitly declared as From d1360dbd2ae81f5676e04b67dcdcd88c1939b15a Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Thu, 24 Sep 2020 11:02:08 -0700 Subject: [PATCH 10/11] PR feedback --- .../CSharp/Portable/Binder/BinderFlags.cs | 2 +- .../Portable/Binder/Semantics/AccessCheck.cs | 2 +- .../Lowering/SynthesizedMethodBaseSymbol.cs | 2 +- .../Portable/Symbols/ConstraintsHelper.cs | 3 +- .../Portable/Symbols/NamedTypeSymbol.cs | 9 +++- .../Portable/Symbols/Source/LambdaSymbol.cs | 2 +- .../Symbols/Source/LocalFunctionSymbol.cs | 7 +-- .../Source/SourceConstructorSymbolBase.cs | 2 +- .../Source/SourceDelegateMethodSymbol.cs | 2 +- .../Symbols/Source/SourceDestructorSymbol.cs | 2 +- .../Source/SourceEventAccessorSymbol.cs | 2 +- .../Symbols/Source/SourceMethodSymbol.cs | 2 +- .../Symbols/Source/SourceNamedTypeSymbol.cs | 8 ++-- .../Source/SourceOrdinaryMethodSymbol.cs | 8 ++-- .../Source/SourcePropertyAccessorSymbol.cs | 2 +- .../Source/SourceTypeParameterSymbol.cs | 43 +++++++++---------- .../SourceUserDefinedOperatorSymbolBase.cs | 2 +- .../Source/TypeParameterConstraintClause.cs | 27 +++++++++++- .../CSharp/Portable/Symbols/Symbol.cs | 2 +- .../SynthesizedRecordOrdinaryMethod.cs | 2 +- ...ynthesizedSimpleProgramEntryPointSymbol.cs | 2 +- .../Portable/Symbols/TypeParameterBounds.cs | 21 +++++++++ .../Portable/Symbols/TypeWithAnnotations.cs | 7 +-- 23 files changed, 103 insertions(+), 58 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs b/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs index eae61f63d8fe2..b3d49676eab06 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFlags.cs @@ -111,7 +111,7 @@ internal enum BinderFlags : uint InEEMethodBinder = 1 << 30, /// - /// Assume '#nullable disabled'. + /// Assume '#nullable disable' context. /// IgnoreNullableContext = 1u << 31, diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs index 62d1ad84ddd3f..5c531d29fd7cb 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs @@ -234,7 +234,7 @@ private static bool IsNamedTypeAccessible(NamedTypeSymbol type, Symbol within, r { // type parameters are always accessible, so don't check those (so common it's // worth optimizing this). - if (!typeArg.IsAnnotatedOrUnannotatedTypeParameter && !IsSymbolAccessibleCore(typeArg.Type, within, null, out unused, compilation, ref useSiteDiagnostics, basesBeingResolved)) + if (typeArg.DefaultType.TypeKind != TypeKind.TypeParameter && !IsSymbolAccessibleCore(typeArg.Type, within, null, out unused, compilation, ref useSiteDiagnostics, basesBeingResolved)) { return false; } diff --git a/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs b/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs index c9cd081e07f65..619776a18ed1d 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs @@ -86,7 +86,7 @@ public sealed override ImmutableArray TypeParameters get { return _typeParameters; } } - public sealed override ImmutableArray GetTypeParameterConstraintClauses() + public sealed override ImmutableArray GetTypeParameterConstraintClauses(bool canIgnoreNullableContext) => ImmutableArray.Empty; internal override int ParameterCount diff --git a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs index 5ba55d81b0ef5..8ffa7994a37a9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs @@ -314,6 +314,7 @@ internal static ImmutableArray MakeTypeParameterC ImmutableArray typeParameters, TypeParameterListSyntax typeParameterList, SyntaxList constraintClauses, + bool canIgnoreNullableContext, DiagnosticBag diagnostics) { if (typeParameters.Length == 0) @@ -335,7 +336,7 @@ internal static ImmutableArray MakeTypeParameterC IReadOnlyDictionary isValueTypeOverride = null; return binder.BindTypeParameterConstraintClauses(containingSymbol, typeParameters, typeParameterList, constraintClauses, - canIgnoreNullableContext: false, + canIgnoreNullableContext, ref isValueTypeOverride, diagnostics); } diff --git a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs index 0e71c036e19eb..91116072dccda 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs @@ -63,7 +63,7 @@ internal ImmutableArray TypeArgumentsWithDefinitionUseSiteD foreach (var typeArgument in result) { - typeArgument.AddUseSiteDiagnostics(ref useSiteDiagnostics); + AddDefinitionUseSiteDiagnostics(typeArgument, ref useSiteDiagnostics); } return result; @@ -72,10 +72,15 @@ internal ImmutableArray TypeArgumentsWithDefinitionUseSiteD internal TypeWithAnnotations TypeArgumentWithDefinitionUseSiteDiagnostics(int index, ref HashSet useSiteDiagnostics) { var result = TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[index]; - result.AddUseSiteDiagnostics(ref useSiteDiagnostics); + AddDefinitionUseSiteDiagnostics(result, ref useSiteDiagnostics); return result; } + private static void AddDefinitionUseSiteDiagnostics(TypeWithAnnotations type, ref HashSet useSiteDiagnostics) + { + type.DefaultType.OriginalDefinition.AddUseSiteDiagnostics(ref useSiteDiagnostics); + } + /// /// Returns the type symbol that this type was constructed from. This type symbol /// has the same containing type (if any), but has type arguments that are the same diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs index 1d6afbaf4e36d..3ef132b06ac7b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs @@ -415,7 +415,7 @@ internal override bool GenerateDebugInfo internal override bool IsInitOnly => false; - public override ImmutableArray GetTypeParameterConstraintClauses() => ImmutableArray.Empty; + public override ImmutableArray GetTypeParameterConstraintClauses(bool canIgnoreNullableContext) => ImmutableArray.Empty; internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index 85bb56da41f32..de46c5a936b6b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -461,9 +461,9 @@ private ImmutableArray MakeTypeParameters(Diagn return result.ToImmutableAndFree(); } - public override ImmutableArray GetTypeParameterConstraintClauses() + public override ImmutableArray GetTypeParameterConstraintClauses(bool canIgnoreNullableContext) { - if (_lazyTypeParameterConstraints.IsDefault) + if (!_lazyTypeParameterConstraints.HasValue(canIgnoreNullableContext)) { var syntax = Syntax; var diagnostics = DiagnosticBag.GetInstance(); @@ -472,10 +472,11 @@ public override ImmutableArray GetTypeParameterCo TypeParameters, syntax.TypeParameterList, syntax.ConstraintClauses, + canIgnoreNullableContext, diagnostics); lock (_declarationDiagnostics) { - if (_lazyTypeParameterConstraints.IsDefault) + if (!_lazyTypeParameterConstraints.HasValue(canIgnoreNullableContext: constraints.IgnoresNullableContext())) { _declarationDiagnostics.AddRange(diagnostics); _lazyTypeParameterConstraints = constraints; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs index 80c55c0a3ea02..5e7c7ea98a5e5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs @@ -136,7 +136,7 @@ public sealed override ImmutableArray TypeParameters get { return ImmutableArray.Empty; } } - public sealed override ImmutableArray GetTypeParameterConstraintClauses() + public sealed override ImmutableArray GetTypeParameterConstraintClauses(bool canIgnoreNullableContext) => ImmutableArray.Empty; public override RefKind RefKind diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs index 7a269c009f020..6492d6fc28074 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs @@ -136,7 +136,7 @@ public override ImmutableArray TypeParameters } } - public override ImmutableArray GetTypeParameterConstraintClauses() + public override ImmutableArray GetTypeParameterConstraintClauses(bool canIgnoreNullableContext) => ImmutableArray.Empty; public sealed override TypeWithAnnotations ReturnTypeWithAnnotations diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs index 46e5a112bb8e2..4d378acf1aea6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs @@ -96,7 +96,7 @@ public override ImmutableArray TypeParameters get { return ImmutableArray.Empty; } } - public override ImmutableArray GetTypeParameterConstraintClauses() + public override ImmutableArray GetTypeParameterConstraintClauses(bool canIgnoreNullableContext) => ImmutableArray.Empty; public override RefKind RefKind diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs index c57dc1d96a988..339a2ce01300d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs @@ -206,7 +206,7 @@ public sealed override ImmutableArray TypeParameters get { return ImmutableArray.Empty; } } - public sealed override ImmutableArray GetTypeParameterConstraintClauses() + public sealed override ImmutableArray GetTypeParameterConstraintClauses(bool canIgnoreNullableContext) => ImmutableArray.Empty; internal Location Location diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs index 1322a567e229b..71c0016574301 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs @@ -22,7 +22,7 @@ internal abstract class SourceMethodSymbol : MethodSymbol /// array of clauses, indexed by the constrained type parameter in . /// If a type parameter does not have constraints, the corresponding entry in the array is null. /// - public abstract ImmutableArray GetTypeParameterConstraintClauses(); + public abstract ImmutableArray GetTypeParameterConstraintClauses(bool canIgnoreNullableContext); protected static void ReportBadRefToken(TypeSyntax returnTypeSyntax, DiagnosticBag diagnostics) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index 5f0c82c722abf..f64b0eb432090 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -257,12 +257,10 @@ internal TypeParameterConstraintClause GetTypeParameterConstraintClause(bool can if (!clauses.HasValue(canIgnoreNullableContext)) { var diagnostics = DiagnosticBag.GetInstance(); - if (ImmutableInterlocked.InterlockedCompareExchange(ref _lazyTypeParameterConstraints, MakeTypeParameterConstraints(canIgnoreNullableContext, diagnostics), clauses) == clauses) + if (TypeParameterConstraintClauseExtensions.InterlockedUpdate(ref _lazyTypeParameterConstraints, MakeTypeParameterConstraints(canIgnoreNullableContext, diagnostics)) && + _lazyTypeParameterConstraints.HasValue(canIgnoreNullableContext: false)) { - if (_lazyTypeParameterConstraints.HasValue(canIgnoreNullableContext: false)) - { - this.AddDeclarationDiagnostics(diagnostics); - } + this.AddDeclarationDiagnostics(diagnostics); } diagnostics.Free(); clauses = _lazyTypeParameterConstraints; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs index ac798b8150b9e..38e86fa706d4f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs @@ -281,9 +281,9 @@ protected override void CompleteAsyncMethodChecksBetweenStartAndFinish() } } - public override ImmutableArray GetTypeParameterConstraintClauses() + public override ImmutableArray GetTypeParameterConstraintClauses(bool canIgnoreNullableContext) { - if (_lazyTypeParameterConstraints.IsDefault) + if (!_lazyTypeParameterConstraints.HasValue(canIgnoreNullableContext)) { var diagnostics = DiagnosticBag.GetInstance(); var syntax = GetSyntax(); @@ -296,8 +296,10 @@ public override ImmutableArray GetTypeParameterCo TypeParameters, syntax.TypeParameterList, syntax.ConstraintClauses, + canIgnoreNullableContext, diagnostics); - if (ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameterConstraints, constraints)) + if (TypeParameterConstraintClauseExtensions.InterlockedUpdate(ref _lazyTypeParameterConstraints, constraints) && + _lazyTypeParameterConstraints.HasValue(canIgnoreNullableContext: false)) { this.AddDeclarationDiagnostics(diagnostics); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs index d0c7606e1d05a..307c046d1a029 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs @@ -450,7 +450,7 @@ public sealed override ImmutableArray TypeParameters get { return ImmutableArray.Empty; } } - public sealed override ImmutableArray GetTypeParameterConstraintClauses() + public sealed override ImmutableArray GetTypeParameterConstraintClauses(bool canIgnoreNullableContext) => ImmutableArray.Empty; public sealed override RefKind RefKind diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs index 2a44a0e10b6da..b53b2a8c88a5b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs @@ -220,22 +220,19 @@ private TypeParameterBounds GetBounds(ConsList inProgress, Debug.Assert(!inProgress.ContainsReference(this)); Debug.Assert(!inProgress.Any() || ReferenceEquals(inProgress.Head.ContainingSymbol, this.ContainingSymbol)); - var currentBounds = _lazyBounds; - if (!currentBounds.HasValue(canIgnoreNullableContext)) + if (!_lazyBounds.HasValue(canIgnoreNullableContext)) { var diagnostics = DiagnosticBag.GetInstance(); var bounds = this.ResolveBounds(inProgress, canIgnoreNullableContext, diagnostics); - if (ReferenceEquals(Interlocked.CompareExchange(ref _lazyBounds, bounds, currentBounds), currentBounds)) + if (TypeParameterBoundsExtensions.InterlockedUpdate(ref _lazyBounds, bounds) && + _lazyBounds.HasValue(canIgnoreNullableContext: false)) { - if (_lazyBounds.HasValue(canIgnoreNullableContext: false)) - { - this.CheckConstraintTypeConstraints(diagnostics); - this.CheckUnmanagedConstraint(diagnostics); - this.EnsureAttributesFromConstraints(diagnostics); - this.AddDeclarationDiagnostics(diagnostics); - _state.NotePartComplete(CompletionPart.TypeParameterConstraints); - } + this.CheckConstraintTypeConstraints(diagnostics); + this.CheckUnmanagedConstraint(diagnostics); + this.EnsureAttributesFromConstraints(diagnostics); + this.AddDeclarationDiagnostics(diagnostics); + _state.NotePartComplete(CompletionPart.TypeParameterConstraints); } diagnostics.Free(); @@ -611,7 +608,7 @@ public override bool HasConstructorConstraint { get { - var constraints = this.GetDeclaredConstraints(); + var constraints = this.GetDeclaredConstraints(canIgnoreNullableContext: true); return (constraints & TypeParameterConstraintKind.Constructor) != 0; } } @@ -620,7 +617,7 @@ public override bool HasValueTypeConstraint { get { - var constraints = this.GetDeclaredConstraints(); + var constraints = this.GetDeclaredConstraints(canIgnoreNullableContext: true); return (constraints & TypeParameterConstraintKind.AllValueTypeKinds) != 0; } } @@ -629,7 +626,7 @@ public override bool HasReferenceTypeConstraint { get { - var constraints = this.GetDeclaredConstraints(); + var constraints = this.GetDeclaredConstraints(canIgnoreNullableContext: true); return (constraints & TypeParameterConstraintKind.ReferenceType) != 0; } } @@ -638,7 +635,7 @@ public override bool HasNotNullConstraint { get { - var constraints = this.GetDeclaredConstraints(); + var constraints = this.GetDeclaredConstraints(canIgnoreNullableContext: false); return (constraints & TypeParameterConstraintKind.NotNull) != 0; } } @@ -647,7 +644,7 @@ internal override bool? ReferenceTypeConstraintIsNullable { get { - return CalculateReferenceTypeConstraintIsNullable(this.GetDeclaredConstraints()); + return CalculateReferenceTypeConstraintIsNullable(this.GetDeclaredConstraints(canIgnoreNullableContext: false)); } } @@ -655,7 +652,7 @@ internal override bool? IsNotNullable { get { - if ((this.GetDeclaredConstraints() & TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType) != 0) + if ((this.GetDeclaredConstraints(canIgnoreNullableContext: false) & TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType) != 0) { return null; } @@ -668,7 +665,7 @@ public override bool HasUnmanagedTypeConstraint { get { - var constraints = this.GetDeclaredConstraints(); + var constraints = this.GetDeclaredConstraints(canIgnoreNullableContext: true); return (constraints & TypeParameterConstraintKind.Unmanaged) != 0; } } @@ -678,15 +675,15 @@ protected override ImmutableArray ContainerTypeParameters get { return _owner.TypeParameters; } } - private TypeParameterConstraintClause GetTypeParameterConstraintClause() + private TypeParameterConstraintClause GetTypeParameterConstraintClause(bool canIgnoreNullableContext) { - var constraintClauses = _owner.GetTypeParameterConstraintClauses(); + var constraintClauses = _owner.GetTypeParameterConstraintClauses(canIgnoreNullableContext); return constraintClauses.IsEmpty ? TypeParameterConstraintClause.Empty : constraintClauses[Ordinal]; } protected override TypeParameterBounds ResolveBounds(ConsList inProgress, bool canIgnoreNullableContext, DiagnosticBag diagnostics) { - var constraintClause = GetTypeParameterConstraintClause(); + var constraintClause = GetTypeParameterConstraintClause(canIgnoreNullableContext); if (constraintClause.IsEmpty) { return null; @@ -696,9 +693,9 @@ protected override TypeParameterBounds ResolveBounds(ConsList TypeParameters get { return ImmutableArray.Empty; } } - public sealed override ImmutableArray GetTypeParameterConstraintClauses() + public sealed override ImmutableArray GetTypeParameterConstraintClauses(bool canIgnoreNullableContext) => ImmutableArray.Empty; public sealed override RefKind RefKind diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterConstraintClause.cs b/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterConstraintClause.cs index 8506fdf5bc666..be6d8014d7fda 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterConstraintClause.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterConstraintClause.cs @@ -224,12 +224,37 @@ internal static bool HasValue(this ImmutableArray { return false; } - return canIgnoreNullableContext || !constraintClauses.Any(clause => clause.IgnoresNullableContext); + return canIgnoreNullableContext || !constraintClauses.IgnoresNullableContext(); + } + + internal static bool IgnoresNullableContext(this ImmutableArray constraintClauses) + { + return constraintClauses.Any(clause => clause.IgnoresNullableContext); } internal static bool ContainsOnlyEmptyConstraintClauses(this ImmutableArray constraintClauses) { return constraintClauses.All(clause => clause.IsEmpty); } + + // Returns true if constraintClauses was updated with value. + // Returns false if constraintClauses already had a value with expected 'IgnoresNullableContext' + // or was updated to a value with the expected 'IgnoresNullableContext' value on another thread. + internal static bool InterlockedUpdate(ref ImmutableArray constraintClauses, ImmutableArray value) + { + bool canIgnoreNullableContext = value.IgnoresNullableContext(); + while (true) + { + var comparand = constraintClauses; + if (comparand.HasValue(canIgnoreNullableContext)) + { + return false; + } + if (ImmutableInterlocked.InterlockedCompareExchange(ref constraintClauses, value, comparand) == comparand) + { + return true; + } + } + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs index 7a7cf5e84bccf..372dcc2780d1b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs @@ -1018,7 +1018,7 @@ private DiagnosticInfo GetSymbolSpecificUnsupportedMetadataUseSiteErrorInfo() internal bool DeriveUseSiteDiagnosticFromType(ref DiagnosticInfo result, TypeWithAnnotations type, AllowedRequiredModifierType allowedRequiredModifierType) { - return (!type.IsAnnotatedOrUnannotatedTypeParameter && DeriveUseSiteDiagnosticFromType(ref result, type.Type)) || + return (type.DefaultType.TypeKind != TypeKind.TypeParameter && DeriveUseSiteDiagnosticFromType(ref result, type.Type)) || DeriveUseSiteDiagnosticFromCustomModifiers(ref result, type.CustomModifiers, allowedRequiredModifierType); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordOrdinaryMethod.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordOrdinaryMethod.cs index ff6db188f47ba..db26a5e4bd017 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordOrdinaryMethod.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordOrdinaryMethod.cs @@ -40,7 +40,7 @@ protected SynthesizedRecordOrdinaryMethod(SourceMemberContainerTypeSymbol contai protected sealed override ImmutableArray MakeTypeParameters(CSharpSyntaxNode node, DiagnosticBag diagnostics) => ImmutableArray.Empty; - public sealed override ImmutableArray GetTypeParameterConstraintClauses() => ImmutableArray.Empty; + public sealed override ImmutableArray GetTypeParameterConstraintClauses(bool canIgnoreNullableContext) => ImmutableArray.Empty; protected sealed override void PartialMethodChecks(DiagnosticBag diagnostics) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs index ef17789df2390..9a44ae6c555eb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs @@ -177,7 +177,7 @@ protected override void MethodChecks(DiagnosticBag diagnostics) internal override bool IsExpressionBodied => false; - public override ImmutableArray GetTypeParameterConstraintClauses() + public override ImmutableArray GetTypeParameterConstraintClauses(bool canIgnoreNullableContext) => ImmutableArray.Empty; protected override object MethodChecksLockObject => _declaration; diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeParameterBounds.cs b/src/Compilers/CSharp/Portable/Symbols/TypeParameterBounds.cs index a1168974554f5..64ea0406e5e96 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeParameterBounds.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeParameterBounds.cs @@ -6,6 +6,7 @@ using System.Collections.Immutable; using System.Diagnostics; +using System.Threading; namespace Microsoft.CodeAnalysis.CSharp.Symbols { @@ -95,5 +96,25 @@ internal static bool HasValue(this TypeParameterBounds? boundsOpt, bool canIgnor } return canIgnoreNullableContext || !boundsOpt.IgnoresNullableContext; } + + // Returns true if bounds was updated with value. + // Returns false if bounds already had a value with expected 'IgnoresNullableContext' + // or was updated to a value with the expected 'IgnoresNullableContext' value on another thread. + internal static bool InterlockedUpdate(ref TypeParameterBounds? bounds, TypeParameterBounds? value) + { + bool canIgnoreNullableContext = (value?.IgnoresNullableContext == true); + while (true) + { + var comparand = bounds; + if (comparand != TypeParameterBounds.Unset && comparand.HasValue(canIgnoreNullableContext)) + { + return false; + } + if (Interlocked.CompareExchange(ref bounds, value, comparand) == comparand) + { + return true; + } + } + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs b/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs index ae07f06535101..7ba98a821f54f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs @@ -262,8 +262,6 @@ public TypeWithAnnotations WithModifiers(ImmutableArray customMo public ImmutableArray CustomModifiers => _extensions.CustomModifiers; public TypeKind TypeKind => Type.TypeKind; - public bool IsAnnotatedOrUnannotatedTypeParameter => DefaultType.TypeKind == TypeKind.TypeParameter; - public SpecialType SpecialType => _extensions.GetSpecialType(DefaultType); public Cci.PrimitiveTypeCode PrimitiveTypeCode => Type.PrimitiveTypeCode; @@ -430,9 +428,6 @@ public bool IsAtLeastAsVisibleAs(Symbol sym, ref HashSet useSite return NullableUnderlyingTypeOrSelf.IsAtLeastAsVisibleAs(sym, ref useSiteDiagnostics); } - public void AddUseSiteDiagnostics(ref HashSet useSiteDiagnostics) => - DefaultType.OriginalDefinition.AddUseSiteDiagnostics(ref useSiteDiagnostics); - public TypeWithAnnotations SubstituteType(AbstractTypeMap typeMap) => _extensions.SubstituteType(this, typeMap); @@ -473,7 +468,7 @@ internal TypeWithAnnotations SubstituteTypeCore(AbstractTypeMap typeMap) NullableAnnotation newAnnotation; - //Debug.Assert(!IsIndexedTypeParameter(newTypeWithModifiers.Type) || newTypeWithModifiers.NullableAnnotation.IsOblivious()); + Debug.Assert(!IsIndexedTypeParameter(newTypeWithModifiers.DefaultType) || newTypeWithModifiers.NullableAnnotation.IsOblivious()); if (newTypeWithModifiers.NullableAnnotation.IsAnnotated()) { From 6e1fe389350ebff2729a1faa0f3bbbd876ea8189 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Thu, 24 Sep 2020 13:29:47 -0700 Subject: [PATCH 11/11] Fix test --- .../CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index de46c5a936b6b..7457e960cccf1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -476,9 +476,13 @@ public override ImmutableArray GetTypeParameterCo diagnostics); lock (_declarationDiagnostics) { - if (!_lazyTypeParameterConstraints.HasValue(canIgnoreNullableContext: constraints.IgnoresNullableContext())) + canIgnoreNullableContext = constraints.IgnoresNullableContext(); + if (!_lazyTypeParameterConstraints.HasValue(canIgnoreNullableContext)) { - _declarationDiagnostics.AddRange(diagnostics); + if (!canIgnoreNullableContext) + { + _declarationDiagnostics.AddRange(diagnostics); + } _lazyTypeParameterConstraints = constraints; } }