Browse Source

Add support for ScopedRefAttribute

pull/2992/head
Daniel Grunwald 2 years ago
parent
commit
9359d47c0b
  1. 65
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefFields.cs
  2. 5
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  3. 13
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs
  4. 1
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  5. 1
      ICSharpCode.Decompiler/CSharp/Transforms/EscapeInvalidIdentifiers.cs
  6. 3
      ICSharpCode.Decompiler/DecompilerSettings.cs
  7. 2
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  8. 6
      ICSharpCode.Decompiler/TypeSystem/IParameter.cs
  9. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs
  10. 4
      ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
  11. 19
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs

65
ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefFields.cs

@ -2,18 +2,61 @@ using System; @@ -2,18 +2,61 @@ using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
internal class LifetimeTests
{
private static int staticField;
public Span<int> CreateWithoutCapture(scoped ref int value)
{
// Okay: value is not captured
return new Span<int>(ref staticField);
}
public Span<int> CreateAndCapture(ref int value)
{
// Okay: value Rule 3 specifies that the safe-to-escape be limited to the ref-safe-to-escape
// of the ref argument. That is the *calling method* for value hence this is not allowed.
return new Span<int>(ref value);
}
public Span<int> ScopedRefSpan(scoped ref Span<int> span)
{
return span;
}
public Span<int> ScopedSpan(scoped Span<int> span)
{
return default(Span<int>);
}
public void OutSpan(out Span<int> span)
{
span = default(Span<int>);
}
public void Calls()
{
int value = 0;
Span<int> span = CreateWithoutCapture(ref value);
//span = CreateAndCapture(ref value); -- would need scoped local, not yet implemented
span = ScopedRefSpan(ref span);
span = ScopedSpan(span);
OutSpan(out span);
}
}
internal ref struct RefFields
{
private ref int Field0;
private ref readonly int Field1;
private readonly ref int Field2;
private readonly ref readonly int Field3;
public ref int Field0;
public ref readonly int Field1;
public readonly ref int Field2;
public readonly ref readonly int Field3;
public int PropertyAccessingRefFieldByValue {
get {
get {
return Field0;
}
set {
set {
Field0 = value;
}
}
@ -32,5 +75,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -32,5 +75,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine("No inlining");
field.ToString();
}
public RefFields(ref int v)
{
Field0 = ref v;
Field1 = ref v;
Field2 = ref v;
Field3 = ref v;
}
}
}
}

5
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -2589,11 +2589,6 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -2589,11 +2589,6 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
Space();
break;
}
if (parameterDeclaration.IsValueScoped)
{
WriteKeyword(ParameterDeclaration.ValueScopedRole);
Space();
}
parameterDeclaration.Type.AcceptVisitor(this);
if (!parameterDeclaration.Type.IsNull && !string.IsNullOrEmpty(parameterDeclaration.Name))
{

13
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs

@ -26,6 +26,8 @@ @@ -26,6 +26,8 @@
#nullable enable
using System;
namespace ICSharpCode.Decompiler.CSharp.Syntax
{
public enum ParameterModifier
@ -46,6 +48,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -46,6 +48,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public static readonly TokenRole RefModifierRole = new TokenRole("ref");
public static readonly TokenRole OutModifierRole = new TokenRole("out");
public static readonly TokenRole InModifierRole = new TokenRole("in");
[Obsolete("C# 11 preview: \"ref scoped\" no longer supported")]
public static readonly TokenRole ValueScopedRole = new TokenRole("scoped");
public static readonly TokenRole ParamsModifierRole = new TokenRole("params");
@ -102,7 +105,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -102,7 +105,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
bool hasThisModifier;
bool isRefScoped, isValueScoped;
bool isRefScoped;
public CSharpTokenNode ThisKeyword {
get {
@ -130,12 +133,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -130,12 +133,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
}
[Obsolete("C# 11 preview: \"ref scoped\" no longer supported")]
public bool IsValueScoped {
get { return isValueScoped; }
set {
ThrowIfFrozen();
isValueScoped = value;
}
get { return false; }
set { }
}
ParameterModifier parameterModifier;

1
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -1655,7 +1655,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1655,7 +1655,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
decl.ParameterModifier = ParameterModifier.Params;
}
decl.IsRefScoped = parameter.Lifetime.RefScoped;
decl.IsValueScoped = parameter.Lifetime.ValueScoped;
if (ShowAttributes)
{
decl.Attributes.AddRange(ConvertAttributes(parameter.GetAttributes()));

1
ICSharpCode.Decompiler/CSharp/Transforms/EscapeInvalidIdentifiers.cs

@ -167,6 +167,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -167,6 +167,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
"System.Runtime.CompilerServices.NullableContextAttribute",
"System.Runtime.CompilerServices.NativeIntegerAttribute",
"System.Runtime.CompilerServices.RefSafetyRulesAttribute",
"System.Runtime.CompilerServices.ScopedRefAttribute",
"Microsoft.CodeAnalysis.EmbeddedAttribute",
};

3
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -360,8 +360,7 @@ namespace ICSharpCode.Decompiler @@ -360,8 +360,7 @@ namespace ICSharpCode.Decompiler
bool lifetimeAnnotations = true;
/// <summary>
/// Use C# 9 <c>delegate* unmanaged</c> types.
/// If this option is disabled, function pointers will instead be decompiled with type `IntPtr`.
/// Use C# 11 <c>scoped</c> modifier.
/// </summary>
[Category("C# 11.0 / VS 2022.4")]
[Description("DecompilerSettings.LifetimeAnnotations")]

2
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -120,7 +120,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -120,7 +120,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
FunctionPointers = 0x2000,
/// <summary>
/// Allow C# 11 scoped annotation. If this option is not enabled, LifetimeAnnotationAttribute
/// Allow C# 11 scoped annotation. If this option is not enabled, ScopedRefAttribute
/// will be reported as custom attribute.
/// </summary>
LifetimeAnnotations = 0x4000,

6
ICSharpCode.Decompiler/TypeSystem/IParameter.cs

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
@ -36,7 +37,12 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -36,7 +37,12 @@ namespace ICSharpCode.Decompiler.TypeSystem
public struct LifetimeAnnotation
{
/// <summary>
/// C# 11 scoped annotation: "scoped ref" (ScopedRefAttribute)
/// </summary>
public bool RefScoped;
[Obsolete("C# 11 preview: \"ref scoped\" no longer supported")]
public bool ValueScoped;
}

2
ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs

@ -252,7 +252,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -252,7 +252,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
case "NullableContextAttribute":
return (options & TypeSystemOptions.NullabilityAnnotations) != 0
&& (target == SymbolKind.TypeDefinition || IsMethodLike(target));
case "LifetimeAnnotationAttribute":
case "ScopedRefAttribute":
return (options & TypeSystemOptions.LifetimeAnnotations) != 0
&& (target == SymbolKind.Parameter);
default:

4
ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs

@ -94,7 +94,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -94,7 +94,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
CallerMemberName,
CallerFilePath,
CallerLineNumber,
LifetimeAnnotation,
ScopedRef,
// Type parameter attributes:
IsUnmanaged,
@ -172,7 +172,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -172,7 +172,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CallerMemberNameAttribute)),
new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CallerFilePathAttribute)),
new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CallerLineNumberAttribute)),
new TopLevelTypeName("System.Runtime.CompilerServices", "LifetimeAnnotationAttribute"),
new TopLevelTypeName("System.Runtime.CompilerServices", "ScopedRefAttribute"),
// Type parameter attributes:
new TopLevelTypeName("System.Runtime.CompilerServices", "IsUnmanagedAttribute"),
// Marshalling attributes:

19
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs

@ -124,25 +124,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -124,25 +124,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
var metadata = module.metadata;
var parameterDef = metadata.GetParameter(handle);
foreach (var h in parameterDef.GetCustomAttributes())
if (parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.ScopedRef))
{
var custom = metadata.GetCustomAttribute(h);
if (!custom.IsKnownAttribute(metadata, KnownAttribute.LifetimeAnnotation))
continue;
var value = custom.DecodeValue(module.TypeProvider);
if (value.FixedArguments.Length != 2)
continue;
if (value.FixedArguments[0].Value is bool refScoped
&& value.FixedArguments[1].Value is bool valueScoped)
{
return new LifetimeAnnotation {
RefScoped = refScoped,
ValueScoped = valueScoped
};
}
return new LifetimeAnnotation { RefScoped = true };
}
return default;
}
}

Loading…
Cancel
Save