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

VB: Recognize and check an unmanaged constraint #75665

Merged
merged 5 commits into from
Nov 5, 2024

Conversation

AlekseyTs
Copy link
Contributor

No description provided.

@AlekseyTs AlekseyTs requested review from a team as code owners October 29, 2024 13:39
@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged Issues and PRs which have not yet been triaged by a lead label Oct 29, 2024
@dotnet-policy-service dotnet-policy-service bot added the Needs API Review Needs to be reviewed by the API review council label Oct 29, 2024
@AlekseyTs
Copy link
Contributor Author

@dotnet/roslyn-compiler Please review

@jaredpar jaredpar added this to the 17.13 milestone Oct 29, 2024
@AlekseyTs
Copy link
Contributor Author

@dotnet/roslyn-compiler Please review

@jaredpar jaredpar assigned jaredpar, 333fred and cston and unassigned jaredpar Oct 30, 2024
@jaredpar
Copy link
Member

@333fred, @cston PTAL

@AlekseyTs AlekseyTs requested review from 333fred and cston October 30, 2024 22:34
if (_diagnostics.Add(diagnosticInfo) && diagnosticInfo?.Severity == DiagnosticSeverity.Error)
{
RecordPresenceOfAnError();
}
Copy link
Member

@cston cston Oct 31, 2024

Choose a reason for hiding this comment

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

This is repeated several times. Consider extracting a helper method. #Resolved

/// <summary>
/// True if this type or some containing type has type parameters.
/// </summary>
public bool IsGenericType { get; }
Copy link
Member

@cston cston Oct 31, 2024

Choose a reason for hiding this comment

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

public

Is the public qualifier needed? #Resolved

/// without looking at its fields and Unknown otherwise.
/// Also returns whether or not the given type is generic.
/// </summary>
public static (ThreeState isManaged, bool hasGenerics) IsManagedTypeHelper(INamedTypeSymbolInternal type)
Copy link
Member

@cston cston Oct 31, 2024

Choose a reason for hiding this comment

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

IsManagedTypeHelper

Consider use a top-level class and make this an extension method. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Consider use a top-level class and make this an extension method.

I prefer to keep the code as is. I do not foresee us using this helper much in the future. Therefore, in my opinion there is no good reason to "pollute" name lookup binding space with it. If we could use Default Interface Implementations feature in our code, I would make this method a member of INamedTypeSymbolInternal.

End Function

' <summary>
' IsManagedType is simple for most named types:
Copy link
Member

@cston cston Oct 31, 2024

Choose a reason for hiding this comment

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

IsManagedType

GetManagedKind()? #Resolved

End If

useSiteInfo.Add(field.GetUseSiteInfo())
Dim fieldType As TypeSymbol = field.Type.GetTupleUnderlyingTypeOrSelf()
Copy link
Member

@cston cston Oct 31, 2024

Choose a reason for hiding this comment

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

GetTupleUnderlyingTypeOrSelf()

Are we testing a case where this call makes a difference? #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are we testing a case where this call makes a difference?

I cannot think of a situation when it would make an observable difference in behavior. That is not to say that that it is impossible to run into one in some edge error scenario. Given the difference in the way C# and VB compilers represent tuple symbols, I prefer to be conservative and explicitly unwrap tuple types.

Dim namedTypeSymbol = TryCast(typeSymbol, NamedTypeSymbol)
Return namedTypeSymbol IsNot Nothing AndAlso
namedTypeSymbol.Name = "UnmanagedType" AndAlso
namedTypeSymbol.Arity = 0 AndAlso
Copy link
Member

@cston cston Oct 31, 2024

Choose a reason for hiding this comment

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

namedTypeSymbol.Arity = 0 AndAlso

It looks like the C# version and related C# extension methods are not checking Arity == 0. Should we add that check to those methods? #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, added a test and opened an issue to follow up - #75681

@cston
Copy link
Member

cston commented Oct 31, 2024

    Private Function CreateBinderForTypeDeclaration() As Binder

What does "ForTypeDeclaration" mean here? (It looks like the resulting binder is more specific than for the containing type.) Should this method be named "ForMemberDeclaration" instead?

And would it make sense to pass the BindingLocation in as an optional argument to allow this method to be used in ComputeType as well? #Resolved


Refers to: src/Compilers/VisualBasic/Portable/Symbols/Source/SourceEventSymbol.vb:741 in a55f298. [](commit_id = a55f298, deletion_comment = False)

@cston
Copy link
Member

cston commented Oct 31, 2024

    Private Function CreateBinderForTypeDeclaration() As Binder

Same questions as in SourceEventSymbol:

Should this method be named "ForMemberDeclaration" instead?

And would it make sense to pass the BindingLocation in as an optional argument to allow this method to be used in ComputeType as well? #Resolved


Refers to: src/Compilers/VisualBasic/Portable/Symbols/Source/SourcePropertySymbol.vb:1020 in a55f298. [](commit_id = a55f298, deletion_comment = False)

@AlekseyTs
Copy link
Contributor Author

    Private Function CreateBinderForTypeDeclaration() As Binder

What does "ForTypeDeclaration" mean here? (It looks like the resulting binder is more specific than for the containing type.) Should this method be named "ForMemberDeclaration" instead?

And would it make sense to pass the BindingLocation in as an optional argument to allow this method to be used in ComputeType as well?

This is an existing method that new code is not using. I do not intend to rename it or change its behavior.


In reply to: 2450165726


Refers to: src/Compilers/VisualBasic/Portable/Symbols/Source/SourceEventSymbol.vb:741 in a55f298. [](commit_id = a55f298, deletion_comment = False)

@AlekseyTs
Copy link
Contributor Author

    Private Function CreateBinderForTypeDeclaration() As Binder

Same response


In reply to: 2450168231


Refers to: src/Compilers/VisualBasic/Portable/Symbols/Source/SourcePropertySymbol.vb:1020 in a55f298. [](commit_id = a55f298, deletion_comment = False)

o.M(New Program())
~
]]></expected>)
End Sub
Copy link
Member

@cston cston Oct 31, 2024

Choose a reason for hiding this comment

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

End Sub

Are we testing type parameters?

Sub M1(Of T1, T2 As Class, T3 As Structure)(t1 as T1, t2 As T2, t3 As T3)
    Dim o As New Test
    o.M(t1)
    o.M(t2)
    o.M(t3)
End Sub
``` #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are we testing type parameters?

Type parameters are covered in UnmanagedCheck_TypeParameter and indirectly on several other tests.

Dim s5 = compilation.GetTypeByMetadataName("S5`1")
Assert.Equal(ManagedKind.Managed, s1.GetManagedKind(Nothing))
Assert.Equal(ManagedKind.Unmanaged, s2.GetManagedKind(Nothing))
Assert.Equal(ManagedKind.UnmanagedWithGenerics, s5.GetManagedKind(Nothing))
Copy link
Member

@cston cston Oct 31, 2024

Choose a reason for hiding this comment

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

Assert.Equal

Check S3 and S4 as well? #Resolved

@AlekseyTs
Copy link
Contributor Author

@333fred, @dotnet/roslyn-compiler For the second review.

1 similar comment
@AlekseyTs
Copy link
Contributor Author

@333fred, @dotnet/roslyn-compiler For the second review.

Copy link
Member

@333fred 333fred left a comment

Choose a reason for hiding this comment

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

Generally lgtm. Couple of small comments.

Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean
Get
EnsureAllConstraintsAreResolved()
Return CType(Volatile.Read(_lazyHasIsUnmanagedConstraint), ThreeState).Value()
Copy link
Member

@333fred 333fred Nov 1, 2024

Choose a reason for hiding this comment

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

I don't think we actually need the Volatile.Read; the memory barrier from ImmutableInterlocked.InterlockedInitialize in ResolveConstraints should prevent writes to _lazyHasIsUnmanagedConstraint from being reordered after writes to _lazyConstraintType, and EnsureAllConstaintsAreResolved won't skip calling ResolveConstraints unless this thread has already observed that _lazyConstraintType has been initialized. And if the ordering in that method ever changes (ie, the write to _lazyConstraintType is moved before the write to _lazyHasIsUnmanagedConstraint), then this Volatile.Read won't actually help anyways. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I prefer to keep the Volatile.Read. The "distance" between this operation and the interlocked exchange is several method calls and some of them are virtual calls. In one of the methods we check value of _lazyConstraintTypes without any memory barrier operation in the same body. Assuming that that read and this _lazyHasIsUnmanagedConstraint can be reordered, we might see the old value of _lazyHasIsUnmanagedConstraint here while the _lazyConstraintTypes check in EnsureAllConstraintsAreResolved sees the new value of _lazyConstraintTypes. If that is too paranoid, that is fine with me, Volatile.Read won't make things worse.

Copy link
Member

Choose a reason for hiding this comment

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

Assuming that that read and this _lazyHasIsUnmanagedConstraint can be reordered, we might see the old value of _lazyHasIsUnmanagedConstraint here while the _lazyConstraintTypes check in EnsureAllConstraintsAreResolved sees the new value of _lazyConstraintTypes.

The issue with this reasoning is that that is not what Volatile.Read does. Volatile.Read prevents any reads or writes after the read from being ordered before this read. It does not prevent reads that occur before this read from being reordered to after this read. If that is your concern, you would need Thread.MemoryBarrier() in between the method call and the read of _lazyHasIsUnmanagedConstraint.

Copy link
Contributor Author

@AlekseyTs AlekseyTs Nov 1, 2024

Choose a reason for hiding this comment

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

I remain unconvinced. The documentation says: "Reads the value of the specified field." That is exactly what I am going after. The call reads the specified address. I am not concerned about what happens before or after the call. I am concerned about that specific read happening exactly when the call is executed. Not before the call, not after the call.

Copy link
Member

Choose a reason for hiding this comment

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

Alright, after some extensive offline conversation here, I've opened #75759 to follow up on the race condition here; given that it's present in C# version as well, and has been present for a while, I'm going to approve for now, but we'll be following up on this next week to discuss a better long-term approach for the compiler.

@AlekseyTs AlekseyTs requested a review from 333fred November 1, 2024 21:34
@AlekseyTs AlekseyTs merged commit f97c50e into dotnet:main Nov 5, 2024
28 checks passed
@dotnet-policy-service dotnet-policy-service bot modified the milestones: 17.13, Next Nov 5, 2024
@@ -21,6 +21,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
VisualBasic15_5 = 1505
VisualBasic16 = 1600
VisualBasic16_9 = 1609
VisualBasic17_13 = 1713
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Is there an explanation as to what this enables VB to do?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers Language-VB Needs API Review Needs to be reviewed by the API review council untriaged Issues and PRs which have not yet been triaged by a lead
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants