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

Support byref fields in GenAPI #9487

Merged
merged 4 commits into from
Jun 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,7 @@ public static bool IsOrContainsReferenceType(this ITypeReference type)
if (resolvedType.IsReferenceType)
return true;

// ByReference<T> is a special type understood by runtime to hold a ref T.
if (resolvedType.AreGenericTypeEquivalent(ByReferenceFullName))
if (resolvedType.IsByRef())
return true;

foreach (var field in resolvedType.Fields.Where(f => !f.IsStatic))
Expand Down Expand Up @@ -189,7 +188,7 @@ public static bool IsOrContainsNonEmptyStruct(this ITypeReference type)
if (typeToCheck.TypeCode != PrimitiveTypeCode.NotPrimitive && typeToCheck.TypeCode != PrimitiveTypeCode.Invalid)
return true;

if (resolvedType is Dummy || resolvedType.IsReferenceType || resolvedType.AreGenericTypeEquivalent(ByReferenceFullName))
if (resolvedType is Dummy || resolvedType.IsReferenceType || resolvedType.IsByRef())
{
if (node == 0)
{
Expand Down Expand Up @@ -394,6 +393,13 @@ public static bool IsUnsafeType(this ITypeReference type)
return type.TypeCode == PrimitiveTypeCode.Pointer;
}

public static bool IsByRef(this ITypeReference type)
{
// ByReference<T> is a special type understood by runtime to hold a ref T.
// ByReference<T> was removed in .NET 7 since support for ref T in C# 11 was introduced.
return type.TypeCode == PrimitiveTypeCode.Reference || type.AreGenericTypeEquivalent(ByReferenceFullName);
}

public static bool IsMethodUnsafe(this IMethodDefinition method)
{
foreach (var p in method.Parameters)
Expand Down
3 changes: 2 additions & 1 deletion src/Microsoft.Cci.Extensions/Extensions/TypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,8 @@ public static ITypeReference UnWrap(this ITypeReference reference)
|| reference is INamespaceTypeReference
|| reference is IGenericTypeParameterReference
|| reference is IGenericMethodParameterReference
|| reference is IFunctionPointerTypeReference,
|| reference is IFunctionPointerTypeReference
|| reference is IManagedPointerType,
string.Format("Unexpected type reference that we may need to unwrap {0}", (reference != null ? reference.GetType().FullName : "null")));

return reference;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ private void WritePropertyDefinition(IPropertyDefinition property)
WriteKeyword("readonly");
}

WriteTypeName(property.Type, attributes: property.Attributes);
WriteTypeName(property.Type, property.Attributes);

if (property.IsExplicitInterfaceProperty() && _forCompilationIncludeGlobalprefix)
Write("global::");
Expand Down
39 changes: 30 additions & 9 deletions src/Microsoft.Cci.Extensions/Writers/CSharp/CSDeclarationWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using Microsoft.Cci.Filters;
using Microsoft.Cci.Writers.Syntax;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
Expand Down Expand Up @@ -282,13 +281,20 @@ private bool IsDynamicType(object dynamicAttributeArgument, int arrayIndex)
}

return (bool)dynamicAttributeArgument;
}

private ref struct TypeNameRecursiveState
{
public object DynamicAttributeArgument;
public object NullableAttributeArgument;
public IEnumerable<ICustomAttribute> Attributes;
}

private int WriteTypeNameRecursive(ITypeReference type, NameFormattingOptions namingOptions,
string[] valueTupleNames, ref int valueTupleNameIndex, ref int nullableIndex, object nullableAttributeArgument, object dynamicAttributeArgument,
string[] valueTupleNames, ref int valueTupleNameIndex, ref int nullableIndex, ref TypeNameRecursiveState state,
int typeDepth = 0, int genericParameterIndex = 0, bool isValueTupleParameter = false)
{
object dynamicAttributeArgument = state.DynamicAttributeArgument;
void WriteTypeNameInner(ITypeReference typeReference)
{
if (IsDynamicType(dynamicAttributeArgument, typeDepth))
Expand Down Expand Up @@ -386,7 +392,7 @@ void WriteTypeNameInner(ITypeReference typeReference)

string valueTupleName = isValueTuple ? valueTupleNames?[valueTupleLocalIndex + i] : null;
int destinationTypeDepth = typeDepth + i + genericArgumentsInChildTypes + 1;
genericArgumentsInChildTypes += WriteTypeNameRecursive(parameter, namingOptions, valueTupleNames, ref valueTupleNameIndex, ref nullableIndex, nullableAttributeArgument, dynamicAttributeArgument, destinationTypeDepth, i, isValueTuple);
genericArgumentsInChildTypes += WriteTypeNameRecursive(parameter, namingOptions, valueTupleNames, ref valueTupleNameIndex, ref nullableIndex, ref state, destinationTypeDepth, i, isValueTuple);

if (valueTupleName != null)
{
Expand Down Expand Up @@ -426,7 +432,7 @@ void WriteTypeNameInner(ITypeReference typeReference)
nullableIndex++;

WriteTypeNameRecursive(arrayType.ElementType, namingOptions, valueTupleNames, ref valueTupleNameIndex, ref nullableIndex,
nullableAttributeArgument, dynamicAttributeArgument, typeDepth + 1);
ref state, typeDepth + 1);
WriteSymbol("[");

uint arrayDimension = arrayType.Rank - 1;
Expand All @@ -437,6 +443,15 @@ void WriteTypeNameInner(ITypeReference typeReference)

WriteSymbol("]");
}
else if (type.IsByRef())
{
WriteSymbol("ref", addSpace: true);

if (state.Attributes.HasIsReadOnlyAttribute())
WriteSymbol("readonly", addSpace: true);

WriteTypeNameInner(((IManagedPointerType)type).TargetType);
}
else
{
WriteTypeNameInner(type);
Expand All @@ -448,7 +463,7 @@ void WriteTypeNameInner(ITypeReference typeReference)
}
else if (!type.IsValueType)
{
WriteNullableSymbolForReferenceType(nullableAttributeArgument, nullableLocalIndex);
WriteNullableSymbolForReferenceType(state.NullableAttributeArgument, nullableLocalIndex);
}

return genericArgumentsCount;
Expand All @@ -466,13 +481,13 @@ private void WriteTypeName(ITypeReference type, IEnumerable<ICustomAttribute> at

object dynamicAttributeArgument = dynamicAttribute.GetAttributeArgumentValue<bool>(defaultValue: hasDynamicAttribute);

WriteTypeName(type, noSpace, useTypeKeywords, omitGenericTypeList, nullableAttributeArgument, dynamicAttributeArgument, attributes?.GetValueTupleNames());
WriteTypeName(type, noSpace, useTypeKeywords, omitGenericTypeList, nullableAttributeArgument, dynamicAttributeArgument, attributes);
}

private void WriteTypeName(ITypeReference type, bool noSpace = false, bool useTypeKeywords = true,
bool omitGenericTypeList = false, object nullableAttributeArgument = null, object dynamicAttributeArgument = null, string[] valueTupleNames = null)
bool omitGenericTypeList = false, object nullableAttributeArgument = null, object dynamicAttributeArgument = null, IEnumerable<ICustomAttribute> attributes = null)
{
NameFormattingOptions namingOptions = NameFormattingOptions.TypeParameters | NameFormattingOptions.ContractNullable | NameFormattingOptions.OmitTypeArguments; ;
NameFormattingOptions namingOptions = NameFormattingOptions.TypeParameters | NameFormattingOptions.ContractNullable | NameFormattingOptions.OmitTypeArguments;

if (useTypeKeywords)
namingOptions |= NameFormattingOptions.UseTypeKeywords;
Expand All @@ -488,7 +503,13 @@ private void WriteTypeName(ITypeReference type, bool noSpace = false, bool useTy

int valueTupleNameIndex = 0;
int nullableIndex = 0;
WriteTypeNameRecursive(type, namingOptions, valueTupleNames, ref valueTupleNameIndex, ref nullableIndex, nullableAttributeArgument, dynamicAttributeArgument);
var state = new TypeNameRecursiveState()
{
DynamicAttributeArgument = dynamicAttributeArgument,
NullableAttributeArgument = nullableAttributeArgument,
Attributes = attributes,
};
WriteTypeNameRecursive(type, namingOptions, attributes?.GetValueTupleNames(), ref valueTupleNameIndex, ref nullableIndex, ref state);

if (!noSpace) WriteSpace();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ public override void Visit(ITypeDefinition parentType, IEnumerable<IFieldDefinit
// For compile-time compat, the following rules should work for producing a reference assembly. We drop all private fields,
// but add back certain synthesized private fields for a value type (struct) as follows:

// 1. If there is a reference type field in the struct or within the fields' type closure,
// 1. If there is a ref field or reference type field in the struct or within the fields' type closure,
// it should emit a reference type and a value type dummy field.
// - The reference type dummy field is needed in order to inform the compiler to block
// taking pointers to this struct because the GC will not track updating those references.
Expand Down