-
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 UnscopedRefAttribute #62783
Support UnscopedRefAttribute #62783
Conversation
2751f37
to
1574564
Compare
3f8b7b2
to
6442a41
Compare
85d875c
to
e1c0157
Compare
e1c0157
to
7884b57
Compare
src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_LifetimeAnnotation.cs
Show resolved
Hide resolved
A possible workaround is to change the method signature to pass the parameter by `ref` instead. | ||
Possible workarounds are: | ||
1. Use `System.Diagnostics.CodeAnalysis.UnscopedRefAttribute` to mark the reference as unscoped. | ||
```csharp |
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: other csharp blocks are left-indented. Not sure whether it's material (does that affect markup rendering on GitHub or docs.microsoft.com), but I'd stick with that to be safe. #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.
Without indenting, subsequent bullets start at 1.
if (_moduleSymbol.Module.HasLifetimeAnnotationAttribute(_handle, out var pair)) | ||
if (_moduleSymbol.Module.HasUnscopedRefAttribute(_handle)) | ||
{ | ||
scope = DeclarationScope.Unscoped; |
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.
Should we mark the parameter as bad if there's both attributes? #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.
@@ -978,7 +982,9 @@ public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences | |||
} | |||
} | |||
|
|||
internal sealed override DeclarationScope Scope => _packedFlags.Scope; | |||
internal sealed override DeclarationScope DeclaredScope => _packedFlags.Scope; |
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: Is PEParameterSymbol.DeclaredScope
reachable (aside from the use on next line)? #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.
It looks like PEParameterSymbol.DeclaredScope
is only used below, so perhaps we could limit DeclaredScope
to source symbols only. That change may be a little involved though, so perhaps that could be done later if necessary.
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.
@@ -162,7 +162,7 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r | |||
|
|||
if (ParameterHelpers.RequiresLifetimeAnnotationAttribute(this)) | |||
{ | |||
AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeLifetimeAnnotationAttribute(this, Scope)); | |||
AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeLifetimeAnnotationAttribute(this, DeclaredScope)); |
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.
I agree, there shouldn't be a difference here, but I think I prefer DeclaredScope
since that is what was declared.
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.
Still nit but want to clarify my reasoning: Declared scope is only a step towards producing the real/effective scope. I think most places should use the real/effective scope unless there's a special reason. When it comes to emitting (like here), clearly we should use the real/effective scope, not its precedessor. If we added any more factors into the computations of real/effective scope, we'd want them to affect what we emit.
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.
Currently, we're emitting implicitly scoped parameters with no [ScopedRef]
attribute. If we use EffectiveScope
for emit, then emitting [UnscopedRef] out int i
(where the EffectiveScope
is Unscoped
) will require ensuring an [UnscopedRef]
attribute is emitted, perhaps by assuming an [UnscopedRef]
attribute was specified in source. Instead, if we use DeclaredScope
here (which is perhaps poorly named since DeclaredScope
is (implicitly )RefScoped
for out int i
), then any [UnscopedRef]
from source will provided the expected result in metadata.
A better alternative might be to emit implicitly scoped parameters with a [ScopedRef]
attribute. Then EffectiveScope
may be simpler here. #63078
@@ -101,7 +101,7 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r | |||
|
|||
if (ParameterHelpers.RequiresLifetimeAnnotationAttribute(this)) | |||
{ | |||
AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeLifetimeAnnotationAttribute(this, Scope)); | |||
AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeLifetimeAnnotationAttribute(this, DeclaredScope)); |
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.
} | ||
return scope; | ||
|
||
static bool hasUnscopedRefAttribute(MethodSymbol? containingMethod, NamedTypeSymbol? containingType) |
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 comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); | ||
comp.VerifyEmitDiagnostics( | ||
// (6,23): error CS9064: UnscopedRefAttribute can only be applied to parameters that are passed by reference and implicitly scoped. |
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.
UnscopedRefAttribute can also be applied to ref
parameters of ref struct
types.
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 the text to cover the two cases as we discussed.
t3 = default; | ||
return ref t3; | ||
} | ||
public ref T F4A([UnscopedRef] scoped out T t4) |
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'd expect an error (this is not "implicitly-scoped") #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.
Done with review pass (iteration 8)
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 Thanks (iteration 8)
} | ||
public class A<T> | ||
{ | ||
public ref T F1A(ref R<T> r1) |
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.
Looking at the verification below (VerifyParameterSymbol(baseType.GetMethod("F1A").Parameters[0], "ref R<System.Int32> r1", RefKind.Ref, DeclarationScope.Unscoped);
), it seems we're not treating this parameter as scoped. But shouldn't it be implicitly-scoped according to the rule "ref
parameters which refer to a ref struct
"? #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.
The change to treat ref
parameters to ref struct
types as implicitly scoped is handled in a subsequent PR: #62778.
@@ -218,6 +218,8 @@ public bool IsWriteOnly | |||
|
|||
internal abstract bool MustCallMethodsDirectly { get; } | |||
|
|||
internal abstract bool HasUnscopedRefAttribute { get; } |
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.
Done review pass (commit 11). I did not look at tests yet.
...p/Portable/Symbols/Attributes/WellKnownAttributeData/ParameterEarlyWellKnownAttributeData.cs
Show resolved
Hide resolved
src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs
Show resolved
Hide resolved
src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs
Show resolved
Hide resolved
src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs
Show resolved
Hide resolved
src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs
Show resolved
Hide resolved
71f521a
to
df68917
Compare
public void UnscopedRefAttribute_Method_01(string type) | ||
{ | ||
var source = | ||
$@"using System.Diagnostics.CodeAnalysis; |
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: raw interpolated strings would be more readable here :).
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 (commit 14).
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 Thanks (iteration 14)
See dotnet/runtime#72074.
See low-level-struct-improvements.md.
Corresponding spec update: dotnet/csharplang#6329
Fixes #62780.
Relates to test plan #59194