-
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
Support unmanaged constraint #24817
Support unmanaged constraint #24817
Conversation
@dotnet/roslyn-compiler @VSadov for review #Closed |
@dotnet/compiler-api for API change. #Closed |
@dotnet/roslyn-compiler @dotnet/compiler-api @VSadov #Closed |
extends [mscorlib]System.Object | ||
{ | ||
.method public hidebysig instance void | ||
M1<valuetype .ctor (class [mscorlib]System.ValueType modreq([mscorlib]System.Runtime.InteropServices.UnmanagedType)) T>() cil managed |
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.
so it looks like we honor "unmanaged" modreq even if not paired with an attribute. That seems ok to me, just wanted to be sure that this is intentional. #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.
@VSadov no we don't. I'll make this test more explicit (it should have the attribute).
Please check other tests that have only the modreq or the attribute and fail at both. #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.
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
@dotnet/roslyn-compiler @dotnet/compiler-api can I get another sign off? #Closed |
if (hasUnmanagedModreq != this._lazyHasIsUnmanagedAttribute.Value()) | ||
{ | ||
// The presence of UnmanagedType modreq has to match the presence of the IsUnmanagedAttribute | ||
Interlocked.CompareExchange(ref _lazyConstraintsUseSiteErrorInfo, new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this), CSDiagnosticInfo.EmptyErrorInfo); |
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 _lazyConstraintsUseSiteErrorInfo, new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this), CSDiagnosticInfo.EmptyErrorInfo); [](start = 28, length = 154)
Why is it the right thing to report an error here? I can imagine an interface constraint combined with managed constraint, it won't be combined with the modifier. #Closed
@@ -234,31 +254,44 @@ public override bool HasConstructorConstraint | |||
{ | |||
get | |||
{ | |||
return (_flags & GenericParameterAttributes.DefaultConstructorConstraint) != 0; | |||
GetDeclaredConstraintTypes(); |
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.
GetDeclaredConstraintTypes(); [](start = 16, length = 29)
Given my comments for GetDeclaredConstraintTypes, I don't think this change is necessary #Closed
} | ||
} | ||
|
||
public override bool HasReferenceTypeConstraint | ||
{ | ||
get | ||
{ | ||
return (_flags & GenericParameterAttributes.ReferenceTypeConstraint) != 0; | ||
GetDeclaredConstraintTypes(); |
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.
GetDeclaredConstraintTypes(); [](start = 16, length = 29)
Given my comments for GetDeclaredConstraintTypes, I don't think this change is necessary #Closed
} | ||
} | ||
|
||
public override bool HasValueTypeConstraint | ||
{ | ||
get | ||
{ | ||
return (_flags & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0; | ||
GetDeclaredConstraintTypes(); |
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.
GetDeclaredConstraintTypes(); [](start = 16, length = 29)
I don't think this change is necessary #Closed
} | ||
} | ||
|
||
public override bool HasValueTypeConstraint | ||
{ | ||
get | ||
{ | ||
return (_flags & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0; | ||
GetDeclaredConstraintTypes(); | ||
return (_lazyFlags & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0 || HasUnmanagedTypeConstraint; |
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.
|| HasUnmanagedTypeConstraint [](start = 101, length = 29)
Presence of the attribute should not imply any constraints, whatever is specified in metadata flags should define constraints. #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.
Agree on this. Valid unmanaged
pattern should always have valuetype
constraint.
The "|| HasUnmanagedTypeConstraint" should not be needed - either there is valuetype
constraint or the type is broken.
In reply to: 172413076 [](ancestors = 172413076)
{ | ||
get | ||
{ | ||
GetDeclaredConstraintTypes(); |
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.
GetDeclaredConstraintTypes(); [](start = 16, length = 29)
I don't think this change is necessary , presence of an attribute can be checked independently as we do everywhere else in PE symbols. #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.
On the other hand, driving result of off an attribute is probably not the right thing, the custom modifier on ValueType is probably a better indicator.
In reply to: 172413190 [](ancestors = 172413190)
} | ||
} | ||
|
||
public override VarianceKind Variance | ||
{ | ||
get | ||
{ | ||
return (VarianceKind)(_flags & GenericParameterAttributes.VarianceMask); | ||
GetDeclaredConstraintTypes(); |
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.
GetDeclaredConstraintTypes(); [](start = 16, length = 29)
I don't think this change is necessary #Closed
out _, | ||
default); | ||
|
||
this._lazyHasIsUnmanagedAttribute = (!isUnmanagedAttribute.IsNil).ToThreeState(); |
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.
this._lazyHasIsUnmanagedAttribute = (!isUnmanagedAttribute.IsNil).ToThreeState(); [](start = 16, length = 81)
We should not rely on GetAttributes to populate this value. #Closed
@@ -803,6 +817,13 @@ private static bool HasDuplicateInterfaces(NamedTypeSymbol type, ConsList<Symbol | |||
return false; | |||
} | |||
|
|||
if (typeParameter.HasUnmanagedTypeConstraint && (typeArgument.IsManagedType || !typeArgument.IsNonNullableValueType())) |
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.
typeArgument.IsManagedType || !typeArgument.IsNonNullableValueType() [](start = 61, length = 68)
It might be better to check these two requirements independently and to report separate errors for them. For example for the IsNonNullableValueType we would report the same error as in the if
below and for the typeArgument.IsManagedType we would say about members. Alternatively we, I think it would be better to say "must be non-nullable value type" instead of saying "cannot be a reference type", unless that is inaccurate. #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.
I prefer the second option. I think it is great to have a separate full error message for unmanaged
constraint for developers to mentally differentiate.
In reply to: 172414637 [](ancestors = 172414637)
syntaxNodeOpt: (CSharpSyntaxNode)context.SyntaxNodeOpt, | ||
diagnostics: context.Diagnostics); | ||
var typeRef = moduleBeingBuilt.Translate( | ||
typeSymbol: moduleBeingBuilt.Compilation.GetSpecialType(SpecialType.System_ValueType), |
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.
moduleBeingBuilt.Compilation.GetSpecialType(SpecialType.System_ValueType) [](start = 32, length = 73)
Where do we check goodness of this type? #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.
Also, it looks like below we are getting this type differently, I think we should use the same approach.
In reply to: 172414868 [](ancestors = 172414868)
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.
Vlad changed that bit. Also we check this during binding: BindTypeParameterConstraints
. We also have tests in case this type is missing.
In reply to: 172415526 [](ancestors = 172415526,172414868)
yield return type.GetTypeRefWithAttributes(this.DeclaringCompilation, | ||
typeRef); | ||
var modifier = CSharpCustomModifier.CreateRequired( | ||
moduleBeingBuilt.Compilation.GetWellKnownType(WellKnownType.System_Runtime_InteropServices_UnmanagedType)); |
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.
moduleBeingBuilt.Compilation.GetWellKnownType(WellKnownType.System_Runtime_InteropServices_UnmanagedType) [](start = 19, length = 106)
Where do we check goodness of this type? #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.
We check this during binding: BindTypeParameterConstraints
. We also have tests in case this type is missing.
In reply to: 172415132 [](ancestors = 172415132)
var modifier = CSharpCustomModifier.CreateRequired( | ||
moduleBeingBuilt.Compilation.GetWellKnownType(WellKnownType.System_Runtime_InteropServices_UnmanagedType)); | ||
|
||
yield return new Cci.TypeReferenceWithAttributes(new Cci.ModifiedTypeReference(typeRef, ImmutableArray.Create<Cci.ICustomModifier>(modifier))); |
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.
yield return new Cci.TypeReferenceWithAttributes(new Cci.ModifiedTypeReference(typeRef, ImmutableArray.Create<Cci.ICustomModifier>(modifier))); [](start = 16, length = 143)
Why are we not emitting other constraint types? For example, interfaces? #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.
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.
Why are we not implying MustHaveDefaultConstructor for HasUnmanagedTypeConstraint? We are implying MustBeValueType. #Closed Refers to: src/Compilers/CSharp/Portable/Emitter/Model/TypeParameterSymbolAdapter.cs:295 in 97d0909. [](commit_id = 97d0909, deletion_comment = False) |
@@ -133,55 +135,80 @@ internal partial class Binder | |||
{ | |||
var typeConstraintSyntax = (TypeConstraintSyntax)syntax; | |||
var typeSyntax = typeConstraintSyntax.Type; | |||
if (typeSyntax.Kind() != SyntaxKind.PredefinedType && !SyntaxFacts.IsName(typeSyntax.Kind())) | |||
var typeSyntaxKind = typeSyntax.Kind(); | |||
if (typeSyntaxKind != SyntaxKind.PredefinedType && typeSyntaxKind != SyntaxKind.PointerType && !SyntaxFacts.IsName(typeSyntax.Kind())) |
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.
typeSyntaxKind != SyntaxKind.PointerType [](start = 79, length = 40)
It is not obvious why we are no longer reporting this error for pointers. Could you elaborate? #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.
Added a comment. This error is reported for pointers during binding typeSyntax (to prevent cycles). So we don't need a duplicate error here.
In reply to: 172416549 [](ancestors = 172416549)
} | ||
if (constraints != 0 || constraintTypes.Any()) | ||
{ | ||
diagnostics.Add(ErrorCode.ERR_UnmanagedConstraintMustBeAlone, typeSyntax.GetLocation()); |
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.
diagnostics.Add(ErrorCode.ERR_UnmanagedConstraintMustBeAlone, typeSyntax.GetLocation()); [](start = 36, length = 88)
Why can't it be combined with an interface? #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 should be able to combine with interfaces. I think we discussed this on the tesplan review as a potential hole/followup issue.
In reply to: 172416658 [](ancestors = 172416658)
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.
@@ -482,6 +482,15 @@ internal override void ForceComplete(SourceLocation locationOpt, CancellationTok | |||
foreach (var typeParameter in this.TypeParameters) | |||
{ | |||
typeParameter.ForceComplete(locationOpt, cancellationToken); | |||
var diagnostics = DiagnosticBag.GetInstance(); |
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.
var diagnostics = DiagnosticBag.GetInstance(); [](start = 28, length = 46)
It feels like all this added code belongs in Force complete for the type parameter. Looks like CompletionPart.TypeParameterConstraints is the relevant part. #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.
Since we decided to enable local functions, the last parameter would be false. Will add that now.
In reply to: 172417776 [](ancestors = 172417776)
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.
All added code belongs in typeParameter.ForceComplete?
In reply to: 175998462 [](ancestors = 175998462,172417776)
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.
Consolidated into the other comment.
In reply to: 177238909 [](ancestors = 177238909,175998462,172417776)
|
||
foreach (var typeParameter in _typeParameters) | ||
{ | ||
if (typeParameter.HasUnmanagedTypeConstraint) |
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 (typeParameter.HasUnmanagedTypeConstraint) [](start = 16, length = 45)
This check belong in completion for the type parameter, this feels like a wrong place for this code. #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.
Since we decided to enable local functions, the last parameter would be false. Will add that now.
In reply to: 172418067 [](ancestors = 172418067)
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.
I thin the added code belongs in completion for the type parameter
In reply to: 175998583 [](ancestors = 175998583,172418067)
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.
Consolidated into the other comment.
In reply to: 177244069 [](ancestors = 177244069,175998583,172418067)
|
||
if (typeParam.HasUnmanagedTypeConstraint) | ||
{ | ||
addTo.Add(ErrorCode.ERR_UnmanagedConstraintWithLocalFunctions, typeParam.GetNonNullSyntaxNode().Location); |
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.
addTo.Add(ErrorCode.ERR_UnmanagedConstraintWithLocalFunctions, typeParam.GetNonNullSyntaxNode().Location); [](start = 20, length = 106)
It feels like typeParam.ForceComplete is the right place to report this error. #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.
Will remove since we decided to enable local functions.
In reply to: 172418267 [](ancestors = 172418267)
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.
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.
Consolidated into the other comment.
In reply to: 177237879 [](ancestors = 177237879,175998657,172418267)
@@ -73,6 +73,14 @@ public override bool HasReferenceTypeConstraint | |||
} | |||
} | |||
|
|||
public override bool HasUnmanagedTypeConstraint |
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.
public override bool HasUnmanagedTypeConstraint [](start = 8, length = 47)
Do we have tests covering all subclasses of the enclosing class? #Resolved
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.
can you please clarify? which scenario do you see uncovered by this property?
In reply to: 172419029 [](ancestors = 172419029)
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.
can you please clarify? which scenario do you see uncovered by this property?
I asked the question whether you covered all leaf classes derived from this one in tests. Did you?
In reply to: 175999068 [](ancestors = 175999068,172419029)
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.
Added tests for retargeting and non-synthesized type parameters. We already have tests for synthesized ones.
In reply to: 176269706 [](ancestors = 176269706,175999068,172419029)
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.
Added tests for retargeting and non-synthesized type parameters. We already have tests for synthesized ones.
What about SubstitutedTypeParameterSymbol?
In reply to: 177881475 [](ancestors = 177881475,176269706,175999068,172419029)
@OmarTawfik I believe there is an actionable feedback for this PR, even though it has been already merged. #Resolved |
@AlekseyTs I'm currently OOF. Created #25261 so that I don't forget to follow up when I get back :) #Closed |
This is arguable. Emitting In reply to: 370671892 [](ancestors = 370671892) Refers to: src/Compilers/CSharp/Portable/Emitter/Model/TypeParameterSymbolAdapter.cs:295 in 97d0909. [](commit_id = 97d0909, deletion_comment = False) |
I guess, we might want to extend the practice to In reply to: 371325898 [](ancestors = 371325898,370671892) Refers to: src/Compilers/CSharp/Portable/Emitter/Model/TypeParameterSymbolAdapter.cs:295 in 97d0909. [](commit_id = 97d0909, deletion_comment = False) |
I wonder if we want to require In reply to: 371326199 [](ancestors = 371326199,371325898,370671892) Refers to: src/Compilers/CSharp/Portable/Emitter/Model/TypeParameterSymbolAdapter.cs:295 in 97d0909. [](commit_id = 97d0909, deletion_comment = False) |
If we require In reply to: 371326436 [](ancestors = 371326436,371326199,371325898,370671892) Refers to: src/Compilers/CSharp/Portable/Emitter/Model/TypeParameterSymbolAdapter.cs:295 in 97d0909. [](commit_id = 97d0909, deletion_comment = False) |
We accept In reply to: 371327262 [](ancestors = 371327262,371326436,371326199,371325898,370671892) Refers to: src/Compilers/CSharp/Portable/Emitter/Model/TypeParameterSymbolAdapter.cs:295 in 97d0909. [](commit_id = 97d0909, deletion_comment = False) |
} | ||
else if (!modifiers.IsDefaultOrEmpty) | ||
{ | ||
// Optional modifiers (also, other required parameters) not allowed on generic parameters |
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.
parameters [](start = 76, length = 10)
What parameters does this refer to? #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.
} | ||
else if (!modifiers.IsDefaultOrEmpty) | ||
{ | ||
// Optional modifiers (also, other required parameters) not allowed on generic parameters |
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.
not allowed on generic parameters [](start = 88, length = 33)
This function as about constraints rather than about generic type parameters #Closed
|
||
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Emit | ||
{ | ||
public class UnmanagedTypeModifierTests : CSharpTestBase |
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.
UnmanagedTypeModifierTests [](start = 17, length = 26)
test with best betternes when features are combined. - Since we can resolve overloading on constraints now. #Resolved
Adds support for the unmanaged constraint in C#.