Skip to content
Permalink
Browse files

Fix #1943: "ref readonly" returns from properties/indexers

  • Loading branch information
dgrunwald committed Mar 7, 2020
1 parent a49f55a commit 3bf9f7c301e2c6e171af27c4e560c4956ccb58cb
@@ -62,6 +62,7 @@ public static void Test()
public struct NormalStruct
{
private readonly int dummy;
private int[] arr;

public int Property {
get {
@@ -96,8 +97,17 @@ public struct NormalStruct
}
}

public ref int RefProperty => ref arr[0];
public ref readonly int RefReadonlyProperty => ref arr[0];
public readonly ref int ReadonlyRefProperty => ref arr[0];
public readonly ref readonly int ReadonlyRefReadonlyProperty => ref arr[0];
#endif

public ref readonly int this[in int index] => ref arr[index];

public event EventHandler NormalEvent;

#if CS80
public readonly event EventHandler ReadOnlyEvent {
add {
}
@@ -1574,6 +1574,9 @@ PropertyDeclaration ConvertProperty(IProperty property)
decl.AddAnnotation(new MemberResolveResult(null, property));
}
decl.ReturnType = ConvertType(property.ReturnType);
if (property.ReturnTypeIsRefReadOnly && decl.ReturnType is ComposedType ct && ct.HasRefSpecifier) {
ct.HasReadOnlySpecifier = true;
}
decl.Name = property.Name;
decl.Getter = ConvertAccessor(property.Getter, property.Accessibility, false);
decl.Setter = ConvertAccessor(property.Setter, property.Accessibility, true);
@@ -1601,6 +1604,9 @@ IndexerDeclaration ConvertIndexer(IProperty indexer)
decl.AddAnnotation(new MemberResolveResult(null, indexer));
}
decl.ReturnType = ConvertType(indexer.ReturnType);
if (indexer.ReturnTypeIsRefReadOnly && decl.ReturnType is ComposedType ct && ct.HasRefSpecifier) {
ct.HasReadOnlySpecifier = true;
}
foreach (IParameter p in indexer.Parameters) {
decl.Parameters.Add(ConvertParameter(p));
}
@@ -1695,6 +1701,9 @@ EntityDeclaration ConvertOperator(IMethod op)
decl.Modifiers = GetMemberModifiers(op);
decl.OperatorType = opType.Value;
decl.ReturnType = ConvertType(op.ReturnType);
if (op.ReturnTypeIsRefReadOnly && decl.ReturnType is ComposedType ct && ct.HasRefSpecifier) {
ct.HasReadOnlySpecifier = true;
}
foreach (IParameter p in op.Parameters) {
decl.Parameters.Add(ConvertParameter(p));
}
@@ -144,7 +144,10 @@ public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclarati
Name = Pattern.AnyString,
PrivateImplementationType = new AnyNodeOrNull(),
ReturnType = new AnyNode(),
Getter = new Accessor() { Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) } }
Getter = new Accessor() {
Modifiers = Modifiers.Any,
Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) }
}
};

static readonly IndexerDeclaration CalculatedGetterOnlyIndexerPattern = new IndexerDeclaration() {
@@ -153,14 +156,25 @@ public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclarati
PrivateImplementationType = new AnyNodeOrNull(),
Parameters = { new Repeat(new AnyNode()) },
ReturnType = new AnyNode(),
Getter = new Accessor() { Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) } }
Getter = new Accessor() {
Modifiers = Modifiers.Any,
Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) }
}
};

/// <summary>
/// Modifiers that are emitted on accessors, but can be moved to the property declaration.
/// </summary>
const Modifiers movableModifiers = Modifiers.Readonly;

void SimplifyPropertyDeclaration(PropertyDeclaration propertyDeclaration)
{
var m = CalculatedGetterOnlyPropertyPattern.Match(propertyDeclaration);
if (!m.Success)
return;
if ((propertyDeclaration.Getter.Modifiers & ~movableModifiers) != 0)
return;
propertyDeclaration.Modifiers |= propertyDeclaration.Getter.Modifiers;
propertyDeclaration.ExpressionBody = m.Get<Expression>("expression").Single().Detach();
propertyDeclaration.Getter.Remove();
}
@@ -170,6 +184,9 @@ void SimplifyIndexerDeclaration(IndexerDeclaration indexerDeclaration)
var m = CalculatedGetterOnlyIndexerPattern.Match(indexerDeclaration);
if (!m.Success)
return;
if ((indexerDeclaration.Getter.Modifiers & ~movableModifiers) != 0)
return;
indexerDeclaration.Modifiers |= indexerDeclaration.Getter.Modifiers;
indexerDeclaration.ExpressionBody = m.Get<Expression>("expression").Single().Detach();
indexerDeclaration.Getter.Remove();
}
@@ -228,7 +228,7 @@ ProjectId WriteProjectFile(TextWriter writer, IEnumerable<Tuple<string, string>>
}
w.WriteElementString("WarningLevel", "4");
w.WriteElementString("AllowUnsafeBlocks", "True");

if (StrongNameKeyFile != null) {
w.WriteElementString("SignAssembly", "True");
w.WriteElementString("AssemblyOriginatorKeyFile", Path.GetFileName(StrongNameKeyFile));
@@ -463,7 +463,7 @@ CSharpDecompiler CreateDecompiler(DecompilerTypeSystem ts)
} catch (EndOfStreamException) {
// if the .resources can't be decoded, just save them as-is
}
}
}
using (FileStream fs = new FileStream(Path.Combine(targetDirectory, fileName), FileMode.Create, FileAccess.Write)) {
entryStream.CopyTo(fs);
}
@@ -515,7 +515,10 @@ public static string CleanUpFileName(string text)
string name = b.ToString();
if (IsReservedFileSystemName(name))
return name + "_";
return name;
else if (name == ".")
return "_";
else
return name;
}

static bool IsReservedFileSystemName(string name)
@@ -30,5 +30,10 @@ public interface IProperty : IParameterizedMember
IMethod Setter { get; }

bool IsIndexer { get; }

/// <summary>
/// Gets whether the return type is 'ref readonly'.
/// </summary>
bool ReturnTypeIsRefReadOnly { get; }
}
}
@@ -73,6 +73,10 @@ public enum SymbolKind : byte
/// Constraint on a type parameter.
/// </summary>
Constraint,
/// <summary>
/// Return type. Not actually an ISymbol implementation; but can appear as attribut target.
/// </summary>
ReturnType,
}

/// <summary>
@@ -208,6 +208,10 @@ bool IgnoreAttribute(IType attributeType, SymbolKind target)
case SymbolKind.Method:
case SymbolKind.Accessor:
return (options & TypeSystemOptions.ReadOnlyMethods) != 0;
case SymbolKind.ReturnType:
case SymbolKind.Property:
case SymbolKind.Indexer:
return true; // "ref readonly" is currently always active
default:
return false;
}
@@ -400,7 +400,7 @@ public IEnumerable<IAttribute> GetReturnTypeAttributes()
var retParam = metadata.GetParameter(parameters.First());
if (retParam.SequenceNumber == 0) {
b.AddMarshalInfo(retParam.GetMarshallingDescriptor());
b.Add(retParam.GetCustomAttributes(), symbolKind);
b.Add(retParam.GetCustomAttributes(), SymbolKind.ReturnType);
}
}
return b.Build();
@@ -113,6 +113,13 @@ public override string ToString()
}
}

public bool ReturnTypeIsRefReadOnly {
get {
var propertyDef = module.metadata.GetPropertyDefinition(propertyHandle);
return propertyDef.GetCustomAttributes().HasKnownAttribute(module.metadata, KnownAttribute.IsReadOnly);
}
}

private void DecodeSignature()
{
var propertyDef = module.metadata.GetPropertyDefinition(propertyHandle);
@@ -65,5 +65,7 @@ public SpecializedProperty(IProperty propertyDefinition, TypeParameterSubstituti
public bool IsIndexer {
get { return propertyDefinition.IsIndexer; }
}

public bool ReturnTypeIsRefReadOnly => propertyDefinition.ReturnTypeIsRefReadOnly;
}
}

0 comments on commit 3bf9f7c

Please sign in to comment.
You can’t perform that action at this time.