diff --git a/src/Common/src/Internal/Runtime/EEType.Constants.cs b/src/Common/src/Internal/Runtime/EEType.Constants.cs
index 7aef1aebf08..fcd23ca0906 100644
--- a/src/Common/src/Internal/Runtime/EEType.Constants.cs
+++ b/src/Common/src/Internal/Runtime/EEType.Constants.cs
@@ -189,6 +189,11 @@ internal enum EETypeRareFlags : int
/// This EEType is an abstract class (but not an interface).
///
IsAbstractClassFlag = 0x00004000,
+
+ ///
+ /// This EEType is for a Byref-like class (TypedReference, Span<T>,...)
+ ///
+ IsByRefLikeFlag = 0x00008000,
}
internal enum EETypeField
diff --git a/src/Common/src/Internal/Runtime/EEType.cs b/src/Common/src/Internal/Runtime/EEType.cs
index e2b760bfd97..94f21f943fe 100644
--- a/src/Common/src/Internal/Runtime/EEType.cs
+++ b/src/Common/src/Internal/Runtime/EEType.cs
@@ -507,6 +507,14 @@ internal bool IsAbstract
}
}
+ internal bool IsByRefLike
+ {
+ get
+ {
+ return (RareFlags & EETypeRareFlags.IsByRefLikeFlag) != 0;
+ }
+ }
+
internal bool IsDynamicType
{
get
diff --git a/src/Common/src/TypeSystem/Common/DefType.FieldLayout.cs b/src/Common/src/TypeSystem/Common/DefType.FieldLayout.cs
index d5f530471c5..5ef722b6d06 100644
--- a/src/Common/src/TypeSystem/Common/DefType.FieldLayout.cs
+++ b/src/Common/src/TypeSystem/Common/DefType.FieldLayout.cs
@@ -49,6 +49,16 @@ private class FieldLayoutFlags
/// True if information about the shape of value type has been computed.
///
public const int ComputedValueTypeShapeCharacteristics = 0x40;
+
+ ///
+ /// True if has been computed.
+ ///
+ public const int ComputedIsByRefLike = 0x80;
+
+ ///
+ /// True if this is a byref-like type.
+ ///
+ public const int IsByRefLike = 0x100;
}
private class StaticBlockInfo
@@ -275,6 +285,22 @@ public DefType HfaElementType
}
}
+ ///
+ /// Gets a value indicating whether this is a byref-like type
+ /// (a TypedReference
, Span<T>
, etc.).
+ ///
+ public bool IsByRefLike
+ {
+ get
+ {
+ if (!_fieldLayoutFlags.HasFlags(FieldLayoutFlags.ComputedIsByRefLike))
+ {
+ ComputeIsByRefLike();
+ }
+ return _fieldLayoutFlags.HasFlags(FieldLayoutFlags.IsByRefLike);
+ }
+ }
+
private void ComputeValueTypeShapeCharacteristics()
{
_valueTypeShapeCharacteristics = this.Context.GetLayoutAlgorithmForType(this).ComputeValueTypeShapeCharacteristics(this);
@@ -360,6 +386,21 @@ public void ComputeTypeContainsGCPointers()
_fieldLayoutFlags.AddFlags(flagsToAdd);
}
+
+ public void ComputeIsByRefLike()
+ {
+ if (_fieldLayoutFlags.HasFlags(FieldLayoutFlags.ComputedIsByRefLike))
+ return;
+
+ int flagsToAdd = FieldLayoutFlags.ComputedIsByRefLike;
+
+ if (this.Context.GetLayoutAlgorithmForType(this).ComputeIsByRefLike(this))
+ {
+ flagsToAdd |= FieldLayoutFlags.IsByRefLike;
+ }
+
+ _fieldLayoutFlags.AddFlags(flagsToAdd);
+ }
}
}
diff --git a/src/Common/src/TypeSystem/Common/FieldLayoutAlgorithm.cs b/src/Common/src/TypeSystem/Common/FieldLayoutAlgorithm.cs
index 6cd253aa8a9..80babbd66c2 100644
--- a/src/Common/src/TypeSystem/Common/FieldLayoutAlgorithm.cs
+++ b/src/Common/src/TypeSystem/Common/FieldLayoutAlgorithm.cs
@@ -43,6 +43,11 @@ public abstract class FieldLayoutAlgorithm
/// the element type of the homogenous float aggregate. This will either be System.Double or System.Float.
///
public abstract DefType ComputeHomogeneousFloatAggregateElementType(DefType type);
+
+ ///
+ /// Compute whether '' is a ByRef-like value type (TypedReference, Span<T>, etc.).
+ ///
+ public abstract bool ComputeIsByRefLike(DefType type);
}
///
diff --git a/src/Common/src/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs b/src/Common/src/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs
index 490134275fa..b9ca36f40f8 100644
--- a/src/Common/src/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs
+++ b/src/Common/src/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs
@@ -27,8 +27,25 @@ public override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType defTyp
// Count the number of instance fields in advance for convenience
int numInstanceFields = 0;
foreach (var field in type.GetFields())
- if (!field.IsStatic)
- numInstanceFields++;
+ {
+ if (field.IsStatic)
+ continue;
+
+ TypeDesc fieldType = field.FieldType;
+
+ // ByRef instance fields are not allowed.
+ if (fieldType.IsByRef)
+ throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
+
+ // ByRef-like instance fields on reference types are not allowed.
+ if (fieldType.IsValueType && !type.IsValueType)
+ {
+ if (((DefType)fieldType).IsByRefLike)
+ throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
+ }
+
+ numInstanceFields++;
+ }
if (type.IsModuleType)
{
@@ -123,11 +140,6 @@ out instanceByteSizeAndAlignment
return result;
}
- // Verify that no ByRef types present in this type's fields
- foreach (var field in type.GetFields())
- if (field.FieldType.IsByRef)
- throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
-
// If the type has layout, read its packing and size info
// If the type has explicit layout, also read the field offset info
if (type.IsExplicitLayout || type.IsSequentialLayout)
@@ -198,12 +210,18 @@ public unsafe override ComputedStaticFieldLayout ComputeStaticFieldLayout(DefTyp
if (!field.IsStatic || field.HasRva || field.IsLiteral)
continue;
+ TypeDesc fieldType = field.FieldType;
+ if (fieldType.IsByRef || (fieldType.IsValueType && ((DefType)fieldType).IsByRefLike))
+ {
+ throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
+ }
+
StaticsBlock* block =
field.IsThreadStatic ? &result.ThreadStatics :
field.HasGCStaticBase ? &result.GcStatics :
&result.NonGcStatics;
- SizeAndAlignment sizeAndAlignment = ComputeFieldSizeAndAlignment(field.FieldType, type.Context.Target.DefaultPackingSize);
+ SizeAndAlignment sizeAndAlignment = ComputeFieldSizeAndAlignment(fieldType, type.Context.Target.DefaultPackingSize);
block->Size = LayoutInt.AlignUp(block->Size, sizeAndAlignment.Alignment);
result.Offsets[index] = new FieldAndOffset(field, block->Size);
@@ -240,7 +258,7 @@ public override bool ComputeContainsGCPointers(DefType type)
break;
}
}
- else if (fieldType.IsGCPointer || fieldType.IsByRef)
+ else if (fieldType.IsGCPointer)
{
someFieldContainsPointers = true;
break;
@@ -403,7 +421,7 @@ private static SizeAndAlignment ComputeFieldSizeAndAlignment(TypeDesc fieldType,
result.Alignment = fieldType.Context.Target.LayoutPointerSize;
}
}
- else if (fieldType.IsByRef || fieldType.IsArray)
+ else if (fieldType.IsArray)
{
// This could use InstanceFieldSize/Alignment (and those results should match what's here)
// but, its more efficient to just assume pointer size instead of fulling processing
@@ -601,6 +619,34 @@ public override DefType ComputeHomogeneousFloatAggregateElementType(DefType type
}
}
+ public override bool ComputeIsByRefLike(DefType type)
+ {
+ // Reference types can never be ByRef-like.
+ if (!type.IsValueType)
+ return false;
+
+ if (type.IsByReferenceOfT)
+ return true;
+
+ foreach (FieldDesc field in type.GetFields())
+ {
+ if (field.IsStatic)
+ continue;
+
+ TypeDesc fieldType = field.FieldType;
+ if (fieldType.IsValueType && !fieldType.IsPrimitive)
+ {
+ DefType fieldDefType = (DefType)fieldType;
+ if (fieldDefType.IsByRefLike)
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
private struct SizeAndAlignment
{
public LayoutInt Size;
diff --git a/src/Common/src/TypeSystem/Common/MetadataTypeSystemContext.cs b/src/Common/src/TypeSystem/Common/MetadataTypeSystemContext.cs
index ed60c69f8fb..3c5278b0822 100644
--- a/src/Common/src/TypeSystem/Common/MetadataTypeSystemContext.cs
+++ b/src/Common/src/TypeSystem/Common/MetadataTypeSystemContext.cs
@@ -40,6 +40,9 @@ public abstract partial class MetadataTypeSystemContext : TypeSystemContext
"RuntimeFieldHandle",
"Exception",
+
+ "TypedReference",
+ "ByReference`1",
};
private MetadataType[] _wellKnownTypes;
diff --git a/src/Common/src/TypeSystem/Common/TypeDesc.cs b/src/Common/src/TypeSystem/Common/TypeDesc.cs
index 7b57a65b049..ab7333d3e6d 100644
--- a/src/Common/src/TypeSystem/Common/TypeDesc.cs
+++ b/src/Common/src/TypeSystem/Common/TypeDesc.cs
@@ -115,6 +115,8 @@ internal void SetWellKnownType(WellKnownType wellKnownType)
case WellKnownType.RuntimeTypeHandle:
case WellKnownType.RuntimeMethodHandle:
case WellKnownType.RuntimeFieldHandle:
+ case WellKnownType.TypedReference:
+ case WellKnownType.ByReferenceOfT:
flags = TypeFlags.ValueType;
break;
@@ -265,6 +267,18 @@ public bool IsNullable
}
}
+ ///
+ /// Gets a value indicating whether this is a generic definition, or
+ /// an instance of System.ByReference`1.
+ ///
+ public bool IsByReferenceOfT
+ {
+ get
+ {
+ return this.GetTypeDefinition().IsWellKnownType(WellKnownType.ByReferenceOfT);
+ }
+ }
+
///
/// Gets a value indicating whether this is an array type ().
/// Note this will return true for both multidimensional array types and vector types.
diff --git a/src/Common/src/TypeSystem/Common/UniversalCanonLayoutAlgorithm.cs b/src/Common/src/TypeSystem/Common/UniversalCanonLayoutAlgorithm.cs
index aad04e31d1e..0c001845473 100644
--- a/src/Common/src/TypeSystem/Common/UniversalCanonLayoutAlgorithm.cs
+++ b/src/Common/src/TypeSystem/Common/UniversalCanonLayoutAlgorithm.cs
@@ -19,7 +19,12 @@ public class UniversalCanonLayoutAlgorithm : FieldLayoutAlgorithm
public override bool ComputeContainsGCPointers(DefType type)
{
// This should never be called
- throw new ArgumentException();
+ throw new NotSupportedException();
+ }
+
+ public override bool ComputeIsByRefLike(DefType type)
+ {
+ return false;
}
public override DefType ComputeHomogeneousFloatAggregateElementType(DefType type)
diff --git a/src/Common/src/TypeSystem/Common/WellKnownType.cs b/src/Common/src/TypeSystem/Common/WellKnownType.cs
index 767ed125a23..3cd9988b863 100644
--- a/src/Common/src/TypeSystem/Common/WellKnownType.cs
+++ b/src/Common/src/TypeSystem/Common/WellKnownType.cs
@@ -41,5 +41,8 @@ public enum WellKnownType
RuntimeFieldHandle,
Exception,
+
+ TypedReference,
+ ByReferenceOfT,
}
}
diff --git a/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs b/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs
index 3700411febc..ed8b83801c6 100644
--- a/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs
+++ b/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs
@@ -109,7 +109,7 @@ private TypeDesc ParseType(SignatureTypeCode typeCode)
return _module.Context.GetInstantiatedType(metadataTypeDef, new Instantiation(instance));
}
case SignatureTypeCode.TypedReference:
- throw new PlatformNotSupportedException("TypedReference not supported in .NET Core");
+ return GetWellKnownType(WellKnownType.TypedReference);
case SignatureTypeCode.FunctionPointer:
return _module.Context.GetFunctionPointerType(ParseMethodSignature());
default:
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs
index 9e4b49f884e..12a81660890 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs
@@ -272,6 +272,10 @@ public static bool CreationAllowed(TypeDesc type)
if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any))
return false;
+ // Byref-like types have interior pointers and cannot be heap allocated.
+ if (type.IsValueType && ((DefType)type).IsByRefLike)
+ return false;
+
break;
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs
index 4f42bc6b812..6a7d3060896 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs
@@ -522,6 +522,8 @@ void ComputeRareFlags(NodeFactory factory)
{
uint flags = 0;
+ MetadataType metadataType = _type as MetadataType;
+
if (_type.IsNullable)
{
flags |= (uint)EETypeRareFlags.IsNullableFlag;
@@ -537,7 +539,7 @@ void ComputeRareFlags(NodeFactory factory)
flags |= (uint)EETypeRareFlags.RequiresAlign8Flag;
}
- if (_type.IsDefType && ((DefType)_type).IsHfa)
+ if (metadataType != null && metadataType.IsHfa)
{
flags |= (uint)EETypeRareFlags.IsHFAFlag;
}
@@ -551,11 +553,16 @@ void ComputeRareFlags(NodeFactory factory)
}
}
- if ((_type is MetadataType) && !_type.IsInterface && ((MetadataType)_type).IsAbstract)
+ if (metadataType != null && !_type.IsInterface && metadataType.IsAbstract)
{
flags |= (uint)EETypeRareFlags.IsAbstractClassFlag;
}
+ if (metadataType != null && metadataType.IsByRefLike)
+ {
+ flags |= (uint)EETypeRareFlags.IsByRefLikeFlag;
+ }
+
if (flags != 0)
{
_optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.RareFlags, flags);
@@ -699,7 +706,11 @@ public static void CheckCanGenerateEEType(NodeFactory factory, TypeDesc type)
foreach (TypeDesc typeArg in defType.Instantiation)
{
// ByRefs, pointers, function pointers, and System.Void are never valid instantiation arguments
- if (typeArg.IsByRef || typeArg.IsPointer || typeArg.IsFunctionPointer || typeArg.IsVoid)
+ if (typeArg.IsByRef
+ || typeArg.IsPointer
+ || typeArg.IsFunctionPointer
+ || typeArg.IsVoid
+ || (typeArg.IsValueType && ((DefType)typeArg).IsByRefLike))
{
throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
}
@@ -739,6 +750,12 @@ public static void CheckCanGenerateEEType(NodeFactory factory, TypeDesc type)
{
throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadRankTooLarge, type);
}
+
+ if ((parameterType.IsDefType) && ((DefType)parameterType).IsByRefLike)
+ {
+ // Arrays of byref-like types are not allowed
+ throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
+ }
}
// Validate we're not constructing a type over a ByRef
diff --git a/src/ILCompiler.Compiler/src/Compiler/JitHelper.cs b/src/ILCompiler.Compiler/src/Compiler/JitHelper.cs
index d0551428d2c..59b675487fb 100644
--- a/src/ILCompiler.Compiler/src/Compiler/JitHelper.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/JitHelper.cs
@@ -178,6 +178,13 @@ public static void GetEntryPoint(TypeSystemContext context, ReadyToRunHelper id,
methodDesc = context.SystemModule.GetKnownType("System.Runtime", "TypeLoaderExports").GetKnownMethod("GVMLookupForSlot", null);
break;
+ case ReadyToRunHelper.TypeHandleToRuntimeType:
+ methodDesc = context.GetHelperEntryPoint("TypedReferenceHelpers", "TypeHandleToRuntimeTypeMaybeNull");
+ break;
+ case ReadyToRunHelper.GetRefAny:
+ methodDesc = context.GetHelperEntryPoint("TypedReferenceHelpers", "GetRefAny");
+ break;
+
default:
throw new NotImplementedException(id.ToString());
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs
index b8795fd136a..75c6c3d82a1 100644
--- a/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs
@@ -192,7 +192,10 @@ public bool IsReflectionInvokable (MethodDesc method)
{
var signature = method.Signature;
+ // ----------------------------------------------------------------
// TODO: support for methods returning pointer types - https://github.com/dotnet/corert/issues/2113
+ // ----------------------------------------------------------------
+
if (signature.ReturnType.IsPointer)
return false;
@@ -200,7 +203,10 @@ public bool IsReflectionInvokable (MethodDesc method)
if (signature[i].IsByRef && ((ByRefType)signature[i]).ParameterType.IsPointer)
return false;
+ // ----------------------------------------------------------------
// TODO: function pointer types are odd: https://github.com/dotnet/corert/issues/1929
+ // ----------------------------------------------------------------
+
if (signature.ReturnType.IsFunctionPointer)
return false;
@@ -208,11 +214,31 @@ public bool IsReflectionInvokable (MethodDesc method)
if (signature[i].IsFunctionPointer)
return false;
+ // ----------------------------------------------------------------
// Methods with ByRef returns can't be reflection invoked
+ // ----------------------------------------------------------------
+
if (signature.ReturnType.IsByRef)
return false;
+ // ----------------------------------------------------------------
+ // Methods that return ByRef-like types or take them by reference can't be reflection invoked
+ // ----------------------------------------------------------------
+
+ if (signature.ReturnType.IsDefType && ((DefType)signature.ReturnType).IsByRefLike)
+ return false;
+
+ for (int i = 0; i < signature.Length; i++)
+ {
+ ByRefType paramType = signature[i] as ByRefType;
+ if (paramType != null && paramType.ParameterType.IsDefType && ((DefType)paramType.ParameterType).IsByRefLike)
+ return false;
+ }
+
+ // ----------------------------------------------------------------
// Delegate construction is only allowed through specific IL sequences
+ // ----------------------------------------------------------------
+
if (method.OwningType.IsDelegate && method.IsConstructor)
return false;
diff --git a/src/ILCompiler.Compiler/src/Compiler/ReadyToRun.cs b/src/ILCompiler.Compiler/src/Compiler/ReadyToRun.cs
index 17ecac5a35d..462255ace1f 100644
--- a/src/ILCompiler.Compiler/src/Compiler/ReadyToRun.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/ReadyToRun.cs
@@ -115,5 +115,9 @@ public enum ReadyToRunHelper
// GVM lookup helper
GVMLookupForSlot = 0x100,
+
+ // TypedReference
+ TypeHandleToRuntimeType = 0x110,
+ GetRefAny = 0x111,
}
}
diff --git a/src/ILCompiler.MetadataTransform/tests/PrimaryMetadataAssembly/Platform.cs b/src/ILCompiler.MetadataTransform/tests/PrimaryMetadataAssembly/Platform.cs
index b6bea6761e6..c89e50ead6a 100644
--- a/src/ILCompiler.MetadataTransform/tests/PrimaryMetadataAssembly/Platform.cs
+++ b/src/ILCompiler.MetadataTransform/tests/PrimaryMetadataAssembly/Platform.cs
@@ -69,6 +69,10 @@ public class Type
public sealed class ParamArrayAttribute : Attribute
{
}
+
+ public struct TypedReference { }
+
+ public struct ByReference { }
}
namespace System.Collections
diff --git a/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/InstanceFieldLayout.cs b/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/InstanceFieldLayout.cs
index 827f9f47896..912d7e778c1 100644
--- a/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/InstanceFieldLayout.cs
+++ b/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/InstanceFieldLayout.cs
@@ -146,3 +146,31 @@ public class ClassBoolDoubleBool
}
}
+namespace IsByRefLike
+{
+ public struct ByRefLikeStruct
+ {
+ ByReference