-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle cycles in IsValueType and IsReferenceType binding constraints #47968
Conversation
@@ -212,6 +218,15 @@ static void adjustConstraintTypes(Symbol container, ImmutableArray<TypeWithAnnot | |||
|
|||
internal static class TypeParameterConstraintClauseExtensions | |||
{ | |||
internal static bool IsSet(this ImmutableArray<TypeParameterConstraintClause> constraintClauses, bool canIgnoreNullableContext) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IsSet [](start = 29, length = 5)
HasValue? #Closed
/// Creates a "early" bound instance that has constraint types set | ||
/// but no other fields. | ||
/// </summary> | ||
public TypeParameterBounds(ImmutableArray<TypeWithAnnotations> constraintTypes) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TypeParameterBounds [](start = 15, length = 19)
Would it make sense to keep this constructor private and to add a factory method with very specific name for consumers to use? #Closed
{ | ||
this.AddDeclarationDiagnostics(diagnostics); | ||
if (_lazyTypeParameterConstraints.HasValue(canIgnoreNullableContext: false)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: formatting #Resolved
{ | ||
var diagnostics = DiagnosticBag.GetInstance(); | ||
if (ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameterConstraints, MakeTypeParameterConstraints(diagnostics))) | ||
if (ImmutableInterlocked.InterlockedCompareExchange(ref _lazyTypeParameterConstraints, MakeTypeParameterConstraints(canIgnoreNullableContext, diagnostics), clauses) == clauses) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
InterlockedCompareExchange [](start = 41, length = 26)
Is there a possibility of a race here? If a thread with canIgnoreNullableContext==true
completes first, is the thread with canIgnoreNullableContext==false
going to return expected value? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
if (ReferenceEquals(Interlocked.CompareExchange(ref _lazyBounds, bounds, TypeParameterBounds.Unset), TypeParameterBounds.Unset)) | ||
if (ReferenceEquals(Interlocked.CompareExchange(ref _lazyBounds, bounds, currentBounds), currentBounds)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interlocked.CompareExchange(ref _lazyBounds, bounds, currentBounds), currentBounds) [](start = 36, length = 83)
Is there a race here? #Closed
Should we add Refers to: src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs:284 in cbbf3b8. [](commit_id = cbbf3b8, deletion_comment = False) |
Should we add bool canIgnoreNullableContext here as well? #Closed Refers to: src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs:464 in cbbf3b8. [](commit_id = cbbf3b8, deletion_comment = False) |
@@ -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)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type [](start = 32, length = 4)
Why not simply access DefaultType
here? #Closed
@@ -63,7 +63,7 @@ internal ImmutableArray<TypeWithAnnotations> TypeArgumentsWithDefinitionUseSiteD | |||
|
|||
foreach (var typeArgument in result) | |||
{ | |||
typeArgument.Type.OriginalDefinition.AddUseSiteDiagnostics(ref useSiteDiagnostics); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type [](start = 29, length = 4)
Why not simply access DefaultType here? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good to avoid repeating type.Default.OriginalDefinition.AddUseSiteDiagnostics()
. Moved the helper method here.
In reply to: 493955260 [](ancestors = 493955260)
@@ -72,7 +72,7 @@ internal ImmutableArray<TypeWithAnnotations> TypeArgumentsWithDefinitionUseSiteD | |||
internal TypeWithAnnotations TypeArgumentWithDefinitionUseSiteDiagnostics(int index, ref HashSet<DiagnosticInfo> useSiteDiagnostics) | |||
{ | |||
var result = TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[index]; | |||
result.Type.OriginalDefinition.AddUseSiteDiagnostics(ref useSiteDiagnostics); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type [](start = 19, length = 4)
Why not simply access DefaultType here? #Closed
@@ -428,6 +430,9 @@ public bool IsAtLeastAsVisibleAs(Symbol sym, ref HashSet<DiagnosticInfo> useSite | |||
return NullableUnderlyingTypeOrSelf.IsAtLeastAsVisibleAs(sym, ref useSiteDiagnostics); | |||
} | |||
|
|||
public void AddUseSiteDiagnostics(ref HashSet<DiagnosticInfo> useSiteDiagnostics) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AddUseSiteDiagnostics [](start = 20, length = 21)
AddDefinitionUseSiteDiagnostics? #Closed
@@ -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()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
//Debug. [](start = 12, length = 8)
Commented out code #Closed
@@ -295,7 +297,7 @@ internal static class ConstraintsHelper | |||
return null; | |||
} | |||
|
|||
var bounds = new TypeParameterBounds(constraintTypes, interfaces, effectiveBaseClass, deducedBaseType); | |||
var bounds = new TypeParameterBounds(constraintTypes, interfaces, effectiveBaseClass, deducedBaseType, ignoresNullableContext); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ignoresNullableContext [](start = 115, length = 22)
It doesn't look like this value actually affects other content in the TypeParameterBounds instance. Why not always use false
and and getting rid of the parameter. #Closed
@@ -680,7 +684,7 @@ private TypeParameterConstraintClause GetTypeParameterConstraintClause() | |||
return constraintClauses.IsEmpty ? TypeParameterConstraintClause.Empty : constraintClauses[Ordinal]; | |||
} | |||
|
|||
protected override TypeParameterBounds ResolveBounds(ConsList<TypeParameterSymbol> inProgress, DiagnosticBag diagnostics) | |||
protected override TypeParameterBounds ResolveBounds(ConsList<TypeParameterSymbol> inProgress, bool canIgnoreNullableContext, DiagnosticBag diagnostics) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
canIgnoreNullableContext [](start = 108, length = 24)
It looks like the parameter is no longer used. Is this intentional? #Closed
|
||
// 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
another [](start = 89, length = 7)
"this"? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM (iteration 11), assuming CI is passing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, just had some comment nits to pick. No need to address in this PR if it is not practical
} | ||
|
||
// Returns true if bounds was updated with value. | ||
// Returns false if bounds already had a value with expected 'IgnoresNullableContext' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: consider wording this as "a value with sufficient IgnoresNullableContext". Similar on the next line.
Something to indicate that when the caller requests a bounds that ignores nullable context, the symbol may provide a bounds that doesn't ignore nullable context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated comment in #48041.
internal static bool ContainsOnlyEmptyConstraintClauses(this ImmutableArray<TypeParameterConstraintClause> 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' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment here as in TypeParameterBounds.cs
.
Revert 1f53c66 (dotnet@1f53c66)
Revert 1f53c66 (dotnet@1f53c66)
Ignore
#nullable
context when evaluatingIsValueType
andIsReferenceType
.Fixes #45713.
Fixes #45863.
Binding constraints in the repros results in a call to
TypeWithAnnotations.SubstituteTypeCore()
which callsIsTypeParameterDisallowingAnnotationsInCSharp8()
which callsSourceTypeParameterSymbol.IsValueType
. The call toIsValueType
requires binding constraints. It seems unnecessary to haveIsValueType
orIsReferenceType
depend on nullability though.