Skip to content
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

Merged
merged 11 commits into from
Sep 25, 2020

Conversation

cston
Copy link
Member

@cston cston commented Sep 23, 2020

Ignore #nullable context when evaluating IsValueType and IsReferenceType.

Fixes #45713.
Fixes #45863.

Binding constraints in the repros results in a call to TypeWithAnnotations.SubstituteTypeCore() which calls IsTypeParameterDisallowingAnnotationsInCSharp8() which calls SourceTypeParameterSymbol.IsValueType. The call to IsValueType requires binding constraints. It seems unnecessary to have IsValueType or IsReferenceType depend on nullability though.

@cston cston requested a review from a team as a code owner September 23, 2020 05:17
@@ -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)
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 23, 2020

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)
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 23, 2020

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))
Copy link
Contributor

@RikkiGibson RikkiGibson Sep 23, 2020

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)
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 23, 2020

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, thanks.


In reply to: 493779200 [](ancestors = 493779200)


if (ReferenceEquals(Interlocked.CompareExchange(ref _lazyBounds, bounds, TypeParameterBounds.Unset), TypeParameterBounds.Unset))
if (ReferenceEquals(Interlocked.CompareExchange(ref _lazyBounds, bounds, currentBounds), currentBounds))
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 23, 2020

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

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Sep 23, 2020

    public override ImmutableArray<TypeParameterConstraintClause> GetTypeParameterConstraintClauses()

Should we add bool canIgnoreNullableContext here as well? #Closed


Refers to: src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs:284 in cbbf3b8. [](commit_id = cbbf3b8, deletion_comment = False)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Sep 23, 2020

    public override ImmutableArray<TypeParameterConstraintClause> GetTypeParameterConstraintClauses()

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))
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 23, 2020

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);
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 23, 2020

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

Copy link
Member Author

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);
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 23, 2020

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) =>
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 23, 2020

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());
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 23, 2020

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);
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 24, 2020

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)
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 24, 2020

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.
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 24, 2020

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

Copy link
Contributor

@AlekseyTs AlekseyTs left a 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.

Copy link
Contributor

@RikkiGibson RikkiGibson left a 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'
Copy link
Contributor

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.

Copy link
Member Author

@cston cston Sep 25, 2020

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'
Copy link
Contributor

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.

@cston cston merged commit 1f53c66 into dotnet:release/dev16.8 Sep 25, 2020
@cston cston deleted the 47513 branch September 25, 2020 00:05
@cston cston added this to the 16.8.P4 milestone Sep 25, 2020
AlekseyTs added a commit to AlekseyTs/roslyn that referenced this pull request Nov 5, 2020
AlekseyTs added a commit to AlekseyTs/roslyn that referenced this pull request Nov 12, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants